From a83c31a3af001690b1cf2dc9bd7acf27cc890102 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Thu, 11 Nov 2021 20:06:42 +0100 Subject: [PATCH 1/2] add PyType::is_subclass_of and PyAny::is_instance_of which get the type to check against as an arguments, as opposed to a compile-time generic type. --- CHANGELOG.md | 10 +++++++++ src/conversions/path.rs | 2 +- src/types/any.rs | 23 +++++++++++++++++++-- src/types/typeobject.rs | 43 ++++++++++++++++++++++++++++++--------- tests/test_inheritance.rs | 18 ++++++++++++++++ 5 files changed, 83 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c23dfbf8d3..686700392c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add implementations for `Py::as_ref()` and `Py::into_ref()` for `Py`, `Py` and `Py`. [#1682](https://github.com/PyO3/pyo3/pull/1682) - Add `PyTraceback` type to represent and format Python tracebacks. [#1977](https://github.com/PyO3/pyo3/pull/1977) +### Added + +- Add `PyType::is_subclass_of` and `PyAny::is_instance_of` which operate not on + a type known at compile-time but a run-time type object. [#1985](https://github.com/PyO3/pyo3/pull/1985) + ### Changed - `#[classattr]` constants with a known magic method name (which is lowercase) no longer trigger lint warnings expecting constants to be uppercase. [#1969](https://github.com/PyO3/pyo3/pull/1969) @@ -42,6 +47,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix mingw platform detection. [#1993](https://github.com/PyO3/pyo3/pull/1993) - Fix panic in `__get__` implementation when accessing descriptor on type object. [#1997](https://github.com/PyO3/pyo3/pull/1997) +### Removed + +- Remove `PyType::is_instance`, which is unintuitive; instead of `typ.is_instance(obj)`, you should + now use `obj.is_instance_of(typ)`. [#1985](https://github.com/PyO3/pyo3/pull/1985) + ## [0.15.0] - 2021-11-03 ### Packaging diff --git a/src/conversions/path.rs b/src/conversions/path.rs index 91a41666113..552dd8c6d52 100644 --- a/src/conversions/path.rs +++ b/src/conversions/path.rs @@ -20,7 +20,7 @@ impl FromPyObject<'_> for PathBuf { let py = ob.py(); let pathlib = py.import("pathlib")?; let pathlib_path: &PyType = pathlib.getattr("Path")?.downcast()?; - if pathlib_path.is_instance(ob)? { + if ob.is_instance_of(pathlib_path)? { let path_str = ob.call_method0("__str__")?; OsString::extract(path_str)? } else { diff --git a/src/types/any.rs b/src/types/any.rs index 8fc8640d4e3..66b9ee768e1 100644 --- a/src/types/any.rs +++ b/src/types/any.rs @@ -667,9 +667,19 @@ impl PyAny { /// Checks whether this object is an instance of type `T`. /// - /// This is equivalent to the Python expression `isinstance(self, T)`. + /// This is equivalent to the Python expression `isinstance(self, T)`, + /// if the type `T` is known at compile time. pub fn is_instance(&self) -> PyResult { - T::type_object(self.py()).is_instance(self) + self.is_instance_of(T::type_object(self.py())) + } + + /// Checks whether this object is an instance of type `typ`. + /// + /// This is equivalent to the Python expression `isinstance(self, typ)`. + pub fn is_instance_of(&self, typ: &PyType) -> PyResult { + let result = unsafe { ffi::PyObject_IsInstance(self.as_ptr(), typ.as_ptr()) }; + err::error_on_minusone(self.py(), result)?; + Ok(result == 1) } /// Returns a GIL marker constrained to the lifetime of this type. @@ -682,6 +692,7 @@ impl PyAny { #[cfg(test)] mod tests { use crate::{ + type_object::PyTypeObject, types::{IntoPyDict, PyList, PyLong, PyModule}, Python, ToPyObject, }; @@ -782,4 +793,12 @@ mod tests { assert!(l.is_instance::().unwrap()); }); } + + #[test] + fn test_any_isinstance_of() { + Python::with_gil(|py| { + let l = vec![1u8, 2].to_object(py).into_ref(py); + assert!(l.is_instance_of(PyList::type_object(py)).unwrap()); + }); + } } diff --git a/src/types/typeobject.rs b/src/types/typeobject.rs index 09a1f8f29ec..f91967b36e2 100644 --- a/src/types/typeobject.rs +++ b/src/types/typeobject.rs @@ -40,25 +40,48 @@ impl PyType { self.getattr("__qualname__")?.extract() } - /// Checks whether `self` is subclass of type `T`. + /// Checks whether `self` is a subclass of type `T`. /// - /// Equivalent to Python's `issubclass` function. + /// Equivalent to the Python expression `issubclass(self, T)`, if the type + /// `T` is known at compile time. pub fn is_subclass(&self) -> PyResult where T: PyTypeObject, { - let result = - unsafe { ffi::PyObject_IsSubclass(self.as_ptr(), T::type_object(self.py()).as_ptr()) }; - err::error_on_minusone(self.py(), result)?; - Ok(result == 1) + self.is_subclass_of(T::type_object(self.py())) } - /// Check whether `obj` is an instance of `self`. + /// Checks whether `self` is a subclass of `other`. /// - /// Equivalent to Python's `isinstance` function. - pub fn is_instance(&self, obj: &T) -> PyResult { - let result = unsafe { ffi::PyObject_IsInstance(obj.as_ptr(), self.as_ptr()) }; + /// Equivalent to the Python expression `issubclass(self, other)`. + pub fn is_subclass_of(&self, other: &PyType) -> PyResult { + let result = unsafe { ffi::PyObject_IsSubclass(self.as_ptr(), other.as_ptr()) }; err::error_on_minusone(self.py(), result)?; Ok(result == 1) } } + +#[cfg(test)] +mod tests { + use crate::{ + type_object::PyTypeObject, + types::{PyBool, PyLong}, + Python, + }; + + #[test] + fn test_type_is_subclass() { + Python::with_gil(|py| { + assert!(PyBool::type_object(py).is_subclass::().unwrap()); + }); + } + + #[test] + fn test_type_is_subclass_of() { + Python::with_gil(|py| { + let bool_type = PyBool::type_object(py); + let long_type = PyLong::type_object(py); + assert!(bool_type.is_subclass_of(long_type).unwrap()); + }); + } +} diff --git a/tests/test_inheritance.rs b/tests/test_inheritance.rs index 01b124b8be9..8dcdd9b6ac6 100644 --- a/tests/test_inheritance.rs +++ b/tests/test_inheritance.rs @@ -1,5 +1,6 @@ use pyo3::prelude::*; use pyo3::py_run; +use pyo3::type_object::PyTypeObject; use pyo3::types::IntoPyDict; @@ -102,6 +103,23 @@ fn mutation_fails() { assert_eq!(&e.to_string(), "RuntimeError: Already borrowed") } +#[test] +fn is_subclass_and_is_instance() { + let gil = Python::acquire_gil(); + let py = gil.python(); + + let sub_ty = SubClass::type_object(py); + let base_ty = BaseClass::type_object(py); + assert!(sub_ty.is_subclass::().unwrap()); + assert!(sub_ty.is_subclass_of(base_ty).unwrap()); + + let obj = PyCell::new(py, SubClass::new()).unwrap(); + assert!(obj.is_instance::().unwrap()); + assert!(obj.is_instance::().unwrap()); + assert!(obj.is_instance_of(sub_ty).unwrap()); + assert!(obj.is_instance_of(base_ty).unwrap()); +} + #[pyclass(subclass)] struct BaseClassWithResult { _val: usize, From 43893158b1d61ce2fbc603a96210e2dcc78e198a Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Fri, 12 Nov 2021 19:19:54 +0100 Subject: [PATCH 2/2] switch is_instance/is_instance_of, is_subclass/is_subclass_of --- CHANGELOG.md | 17 ++++++----------- guide/src/conversions/traits.md | 2 +- guide/src/ecosystem/async-await.md | 13 +++++++------ guide/src/exception.md | 26 ++++++++++++-------------- src/conversions/path.rs | 2 +- src/err/mod.rs | 21 ++++++++++++--------- src/exceptions.rs | 8 ++++---- src/types/any.rs | 26 +++++++++++++------------- src/types/bytearray.rs | 4 ++-- src/types/bytes.rs | 2 +- src/types/iterator.rs | 2 +- src/types/mapping.rs | 4 ++-- src/types/module.rs | 2 +- src/types/num.rs | 8 ++++---- src/types/typeobject.rs | 30 +++++++++++++++--------------- tests/test_inheritance.rs | 12 ++++++------ tests/test_proto_methods.rs | 2 +- 17 files changed, 89 insertions(+), 92 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 686700392c5..daa5d2be258 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,8 +19,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add `Py::setattr` method. [#2009](https://github.com/PyO3/pyo3/pull/2009) -## Removed +### Changed + +- `PyType::is_subclass`, `PyErr::is_instance` and `PyAny::is_instance` now operate run-time type object instead of a type known at compile-time. The old behavior is still available as `PyType::is_subclass_of`, `PyErr::is_instance_of` and `PyAny::is_instance_of`. [#1985](https://github.com/PyO3/pyo3/pull/1985) +### Removed + +- Remove `PyType::is_instance`, which is unintuitive; instead of `typ.is_instance(obj)`, use `obj.is_instance(typ)`. [#1985](https://github.com/PyO3/pyo3/pull/1985) - Remove all functionality deprecated in PyO3 0.14. [#2007](https://github.com/PyO3/pyo3/pull/2007) ## [0.15.1] - 2021-11-19 @@ -30,11 +35,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add implementations for `Py::as_ref()` and `Py::into_ref()` for `Py`, `Py` and `Py`. [#1682](https://github.com/PyO3/pyo3/pull/1682) - Add `PyTraceback` type to represent and format Python tracebacks. [#1977](https://github.com/PyO3/pyo3/pull/1977) -### Added - -- Add `PyType::is_subclass_of` and `PyAny::is_instance_of` which operate not on - a type known at compile-time but a run-time type object. [#1985](https://github.com/PyO3/pyo3/pull/1985) - ### Changed - `#[classattr]` constants with a known magic method name (which is lowercase) no longer trigger lint warnings expecting constants to be uppercase. [#1969](https://github.com/PyO3/pyo3/pull/1969) @@ -47,11 +47,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix mingw platform detection. [#1993](https://github.com/PyO3/pyo3/pull/1993) - Fix panic in `__get__` implementation when accessing descriptor on type object. [#1997](https://github.com/PyO3/pyo3/pull/1997) -### Removed - -- Remove `PyType::is_instance`, which is unintuitive; instead of `typ.is_instance(obj)`, you should - now use `obj.is_instance_of(typ)`. [#1985](https://github.com/PyO3/pyo3/pull/1985) - ## [0.15.0] - 2021-11-03 ### Packaging diff --git a/guide/src/conversions/traits.md b/guide/src/conversions/traits.md index d0089ef135d..619aac80c73 100644 --- a/guide/src/conversions/traits.md +++ b/guide/src/conversions/traits.md @@ -413,7 +413,7 @@ enum RustyEnum { # { # let thing = b"foo".to_object(py); # let error = thing.extract::(py).unwrap_err(); -# assert!(error.is_instance::(py)); +# assert!(error.is_instance_of::(py)); # } # # Ok(()) diff --git a/guide/src/ecosystem/async-await.md b/guide/src/ecosystem/async-await.md index 7c92ea4641a..37e1333f9bc 100644 --- a/guide/src/ecosystem/async-await.md +++ b/guide/src/ecosystem/async-await.md @@ -536,12 +536,13 @@ fn main() -> PyResult<()> { pyo3_asyncio::async_std::run(py, async move { // verify that we are on a uvloop.Loop Python::with_gil(|py| -> PyResult<()> { - assert!(uvloop - .as_ref(py) - .getattr("Loop")? - .downcast::() - .unwrap() - .is_instance(pyo3_asyncio::async_std::get_current_loop(py)?)?); + assert!(pyo3_asyncio::async_std::get_current_loop(py)?.is_instance( + uvloop + .as_ref(py) + .getattr("Loop")? + .downcast::() + .unwrap() + )?); Ok(()) })?; diff --git a/guide/src/exception.md b/guide/src/exception.md index 63ee6dda2f1..8806c164a62 100644 --- a/guide/src/exception.md +++ b/guide/src/exception.md @@ -100,21 +100,19 @@ PyErr::from_instance(py, err).restore(py); ## 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 every native type has access to the [`PyAny::is_instance`] method which does the same thing. +In PyO3 every object has the [`PyAny::is_instance`] and [`PyAny::is_instance_of`] methods which do the same thing. ```rust use pyo3::Python; use pyo3::types::{PyBool, PyList}; Python::with_gil(|py| { - assert!(PyBool::new(py, true).is_instance::().unwrap()); + assert!(PyBool::new(py, true).is_instance_of::().unwrap()); let list = PyList::new(py, &[1, 2, 3, 4]); - assert!(!list.is_instance::().unwrap()); - assert!(list.is_instance::().unwrap()); + assert!(!list.is_instance_of::().unwrap()); + assert!(list.is_instance_of::().unwrap()); }); ``` -[`PyAny::is_instance`] calls the underlying [`PyType::is_instance`]({{#PYO3_DOCS_URL}}/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: @@ -123,7 +121,7 @@ To check the type of an exception, you can similarly do: # use pyo3::prelude::*; # Python::with_gil(|py| { # let err = PyTypeError::new_err(()); -err.is_instance::(py); +err.is_instance_of::(py); # }); ``` @@ -184,7 +182,7 @@ fn main() { Python::with_gil(|py| { let fun = pyo3::wrap_pyfunction!(connect, py).unwrap(); let err = fun.call1(("0.0.0.0",)).unwrap_err(); - assert!(err.is_instance::(py)); + assert!(err.is_instance_of::(py)); }); } ``` @@ -201,21 +199,21 @@ fn parse_int(s: String) -> PyResult { } # # use pyo3::exceptions::PyValueError; -# +# # fn main() { # Python::with_gil(|py| { # assert_eq!(parse_int(String::from("1")).unwrap(), 1); # assert_eq!(parse_int(String::from("1337")).unwrap(), 1337); -# +# # assert!(parse_int(String::from("-1")) # .unwrap_err() -# .is_instance::(py)); +# .is_instance_of::(py)); # assert!(parse_int(String::from("foo")) # .unwrap_err() -# .is_instance::(py)); +# .is_instance_of::(py)); # assert!(parse_int(String::from("13.37")) # .unwrap_err() -# .is_instance::(py)); +# .is_instance_of::(py)); # }) # } ``` @@ -257,5 +255,5 @@ defines exceptions for several standard library modules. [`PyErr`]: {{#PYO3_DOCS_URL}}/pyo3/struct.PyErr.html [`PyResult`]: {{#PYO3_DOCS_URL}}/pyo3/type.PyResult.html [`PyErr::from_instance`]: {{#PYO3_DOCS_URL}}/pyo3/struct.PyErr.html#method.from_instance -[`Python::is_instance`]: {{#PYO3_DOCS_URL}}/pyo3/struct.Python.html#method.is_instance [`PyAny::is_instance`]: {{#PYO3_DOCS_URL}}/pyo3/struct.PyAny.html#method.is_instance +[`PyAny::is_instance_of`]: {{#PYO3_DOCS_URL}}/pyo3/struct.PyAny.html#method.is_instance_of diff --git a/src/conversions/path.rs b/src/conversions/path.rs index 552dd8c6d52..bd0eca69e9c 100644 --- a/src/conversions/path.rs +++ b/src/conversions/path.rs @@ -20,7 +20,7 @@ impl FromPyObject<'_> for PathBuf { let py = ob.py(); let pathlib = py.import("pathlib")?; let pathlib_path: &PyType = pathlib.getattr("Path")?.downcast()?; - if ob.is_instance_of(pathlib_path)? { + if ob.is_instance(pathlib_path)? { let path_str = ob.call_method0("__str__")?; OsString::extract(path_str)? } else { diff --git a/src/err/mod.rs b/src/err/mod.rs index db23c6782c5..3e2de790f2e 100644 --- a/src/err/mod.rs +++ b/src/err/mod.rs @@ -180,7 +180,7 @@ impl PyErr { /// /// Python::with_gil(|py| { /// let err: PyErr = PyTypeError::new_err(("some type error",)); - /// assert!(err.is_instance::(py)); + /// assert!(err.is_instance_of::(py)); /// assert_eq!(err.pvalue(py).to_string(), "some type error"); /// }); /// ``` @@ -366,13 +366,16 @@ impl PyErr { } /// Returns true if the current exception is instance of `T`. - pub fn is_instance(&self, py: Python) -> bool + pub fn is_instance(&self, py: Python, typ: &PyType) -> bool { + unsafe { ffi::PyErr_GivenExceptionMatches(self.ptype_ptr(py), typ.as_ptr()) != 0 } + } + + /// Returns true if the current exception is instance of `T`. + pub fn is_instance_of(&self, py: Python) -> bool where T: PyTypeObject, { - unsafe { - ffi::PyErr_GivenExceptionMatches(self.ptype_ptr(py), T::type_object(py).as_ptr()) != 0 - } + self.is_instance(py, T::type_object(py)) } /// Retrieves the exception instance for this error. @@ -612,11 +615,11 @@ mod tests { fn set_valueerror() { Python::with_gil(|py| { let err: PyErr = exceptions::PyValueError::new_err("some exception message"); - assert!(err.is_instance::(py)); + assert!(err.is_instance_of::(py)); err.restore(py); assert!(PyErr::occurred(py)); let err = PyErr::fetch(py); - assert!(err.is_instance::(py)); + assert!(err.is_instance_of::(py)); assert_eq!(err.to_string(), "ValueError: some exception message"); }) } @@ -625,10 +628,10 @@ mod tests { fn invalid_error_type() { Python::with_gil(|py| { let err: PyErr = PyErr::new::(()); - assert!(err.is_instance::(py)); + assert!(err.is_instance_of::(py)); err.restore(py); let err = PyErr::fetch(py); - assert!(err.is_instance::(py)); + assert!(err.is_instance_of::(py)); assert_eq!( err.to_string(), "TypeError: exceptions must derive from BaseException" diff --git a/src/exceptions.rs b/src/exceptions.rs index 004e19f4f9f..2020f5eb1d8 100644 --- a/src/exceptions.rs +++ b/src/exceptions.rs @@ -267,7 +267,7 @@ fn always_throws() -> PyResult<()> { # Python::with_gil(|py| { # let fun = pyo3::wrap_pyfunction!(always_throws, py).unwrap(); # let err = fun.call0().expect_err(\"called a function that should always return an error but the return value was Ok\"); -# assert!(err.is_instance::(py)) +# assert!(err.is_instance_of::(py)) # }); ``` @@ -292,7 +292,7 @@ Python::with_gil(|py| { let error_type = match result { Ok(_) => \"Not an error\", - Err(error) if error.is_instance::(py) => \"" , $name, "\", + Err(error) if error.is_instance_of::(py) => \"" , $name, "\", Err(_) => \"Some other error\", }; @@ -611,7 +611,7 @@ macro_rules! test_exception { .unwrap_or($exc_ty::new_err("a test exception")) }; - assert!(err.is_instance::<$exc_ty>(py)); + assert!(err.is_instance_of::<$exc_ty>(py)); let value: &$exc_ty = err.instance(py).downcast().unwrap(); assert!(value.source().is_none()); @@ -619,7 +619,7 @@ macro_rules! test_exception { err.set_cause(py, Some($crate::exceptions::PyValueError::new_err("a cause"))); assert!(value.source().is_some()); - assert!($crate::PyErr::from(value).is_instance::<$exc_ty>(py)); + assert!($crate::PyErr::from(value).is_instance_of::<$exc_ty>(py)); }) } }; diff --git a/src/types/any.rs b/src/types/any.rs index 66b9ee768e1..2d45255b3ce 100644 --- a/src/types/any.rs +++ b/src/types/any.rs @@ -74,7 +74,7 @@ impl PyAny { /// /// Python::with_gil(|py| { /// let dict = PyDict::new(py); - /// assert!(dict.is_instance::().unwrap()); + /// assert!(dict.is_instance_of::().unwrap()); /// let any: &PyAny = dict.as_ref(); /// assert!(any.downcast::().is_ok()); /// assert!(any.downcast::().is_err()); @@ -665,23 +665,23 @@ impl PyAny { 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)`, - /// if the type `T` is known at compile time. - pub fn is_instance(&self) -> PyResult { - self.is_instance_of(T::type_object(self.py())) - } - /// Checks whether this object is an instance of type `typ`. /// /// This is equivalent to the Python expression `isinstance(self, typ)`. - pub fn is_instance_of(&self, typ: &PyType) -> PyResult { + pub fn is_instance(&self, typ: &PyType) -> PyResult { let result = unsafe { ffi::PyObject_IsInstance(self.as_ptr(), typ.as_ptr()) }; err::error_on_minusone(self.py(), result)?; Ok(result == 1) } + /// Checks whether this object is an instance of type `T`. + /// + /// This is equivalent to the Python expression `isinstance(self, T)`, + /// if the type `T` is known at compile time. + pub fn is_instance_of(&self) -> PyResult { + self.is_instance(T::type_object(self.py())) + } + /// Returns a GIL marker constrained to the lifetime of this type. #[inline] pub fn py(&self) -> Python<'_> { @@ -787,10 +787,10 @@ mod tests { fn test_any_isinstance() { Python::with_gil(|py| { let x = 5.to_object(py).into_ref(py); - assert!(x.is_instance::().unwrap()); + assert!(x.is_instance_of::().unwrap()); let l = vec![x, x].to_object(py).into_ref(py); - assert!(l.is_instance::().unwrap()); + assert!(l.is_instance_of::().unwrap()); }); } @@ -798,7 +798,7 @@ mod tests { fn test_any_isinstance_of() { Python::with_gil(|py| { let l = vec![1u8, 2].to_object(py).into_ref(py); - assert!(l.is_instance_of(PyList::type_object(py)).unwrap()); + assert!(l.is_instance(PyList::type_object(py)).unwrap()); }); } } diff --git a/src/types/bytearray.rs b/src/types/bytearray.rs index 29750a5c8bd..a63dd4db65a 100644 --- a/src/types/bytearray.rs +++ b/src/types/bytearray.rs @@ -241,7 +241,7 @@ mod tests { fn test_from_err() { Python::with_gil(|py| { if let Err(err) = PyByteArray::from(py, &py.None()) { - assert!(err.is_instance::(py)); + assert!(err.is_instance_of::(py)); } else { panic!("error"); } @@ -293,7 +293,7 @@ mod tests { assert!(py_bytearray_result .err() .unwrap() - .is_instance::(py)); + .is_instance_of::(py)); }) } } diff --git a/src/types/bytes.rs b/src/types/bytes.rs index af61a6fd03a..e733582b970 100644 --- a/src/types/bytes.rs +++ b/src/types/bytes.rs @@ -174,7 +174,7 @@ mod tests { assert!(py_bytes_result .err() .unwrap() - .is_instance::(py)); + .is_instance_of::(py)); }); } } diff --git a/src/types/iterator.rs b/src/types/iterator.rs index a251873259c..243644cb5bc 100644 --- a/src/types/iterator.rs +++ b/src/types/iterator.rs @@ -206,7 +206,7 @@ mod tests { let x = 5.to_object(py); let err = PyIterator::from_object(py, &x).unwrap_err(); - assert!(err.is_instance::(py)); + assert!(err.is_instance_of::(py)); }); } diff --git a/src/types/mapping.rs b/src/types/mapping.rs index 651bcc560b6..0ff430a004c 100644 --- a/src/types/mapping.rs +++ b/src/types/mapping.rs @@ -180,7 +180,7 @@ mod tests { assert!(mapping .get_item(8i32) .unwrap_err() - .is_instance::(py)); + .is_instance_of::(py)); }); } @@ -216,7 +216,7 @@ mod tests { assert!(mapping .get_item(7i32) .unwrap_err() - .is_instance::(py)); + .is_instance_of::(py)); }); } diff --git a/src/types/module.rs b/src/types/module.rs index 57bf9035958..8330f29d54a 100644 --- a/src/types/module.rs +++ b/src/types/module.rs @@ -146,7 +146,7 @@ impl PyModule { match self.getattr("__all__") { Ok(idx) => idx.downcast().map_err(PyErr::from), Err(err) => { - if err.is_instance::(self.py()) { + if err.is_instance_of::(self.py()) { let l = PyList::empty(self.py()); self.setattr("__all__", l).map_err(PyErr::from)?; Ok(l) diff --git a/src/types/num.rs b/src/types/num.rs index c9e05eb004e..2128b937e14 100644 --- a/src/types/num.rs +++ b/src/types/num.rs @@ -345,7 +345,7 @@ mod test_128bit_intergers { Python::with_gil(|py| { let obj = py.eval("(1 << 130) * -1", None, None).unwrap(); let err = obj.extract::().unwrap_err(); - assert!(err.is_instance::(py)); + assert!(err.is_instance_of::(py)); }) } @@ -354,7 +354,7 @@ mod test_128bit_intergers { Python::with_gil(|py| { let obj = py.eval("1 << 130", None, None).unwrap(); let err = obj.extract::().unwrap_err(); - assert!(err.is_instance::(py)); + assert!(err.is_instance_of::(py)); }) } } @@ -421,7 +421,7 @@ mod tests { let obj = ("123").to_object(py); let err = obj.extract::<$t>(py).unwrap_err(); - assert!(err.is_instance::(py)); + assert!(err.is_instance_of::(py)); }); } @@ -431,7 +431,7 @@ mod tests { let obj = (12.3).to_object(py); let err = obj.extract::<$t>(py).unwrap_err(); - assert!(err.is_instance::(py));}); + assert!(err.is_instance_of::(py));}); } #[test] diff --git a/src/types/typeobject.rs b/src/types/typeobject.rs index f91967b36e2..db8eccf3449 100644 --- a/src/types/typeobject.rs +++ b/src/types/typeobject.rs @@ -40,24 +40,24 @@ impl PyType { self.getattr("__qualname__")?.extract() } + /// Checks whether `self` is a subclass of `other`. + /// + /// Equivalent to the Python expression `issubclass(self, other)`. + pub fn is_subclass(&self, other: &PyType) -> PyResult { + let result = unsafe { ffi::PyObject_IsSubclass(self.as_ptr(), other.as_ptr()) }; + err::error_on_minusone(self.py(), result)?; + Ok(result == 1) + } + /// Checks whether `self` is a subclass of type `T`. /// /// Equivalent to the Python expression `issubclass(self, T)`, if the type /// `T` is known at compile time. - pub fn is_subclass(&self) -> PyResult + pub fn is_subclass_of(&self) -> PyResult where T: PyTypeObject, { - self.is_subclass_of(T::type_object(self.py())) - } - - /// Checks whether `self` is a subclass of `other`. - /// - /// Equivalent to the Python expression `issubclass(self, other)`. - pub fn is_subclass_of(&self, other: &PyType) -> PyResult { - let result = unsafe { ffi::PyObject_IsSubclass(self.as_ptr(), other.as_ptr()) }; - err::error_on_minusone(self.py(), result)?; - Ok(result == 1) + self.is_subclass(T::type_object(self.py())) } } @@ -72,16 +72,16 @@ mod tests { #[test] fn test_type_is_subclass() { Python::with_gil(|py| { - assert!(PyBool::type_object(py).is_subclass::().unwrap()); + let bool_type = PyBool::type_object(py); + let long_type = PyLong::type_object(py); + assert!(bool_type.is_subclass(long_type).unwrap()); }); } #[test] fn test_type_is_subclass_of() { Python::with_gil(|py| { - let bool_type = PyBool::type_object(py); - let long_type = PyLong::type_object(py); - assert!(bool_type.is_subclass_of(long_type).unwrap()); + assert!(PyBool::type_object(py).is_subclass_of::().unwrap()); }); } } diff --git a/tests/test_inheritance.rs b/tests/test_inheritance.rs index 8dcdd9b6ac6..cb49b474552 100644 --- a/tests/test_inheritance.rs +++ b/tests/test_inheritance.rs @@ -110,14 +110,14 @@ fn is_subclass_and_is_instance() { let sub_ty = SubClass::type_object(py); let base_ty = BaseClass::type_object(py); - assert!(sub_ty.is_subclass::().unwrap()); - assert!(sub_ty.is_subclass_of(base_ty).unwrap()); + assert!(sub_ty.is_subclass_of::().unwrap()); + assert!(sub_ty.is_subclass(base_ty).unwrap()); let obj = PyCell::new(py, SubClass::new()).unwrap(); - assert!(obj.is_instance::().unwrap()); - assert!(obj.is_instance::().unwrap()); - assert!(obj.is_instance_of(sub_ty).unwrap()); - assert!(obj.is_instance_of(base_ty).unwrap()); + assert!(obj.is_instance_of::().unwrap()); + assert!(obj.is_instance_of::().unwrap()); + assert!(obj.is_instance(sub_ty).unwrap()); + assert!(obj.is_instance(base_ty).unwrap()); } #[pyclass(subclass)] diff --git a/tests/test_proto_methods.rs b/tests/test_proto_methods.rs index ad1ce58fd48..df9c0d01c50 100644 --- a/tests/test_proto_methods.rs +++ b/tests/test_proto_methods.rs @@ -97,7 +97,7 @@ fn test_getattr() { assert!(example_py .getattr("other_attr") .unwrap_err() - .is_instance::(py)); + .is_instance_of::(py)); }) }