From fc34e4116390143fe364d79fa8cf3a14f76add96 Mon Sep 17 00:00:00 2001 From: David Hewitt <1939362+davidhewitt@users.noreply.github.com> Date: Sun, 15 Nov 2020 14:30:21 +0000 Subject: [PATCH] pyany: add is_instance --- CHANGELOG.md | 1 + guide/src/exception.md | 12 ++++++------ src/types/any.rs | 29 ++++++++++++++++++++++++----- 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2776b644..12cd6f72 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. - 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 diff --git a/guide/src/exception.md b/guide/src/exception.md index 62ab6422..20c21da7 100644 --- a/guide/src/exception.md +++ b/guide/src/exception.md @@ -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; @@ -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::new(py, true)).unwrap()); + assert!(PyBool::new(py, true).is_instance::().unwrap()); let list = PyList::new(py, &[1, 2, 3, 4]); - assert!(!py.is_instance::(list.as_ref()).unwrap()); - assert!(py.is_instance::(list.as_ref()).unwrap()); + assert!(!list.is_instance::().unwrap()); + assert!(list.is_instance::().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: diff --git a/src/types/any.rs b/src/types/any.rs index faf0b119..1a79aa58 100644 --- a/src/types/any.rs +++ b/src/types/any.rs @@ -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; @@ -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::(dict).unwrap()); -/// let any = dict.as_ref(); +/// assert!(dict.is_instance::().unwrap()); +/// let any: &PyAny = dict.as_ref(); /// assert!(any.downcast::().is_ok()); /// assert!(any.downcast::().is_err()); /// ``` @@ -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(&self) -> PyResult { + 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() { @@ -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::().unwrap()); + + let l = vec![x, x].to_object(py).into_ref(py); + assert!(l.is_instance::().unwrap()); + } }