diff --git a/newsfragments/3577.added.md b/newsfragments/3577.added.md new file mode 100644 index 00000000000..632274984ec --- /dev/null +++ b/newsfragments/3577.added.md @@ -0,0 +1 @@ +Implement `PyTypeInfo` for `PyEllipsis`, `PyNone` and `PyNotImplemented`. diff --git a/newsfragments/3577.changed.md b/newsfragments/3577.changed.md new file mode 100644 index 00000000000..a7e6629d6a5 --- /dev/null +++ b/newsfragments/3577.changed.md @@ -0,0 +1 @@ +Deprecate `Py::is_ellipsis` and `PyAny::is_ellipsis` in favour of `any.is(py.Ellipsis())`. diff --git a/src/instance.rs b/src/instance.rs index 3df28261c4b..07a6f872bf4 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -776,6 +776,7 @@ impl Py { /// Returns whether the object is Ellipsis, e.g. `...`. /// /// This is equivalent to the Python expression `self is ...`. + #[deprecated(since = "0.20.0", note = "use `.is(py.Ellipsis())` instead")] pub fn is_ellipsis(&self) -> bool { unsafe { ffi::Py_Ellipsis() == self.as_ptr() } } @@ -1474,6 +1475,7 @@ a = A() } #[test] + #[allow(deprecated)] fn test_is_ellipsis() { Python::with_gil(|py| { let v = py diff --git a/src/types/any.rs b/src/types/any.rs index 8139c06563c..8d3cfc6d1a0 100644 --- a/src/types/any.rs +++ b/src/types/any.rs @@ -653,6 +653,7 @@ impl PyAny { /// Returns whether the object is Ellipsis, e.g. `...`. /// /// This is equivalent to the Python expression `self is ...`. + #[deprecated(since = "0.20.0", note = "use `.is(py.Ellipsis())` instead")] pub fn is_ellipsis(&self) -> bool { Py2::::borrowed_from_gil_ref(&self).is_ellipsis() } @@ -2557,6 +2558,7 @@ class SimpleClass: } #[test] + #[allow(deprecated)] fn test_is_ellipsis() { Python::with_gil(|py| { let v = py diff --git a/src/types/ellipsis.rs b/src/types/ellipsis.rs index f0603b664c9..c1c594f8f24 100644 --- a/src/types/ellipsis.rs +++ b/src/types/ellipsis.rs @@ -1,4 +1,4 @@ -use crate::{ffi, PyAny, PyDowncastError, PyTryFrom, Python}; +use crate::{ffi, PyAny, PyTypeInfo, Python}; /// Represents the Python `Ellipsis` object. #[repr(transparent)] @@ -15,39 +15,48 @@ impl PyEllipsis { } } -impl<'v> PyTryFrom<'v> for PyEllipsis { - fn try_from>(value: V) -> Result<&'v Self, crate::PyDowncastError<'v>> { - let value: &PyAny = value.into(); - if unsafe { ffi::Py_Ellipsis() == value.as_ptr() } { - return unsafe { Ok(value.downcast_unchecked()) }; - } - Err(PyDowncastError::new(value, "ellipsis")) +unsafe impl PyTypeInfo for PyEllipsis { + const NAME: &'static str = "ellipsis"; + + const MODULE: Option<&'static str> = None; + + type AsRefTarget = PyEllipsis; + + fn type_object_raw(_py: Python<'_>) -> *mut ffi::PyTypeObject { + unsafe { ffi::Py_TYPE(ffi::Py_Ellipsis()) } } - fn try_from_exact>( - value: V, - ) -> Result<&'v Self, crate::PyDowncastError<'v>> { - value.into().downcast() + #[inline] + fn is_type_of(object: &PyAny) -> bool { + // ellipsis is not usable as a base type + Self::is_exact_type_of(object) } - unsafe fn try_from_unchecked>(value: V) -> &'v Self { - let ptr = value.into() as *const _ as *const PyEllipsis; - &*ptr + #[inline] + fn is_exact_type_of(object: &PyAny) -> bool { + object.is(Self::get(object.py())) } } #[cfg(test)] mod tests { use crate::types::{PyDict, PyEllipsis}; - use crate::Python; + use crate::{PyTypeInfo, Python}; #[test] fn test_ellipsis_is_itself() { + Python::with_gil(|py| { + assert!(PyEllipsis::get(py).is_instance_of::()); + assert!(PyEllipsis::get(py).is_exact_instance_of::()); + }) + } + + #[test] + fn test_ellipsis_type_object_consistent() { Python::with_gil(|py| { assert!(PyEllipsis::get(py) - .downcast::() - .unwrap() - .is_ellipsis()); + .get_type() + .is(PyEllipsis::type_object(py))); }) } diff --git a/src/types/none.rs b/src/types/none.rs index d80faac20be..fa189911151 100644 --- a/src/types/none.rs +++ b/src/types/none.rs @@ -1,4 +1,4 @@ -use crate::{ffi, IntoPy, PyAny, PyDowncastError, PyObject, PyTryFrom, Python, ToPyObject}; +use crate::{ffi, IntoPy, PyAny, PyObject, PyTypeInfo, Python, ToPyObject}; /// Represents the Python `None` object. #[repr(transparent)] @@ -15,24 +15,26 @@ impl PyNone { } } -impl<'v> PyTryFrom<'v> for PyNone { - fn try_from>(value: V) -> Result<&'v Self, crate::PyDowncastError<'v>> { - let value: &PyAny = value.into(); - if value.is_none() { - return unsafe { Ok(value.downcast_unchecked()) }; - } - Err(PyDowncastError::new(value, "NoneType")) +unsafe impl PyTypeInfo for PyNone { + const NAME: &'static str = "NoneType"; + + const MODULE: Option<&'static str> = None; + + type AsRefTarget = PyNone; + + fn type_object_raw(_py: Python<'_>) -> *mut ffi::PyTypeObject { + unsafe { ffi::Py_TYPE(ffi::Py_None()) } } - fn try_from_exact>( - value: V, - ) -> Result<&'v Self, crate::PyDowncastError<'v>> { - value.into().downcast() + #[inline] + fn is_type_of(object: &PyAny) -> bool { + // NoneType is not usable as a base type + Self::is_exact_type_of(object) } - unsafe fn try_from_unchecked>(value: V) -> &'v Self { - let ptr = value.into() as *const _ as *const PyNone; - &*ptr + #[inline] + fn is_exact_type_of(object: &PyAny) -> bool { + object.is(Self::get(object.py())) } } @@ -53,7 +55,22 @@ impl IntoPy for () { #[cfg(test)] mod tests { use crate::types::{PyDict, PyNone}; - use crate::{IntoPy, PyObject, Python, ToPyObject}; + use crate::{IntoPy, PyObject, PyTypeInfo, Python, ToPyObject}; + + #[test] + fn test_none_is_itself() { + Python::with_gil(|py| { + assert!(PyNone::get(py).is_instance_of::()); + assert!(PyNone::get(py).is_exact_instance_of::()); + }) + } + + #[test] + fn test_none_type_object_consistent() { + Python::with_gil(|py| { + assert!(PyNone::get(py).get_type().is(PyNone::type_object(py))); + }) + } #[test] fn test_none_is_none() { diff --git a/src/types/notimplemented.rs b/src/types/notimplemented.rs index 258e584c517..2d4b7737053 100644 --- a/src/types/notimplemented.rs +++ b/src/types/notimplemented.rs @@ -1,4 +1,4 @@ -use crate::{ffi, PyAny, PyDowncastError, PyTryFrom, Python}; +use crate::{ffi, PyAny, PyTypeInfo, Python}; /// Represents the Python `NotImplemented` object. #[repr(transparent)] @@ -15,39 +15,48 @@ impl PyNotImplemented { } } -impl<'v> PyTryFrom<'v> for PyNotImplemented { - fn try_from>(value: V) -> Result<&'v Self, crate::PyDowncastError<'v>> { - let value: &PyAny = value.into(); - if unsafe { ffi::Py_NotImplemented() == value.as_ptr() } { - return unsafe { Ok(value.downcast_unchecked()) }; - } - Err(PyDowncastError::new(value, "NotImplementedType")) +unsafe impl PyTypeInfo for PyNotImplemented { + const NAME: &'static str = "NotImplementedType"; + + const MODULE: Option<&'static str> = None; + + type AsRefTarget = PyNotImplemented; + + fn type_object_raw(_py: Python<'_>) -> *mut ffi::PyTypeObject { + unsafe { ffi::Py_TYPE(ffi::Py_NotImplemented()) } } - fn try_from_exact>( - value: V, - ) -> Result<&'v Self, crate::PyDowncastError<'v>> { - value.into().downcast() + #[inline] + fn is_type_of(object: &PyAny) -> bool { + // NotImplementedType is not usable as a base type + Self::is_exact_type_of(object) } - unsafe fn try_from_unchecked>(value: V) -> &'v Self { - let ptr = value.into() as *const _ as *const PyNotImplemented; - &*ptr + #[inline] + fn is_exact_type_of(object: &PyAny) -> bool { + object.is(Self::get(object.py())) } } #[cfg(test)] mod tests { use crate::types::{PyDict, PyNotImplemented}; - use crate::Python; + use crate::{PyTypeInfo, Python}; #[test] fn test_notimplemented_is_itself() { + Python::with_gil(|py| { + assert!(PyNotImplemented::get(py).is_instance_of::()); + assert!(PyNotImplemented::get(py).is_exact_instance_of::()); + }) + } + + #[test] + fn test_notimplemented_type_object_consistent() { Python::with_gil(|py| { assert!(PyNotImplemented::get(py) - .downcast::() - .unwrap() - .is(py.NotImplemented())); + .get_type() + .is(PyNotImplemented::type_object(py))); }) }