diff --git a/newsfragments/3408.added.md b/newsfragments/3408.added.md new file mode 100644 index 00000000000..db1a1efe78e --- /dev/null +++ b/newsfragments/3408.added.md @@ -0,0 +1 @@ +Add types for `None`, `Ellipsis`, and `NotImplemented` diff --git a/src/conversion.rs b/src/conversion.rs index 6fc7a6bf328..5f5f78ccaaf 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -263,19 +263,6 @@ where } } -/// `()` is converted to Python `None`. -impl ToPyObject for () { - fn to_object(&self, py: Python<'_>) -> PyObject { - py.None() - } -} - -impl IntoPy for () { - fn into_py(self, py: Python<'_>) -> PyObject { - py.None() - } -} - impl IntoPy for &'_ PyAny { #[inline] fn into_py(self, py: Python<'_>) -> PyObject { diff --git a/src/marker.rs b/src/marker.rs index 58a1104d764..2d130ae4ad1 100644 --- a/src/marker.rs +++ b/src/marker.rs @@ -118,7 +118,9 @@ use crate::err::{self, PyDowncastError, PyErr, PyResult}; use crate::gil::{GILGuard, GILPool, SuspendGIL}; use crate::impl_::not_send::NotSend; -use crate::types::{PyAny, PyDict, PyModule, PyString, PyType}; +use crate::types::{ + PyAny, PyDict, PyEllipsis, PyModule, PyNone, PyNotImplemented, PyString, PyType, +}; use crate::version::PythonVersionInfo; use crate::{ffi, FromPyPointer, IntoPy, Py, PyNativeType, PyObject, PyTryFrom, PyTypeInfo}; use std::ffi::{CStr, CString}; @@ -690,21 +692,21 @@ impl<'py> Python<'py> { #[allow(non_snake_case)] // the Python keyword starts with uppercase #[inline] pub fn None(self) -> PyObject { - unsafe { PyObject::from_borrowed_ptr(self, ffi::Py_None()) } + PyNone::get(self).into() } /// Gets the Python builtin value `Ellipsis`, or `...`. #[allow(non_snake_case)] // the Python keyword starts with uppercase #[inline] pub fn Ellipsis(self) -> PyObject { - unsafe { PyObject::from_borrowed_ptr(self, ffi::Py_Ellipsis()) } + PyEllipsis::get(self).into() } /// Gets the Python builtin value `NotImplemented`. #[allow(non_snake_case)] // the Python keyword starts with uppercase #[inline] pub fn NotImplemented(self) -> PyObject { - unsafe { PyObject::from_borrowed_ptr(self, ffi::Py_NotImplemented()) } + PyNotImplemented::get(self).into() } /// Gets the running Python interpreter version as a string. diff --git a/src/types/ellipsis.rs b/src/types/ellipsis.rs new file mode 100644 index 00000000000..ac50e11bdd7 --- /dev/null +++ b/src/types/ellipsis.rs @@ -0,0 +1,19 @@ +use crate::{ffi, PyAny, Python}; + +/// Represents the Python `Ellipsis` object. +#[repr(transparent)] +pub struct PyEllipsis(PyAny); + +pyobject_native_type!( + PyEllipsis, + ffi::PyObject, + pyobject_native_static_type_object!(ffi::PyEllipsis_Type) +); + +impl PyEllipsis { + /// Returns the `Ellipsis` object. + #[inline] + pub fn get(py: Python<'_>) -> &PyEllipsis { + unsafe { py.from_borrowed_ptr(ffi::Py_Ellipsis()) } + } +} diff --git a/src/types/mod.rs b/src/types/mod.rs index 4d8b0513adf..dc6e6d69a57 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -16,6 +16,7 @@ pub use self::datetime::{ pub use self::dict::{IntoPyDict, PyDict}; #[cfg(not(PyPy))] pub use self::dict::{PyDictItems, PyDictKeys, PyDictValues}; +pub use self::ellipsis::PyEllipsis; pub use self::floatob::PyFloat; #[cfg(all(not(Py_LIMITED_API), not(PyPy)))] pub use self::frame::PyFrame; @@ -27,6 +28,8 @@ pub use self::iterator::PyIterator; pub use self::list::PyList; pub use self::mapping::PyMapping; pub use self::module::PyModule; +pub use self::none::PyNone; +pub use self::notimplemented::PyNotImplemented; pub use self::num::PyLong; pub use self::num::PyLong as PyInt; #[cfg(not(PyPy))] @@ -278,6 +281,7 @@ mod complex; #[cfg(not(Py_LIMITED_API))] mod datetime; mod dict; +mod ellipsis; mod floatob; #[cfg(all(not(Py_LIMITED_API), not(PyPy)))] mod frame; @@ -287,6 +291,8 @@ mod iterator; pub(crate) mod list; mod mapping; mod module; +mod none; +mod notimplemented; mod num; #[cfg(not(PyPy))] mod pysuper; diff --git a/src/types/none.rs b/src/types/none.rs new file mode 100644 index 00000000000..2d338efb82e --- /dev/null +++ b/src/types/none.rs @@ -0,0 +1,51 @@ +use crate::{ffi, IntoPy, PyAny, PyDowncastError, PyObject, PyTryFrom, Python, ToPyObject}; + +/// Represents the Python `None` object. +#[repr(transparent)] +pub struct PyNone(PyAny); + +pyobject_native_type_named!(PyNone); +pyobject_native_type_extract!(PyNone); + +impl PyNone { + /// Returns the `None` object. + #[inline] + pub fn get(py: Python<'_>) -> &PyNone { + unsafe { py.from_borrowed_ptr(ffi::Py_None()) } + } +} + +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")) + } + + fn try_from_exact>( + value: V, + ) -> Result<&'v Self, crate::PyDowncastError<'v>> { + value.into().downcast() + } + + unsafe fn try_from_unchecked>(value: V) -> &'v Self { + let ptr = value.into() as *const _ as *const PyNone; + &*ptr + } +} + +/// `()` is converted to Python `None`. +impl ToPyObject for () { + fn to_object(&self, py: Python<'_>) -> PyObject { + PyNone::get(py).into() + } +} + +impl IntoPy for () { + #[inline] + fn into_py(self, py: Python<'_>) -> PyObject { + PyNone::get(py).into() + } +} diff --git a/src/types/notimplemented.rs b/src/types/notimplemented.rs new file mode 100644 index 00000000000..68d5e173333 --- /dev/null +++ b/src/types/notimplemented.rs @@ -0,0 +1,37 @@ +use crate::{ffi, PyAny, PyDowncastError, PyTryFrom, Python}; + +/// Represents the Python `NotImplemented` object. +#[repr(transparent)] +pub struct PyNotImplemented(PyAny); + +pyobject_native_type_named!(PyNotImplemented); +pyobject_native_type_extract!(PyNotImplemented); + +impl PyNotImplemented { + /// Returns the `NotImplemented` object. + #[inline] + pub fn get(py: Python<'_>) -> &PyNotImplemented { + unsafe { py.from_borrowed_ptr(ffi::Py_NotImplemented()) } + } +} + +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")) + } + + fn try_from_exact>( + value: V, + ) -> Result<&'v Self, crate::PyDowncastError<'v>> { + value.into().downcast() + } + + unsafe fn try_from_unchecked>(value: V) -> &'v Self { + let ptr = value.into() as *const _ as *const PyNotImplemented; + &*ptr + } +}