diff --git a/CHANGELOG.md b/CHANGELOG.md index bcac80ed73c..689b1c84b5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Added * `_PyDict_NewPresized`. [#849](https://github.com/PyO3/pyo3/pull/849) * `IntoPy` for `HashSet` and `BTreeSet`. [#864](https://github.com/PyO3/pyo3/pull/864) +* `ObjectProtocol::dir`. [#886](https://github.com/PyO3/pyo3/pull/886) ### Fixed * `__radd__` and other `__r*__` methods now correctly work with operators. [#839](https://github.com/PyO3/pyo3/pull/839) diff --git a/src/objectprotocol.rs b/src/objectprotocol.rs index 04a70d4bada..2385f91b68d 100644 --- a/src/objectprotocol.rs +++ b/src/objectprotocol.rs @@ -3,7 +3,7 @@ use crate::class::basic::CompareOp; use crate::err::{self, PyDowncastError, PyErr, PyResult}; use crate::exceptions::TypeError; -use crate::types::{PyAny, PyDict, PyIterator, PyString, PyTuple, PyType}; +use crate::types::{PyAny, PyDict, PyIterator, PyList, PyString, PyTuple, PyType}; use crate::{ ffi, AsPyPointer, FromPyObject, IntoPy, IntoPyPointer, Py, PyNativeType, PyObject, PyTryFrom, Python, ToBorrowedObject, ToPyObject, @@ -212,6 +212,9 @@ pub trait ObjectProtocol { /// Returns the reference count for the Python object. fn get_refcnt(&self) -> isize; + /// Returns the list of attributes of this object. + fn dir(&self) -> &PyList; + /// Gets the Python builtin value `None`. #[allow(non_snake_case)] // the Python keyword starts with uppercase fn None(&self) -> PyObject; @@ -485,6 +488,10 @@ where unsafe { ffi::Py_REFCNT(self.as_ptr()) } } + fn dir(&self) -> &PyList { + unsafe { self.py().from_owned_ptr(ffi::PyObject_Dir(self.as_ptr())) } + } + #[allow(non_snake_case)] // the Python keyword starts with uppercase fn None(&self) -> PyObject { unsafe { PyObject::from_borrowed_ptr(self.py(), ffi::Py_None()) } @@ -545,4 +552,22 @@ mod test { let obj = py.eval("42", None, None).unwrap(); assert_eq!(unsafe { obj.get_type().as_type_ptr() }, obj.get_type_ptr()) } + + #[test] + fn test_dir() { + let gil = Python::acquire_gil(); + let py = gil.python(); + let obj = py.eval("42", None, None).unwrap(); + let dir = py + .eval("dir(42)", None, None) + .unwrap() + .extract::<&PyList>() + .unwrap(); + let a = obj + .dir() + .into_iter() + .map(|x| x.extract::().unwrap()); + let b = dir.into_iter().map(|x| x.extract::().unwrap()); + assert!(a.eq(b)); + } }