Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pyany: add is_instance #1276

Merged
merged 1 commit into from
Nov 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Add FFI definitions for PEP 587 "Python Initialization Configuration". [#1247](https://github.com/PyO3/pyo3/pull/1247)
- Add `PyEval_SetProfile` and `PyEval_SetTrace` to FFI. [#1255](https://github.com/PyO3/pyo3/pull/1255)
- Add context.h functions (`PyContext_New`, etc) to FFI. [#1259](https://github.com/PyO3/pyo3/pull/1259)
- Add `PyAny::is_instance()` method. [#1276](https://github.com/PyO3/pyo3/pull/1276)
- Add support for conversion between `char` and `PyString`. [#1282](https://github.com/PyO3/pyo3/pull/1282)

### Changed
Expand Down
12 changes: 6 additions & 6 deletions guide/src/exception.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ fn my_func(arg: PyObject) -> PyResult<()> {

## Checking exception types

Python has an [`isinstance`](https://docs.python.org/3/library/functions.html#isinstance) method to check an object's type,
in PyO3 there is a [`Python::is_instance`] method which does the same thing.
Python has an [`isinstance`](https://docs.python.org/3/library/functions.html#isinstance) method to check an object's type.
In PyO3 every native type has access to the [`PyAny::is_instance`] method which does the same thing.

```rust
use pyo3::Python;
Expand All @@ -106,13 +106,13 @@ use pyo3::types::{PyBool, PyList};
fn main() {
let gil = Python::acquire_gil();
let py = gil.python();
assert!(py.is_instance::<PyBool, _>(PyBool::new(py, true)).unwrap());
assert!(PyBool::new(py, true).is_instance::<PyBool>().unwrap());
let list = PyList::new(py, &[1, 2, 3, 4]);
assert!(!py.is_instance::<PyBool, _>(list.as_ref()).unwrap());
assert!(py.is_instance::<PyList, _>(list.as_ref()).unwrap());
assert!(!list.is_instance::<PyBool>().unwrap());
assert!(list.is_instance::<PyList>().unwrap());
}
```
[`Python::is_instance`] calls the underlying [`PyType::is_instance`](https://docs.rs/pyo3/latest/pyo3/types/struct.PyType.html#method.is_instance)
[`PyAny::is_instance`] calls the underlying [`PyType::is_instance`](https://docs.rs/pyo3/latest/pyo3/types/struct.PyType.html#method.is_instance)
method to do the actual work.

To check the type of an exception, you can similarly do:
Expand Down
29 changes: 24 additions & 5 deletions src/types/any.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::conversion::{
};
use crate::err::{PyDowncastError, PyErr, PyResult};
use crate::exceptions::PyTypeError;
use crate::type_object::PyTypeObject;
use crate::types::{PyDict, PyIterator, PyList, PyString, PyTuple, PyType};
use crate::{err, ffi, Py, PyNativeType, PyObject};
use libc::c_int;
Expand All @@ -29,8 +30,8 @@ use std::cmp::Ordering;
/// use pyo3::types::{PyAny, PyDict, PyList};
/// let gil = Python::acquire_gil();
/// let dict = PyDict::new(gil.python());
/// assert!(gil.python().is_instance::<PyAny, _>(dict).unwrap());
/// let any = dict.as_ref();
/// assert!(dict.is_instance::<PyAny>().unwrap());
/// let any: &PyAny = dict.as_ref();
/// assert!(any.downcast::<PyDict>().is_ok());
/// assert!(any.downcast::<PyList>().is_err());
/// ```
Expand Down Expand Up @@ -452,13 +453,19 @@ impl PyAny {
pub fn dir(&self) -> &PyList {
unsafe { self.py().from_owned_ptr(ffi::PyObject_Dir(self.as_ptr())) }
}

/// Checks whether this object is an instance of type `T`.
///
/// This is equivalent to the Python expression `isinstance(self, T)`.
pub fn is_instance<T: PyTypeObject>(&self) -> PyResult<bool> {
T::type_object(self.py()).is_instance(self)
}
}

#[cfg(test)]
mod test {
use crate::types::{IntoPyDict, PyList};
use crate::Python;
use crate::ToPyObject;
use crate::types::{IntoPyDict, PyList, PyLong};
use crate::{Python, ToPyObject};

#[test]
fn test_call_for_non_existing_method() {
Expand Down Expand Up @@ -514,4 +521,16 @@ mod test {
let nan = py.eval("float('nan')", None, None).unwrap();
assert!(nan.compare(nan).is_err());
}

#[test]
fn test_any_isinstance() {
let gil = Python::acquire_gil();
let py = gil.python();

let x = 5.to_object(py).into_ref(py);
assert!(x.is_instance::<PyLong>().unwrap());

let l = vec![x, x].to_object(py).into_ref(py);
assert!(l.is_instance::<PyList>().unwrap());
}
}