cleanup: deprecate PyTypeObject trait

This commit is contained in:
David Hewitt 2022-04-07 14:37:45 +01:00
parent 76bc2a6a4a
commit 7e2d3117ce
20 changed files with 116 additions and 74 deletions

View file

@ -135,12 +135,12 @@ to ensure Rust's borrow rules.
See [the documentation](https://docs.rs/pyo3/latest/pyo3/pycell/struct.PyCell.html) for more.
`PyCell<T>` requires that `T` implements `PyClass`.
This trait is somewhat complex and derives many traits, but the most important one is `PyTypeObject`
This trait is somewhat complex and derives many traits, but the most important one is `PyTypeInfo`
in [`src/type_object.rs`].
`PyTypeObject` is also implemented for built-in types.
`PyTypeInfo` is also implemented for built-in types.
In Python, all objects have their types, and types are also objects of `type`.
For example, you can see `type({})` shows `dict` and `type(type({}))` shows `type` in Python REPL.
`T: PyTypeObject` implies that `T` has a corresponding type object.
`T: PyTypeInfo` implies that `T` has a corresponding type object.
## 4. Protocol methods

View file

@ -6,7 +6,7 @@ PyO3 versions, please see the [migration guide](https://pyo3.rs/latest/migration
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
## [Unreleased]
### Added
@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Move `PyTypeObject::type_object` method to `PyTypeInfo` trait, and deprecate `PyTypeObject` trait. [#2284](https://github.com/PyO3/pyo3/pull/2284)
- The deprecated `pyproto` feature is now disabled by default. [#2321](https://github.com/PyO3/pyo3/pull/2321)
## [0.16.4] - 2022-04-14

View file

@ -52,7 +52,7 @@ enum MyEnum {
Because Python objects are freely shared between threads by the Python interpreter, all types annotated with `#[pyclass]` must implement `Send` (unless annotated with [`#[pyclass(unsendable)]`](#customizing-the-class)).
The above example generates implementations for [`PyTypeInfo`], [`PyTypeObject`], and [`PyClass`] for `MyClass` and `MyEnum`. To see these generated implementations, refer to the [implementation details](#implementation-details) at the end of this chapter.
The above example generates implementations for [`PyTypeInfo`] and [`PyClass`] for `MyClass` and `MyEnum`. To see these generated implementations, refer to the [implementation details](#implementation-details) at the end of this chapter.
## Constructor
@ -1004,7 +1004,6 @@ impl pyo3::impl_::pyclass::PyClassImpl for MyClass {
[`GILGuard`]: {{#PYO3_DOCS_URL}}/pyo3/struct.GILGuard.html
[`PyTypeInfo`]: {{#PYO3_DOCS_URL}}/pyo3/type_object/trait.PyTypeInfo.html
[`PyTypeObject`]: {{#PYO3_DOCS_URL}}/pyo3/type_object/trait.PyTypeObject.html
[`PyCell`]: {{#PYO3_DOCS_URL}}/pyo3/pycell/struct.PyCell.html
[`PyClass`]: {{#PYO3_DOCS_URL}}/pyo3/pyclass/trait.PyClass.html

View file

@ -7,7 +7,7 @@ Before proceeding, we should think about how we want to handle overflows. There
be reinventing the wheel.
- We can raise exceptions whenever `Number` overflows, but that makes the API painful to use.
- We can wrap around the boundary of `i32`. This is the approach we'll take here. To do that we'll just forward to `i32`'s
`wrapping_*` methods.
`wrapping_*` methods.
### Fixing our constructor
@ -336,39 +336,39 @@ fn my_module(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
# def hash_djb2(s: str):
# n = Number(0)
# five = Number(5)
#
#
# for x in s:
# n = Number(ord(x)) + ((n << five) - n)
# return n
#
#
# assert hash_djb2('l50_50') == Number(-1152549421)
# assert hash_djb2('logo') == Number(3327403)
# assert hash_djb2('horizon') == Number(1097468315)
#
#
#
#
# assert Number(2) + Number(2) == Number(4)
# assert Number(2) + Number(2) != Number(5)
#
#
# assert Number(13) - Number(7) == Number(6)
# assert Number(13) - Number(-7) == Number(20)
#
#
# assert Number(13) / Number(7) == Number(1)
# assert Number(13) // Number(7) == Number(1)
#
#
# assert Number(13) * Number(7) == Number(13*7)
#
#
# assert Number(13) > Number(7)
# assert Number(13) < Number(20)
# assert Number(13) == Number(13)
# assert Number(13) >= Number(7)
# assert Number(13) <= Number(20)
# assert Number(13) == Number(13)
#
#
#
#
# assert (True if Number(1) else False)
# assert (False if Number(0) else True)
#
#
#
#
# assert int(Number(13)) == 13
# assert float(Number(13)) == 13
# assert Number.__doc__ == "Did you ever hear the tragedy of Darth Signed The Overfloweth? I thought not.\nIt's not a story C would tell you. It's a Rust legend."
@ -383,14 +383,14 @@ fn my_module(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
# assert Number(1337).__repr__() == 'Number(1337)'
"#;
#
# use pyo3::type_object::PyTypeObject;
#
#
# use pyo3::PyTypeInfo;
#
# fn main() -> PyResult<()> {
# Python::with_gil(|py| -> PyResult<()> {
# let globals = PyModule::import(py, "__main__")?.dict();
# globals.set_item("Number", Number::type_object(py))?;
#
#
# py.run(SCRIPT, Some(globals), None)?;
# Ok(())
# })
@ -446,4 +446,4 @@ fn wrap(obj: &PyAny) -> Result<i32, PyErr> {
[`PyErr::take`]: https://docs.rs/pyo3/latest/pyo3/prelude/struct.PyErr.html#method.take
[`Python`]: https://docs.rs/pyo3/latest/pyo3/struct.Python.html
[`FromPyObject`]: https://docs.rs/pyo3/latest/pyo3/conversion/trait.FromPyObject.html
[`pyo3::ffi::PyLong_AsUnsignedLongMask`]: https://docs.rs/pyo3/latest/pyo3/ffi/fn.PyLong_AsUnsignedLongMask.html
[`pyo3::ffi::PyLong_AsUnsignedLongMask`]: https://docs.rs/pyo3/latest/pyo3/ffi/fn.PyLong_AsUnsignedLongMask.html

View file

@ -9,6 +9,37 @@ For a detailed list of all changes, see the [CHANGELOG](changelog.md).
In preparation for removing the deprecated `#[pyproto]` attribute macro in a future PyO3 version, it is now gated behind an opt-in feature flag. This also gives a slight saving to compile times for code which does not use the deprecated macro.
### `PyTypeObject` trait has been deprecated
The `PyTypeObject` trait already was near-useless; almost all functionality was already on the `PyTypeInfo` trait, which `PyTypeObject` had a blanket implementation based upon. In PyO3 0.17 the final method, `PyTypeObject::type_object` was moved to `PyTypeInfo::type_object`.
To migrate, update trait bounds and imports from `PyTypeObject` to `PyTypeInfo`.
Before:
```rust,ignore
use pyo3::Python;
use pyo3::type_object::PyTypeObject;
use pyo3::types::PyType;
fn get_type_object<T: PyTypeObject>(py: Python<'_>) -> &PyType {
T::type_object(py)
}
```
After
```rust
use pyo3::{Python, PyTypeInfo};
use pyo3::types::PyType;
fn get_type_object<T: PyTypeInfo>(py: Python<'_>) -> &PyType {
T::type_object(py)
}
# Python::with_gil(|py| { get_type_object::<pyo3::types::PyList>(py); });
```
## from 0.15.* to 0.16
### Drop support for older technologies

View file

@ -1,9 +1,8 @@
use crate::{
exceptions::{PyBaseException, PyTypeError},
ffi,
type_object::PyTypeObject,
types::{PyTraceback, PyType},
AsPyPointer, IntoPy, IntoPyPointer, Py, PyObject, Python,
AsPyPointer, IntoPy, IntoPyPointer, Py, PyObject, PyTypeInfo, Python,
};
#[derive(Clone)]

View file

@ -1,7 +1,7 @@
// Copyright (c) 2017-present PyO3 Project and Contributors
use crate::panic::PanicException;
use crate::type_object::PyTypeObject;
use crate::type_object::PyTypeInfo;
use crate::types::{PyTraceback, PyType};
use crate::{
exceptions::{self, PyBaseException},
@ -92,7 +92,7 @@ impl PyErr {
#[inline]
pub fn new<T, A>(args: A) -> PyErr
where
T: PyTypeObject,
T: PyTypeInfo,
A: PyErrArguments + Send + Sync + 'static,
{
PyErr::from_state(PyErrState::LazyTypeAndValue {
@ -428,7 +428,7 @@ impl PyErr {
#[inline]
pub fn is_instance_of<T>(&self, py: Python<'_>) -> bool
where
T: PyTypeObject,
T: PyTypeInfo,
{
self.is_instance(py, T::type_object(py))
}

View file

@ -218,7 +218,7 @@ macro_rules! create_exception {
};
}
/// `impl $crate::type_object::PyTypeObject for $name` where `$name` is an
/// `impl PyTypeInfo for $name` where `$name` is an
/// exception newly defined in Rust code.
#[doc(hidden)]
#[macro_export]

View file

@ -1,9 +1,8 @@
use crate::{
exceptions::PyTypeError,
ffi,
type_object::PyTypeObject,
types::{PyDict, PyString, PyTuple},
FromPyObject, PyAny, PyErr, PyResult, Python,
FromPyObject, PyAny, PyErr, PyResult, PyTypeInfo, Python,
};
/// The standard implementation of how PyO3 extracts a `#[pyfunction]` or `#[pymethod]` function argument.

View file

@ -4,7 +4,7 @@ use crate::{
impl_::freelist::FreeList,
pycell::{GetBorrowChecker, Mutability, PyCellLayout, PyClassMutability},
pyclass_init::PyObjectInit,
type_object::{PyLayout, PyTypeObject},
type_object::PyLayout,
Py, PyAny, PyCell, PyClass, PyErr, PyMethodDefType, PyNativeType, PyResult, PyTypeInfo, Python,
};
use std::{
@ -162,7 +162,7 @@ pub trait PyClassImpl: Sized {
type Layout: PyLayout<Self>;
/// Base class
type BaseType: PyTypeInfo + PyTypeObject + PyClassBaseType;
type BaseType: PyTypeInfo + PyClassBaseType;
/// Immutable or mutable
type Mutability: Mutability;

View file

@ -122,10 +122,11 @@
use crate::err::{self, PyDowncastError, PyErr, PyResult};
use crate::gil::{self, GILGuard, GILPool};
use crate::impl_::not_send::NotSend;
use crate::type_object::{PyTypeInfo, PyTypeObject};
use crate::types::{PyAny, PyDict, PyModule, PyType};
use crate::version::PythonVersionInfo;
use crate::{ffi, AsPyPointer, FromPyPointer, IntoPyPointer, PyNativeType, PyObject, PyTryFrom};
use crate::{
ffi, AsPyPointer, FromPyPointer, IntoPyPointer, PyNativeType, PyObject, PyTryFrom, PyTypeInfo,
};
use std::ffi::{CStr, CString};
use std::marker::PhantomData;
use std::os::raw::c_int;
@ -583,7 +584,7 @@ impl<'py> Python<'py> {
/// Gets the Python type object for type `T`.
pub fn get_type<T>(self) -> &'py PyType
where
T: PyTypeObject,
T: PyTypeInfo,
{
T::type_object(self)
}

View file

@ -51,9 +51,14 @@ pub unsafe trait PyTypeInfo: Sized {
/// Utility type to make Py::as_ref work.
type AsRefTarget: PyNativeType;
/// PyTypeObject instance for this type.
/// Returns the PyTypeObject instance for this type.
fn type_object_raw(py: Python<'_>) -> *mut ffi::PyTypeObject;
/// Returns the safe abstraction over the type object.
fn type_object(py: Python<'_>) -> &PyType {
unsafe { py.from_borrowed_ptr(Self::type_object_raw(py) as _) }
}
/// Checks if `object` is an instance of this type or a subclass of this type.
fn is_type_of(object: &PyAny) -> bool {
unsafe { ffi::PyObject_TypeCheck(object.as_ptr(), Self::type_object_raw(object.py())) != 0 }
@ -65,27 +70,19 @@ pub unsafe trait PyTypeInfo: Sized {
}
}
/// Python object types that have a corresponding type object.
/// Legacy trait which previously held the `type_object` method now found on `PyTypeInfo`.
///
/// # Safety
///
/// This trait is marked unsafe because not fulfilling the contract for type_object
/// leads to UB.
///
/// See also [PyTypeInfo::type_object_raw](trait.PyTypeInfo.html#tymethod.type_object_raw).
pub unsafe trait PyTypeObject {
/// Returns the safe abstraction over the type object.
fn type_object(py: Python<'_>) -> &PyType;
}
/// This trait used to have stringent safety requirements, but they are now irrelevant as it is deprecated.
#[deprecated(
since = "0.17.0",
note = "PyTypeObject::type_object was moved to PyTypeInfo::type_object"
)]
pub unsafe trait PyTypeObject: PyTypeInfo {}
unsafe impl<T> PyTypeObject for T
where
T: PyTypeInfo,
{
fn type_object(py: Python<'_>) -> &PyType {
unsafe { py.from_borrowed_ptr(Self::type_object_raw(py) as _) }
}
}
#[allow(deprecated)]
unsafe impl<T: PyTypeInfo> PyTypeObject for T {}
/// Lazy type object for PyClass.
#[doc(hidden)]
@ -232,3 +229,23 @@ pub(crate) unsafe fn get_tp_free(tp: *mut ffi::PyTypeObject) -> ffi::freefunc {
std::mem::transmute(ptr)
}
}
#[cfg(test)]
mod tests {
#[test]
#[allow(deprecated)]
fn test_deprecated_type_object() {
// Even though PyTypeObject is deprecated, simple usages of it as a trait bound should continue to work.
use super::PyTypeObject;
use crate::types::{PyList, PyType};
use crate::Python;
fn get_type_object<T: PyTypeObject>(py: Python<'_>) -> &PyType {
T::type_object(py)
}
Python::with_gil(|py| {
assert!(get_type_object::<PyList>(py).is(<PyList as crate::PyTypeInfo>::type_object(py)))
});
}
}

View file

@ -4,7 +4,7 @@ use crate::conversion::{
};
use crate::err::{PyDowncastError, PyErr, PyResult};
use crate::exceptions::PyTypeError;
use crate::type_object::PyTypeObject;
use crate::type_object::PyTypeInfo;
use crate::types::{PyDict, PyIterator, PyList, PyString, PyTuple, PyType};
use crate::{err, ffi, Py, PyNativeType, PyObject, Python};
use std::cell::UnsafeCell;
@ -782,7 +782,7 @@ impl PyAny {
///
/// This is equivalent to the Python expression `isinstance(self, T)`,
/// if the type `T` is known at compile time.
pub fn is_instance_of<T: PyTypeObject>(&self) -> PyResult<bool> {
pub fn is_instance_of<T: PyTypeInfo>(&self) -> PyResult<bool> {
self.is_instance(T::type_object(self.py()))
}
@ -814,7 +814,7 @@ impl PyAny {
#[cfg(test)]
mod tests {
use crate::{
type_object::PyTypeObject,
type_object::PyTypeInfo,
types::{IntoPyDict, PyList, PyLong, PyModule},
Python, ToPyObject,
};

View file

@ -7,7 +7,6 @@ use crate::err::{PyErr, PyResult};
use crate::exceptions;
use crate::ffi;
use crate::pyclass::PyClass;
use crate::type_object::PyTypeObject;
use crate::types::{PyAny, PyCFunction, PyDict, PyList, PyString};
use crate::{AsPyPointer, IntoPy, PyObject, Python};
use std::ffi::{CStr, CString};
@ -291,7 +290,7 @@ impl PyModule {
where
T: PyClass,
{
self.add(T::NAME, <T as PyTypeObject>::type_object(self.py()))
self.add(T::NAME, T::type_object(self.py()))
}
/// Adds a function or a (sub)module to a module, using the functions name as name.

View file

@ -385,7 +385,7 @@ impl FromPyObject<'_> for char {
mod tests {
use super::*;
#[cfg(all(not(Py_LIMITED_API), target_endian = "little"))]
use crate::type_object::PyTypeObject;
use crate::PyTypeInfo;
use crate::Python;
use crate::{FromPyObject, PyObject, PyTryFrom, ToPyObject};
#[cfg(all(not(Py_LIMITED_API), target_endian = "little"))]

View file

@ -3,8 +3,7 @@
// based on Daniel Grunwald's https://github.com/dgrunwald/rust-cpython
use crate::err::{self, PyResult};
use crate::type_object::PyTypeObject;
use crate::{ffi, AsPyPointer, PyAny, Python};
use crate::{ffi, AsPyPointer, PyAny, PyTypeInfo, Python};
/// Represents a reference to a Python `type object`.
#[repr(transparent)]
@ -15,7 +14,7 @@ pyobject_native_type_core!(PyType, ffi::PyType_Type, #checkfunction=ffi::PyType_
impl PyType {
/// Creates a new type object.
#[inline]
pub fn new<T: PyTypeObject>(py: Python<'_>) -> &PyType {
pub fn new<T: PyTypeInfo>(py: Python<'_>) -> &PyType {
T::type_object(py)
}
@ -55,7 +54,7 @@ impl PyType {
/// `T` is known at compile time.
pub fn is_subclass_of<T>(&self) -> PyResult<bool>
where
T: PyTypeObject,
T: PyTypeInfo,
{
self.is_subclass(T::type_object(self.py()))
}
@ -77,9 +76,8 @@ impl PyType {
#[cfg(test)]
mod tests {
use crate::{
type_object::PyTypeObject,
types::{PyBool, PyLong},
Python,
PyTypeInfo, Python,
};
#[test]

View file

@ -3,7 +3,7 @@
use pyo3::class::PyTraverseError;
use pyo3::class::PyVisit;
use pyo3::prelude::*;
use pyo3::type_object::PyTypeObject;
use pyo3::PyTypeInfo;
use pyo3::{py_run, AsPyPointer, PyCell, PyTryInto};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;

View file

@ -6,8 +6,7 @@ use pyo3::class::PyGCProtocol;
use pyo3::class::PyTraverseError;
use pyo3::class::PyVisit;
use pyo3::prelude::*;
use pyo3::type_object::PyTypeObject;
use pyo3::{py_run, AsPyPointer, PyCell, PyTryInto};
use pyo3::{py_run, AsPyPointer, PyCell, PyTryInto, PyTypeInfo};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;

View file

@ -1,8 +1,7 @@
#![cfg(feature = "macros")]
use pyo3::prelude::*;
use pyo3::py_run;
use pyo3::type_object::PyTypeObject;
use pyo3::{py_run, PyTypeInfo};
use pyo3::types::IntoPyDict;
@ -305,7 +304,7 @@ impl SimpleClass {
#[test]
fn test_subclass_ref_counts() {
// regression test for issue #1363
use pyo3::type_object::PyTypeObject;
use pyo3::PyTypeInfo;
Python::with_gil(|py| {
#[allow(non_snake_case)]
let SimpleClass = SimpleClass::type_object(py);

View file

@ -1,8 +1,8 @@
#![cfg(feature = "multiple-pymethods")]
use pyo3::prelude::*;
use pyo3::type_object::PyTypeObject;
use pyo3::types::PyType;
use pyo3::PyTypeInfo;
#[macro_use]
mod common;