From 8deaef8f2a7dd575b47d8731b47029e8de738fbe Mon Sep 17 00:00:00 2001 From: Thomas Pellissier-Tanon Date: Wed, 5 Nov 2025 11:44:25 +0100 Subject: [PATCH 1/5] Fill PyTypeInfo::TYPE_HINT for built-in types Makes internal macros always require a module and a name These values are then used to fill PyTypeInfo::MODULE and PyTypeInfo::TYPE_HINT (PyTypeInfo::NAME stays unchanged) This changes some values of PyTypeInfo::MODULE --- newsfragments/5619.changed.md | 1 + pytests/stubs/pyclasses.pyi | 2 +- pytests/stubs/pyfunctions.pyi | 44 +++---- src/exceptions.rs | 217 +++++++++++++++++++++++++++------ src/types/any.rs | 3 +- src/types/boolobject.rs | 2 +- src/types/bytearray.rs | 2 +- src/types/bytes.rs | 2 +- src/types/capsule.rs | 2 +- src/types/code.rs | 15 ++- src/types/complex.rs | 2 + src/types/datetime.rs | 36 ++++-- src/types/dict.rs | 8 ++ src/types/float.rs | 2 + src/types/frame.rs | 2 + src/types/frozenset.rs | 4 + src/types/function.rs | 4 +- src/types/genericalias.rs | 4 +- src/types/iterator.rs | 3 +- src/types/list.rs | 2 +- src/types/mappingproxy.rs | 2 + src/types/memoryview.rs | 2 +- src/types/mod.rs | 18 +-- src/types/module.rs | 2 +- src/types/num.rs | 2 +- src/types/pysuper.rs | 4 +- src/types/range.rs | 2 +- src/types/set.rs | 4 + src/types/slice.rs | 2 + src/types/string.rs | 2 +- src/types/traceback.rs | 2 + src/types/tuple.rs | 2 +- src/types/typeobject.rs | 2 +- src/types/weakref/reference.rs | 6 +- 34 files changed, 308 insertions(+), 101 deletions(-) create mode 100644 newsfragments/5619.changed.md diff --git a/newsfragments/5619.changed.md b/newsfragments/5619.changed.md new file mode 100644 index 00000000000..54e65d2e33b --- /dev/null +++ b/newsfragments/5619.changed.md @@ -0,0 +1 @@ +Fill values of `PyTypeInfo::TYPE_HINT` on all implementations and change some `PyTypeInfo::MODULE` values. \ No newline at end of file diff --git a/pytests/stubs/pyclasses.pyi b/pytests/stubs/pyclasses.pyi index 7aba82f4dfa..f12a4d97ef6 100644 --- a/pytests/stubs/pyclasses.pyi +++ b/pytests/stubs/pyclasses.pyi @@ -2,7 +2,7 @@ from _typeshed import Incomplete from typing import Any, final class AssertingBaseClass: - def __new__(cls, /, expected_type: Any) -> AssertingBaseClass: ... + def __new__(cls, /, expected_type: type) -> AssertingBaseClass: ... @final class ClassWithDecorators: diff --git a/pytests/stubs/pyfunctions.pyi b/pytests/stubs/pyfunctions.pyi index 9513072d023..9164badcf6a 100644 --- a/pytests/stubs/pyfunctions.pyi +++ b/pytests/stubs/pyfunctions.pyi @@ -3,32 +3,34 @@ from typing import Any def args_kwargs(*args, **kwargs) -> Any: ... def many_keyword_arguments( *, - ant: Any | None = None, - bear: Any | None = None, - cat: Any | None = None, - dog: Any | None = None, - elephant: Any | None = None, - fox: Any | None = None, - goat: Any | None = None, - horse: Any | None = None, - iguana: Any | None = None, - jaguar: Any | None = None, - koala: Any | None = None, - lion: Any | None = None, - monkey: Any | None = None, - newt: Any | None = None, - owl: Any | None = None, - penguin: Any | None = None, + ant: object | None = None, + bear: object | None = None, + cat: object | None = None, + dog: object | None = None, + elephant: object | None = None, + fox: object | None = None, + goat: object | None = None, + horse: object | None = None, + iguana: object | None = None, + jaguar: object | None = None, + koala: object | None = None, + lion: object | None = None, + monkey: object | None = None, + newt: object | None = None, + owl: object | None = None, + penguin: object | None = None, ) -> None: ... def none() -> None: ... -def positional_only(a: Any, /, b: Any) -> Any: ... -def simple(a: Any, b: Any | None = None, *, c: Any | None = None) -> Any: ... -def simple_args(a: Any, b: Any | None = None, *args, c: Any | None = None) -> Any: ... +def positional_only(a: object, /, b: object) -> Any: ... +def simple(a: object, b: object | None = None, *, c: object | None = None) -> Any: ... +def simple_args( + a: object, b: object | None = None, *args, c: object | None = None +) -> Any: ... def simple_args_kwargs( - a: Any, b: Any | None = None, *args, c: Any | None = None, **kwargs + a: object, b: object | None = None, *args, c: object | None = None, **kwargs ) -> Any: ... def simple_kwargs( - a: Any, b: Any | None = None, c: Any | None = None, **kwargs + a: object, b: object | None = None, c: object | None = None, **kwargs ) -> Any: ... def with_custom_type_annotations( a: int, *_args: str, _b: int | None = None, **_kwargs: bool diff --git a/src/exceptions.rs b/src/exceptions.rs index c606a3ffcce..3535306caa4 100644 --- a/src/exceptions.rs +++ b/src/exceptions.rs @@ -72,7 +72,10 @@ macro_rules! import_exception { /// /// [`pyo3::import_exception!`]: https://docs.rs/pyo3/latest/pyo3/macro.import_exception.html "import_exception in pyo3" #[repr(transparent)] - #[allow(non_camel_case_types, reason = "matches imported exception name, e.g. `socket.herror`")] + #[allow( + non_camel_case_types, + reason = "matches imported exception name, e.g. `socket.herror`" + )] pub struct $name($crate::PyAny); $crate::impl_exception_boilerplate!($name); @@ -80,14 +83,18 @@ macro_rules! import_exception { $crate::pyobject_native_type_core!( $name, $name::type_object_raw, - #module=::std::option::Option::Some(stringify!($module)) + stringify!($name), + stringify!($module) ); impl $name { fn type_object_raw(py: $crate::Python<'_>) -> *mut $crate::ffi::PyTypeObject { use $crate::types::PyTypeMethods; static TYPE_OBJECT: $crate::impl_::exceptions::ImportedExceptionTypeObject = - $crate::impl_::exceptions::ImportedExceptionTypeObject::new(stringify!($module), stringify!($name)); + $crate::impl_::exceptions::ImportedExceptionTypeObject::new( + stringify!($module), + stringify!($name), + ); TYPE_OBJECT.get(py).as_type_ptr() } } @@ -199,13 +206,19 @@ macro_rules! create_exception_type_object { $crate::create_exception_type_object!($module, $name, $base, ::std::option::Option::None); }; ($module: expr, $name: ident, $base: ty, Some($doc: expr)) => { - $crate::create_exception_type_object!($module, $name, $base, ::std::option::Option::Some($crate::ffi::c_str!($doc))); + $crate::create_exception_type_object!( + $module, + $name, + $base, + ::std::option::Option::Some($crate::ffi::c_str!($doc)) + ); }; ($module: expr, $name: ident, $base: ty, $doc: expr) => { $crate::pyobject_native_type_core!( $name, $name::type_object_raw, - #module=::std::option::Option::Some(stringify!($module)) + stringify!($name), + stringify!($module) ); impl $name { @@ -215,39 +228,45 @@ macro_rules! create_exception_type_object { PyOnceLock::new(); TYPE_OBJECT - .get_or_init(py, || + .get_or_init(py, || { $crate::PyErr::new_type( py, - $crate::ffi::c_str!(concat!(stringify!($module), ".", stringify!($name))), + $crate::ffi::c_str!(concat!( + stringify!($module), + ".", + stringify!($name) + )), $doc, ::std::option::Option::Some(&py.get_type::<$base>()), ::std::option::Option::None, - ).expect("Failed to initialize new exception type.") - ).as_ptr() as *mut $crate::ffi::PyTypeObject + ) + .expect("Failed to initialize new exception type.") + }) + .as_ptr() as *mut $crate::ffi::PyTypeObject } } }; } macro_rules! impl_native_exception ( - ($name:ident, $exc_name:ident, $doc:expr, $layout:path $(, #checkfunction=$checkfunction:path)?) => ( + ($name:ident, $exc_name:ident, $python_name:expr, $doc:expr, $layout:path $(, #checkfunction=$checkfunction:path)?) => ( #[doc = $doc] #[repr(transparent)] #[allow(clippy::upper_case_acronyms, reason = "Python exception names")] pub struct $name($crate::PyAny); $crate::impl_exception_boilerplate!($name); - $crate::pyobject_native_type!($name, $layout, |_py| unsafe { $crate::ffi::$exc_name as *mut $crate::ffi::PyTypeObject } $(, #checkfunction=$checkfunction)?); + $crate::pyobject_native_type!($name, $layout, |_py| unsafe { $crate::ffi::$exc_name as *mut $crate::ffi::PyTypeObject }, "builtins", $python_name $(, #checkfunction=$checkfunction)?); $crate::pyobject_subclassable_native_type!($name, $layout); ); - ($name:ident, $exc_name:ident, $doc:expr) => ( - impl_native_exception!($name, $exc_name, $doc, $crate::ffi::PyBaseExceptionObject); + ($name:ident, $exc_name:ident, $python_name:expr, $doc:expr) => ( + impl_native_exception!($name, $exc_name, $python_name, $doc, $crate::ffi::PyBaseExceptionObject); ) ); #[cfg(windows)] macro_rules! impl_windows_native_exception ( - ($name:ident, $exc_name:ident, $doc:expr, $layout:path) => ( + ($name:ident, $exc_name:ident, $python_name:expr, $doc:expr, $layout:path) => ( #[cfg(windows)] #[doc = $doc] #[repr(transparent)] @@ -255,10 +274,10 @@ macro_rules! impl_windows_native_exception ( pub struct $name($crate::PyAny); $crate::impl_exception_boilerplate!($name); - $crate::pyobject_native_type!($name, $layout, |_py| unsafe { $crate::ffi::$exc_name as *mut $crate::ffi::PyTypeObject }); + $crate::pyobject_native_type!($name, $layout, |_py| unsafe { $crate::ffi::$exc_name as *mut $crate::ffi::PyTypeObject }, "builtins", $python_name); ); - ($name:ident, $exc_name:ident, $doc:expr) => ( - impl_windows_native_exception!($name, $exc_name, $doc, $crate::ffi::PyBaseExceptionObject); + ($name:ident, $exc_name:ident, $python_name:expr, $doc:expr) => ( + impl_windows_native_exception!($name, $exc_name, $python_name, $doc, $crate::ffi::PyBaseExceptionObject); ) ); @@ -333,131 +352,216 @@ Python::attach(|py| { impl_native_exception!( PyBaseException, PyExc_BaseException, + "BaseException", native_doc!("BaseException"), ffi::PyBaseExceptionObject, #checkfunction=ffi::PyExceptionInstance_Check ); -impl_native_exception!(PyException, PyExc_Exception, native_doc!("Exception")); +impl_native_exception!( + PyException, + PyExc_Exception, + "Exception", + native_doc!("Exception") +); impl_native_exception!( PyStopAsyncIteration, PyExc_StopAsyncIteration, + "StopAsyncIteration", native_doc!("StopAsyncIteration") ); impl_native_exception!( PyStopIteration, PyExc_StopIteration, + "StopIteration", native_doc!("StopIteration"), ffi::PyStopIterationObject ); impl_native_exception!( PyGeneratorExit, PyExc_GeneratorExit, + "GeneratorExit", native_doc!("GeneratorExit") ); impl_native_exception!( PyArithmeticError, PyExc_ArithmeticError, + "ArithmeticError", native_doc!("ArithmeticError") ); -impl_native_exception!(PyLookupError, PyExc_LookupError, native_doc!("LookupError")); +impl_native_exception!( + PyLookupError, + PyExc_LookupError, + "LookupError", + native_doc!("LookupError") +); impl_native_exception!( PyAssertionError, PyExc_AssertionError, + "AssertionError", native_doc!("AssertionError") ); impl_native_exception!( PyAttributeError, PyExc_AttributeError, + "AttributeError", native_doc!("AttributeError") ); -impl_native_exception!(PyBufferError, PyExc_BufferError, native_doc!("BufferError")); -impl_native_exception!(PyEOFError, PyExc_EOFError, native_doc!("EOFError")); +impl_native_exception!( + PyBufferError, + PyExc_BufferError, + "BufferError", + native_doc!("BufferError") +); +impl_native_exception!( + PyEOFError, + PyExc_EOFError, + "EOFError", + native_doc!("EOFError") +); impl_native_exception!( PyFloatingPointError, PyExc_FloatingPointError, + "FloatingPointError", native_doc!("FloatingPointError") ); #[cfg(not(any(PyPy, GraalPy)))] impl_native_exception!( PyOSError, PyExc_OSError, + "OSError", native_doc!("OSError"), ffi::PyOSErrorObject ); #[cfg(any(PyPy, GraalPy))] -impl_native_exception!(PyOSError, PyExc_OSError, native_doc!("OSError")); -impl_native_exception!(PyImportError, PyExc_ImportError, native_doc!("ImportError")); +impl_native_exception!(PyOSError, PyExc_OSError, "OSError", native_doc!("OSError")); +impl_native_exception!( + PyImportError, + PyExc_ImportError, + "ImportError", + native_doc!("ImportError") +); impl_native_exception!( PyModuleNotFoundError, PyExc_ModuleNotFoundError, + "ModuleNotFoundError", native_doc!("ModuleNotFoundError") ); -impl_native_exception!(PyIndexError, PyExc_IndexError, native_doc!("IndexError")); -impl_native_exception!(PyKeyError, PyExc_KeyError, native_doc!("KeyError")); +impl_native_exception!( + PyIndexError, + PyExc_IndexError, + "IndexError", + native_doc!("IndexError") +); +impl_native_exception!( + PyKeyError, + PyExc_KeyError, + "KeyError", + native_doc!("KeyError") +); impl_native_exception!( PyKeyboardInterrupt, PyExc_KeyboardInterrupt, + "KeyboardInterrupt", native_doc!("KeyboardInterrupt") ); -impl_native_exception!(PyMemoryError, PyExc_MemoryError, native_doc!("MemoryError")); -impl_native_exception!(PyNameError, PyExc_NameError, native_doc!("NameError")); +impl_native_exception!( + PyMemoryError, + PyExc_MemoryError, + "MemoryError", + native_doc!("MemoryError") +); +impl_native_exception!( + PyNameError, + PyExc_NameError, + "NameError", + native_doc!("NameError") +); impl_native_exception!( PyOverflowError, PyExc_OverflowError, + "OverflowError", native_doc!("OverflowError") ); impl_native_exception!( PyRuntimeError, PyExc_RuntimeError, + "RuntimeError", native_doc!("RuntimeError") ); impl_native_exception!( PyRecursionError, PyExc_RecursionError, + "RecursionError", native_doc!("RecursionError") ); impl_native_exception!( PyNotImplementedError, PyExc_NotImplementedError, + "NotImplementedError", native_doc!("NotImplementedError") ); #[cfg(not(any(PyPy, GraalPy)))] impl_native_exception!( PySyntaxError, PyExc_SyntaxError, + "SyntaxError", native_doc!("SyntaxError"), ffi::PySyntaxErrorObject ); #[cfg(any(PyPy, GraalPy))] -impl_native_exception!(PySyntaxError, PyExc_SyntaxError, native_doc!("SyntaxError")); +impl_native_exception!( + PySyntaxError, + PyExc_SyntaxError, + "SyntaxError", + native_doc!("SyntaxError") +); impl_native_exception!( PyReferenceError, PyExc_ReferenceError, + "ReferenceError", native_doc!("ReferenceError") ); -impl_native_exception!(PySystemError, PyExc_SystemError, native_doc!("SystemError")); +impl_native_exception!( + PySystemError, + PyExc_SystemError, + "SystemError", + native_doc!("SystemError") +); #[cfg(not(any(PyPy, GraalPy)))] impl_native_exception!( PySystemExit, PyExc_SystemExit, + "SystemExit", native_doc!("SystemExit"), ffi::PySystemExitObject ); #[cfg(any(PyPy, GraalPy))] -impl_native_exception!(PySystemExit, PyExc_SystemExit, native_doc!("SystemExit")); -impl_native_exception!(PyTypeError, PyExc_TypeError, native_doc!("TypeError")); +impl_native_exception!( + PySystemExit, + PyExc_SystemExit, + "SystemExit", + native_doc!("SystemExit") +); +impl_native_exception!( + PyTypeError, + PyExc_TypeError, + "TypeError", + native_doc!("TypeError") +); impl_native_exception!( PyUnboundLocalError, PyExc_UnboundLocalError, + "UnboundLocalError", native_doc!("UnboundLocalError") ); #[cfg(not(any(PyPy, GraalPy)))] impl_native_exception!( PyUnicodeError, PyExc_UnicodeError, + "UnicodeError", native_doc!("UnicodeError"), ffi::PyUnicodeErrorObject ); @@ -465,124 +569,152 @@ impl_native_exception!( impl_native_exception!( PyUnicodeError, PyExc_UnicodeError, + "UnicodeError", native_doc!("UnicodeError") ); // these four errors need arguments, so they're too annoying to write tests for using macros... impl_native_exception!( PyUnicodeDecodeError, PyExc_UnicodeDecodeError, + "UnicodeDecodeError", native_doc!("UnicodeDecodeError", "") ); impl_native_exception!( PyUnicodeEncodeError, PyExc_UnicodeEncodeError, + "UnicodeEncodeError", native_doc!("UnicodeEncodeError", "") ); impl_native_exception!( PyUnicodeTranslateError, PyExc_UnicodeTranslateError, + "UnicodeTranslateError", native_doc!("UnicodeTranslateError", "") ); #[cfg(Py_3_11)] impl_native_exception!( PyBaseExceptionGroup, PyExc_BaseExceptionGroup, + "BaseExceptionGroup", native_doc!("BaseExceptionGroup", "") ); -impl_native_exception!(PyValueError, PyExc_ValueError, native_doc!("ValueError")); +impl_native_exception!( + PyValueError, + PyExc_ValueError, + "ValueError", + native_doc!("ValueError") +); impl_native_exception!( PyZeroDivisionError, PyExc_ZeroDivisionError, + "ZeroDivisionError", native_doc!("ZeroDivisionError") ); impl_native_exception!( PyBlockingIOError, PyExc_BlockingIOError, + "BlockingIOError", native_doc!("BlockingIOError") ); impl_native_exception!( PyBrokenPipeError, PyExc_BrokenPipeError, + "BrokenPipeError", native_doc!("BrokenPipeError") ); impl_native_exception!( PyChildProcessError, PyExc_ChildProcessError, + "ChildProcessError", native_doc!("ChildProcessError") ); impl_native_exception!( PyConnectionError, PyExc_ConnectionError, + "ConnectionError", native_doc!("ConnectionError") ); impl_native_exception!( PyConnectionAbortedError, PyExc_ConnectionAbortedError, + "ConnectionAbortedError", native_doc!("ConnectionAbortedError") ); impl_native_exception!( PyConnectionRefusedError, PyExc_ConnectionRefusedError, + "ConnectionRefusedError", native_doc!("ConnectionRefusedError") ); impl_native_exception!( PyConnectionResetError, PyExc_ConnectionResetError, + "ConnectionResetError", native_doc!("ConnectionResetError") ); impl_native_exception!( PyFileExistsError, PyExc_FileExistsError, + "FileExistsError", native_doc!("FileExistsError") ); impl_native_exception!( PyFileNotFoundError, PyExc_FileNotFoundError, + "FileNotFoundError", native_doc!("FileNotFoundError") ); impl_native_exception!( PyInterruptedError, PyExc_InterruptedError, + "InterruptedError", native_doc!("InterruptedError") ); impl_native_exception!( PyIsADirectoryError, PyExc_IsADirectoryError, + "IsADirectoryError", native_doc!("IsADirectoryError") ); impl_native_exception!( PyNotADirectoryError, PyExc_NotADirectoryError, + "NotADirectoryError", native_doc!("NotADirectoryError") ); impl_native_exception!( PyPermissionError, PyExc_PermissionError, + "PermissionError", native_doc!("PermissionError") ); impl_native_exception!( PyProcessLookupError, PyExc_ProcessLookupError, + "ProcessLookupError", native_doc!("ProcessLookupError") ); impl_native_exception!( PyTimeoutError, PyExc_TimeoutError, + "TimeoutError", native_doc!("TimeoutError") ); impl_native_exception!( PyEnvironmentError, PyExc_EnvironmentError, + "EnvironmentError", native_doc!("EnvironmentError") ); -impl_native_exception!(PyIOError, PyExc_IOError, native_doc!("IOError")); +impl_native_exception!(PyIOError, PyExc_IOError, "IOError", native_doc!("IOError")); #[cfg(windows)] impl_windows_native_exception!( PyWindowsError, PyExc_WindowsError, + "WindowsError", native_doc!("WindowsError") ); @@ -642,51 +774,65 @@ impl PyUnicodeDecodeError { } } -impl_native_exception!(PyWarning, PyExc_Warning, native_doc!("Warning")); -impl_native_exception!(PyUserWarning, PyExc_UserWarning, native_doc!("UserWarning")); +impl_native_exception!(PyWarning, PyExc_Warning, "Warning", native_doc!("Warning")); +impl_native_exception!( + PyUserWarning, + PyExc_UserWarning, + "UserWarning", + native_doc!("UserWarning") +); impl_native_exception!( PyDeprecationWarning, PyExc_DeprecationWarning, + "DeprecationWarning", native_doc!("DeprecationWarning") ); impl_native_exception!( PyPendingDeprecationWarning, PyExc_PendingDeprecationWarning, + "PendingDeprecationWarning", native_doc!("PendingDeprecationWarning") ); impl_native_exception!( PySyntaxWarning, PyExc_SyntaxWarning, + "SyntaxWarning", native_doc!("SyntaxWarning") ); impl_native_exception!( PyRuntimeWarning, PyExc_RuntimeWarning, + "RuntimeWarning", native_doc!("RuntimeWarning") ); impl_native_exception!( PyFutureWarning, PyExc_FutureWarning, + "FutureWarning", native_doc!("FutureWarning") ); impl_native_exception!( PyImportWarning, PyExc_ImportWarning, + "ImportWarning", native_doc!("ImportWarning") ); impl_native_exception!( PyUnicodeWarning, PyExc_UnicodeWarning, + "UnicodeWarning", native_doc!("UnicodeWarning") ); impl_native_exception!( PyBytesWarning, PyExc_BytesWarning, + "BytesWarning", native_doc!("BytesWarning") ); impl_native_exception!( PyResourceWarning, PyExc_ResourceWarning, + "ResourceWarning", native_doc!("ResourceWarning") ); @@ -694,6 +840,7 @@ impl_native_exception!( impl_native_exception!( PyEncodingWarning, PyExc_EncodingWarning, + "EncodingWarning", native_doc!("EncodingWarning") ); diff --git a/src/types/any.rs b/src/types/any.rs index 08745c44939..b6c78a2e8c5 100644 --- a/src/types/any.rs +++ b/src/types/any.rs @@ -43,7 +43,8 @@ fn PyObject_Check(_: *mut ffi::PyObject) -> c_int { pyobject_native_type_info!( PyAny, pyobject_native_static_type_object!(ffi::PyBaseObject_Type), - Some("builtins"), + "builtins", + "object", #checkfunction=PyObject_Check ); diff --git a/src/types/boolobject.rs b/src/types/boolobject.rs index c5de574db5b..8dec681c56c 100644 --- a/src/types/boolobject.rs +++ b/src/types/boolobject.rs @@ -22,7 +22,7 @@ use std::ptr; #[repr(transparent)] pub struct PyBool(PyAny); -pyobject_native_type!(PyBool, ffi::PyObject, pyobject_native_static_type_object!(ffi::PyBool_Type), #checkfunction=ffi::PyBool_Check); +pyobject_native_type!(PyBool, ffi::PyObject, pyobject_native_static_type_object!(ffi::PyBool_Type), "builtins", "bool", #checkfunction=ffi::PyBool_Check); impl PyBool { /// Depending on `val`, returns `true` or `false`. diff --git a/src/types/bytearray.rs b/src/types/bytearray.rs index 83fde20d4ec..473d1c60484 100644 --- a/src/types/bytearray.rs +++ b/src/types/bytearray.rs @@ -16,7 +16,7 @@ use std::slice; #[repr(transparent)] pub struct PyByteArray(PyAny); -pyobject_native_type_core!(PyByteArray, pyobject_native_static_type_object!(ffi::PyByteArray_Type), #checkfunction=ffi::PyByteArray_Check); +pyobject_native_type_core!(PyByteArray, pyobject_native_static_type_object!(ffi::PyByteArray_Type), "builtins", "bytearray", #checkfunction=ffi::PyByteArray_Check); impl PyByteArray { /// Creates a new Python bytearray object. diff --git a/src/types/bytes.rs b/src/types/bytes.rs index c6fa1d65329..f5cce1e81cb 100644 --- a/src/types/bytes.rs +++ b/src/types/bytes.rs @@ -47,7 +47,7 @@ use std::str; #[repr(transparent)] pub struct PyBytes(PyAny); -pyobject_native_type_core!(PyBytes, pyobject_native_static_type_object!(ffi::PyBytes_Type), #checkfunction=ffi::PyBytes_Check); +pyobject_native_type_core!(PyBytes, pyobject_native_static_type_object!(ffi::PyBytes_Type), "builtins", "bytes", #checkfunction=ffi::PyBytes_Check); impl PyBytes { /// Creates a new Python bytestring object. diff --git a/src/types/capsule.rs b/src/types/capsule.rs index 9e4f3cf1e64..35bcdb69551 100644 --- a/src/types/capsule.rs +++ b/src/types/capsule.rs @@ -49,7 +49,7 @@ use std::ptr::{self, NonNull}; #[repr(transparent)] pub struct PyCapsule(PyAny); -pyobject_native_type_core!(PyCapsule, pyobject_native_static_type_object!(ffi::PyCapsule_Type), #checkfunction=ffi::PyCapsule_CheckExact); +pyobject_native_type_core!(PyCapsule, pyobject_native_static_type_object!(ffi::PyCapsule_Type), "types", "CapsuleType", #checkfunction=ffi::PyCapsule_CheckExact); impl PyCapsule { /// Constructs a new capsule whose contents are `value`, associated with `name`. diff --git a/src/types/code.rs b/src/types/code.rs index 1a2068e6ab7..ffe6c964095 100644 --- a/src/types/code.rs +++ b/src/types/code.rs @@ -22,14 +22,21 @@ pub struct PyCode(PyAny); pyobject_native_type_core!( PyCode, pyobject_native_static_type_object!(ffi::PyCode_Type), + "types", + "CodeType", #checkfunction=ffi::PyCode_Check ); #[cfg(any(Py_LIMITED_API, PyPy))] -pyobject_native_type_core!(PyCode, |py| { - static TYPE: PyOnceLock> = PyOnceLock::new(); - TYPE.import(py, "types", "CodeType").unwrap().as_type_ptr() -}); +pyobject_native_type_core!( + PyCode, + |py| { + static TYPE: PyOnceLock> = PyOnceLock::new(); + TYPE.import(py, "types", "CodeType").unwrap().as_type_ptr() + }, + "types", + "CodeType" +); /// Compilation mode of [`PyCode::compile`] pub enum PyCodeInput { diff --git a/src/types/complex.rs b/src/types/complex.rs index 7f9ac4e6191..d7b7bca1d4b 100644 --- a/src/types/complex.rs +++ b/src/types/complex.rs @@ -27,6 +27,8 @@ pyobject_native_type!( PyComplex, ffi::PyComplexObject, pyobject_native_static_type_object!(ffi::PyComplex_Type), + "builtins", + "complex", #checkfunction=ffi::PyComplex_Check ); diff --git a/src/types/datetime.rs b/src/types/datetime.rs index 8c57aa8a43a..36c9e87cfbb 100644 --- a/src/types/datetime.rs +++ b/src/types/datetime.rs @@ -201,7 +201,8 @@ pyobject_native_type!( PyDate, crate::ffi::PyDateTime_Date, |py| expect_datetime_api(py).DateType, - #module=Some("datetime"), + "datetime", + "date", #checkfunction=PyDate_Check ); #[cfg(not(Py_LIMITED_API))] @@ -214,7 +215,8 @@ pyobject_native_type_core!( static TYPE: PyOnceLock> = PyOnceLock::new(); TYPE.import(py, "datetime", "date").unwrap().as_type_ptr() }, - #module=Some("datetime") + "datetime", + "date" ); impl PyDate { @@ -287,7 +289,8 @@ pyobject_native_type!( PyDateTime, crate::ffi::PyDateTime_DateTime, |py| expect_datetime_api(py).DateTimeType, - #module=Some("datetime"), + "datetime", + "datetime", #checkfunction=PyDateTime_Check ); #[cfg(not(Py_LIMITED_API))] @@ -298,9 +301,12 @@ pyobject_native_type_core!( PyDateTime, |py| { static TYPE: PyOnceLock> = PyOnceLock::new(); - TYPE.import(py, "datetime", "datetime").unwrap().as_type_ptr() + TYPE.import(py, "datetime", "datetime") + .unwrap() + .as_type_ptr() }, - #module=Some("datetime") + "datetime", + "datetime" ); impl PyDateTime { @@ -519,7 +525,8 @@ pyobject_native_type!( PyTime, crate::ffi::PyDateTime_Time, |py| expect_datetime_api(py).TimeType, - #module=Some("datetime"), + "datetime", + "time", #checkfunction=PyTime_Check ); #[cfg(not(Py_LIMITED_API))] @@ -532,7 +539,8 @@ pyobject_native_type_core!( static TYPE: PyOnceLock> = PyOnceLock::new(); TYPE.import(py, "datetime", "time").unwrap().as_type_ptr() }, - #module=Some("datetime") + "datetime", + "time" ); impl PyTime { @@ -689,7 +697,7 @@ pyobject_native_type!( PyTzInfo, crate::ffi::PyObject, |py| expect_datetime_api(py).TZInfoType, - #module=Some("datetime"), + "datetime", "tzinfo", #checkfunction=PyTZInfo_Check ); #[cfg(not(Py_LIMITED_API))] @@ -702,7 +710,8 @@ pyobject_native_type_core!( static TYPE: PyOnceLock> = PyOnceLock::new(); TYPE.import(py, "datetime", "tzinfo").unwrap().as_type_ptr() }, - #module=Some("datetime") + "datetime", + "tzinfo" ); impl PyTzInfo { @@ -798,7 +807,7 @@ pyobject_native_type!( PyDelta, crate::ffi::PyDateTime_Delta, |py| expect_datetime_api(py).DeltaType, - #module=Some("datetime"), + "datetime", "timedelta", #checkfunction=PyDelta_Check ); #[cfg(not(Py_LIMITED_API))] @@ -809,9 +818,12 @@ pyobject_native_type_core!( PyDelta, |py| { static TYPE: PyOnceLock> = PyOnceLock::new(); - TYPE.import(py, "datetime", "timedelta").unwrap().as_type_ptr() + TYPE.import(py, "datetime", "timedelta") + .unwrap() + .as_type_ptr() }, - #module=Some("datetime") + "datetime", + "timedelta" ); impl PyDelta { diff --git a/src/types/dict.rs b/src/types/dict.rs index 34add02c608..7f7ad6b65b2 100644 --- a/src/types/dict.rs +++ b/src/types/dict.rs @@ -22,6 +22,8 @@ pyobject_native_type!( PyDict, ffi::PyDictObject, pyobject_native_static_type_object!(ffi::PyDict_Type), + "builtins", + "dict", #checkfunction=ffi::PyDict_Check ); @@ -34,6 +36,8 @@ pub struct PyDictKeys(PyAny); pyobject_native_type_core!( PyDictKeys, pyobject_native_static_type_object!(ffi::PyDictKeys_Type), + "builtins", + "dict_keys", #checkfunction=ffi::PyDictKeys_Check ); @@ -46,6 +50,8 @@ pub struct PyDictValues(PyAny); pyobject_native_type_core!( PyDictValues, pyobject_native_static_type_object!(ffi::PyDictValues_Type), + "builtins", + "dict_values", #checkfunction=ffi::PyDictValues_Check ); @@ -58,6 +64,8 @@ pub struct PyDictItems(PyAny); pyobject_native_type_core!( PyDictItems, pyobject_native_static_type_object!(ffi::PyDictItems_Type), + "builtins", + "dict_items", #checkfunction=ffi::PyDictItems_Check ); diff --git a/src/types/float.rs b/src/types/float.rs index 5a3e22802aa..7e5b2e45ab0 100644 --- a/src/types/float.rs +++ b/src/types/float.rs @@ -29,6 +29,8 @@ pyobject_native_type!( PyFloat, ffi::PyFloatObject, pyobject_native_static_type_object!(ffi::PyFloat_Type), + "builtins", + "float", #checkfunction=ffi::PyFloat_Check ); diff --git a/src/types/frame.rs b/src/types/frame.rs index 8d88d4754ae..e42763f5a77 100644 --- a/src/types/frame.rs +++ b/src/types/frame.rs @@ -11,5 +11,7 @@ pub struct PyFrame(PyAny); pyobject_native_type_core!( PyFrame, pyobject_native_static_type_object!(ffi::PyFrame_Type), + "types", + "FrameType", #checkfunction=ffi::PyFrame_Check ); diff --git a/src/types/frozenset.rs b/src/types/frozenset.rs index 45787a13b63..21d00982718 100644 --- a/src/types/frozenset.rs +++ b/src/types/frozenset.rs @@ -67,6 +67,8 @@ pyobject_native_type!( PyFrozenSet, ffi::PySetObject, pyobject_native_static_type_object!(ffi::PyFrozenSet_Type), + "builtins", + "frozenset", #checkfunction=ffi::PyFrozenSet_Check ); @@ -74,6 +76,8 @@ pyobject_native_type!( pyobject_native_type_core!( PyFrozenSet, pyobject_native_static_type_object!(ffi::PyFrozenSet_Type), + "builtins", + "frozenset", #checkfunction=ffi::PyFrozenSet_Check ); diff --git a/src/types/function.rs b/src/types/function.rs index a43a1aee7b8..443266611a0 100644 --- a/src/types/function.rs +++ b/src/types/function.rs @@ -19,7 +19,7 @@ use std::ptr::NonNull; #[repr(transparent)] pub struct PyCFunction(PyAny); -pyobject_native_type_core!(PyCFunction, pyobject_native_static_type_object!(ffi::PyCFunction_Type), #checkfunction=ffi::PyCFunction_Check); +pyobject_native_type_core!(PyCFunction, pyobject_native_static_type_object!(ffi::PyCFunction_Type), "builtins", "builtin_function_or_method", #checkfunction=ffi::PyCFunction_Check); impl PyCFunction { /// Create a new built-in function with keywords (*args and/or **kwargs). @@ -167,4 +167,4 @@ unsafe impl Send for ClosureDestructor {} pub struct PyFunction(PyAny); #[cfg(not(Py_LIMITED_API))] -pyobject_native_type_core!(PyFunction, pyobject_native_static_type_object!(ffi::PyFunction_Type), #checkfunction=ffi::PyFunction_Check); +pyobject_native_type_core!(PyFunction, pyobject_native_static_type_object!(ffi::PyFunction_Type), "builtins", "function", #checkfunction=ffi::PyFunction_Check); diff --git a/src/types/genericalias.rs b/src/types/genericalias.rs index 3efafb065aa..477ea99f618 100644 --- a/src/types/genericalias.rs +++ b/src/types/genericalias.rs @@ -17,7 +17,9 @@ pub struct PyGenericAlias(PyAny); pyobject_native_type!( PyGenericAlias, ffi::PyDictObject, - pyobject_native_static_type_object!(ffi::Py_GenericAliasType) + pyobject_native_static_type_object!(ffi::Py_GenericAliasType), + "builtins", + "GenericAlias" ); impl PyGenericAlias { diff --git a/src/types/iterator.rs b/src/types/iterator.rs index 2ab1bda1722..a2e31d062d9 100644 --- a/src/types/iterator.rs +++ b/src/types/iterator.rs @@ -40,7 +40,8 @@ pyobject_native_type_core!( .unwrap() .as_type_ptr() }, - #module=Some("collections.abc"), + "collections.abc", + "Iterator", #checkfunction=ffi::PyIter_Check ); diff --git a/src/types/list.rs b/src/types/list.rs index 53b686e8e18..3143a51518b 100644 --- a/src/types/list.rs +++ b/src/types/list.rs @@ -19,7 +19,7 @@ use std::num::NonZero; #[repr(transparent)] pub struct PyList(PyAny); -pyobject_native_type_core!(PyList, pyobject_native_static_type_object!(ffi::PyList_Type), #checkfunction=ffi::PyList_Check); +pyobject_native_type_core!(PyList, pyobject_native_static_type_object!(ffi::PyList_Type), "builtins", "list", #checkfunction=ffi::PyList_Check); #[inline] #[track_caller] diff --git a/src/types/mappingproxy.rs b/src/types/mappingproxy.rs index 478621a7163..d77088e4247 100644 --- a/src/types/mappingproxy.rs +++ b/src/types/mappingproxy.rs @@ -22,6 +22,8 @@ unsafe fn dict_proxy_check(op: *mut ffi::PyObject) -> c_int { pyobject_native_type_core!( PyMappingProxy, pyobject_native_static_type_object!(ffi::PyDictProxy_Type), + "types", + "MappingProxyType", #checkfunction=dict_proxy_check ); diff --git a/src/types/memoryview.rs b/src/types/memoryview.rs index cdb252e72d2..9fd245b4691 100644 --- a/src/types/memoryview.rs +++ b/src/types/memoryview.rs @@ -10,7 +10,7 @@ use crate::{ffi, Bound, PyAny}; #[repr(transparent)] pub struct PyMemoryView(PyAny); -pyobject_native_type_core!(PyMemoryView, pyobject_native_static_type_object!(ffi::PyMemoryView_Type), #checkfunction=ffi::PyMemoryView_Check); +pyobject_native_type_core!(PyMemoryView, pyobject_native_static_type_object!(ffi::PyMemoryView_Type), "builtins", "memoryview", #checkfunction=ffi::PyMemoryView_Check); impl PyMemoryView { /// Creates a new Python `memoryview` object from another Python object that diff --git a/src/types/mod.rs b/src/types/mod.rs index df2051badf1..3bbd62e1ea3 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -150,11 +150,13 @@ macro_rules! pyobject_native_static_type_object( #[doc(hidden)] #[macro_export] macro_rules! pyobject_native_type_info( - ($name:ty, $typeobject:expr, $module:expr $(, #checkfunction=$checkfunction:path)? $(;$generics:ident)*) => { + ($name:ty, $typeobject:expr, $module:expr, $python_name:expr $(, #checkfunction=$checkfunction:path)? $(;$generics:ident)*) => { // SAFETY: macro caller has upheld the safety contracts unsafe impl<$($generics,)*> $crate::type_object::PyTypeInfo for $name { const NAME: &'static str = stringify!($name); - const MODULE: ::std::option::Option<&'static str> = $module; + const MODULE: ::std::option::Option<&'static str> = ::std::option::Option::Some($module); + #[cfg(feature = "experimental-inspect")] + const TYPE_HINT: $crate::inspect::TypeHint = $crate::inspect::TypeHint::module_attr($module, $python_name); #[inline] #[allow(clippy::redundant_closure_call)] @@ -187,12 +189,12 @@ macro_rules! pyobject_native_type_info( #[doc(hidden)] #[macro_export] macro_rules! pyobject_native_type_core { - ($name:ty, $typeobject:expr, #module=$module:expr $(, #checkfunction=$checkfunction:path)? $(;$generics:ident)*) => { + ($name:ty, $typeobject:expr, $module:expr, $python_name:expr $(, #checkfunction=$checkfunction:path)? $(;$generics:ident)*) => { $crate::pyobject_native_type_named!($name $(;$generics)*); - $crate::pyobject_native_type_info!($name, $typeobject, $module $(, #checkfunction=$checkfunction)? $(;$generics)*); + $crate::pyobject_native_type_info!($name, $typeobject, $module, $python_name $(, #checkfunction=$checkfunction)? $(;$generics)*); }; - ($name:ty, $typeobject:expr $(, #checkfunction=$checkfunction:path)? $(;$generics:ident)*) => { - $crate::pyobject_native_type_core!($name, $typeobject, #module=::std::option::Option::Some("builtins") $(, #checkfunction=$checkfunction)? $(;$generics)*); + ($name:ty, $typeobject:expr, $python_name:expr $(, #checkfunction=$checkfunction:path)? $(;$generics:ident)*) => { + $crate::pyobject_native_type_core!($name, $typeobject, "builtins", $python_name $(, #checkfunction=$checkfunction)? $(;$generics)*); }; } @@ -224,8 +226,8 @@ macro_rules! pyobject_native_type_sized { #[doc(hidden)] #[macro_export] macro_rules! pyobject_native_type { - ($name:ty, $layout:path, $typeobject:expr $(, #module=$module:expr)? $(, #checkfunction=$checkfunction:path)? $(;$generics:ident)*) => { - $crate::pyobject_native_type_core!($name, $typeobject $(, #module=$module)? $(, #checkfunction=$checkfunction)? $(;$generics)*); + ($name:ty, $layout:path, $typeobject:expr, $module:expr, $python_name:expr $(, #checkfunction=$checkfunction:path)? $(;$generics:ident)*) => { + $crate::pyobject_native_type_core!($name, $typeobject, $module, $python_name $(, #checkfunction=$checkfunction)? $(;$generics)*); // To prevent inheriting native types with ABI3 #[cfg(not(Py_LIMITED_API))] $crate::pyobject_native_type_sized!($name, $layout $(;$generics)*); diff --git a/src/types/module.rs b/src/types/module.rs index 0f03317d378..bf214a8fcb2 100644 --- a/src/types/module.rs +++ b/src/types/module.rs @@ -30,7 +30,7 @@ use std::str; #[repr(transparent)] pub struct PyModule(PyAny); -pyobject_native_type_core!(PyModule, pyobject_native_static_type_object!(ffi::PyModule_Type), #checkfunction=ffi::PyModule_Check); +pyobject_native_type_core!(PyModule, pyobject_native_static_type_object!(ffi::PyModule_Type), "types", "ModuleType", #checkfunction=ffi::PyModule_Check); impl PyModule { /// Creates a new module object with the `__name__` attribute set to `name`. diff --git a/src/types/num.rs b/src/types/num.rs index 41ecb5ea527..8b6129b5f7d 100644 --- a/src/types/num.rs +++ b/src/types/num.rs @@ -13,7 +13,7 @@ use std::convert::Infallible; #[repr(transparent)] pub struct PyInt(PyAny); -pyobject_native_type_core!(PyInt, pyobject_native_static_type_object!(ffi::PyLong_Type), #checkfunction=ffi::PyLong_Check); +pyobject_native_type_core!(PyInt, pyobject_native_static_type_object!(ffi::PyLong_Type), "builtins", "int", #checkfunction=ffi::PyLong_Check); impl PyInt { /// Creates a new Python int object. diff --git a/src/types/pysuper.rs b/src/types/pysuper.rs index b8f3cef2d06..5723f6951d8 100644 --- a/src/types/pysuper.rs +++ b/src/types/pysuper.rs @@ -13,7 +13,9 @@ pub struct PySuper(PyAny); pyobject_native_type_core!( PySuper, - pyobject_native_static_type_object!(ffi::PySuper_Type) + pyobject_native_static_type_object!(ffi::PySuper_Type), + "builtins", + "super" ); impl PySuper { diff --git a/src/types/range.rs b/src/types/range.rs index 00c7ce2a325..773dd0ed68d 100644 --- a/src/types/range.rs +++ b/src/types/range.rs @@ -12,7 +12,7 @@ use crate::{ffi, Bound, PyAny, PyResult, PyTypeInfo, Python}; #[repr(transparent)] pub struct PyRange(PyAny); -pyobject_native_type_core!(PyRange, pyobject_native_static_type_object!(ffi::PyRange_Type), #checkfunction=ffi::PyRange_Check); +pyobject_native_type_core!(PyRange, pyobject_native_static_type_object!(ffi::PyRange_Type), "builtins", "range", #checkfunction=ffi::PyRange_Check); impl<'py> PyRange { /// Creates a new Python `range` object with a default step of 1. diff --git a/src/types/set.rs b/src/types/set.rs index 2f22b2f9ba2..69804c18092 100644 --- a/src/types/set.rs +++ b/src/types/set.rs @@ -26,6 +26,8 @@ pyobject_native_type!( PySet, ffi::PySetObject, pyobject_native_static_type_object!(ffi::PySet_Type), + "builtins", + "set", #checkfunction=ffi::PySet_Check ); @@ -33,6 +35,8 @@ pyobject_native_type!( pyobject_native_type_core!( PySet, pyobject_native_static_type_object!(ffi::PySet_Type), + "builtins", + "set", #checkfunction=ffi::PySet_Check ); diff --git a/src/types/slice.rs b/src/types/slice.rs index 78248d54c40..89af67687de 100644 --- a/src/types/slice.rs +++ b/src/types/slice.rs @@ -21,6 +21,8 @@ pyobject_native_type!( PySlice, ffi::PySliceObject, pyobject_native_static_type_object!(ffi::PySlice_Type), + "builtins", + "slice", #checkfunction=ffi::PySlice_Check ); diff --git a/src/types/string.rs b/src/types/string.rs index 6d906d09a10..6d993a9a530 100644 --- a/src/types/string.rs +++ b/src/types/string.rs @@ -152,7 +152,7 @@ impl<'a> PyStringData<'a> { #[repr(transparent)] pub struct PyString(PyAny); -pyobject_native_type_core!(PyString, pyobject_native_static_type_object!(ffi::PyUnicode_Type), #checkfunction=ffi::PyUnicode_Check); +pyobject_native_type_core!(PyString, pyobject_native_static_type_object!(ffi::PyUnicode_Type), "builtins", "str", #checkfunction=ffi::PyUnicode_Check); impl PyString { /// Creates a new Python string object. diff --git a/src/types/traceback.rs b/src/types/traceback.rs index 2dfdc3121ba..260916d2b19 100644 --- a/src/types/traceback.rs +++ b/src/types/traceback.rs @@ -15,6 +15,8 @@ pub struct PyTraceback(PyAny); pyobject_native_type_core!( PyTraceback, pyobject_native_static_type_object!(ffi::PyTraceBack_Type), + "builtins", + "traceback", #checkfunction=ffi::PyTraceBack_Check ); diff --git a/src/types/tuple.rs b/src/types/tuple.rs index 719cca50c40..8dab7d5ffba 100644 --- a/src/types/tuple.rs +++ b/src/types/tuple.rs @@ -58,7 +58,7 @@ fn try_new_from_iter<'py>( #[repr(transparent)] pub struct PyTuple(PyAny); -pyobject_native_type_core!(PyTuple, pyobject_native_static_type_object!(ffi::PyTuple_Type), #checkfunction=ffi::PyTuple_Check); +pyobject_native_type_core!(PyTuple, pyobject_native_static_type_object!(ffi::PyTuple_Type), "builtins", "tuple", #checkfunction=ffi::PyTuple_Check); impl PyTuple { /// Constructs a new tuple with the given elements. diff --git a/src/types/typeobject.rs b/src/types/typeobject.rs index f04cc2ada7f..1a49eab1641 100644 --- a/src/types/typeobject.rs +++ b/src/types/typeobject.rs @@ -19,7 +19,7 @@ use super::PyString; #[repr(transparent)] pub struct PyType(PyAny); -pyobject_native_type_core!(PyType, pyobject_native_static_type_object!(ffi::PyType_Type), #checkfunction=ffi::PyType_Check); +pyobject_native_type_core!(PyType, pyobject_native_static_type_object!(ffi::PyType_Type), "builtins", "type", #checkfunction=ffi::PyType_Check); impl PyType { /// Creates a new type object. diff --git a/src/types/weakref/reference.rs b/src/types/weakref/reference.rs index 74369a6ba5a..760cf2eb6bb 100644 --- a/src/types/weakref/reference.rs +++ b/src/types/weakref/reference.rs @@ -29,7 +29,8 @@ pyobject_native_type!( ffi::PyWeakReference, // TODO: should not be depending on a private symbol here! pyobject_native_static_type_object!(ffi::_PyWeakref_RefType), - #module=Some("weakref"), + "weakref", + "ReferenceType", #checkfunction=ffi::PyWeakref_CheckRefExact ); @@ -43,7 +44,8 @@ pyobject_native_type_core!( .unwrap() .as_type_ptr() }, - #module=Some("weakref"), + "weakref", + "ReferenceType", #checkfunction=ffi::PyWeakref_CheckRef ); From ee8cdef9eebb29d1262a6c9925c0afb7b143ea3a Mon Sep 17 00:00:00 2001 From: Thomas Pellissier-Tanon Date: Thu, 13 Nov 2025 17:33:08 +0100 Subject: [PATCH 2/5] Code review feedback --- src/types/any.rs | 1 + src/types/mod.rs | 39 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/types/any.rs b/src/types/any.rs index b6c78a2e8c5..99f893ff39a 100644 --- a/src/types/any.rs +++ b/src/types/any.rs @@ -40,6 +40,7 @@ fn PyObject_Check(_: *mut ffi::PyObject) -> c_int { 1 } +// We follow stub writing guidelines and use "object" instead of "typing.Any": https://typing.python.org/en/latest/guides/writing_stubs.html#using-any pyobject_native_type_info!( PyAny, pyobject_native_static_type_object!(ffi::PyBaseObject_Type), diff --git a/src/types/mod.rs b/src/types/mod.rs index 3bbd62e1ea3..a12c72ee916 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -147,6 +147,44 @@ macro_rules! pyobject_native_static_type_object( /// - `$typeobject` must be a function that produces a valid `*mut PyTypeObject` /// - `$checkfunction` must be a function that accepts arbitrary `*mut PyObject` and returns true / /// false according to whether the object is an instance of the type from `$typeobject` +#[cfg(not(feature = "experimental-inspect"))] +#[doc(hidden)] +#[macro_export] +macro_rules! pyobject_native_type_info( + ($name:ty, $typeobject:expr, $module:expr, $python_name:expr $(, #checkfunction=$checkfunction:path)? $(;$generics:ident)*) => { + // SAFETY: macro caller has upheld the safety contracts + unsafe impl<$($generics,)*> $crate::type_object::PyTypeInfo for $name { + const NAME: &'static str = stringify!($name); + const MODULE: ::std::option::Option<&'static str> = ::std::option::Option::Some($module); + + #[inline] + #[allow(clippy::redundant_closure_call)] + fn type_object_raw(py: $crate::Python<'_>) -> *mut $crate::ffi::PyTypeObject { + $typeobject(py) + } + + $( + #[inline] + fn is_type_of(obj: &$crate::Bound<'_, $crate::PyAny>) -> bool { + #[allow(unused_unsafe, reason = "not all `$checkfunction` are unsafe fn")] + // SAFETY: `$checkfunction` is being called with a valid `PyObject` pointer + unsafe { $checkfunction(obj.as_ptr()) > 0 } + } + )? + } + + impl $name { + #[doc(hidden)] + pub const _PYO3_DEF: $crate::impl_::pymodule::AddTypeToModule = $crate::impl_::pymodule::AddTypeToModule::new(); + + #[allow(dead_code)] + #[doc(hidden)] + pub const _PYO3_INTROSPECTION_ID: &'static str = concat!(stringify!($module), stringify!($name)); + } + }; +); + +#[cfg(feature = "experimental-inspect")] #[doc(hidden)] #[macro_export] macro_rules! pyobject_native_type_info( @@ -155,7 +193,6 @@ macro_rules! pyobject_native_type_info( unsafe impl<$($generics,)*> $crate::type_object::PyTypeInfo for $name { const NAME: &'static str = stringify!($name); const MODULE: ::std::option::Option<&'static str> = ::std::option::Option::Some($module); - #[cfg(feature = "experimental-inspect")] const TYPE_HINT: $crate::inspect::TypeHint = $crate::inspect::TypeHint::module_attr($module, $python_name); #[inline] From 3bf516e3cae01e89653ab30d7c51cbb6964af419 Mon Sep 17 00:00:00 2001 From: Thomas Pellissier-Tanon Date: Thu, 13 Nov 2025 17:48:09 +0100 Subject: [PATCH 3/5] Add back original module --- src/exceptions.rs | 37 ++++++++++------------------------ src/types/any.rs | 1 + src/types/datetime.rs | 26 +++++++++++++++++------- src/types/iterator.rs | 1 + src/types/mod.rs | 22 ++++++++++---------- src/types/weakref/reference.rs | 2 ++ 6 files changed, 45 insertions(+), 44 deletions(-) diff --git a/src/exceptions.rs b/src/exceptions.rs index 3535306caa4..5d5ac6b86f9 100644 --- a/src/exceptions.rs +++ b/src/exceptions.rs @@ -72,10 +72,7 @@ macro_rules! import_exception { /// /// [`pyo3::import_exception!`]: https://docs.rs/pyo3/latest/pyo3/macro.import_exception.html "import_exception in pyo3" #[repr(transparent)] - #[allow( - non_camel_case_types, - reason = "matches imported exception name, e.g. `socket.herror`" - )] + #[allow(non_camel_case_types, reason = "matches imported exception name, e.g. `socket.herror`")] pub struct $name($crate::PyAny); $crate::impl_exception_boilerplate!($name); @@ -84,17 +81,15 @@ macro_rules! import_exception { $name, $name::type_object_raw, stringify!($name), - stringify!($module) + stringify!($module), + #module=::std::option::Option::Some(stringify!($module)) ); impl $name { fn type_object_raw(py: $crate::Python<'_>) -> *mut $crate::ffi::PyTypeObject { use $crate::types::PyTypeMethods; static TYPE_OBJECT: $crate::impl_::exceptions::ImportedExceptionTypeObject = - $crate::impl_::exceptions::ImportedExceptionTypeObject::new( - stringify!($module), - stringify!($name), - ); + $crate::impl_::exceptions::ImportedExceptionTypeObject::new(stringify!($module), stringify!($name)); TYPE_OBJECT.get(py).as_type_ptr() } } @@ -206,19 +201,15 @@ macro_rules! create_exception_type_object { $crate::create_exception_type_object!($module, $name, $base, ::std::option::Option::None); }; ($module: expr, $name: ident, $base: ty, Some($doc: expr)) => { - $crate::create_exception_type_object!( - $module, - $name, - $base, - ::std::option::Option::Some($crate::ffi::c_str!($doc)) - ); + $crate::create_exception_type_object!($module, $name, $base, ::std::option::Option::Some($crate::ffi::c_str!($doc))); }; ($module: expr, $name: ident, $base: ty, $doc: expr) => { $crate::pyobject_native_type_core!( $name, $name::type_object_raw, stringify!($name), - stringify!($module) + stringify!($module), + #module=::std::option::Option::Some(stringify!($module)) ); impl $name { @@ -228,21 +219,15 @@ macro_rules! create_exception_type_object { PyOnceLock::new(); TYPE_OBJECT - .get_or_init(py, || { + .get_or_init(py, || $crate::PyErr::new_type( py, - $crate::ffi::c_str!(concat!( - stringify!($module), - ".", - stringify!($name) - )), + $crate::ffi::c_str!(concat!(stringify!($module), ".", stringify!($name))), $doc, ::std::option::Option::Some(&py.get_type::<$base>()), ::std::option::Option::None, - ) - .expect("Failed to initialize new exception type.") - }) - .as_ptr() as *mut $crate::ffi::PyTypeObject + ).expect("Failed to initialize new exception type.") + ).as_ptr() as *mut $crate::ffi::PyTypeObject } } }; diff --git a/src/types/any.rs b/src/types/any.rs index 99f893ff39a..775d7099aba 100644 --- a/src/types/any.rs +++ b/src/types/any.rs @@ -46,6 +46,7 @@ pyobject_native_type_info!( pyobject_native_static_type_object!(ffi::PyBaseObject_Type), "builtins", "object", + Some("builtins"), #checkfunction=PyObject_Check ); diff --git a/src/types/datetime.rs b/src/types/datetime.rs index 36c9e87cfbb..307cb4a8b25 100644 --- a/src/types/datetime.rs +++ b/src/types/datetime.rs @@ -203,6 +203,7 @@ pyobject_native_type!( |py| expect_datetime_api(py).DateType, "datetime", "date", + #module=Some("datetime"), #checkfunction=PyDate_Check ); #[cfg(not(Py_LIMITED_API))] @@ -216,7 +217,8 @@ pyobject_native_type_core!( TYPE.import(py, "datetime", "date").unwrap().as_type_ptr() }, "datetime", - "date" + "date", + #module=Some("datetime") ); impl PyDate { @@ -291,6 +293,7 @@ pyobject_native_type!( |py| expect_datetime_api(py).DateTimeType, "datetime", "datetime", + #module=Some("datetime"), #checkfunction=PyDateTime_Check ); #[cfg(not(Py_LIMITED_API))] @@ -306,7 +309,8 @@ pyobject_native_type_core!( .as_type_ptr() }, "datetime", - "datetime" + "datetime", + #module=Some("datetime") ); impl PyDateTime { @@ -527,6 +531,7 @@ pyobject_native_type!( |py| expect_datetime_api(py).TimeType, "datetime", "time", + #module=Some("datetime"), #checkfunction=PyTime_Check ); #[cfg(not(Py_LIMITED_API))] @@ -540,7 +545,8 @@ pyobject_native_type_core!( TYPE.import(py, "datetime", "time").unwrap().as_type_ptr() }, "datetime", - "time" + "time", + #module=Some("datetime") ); impl PyTime { @@ -697,7 +703,9 @@ pyobject_native_type!( PyTzInfo, crate::ffi::PyObject, |py| expect_datetime_api(py).TZInfoType, - "datetime", "tzinfo", + "datetime", + "tzinfo", + #module=Some("datetime"), #checkfunction=PyTZInfo_Check ); #[cfg(not(Py_LIMITED_API))] @@ -711,7 +719,8 @@ pyobject_native_type_core!( TYPE.import(py, "datetime", "tzinfo").unwrap().as_type_ptr() }, "datetime", - "tzinfo" + "tzinfo", + #module=Some("datetime") ); impl PyTzInfo { @@ -807,7 +816,9 @@ pyobject_native_type!( PyDelta, crate::ffi::PyDateTime_Delta, |py| expect_datetime_api(py).DeltaType, - "datetime", "timedelta", + "datetime", + "timedelta", + #module=Some("datetime"), #checkfunction=PyDelta_Check ); #[cfg(not(Py_LIMITED_API))] @@ -823,7 +834,8 @@ pyobject_native_type_core!( .as_type_ptr() }, "datetime", - "timedelta" + "timedelta", + #module=Some("datetime") ); impl PyDelta { diff --git a/src/types/iterator.rs b/src/types/iterator.rs index a2e31d062d9..af783587651 100644 --- a/src/types/iterator.rs +++ b/src/types/iterator.rs @@ -42,6 +42,7 @@ pyobject_native_type_core!( }, "collections.abc", "Iterator", + #module=Some("collections.abc"), #checkfunction=ffi::PyIter_Check ); diff --git a/src/types/mod.rs b/src/types/mod.rs index a12c72ee916..4cf949a0a4a 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -151,11 +151,11 @@ macro_rules! pyobject_native_static_type_object( #[doc(hidden)] #[macro_export] macro_rules! pyobject_native_type_info( - ($name:ty, $typeobject:expr, $module:expr, $python_name:expr $(, #checkfunction=$checkfunction:path)? $(;$generics:ident)*) => { + ($name:ty, $typeobject:expr, $type_hint_module:expr, $type_hint_name:expr, $module:expr $(, #checkfunction=$checkfunction:path)? $(;$generics:ident)*) => { // SAFETY: macro caller has upheld the safety contracts unsafe impl<$($generics,)*> $crate::type_object::PyTypeInfo for $name { const NAME: &'static str = stringify!($name); - const MODULE: ::std::option::Option<&'static str> = ::std::option::Option::Some($module); + const MODULE: ::std::option::Option<&'static str> = $module; #[inline] #[allow(clippy::redundant_closure_call)] @@ -188,12 +188,12 @@ macro_rules! pyobject_native_type_info( #[doc(hidden)] #[macro_export] macro_rules! pyobject_native_type_info( - ($name:ty, $typeobject:expr, $module:expr, $python_name:expr $(, #checkfunction=$checkfunction:path)? $(;$generics:ident)*) => { + ($name:ty, $typeobject:expr, $type_hint_module:expr, $type_hint_name:expr, $module:expr $(, #checkfunction=$checkfunction:path)? $(;$generics:ident)*) => { // SAFETY: macro caller has upheld the safety contracts unsafe impl<$($generics,)*> $crate::type_object::PyTypeInfo for $name { const NAME: &'static str = stringify!($name); - const MODULE: ::std::option::Option<&'static str> = ::std::option::Option::Some($module); - const TYPE_HINT: $crate::inspect::TypeHint = $crate::inspect::TypeHint::module_attr($module, $python_name); + const MODULE: ::std::option::Option<&'static str> = $module; + const TYPE_HINT: $crate::inspect::TypeHint = $crate::inspect::TypeHint::module_attr($type_hint_module, $type_hint_name); #[inline] #[allow(clippy::redundant_closure_call)] @@ -226,12 +226,12 @@ macro_rules! pyobject_native_type_info( #[doc(hidden)] #[macro_export] macro_rules! pyobject_native_type_core { - ($name:ty, $typeobject:expr, $module:expr, $python_name:expr $(, #checkfunction=$checkfunction:path)? $(;$generics:ident)*) => { + ($name:ty, $typeobject:expr, $type_hint_module:expr, $type_hint_name:expr, #module=$module:expr $(, #checkfunction=$checkfunction:path)? $(;$generics:ident)*) => { $crate::pyobject_native_type_named!($name $(;$generics)*); - $crate::pyobject_native_type_info!($name, $typeobject, $module, $python_name $(, #checkfunction=$checkfunction)? $(;$generics)*); + $crate::pyobject_native_type_info!($name, $typeobject, $type_hint_module, $type_hint_name, $module $(, #checkfunction=$checkfunction)? $(;$generics)*); }; - ($name:ty, $typeobject:expr, $python_name:expr $(, #checkfunction=$checkfunction:path)? $(;$generics:ident)*) => { - $crate::pyobject_native_type_core!($name, $typeobject, "builtins", $python_name $(, #checkfunction=$checkfunction)? $(;$generics)*); + ($name:ty, $typeobject:expr, $type_hint_module:expr, $type_hint_name:expr $(, #checkfunction=$checkfunction:path)? $(;$generics:ident)*) => { + $crate::pyobject_native_type_core!($name, $typeobject, $type_hint_module, $type_hint_name, #module=::std::option::Option::Some("builtins") $(, #checkfunction=$checkfunction)? $(;$generics)*); }; } @@ -263,8 +263,8 @@ macro_rules! pyobject_native_type_sized { #[doc(hidden)] #[macro_export] macro_rules! pyobject_native_type { - ($name:ty, $layout:path, $typeobject:expr, $module:expr, $python_name:expr $(, #checkfunction=$checkfunction:path)? $(;$generics:ident)*) => { - $crate::pyobject_native_type_core!($name, $typeobject, $module, $python_name $(, #checkfunction=$checkfunction)? $(;$generics)*); + ($name:ty, $layout:path, $typeobject:expr, $type_hint_module:expr, $type_hint_name:expr $(, #module=$module:expr)? $(, #checkfunction=$checkfunction:path)? $(;$generics:ident)*) => { + $crate::pyobject_native_type_core!($name, $typeobject, $type_hint_module, $type_hint_name $(, #module=$module)? $(, #checkfunction=$checkfunction)? $(;$generics)*); // To prevent inheriting native types with ABI3 #[cfg(not(Py_LIMITED_API))] $crate::pyobject_native_type_sized!($name, $layout $(;$generics)*); diff --git a/src/types/weakref/reference.rs b/src/types/weakref/reference.rs index 760cf2eb6bb..ead294f1373 100644 --- a/src/types/weakref/reference.rs +++ b/src/types/weakref/reference.rs @@ -31,6 +31,7 @@ pyobject_native_type!( pyobject_native_static_type_object!(ffi::_PyWeakref_RefType), "weakref", "ReferenceType", + #module=Some("weakref"), #checkfunction=ffi::PyWeakref_CheckRefExact ); @@ -46,6 +47,7 @@ pyobject_native_type_core!( }, "weakref", "ReferenceType", + #module=Some("weakref"), #checkfunction=ffi::PyWeakref_CheckRef ); From 721dec904be570268c43b6a74596a835e648c620 Mon Sep 17 00:00:00 2001 From: Thomas Pellissier-Tanon Date: Tue, 18 Nov 2025 12:51:22 +0100 Subject: [PATCH 4/5] pyobject_type_info_type_hint --- src/types/mod.rs | 57 +++++++++++++++--------------------------------- 1 file changed, 18 insertions(+), 39 deletions(-) diff --git a/src/types/mod.rs b/src/types/mod.rs index 4cf949a0a4a..6abbf87b6aa 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -140,51 +140,30 @@ macro_rules! pyobject_native_static_type_object( }; ); -/// Implements the `PyTypeInfo` trait for a native Python type. -/// -/// # Safety -/// -/// - `$typeobject` must be a function that produces a valid `*mut PyTypeObject` -/// - `$checkfunction` must be a function that accepts arbitrary `*mut PyObject` and returns true / -/// false according to whether the object is an instance of the type from `$typeobject` +/// Adds a TYPE_HINT constant if the `experimental-inspect` feature is enabled. #[cfg(not(feature = "experimental-inspect"))] #[doc(hidden)] #[macro_export] -macro_rules! pyobject_native_type_info( - ($name:ty, $typeobject:expr, $type_hint_module:expr, $type_hint_name:expr, $module:expr $(, #checkfunction=$checkfunction:path)? $(;$generics:ident)*) => { - // SAFETY: macro caller has upheld the safety contracts - unsafe impl<$($generics,)*> $crate::type_object::PyTypeInfo for $name { - const NAME: &'static str = stringify!($name); - const MODULE: ::std::option::Option<&'static str> = $module; - - #[inline] - #[allow(clippy::redundant_closure_call)] - fn type_object_raw(py: $crate::Python<'_>) -> *mut $crate::ffi::PyTypeObject { - $typeobject(py) - } - - $( - #[inline] - fn is_type_of(obj: &$crate::Bound<'_, $crate::PyAny>) -> bool { - #[allow(unused_unsafe, reason = "not all `$checkfunction` are unsafe fn")] - // SAFETY: `$checkfunction` is being called with a valid `PyObject` pointer - unsafe { $checkfunction(obj.as_ptr()) > 0 } - } - )? - } - - impl $name { - #[doc(hidden)] - pub const _PYO3_DEF: $crate::impl_::pymodule::AddTypeToModule = $crate::impl_::pymodule::AddTypeToModule::new(); +macro_rules! pyobject_type_info_type_hint( + ($module:expr, $name:expr) => {}; +); - #[allow(dead_code)] - #[doc(hidden)] - pub const _PYO3_INTROSPECTION_ID: &'static str = concat!(stringify!($module), stringify!($name)); - } +#[cfg(feature = "experimental-inspect")] +#[doc(hidden)] +#[macro_export] +macro_rules! pyobject_type_info_type_hint( + ($module:expr, $name:expr) => { + const TYPE_HINT: $crate::inspect::TypeHint = $crate::inspect::TypeHint::module_attr($module, $name); }; ); -#[cfg(feature = "experimental-inspect")] +/// Implements the `PyTypeInfo` trait for a native Python type. +/// +/// # Safety +/// +/// - `$typeobject` must be a function that produces a valid `*mut PyTypeObject` +/// - `$checkfunction` must be a function that accepts arbitrary `*mut PyObject` and returns true / +/// false according to whether the object is an instance of the type from `$typeobject` #[doc(hidden)] #[macro_export] macro_rules! pyobject_native_type_info( @@ -193,7 +172,7 @@ macro_rules! pyobject_native_type_info( unsafe impl<$($generics,)*> $crate::type_object::PyTypeInfo for $name { const NAME: &'static str = stringify!($name); const MODULE: ::std::option::Option<&'static str> = $module; - const TYPE_HINT: $crate::inspect::TypeHint = $crate::inspect::TypeHint::module_attr($type_hint_module, $type_hint_name); + $crate::pyobject_type_info_type_hint!($type_hint_module, $type_hint_name); #[inline] #[allow(clippy::redundant_closure_call)] From 39c7c608bdbd91550c9c2abd034621d3b7ca7052 Mon Sep 17 00:00:00 2001 From: Thomas Pellissier-Tanon Date: Tue, 18 Nov 2025 15:56:44 +0100 Subject: [PATCH 5/5] Rebase --- pytests/stubs/pyclasses.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytests/stubs/pyclasses.pyi b/pytests/stubs/pyclasses.pyi index f12a4d97ef6..35cd67c7e10 100644 --- a/pytests/stubs/pyclasses.pyi +++ b/pytests/stubs/pyclasses.pyi @@ -1,5 +1,5 @@ from _typeshed import Incomplete -from typing import Any, final +from typing import final class AssertingBaseClass: def __new__(cls, /, expected_type: type) -> AssertingBaseClass: ...