Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cleanup: deprecate PyTypeObject trait #2287

Merged
merged 1 commit into from
Apr 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ 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

- Implement `ToPyObject` for `[T; N]`. [#2313](https://github.com/PyO3/pyo3/pull/2313)

### 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
Expand Down
3 changes: 1 addition & 2 deletions guide/src/class.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down
36 changes: 18 additions & 18 deletions guide/src/class/numeric.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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."
Expand All @@ -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(())
# })
Expand Down Expand Up @@ -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
31 changes: 31 additions & 0 deletions guide/src/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 1 addition & 2 deletions src/err/err_state.rs
Original file line number Diff line number Diff line change
@@ -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)]
Expand Down
6 changes: 3 additions & 3 deletions src/err/mod.rs
Original file line number Diff line number Diff line change
@@ -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},
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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))
}
Expand Down
2 changes: 1 addition & 1 deletion src/exceptions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
3 changes: 1 addition & 2 deletions src/impl_/extract_argument.rs
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
4 changes: 2 additions & 2 deletions src/impl_/pyclass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -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;
Expand Down
7 changes: 4 additions & 3 deletions src/marker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
}
Expand Down
53 changes: 35 additions & 18 deletions src/type_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand All @@ -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 {}
adamreichold marked this conversation as resolved.
Show resolved Hide resolved

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)]
Expand Down Expand Up @@ -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)]
Copy link
Member Author

@davidhewitt davidhewitt Apr 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this line is commented out and <PyList as crate::PyTypeInfo> replaced with just PyList, this is the compiler output:

warning: use of deprecated trait `type_object::PyTypeObject`: PyTypeObject::type_object was moved to PyTypeInfo::type_object
   --> src/type_object.rs:240:20
    |
240 |         use super::PyTypeObject;
    |                    ^^^^^^^^^^^^
    |
    = note: `#[warn(deprecated)]` on by default

warning: use of deprecated trait `type_object::PyTypeObject`: PyTypeObject::type_object was moved to PyTypeInfo::type_object
   --> src/type_object.rs:243:31
    |
243 |         fn get_type_object<T: PyTypeObject>(py: Python<'_>) -> &PyType {
    |                               ^^^^^^^^^^^^

error[E0599]: no function or associated item named `type_object` found for struct `PyList` in the current scope
   --> src/type_object.rs:249:58
    |
249 |                 get_type_object::<PyList>(py).is(PyList::type_object(py)))
    |                                                          ^^^^^^^^^^^ function or associated item not found in `PyList`
    |
   ::: src/types/list.rs:18:1
    |
18  | pub struct PyList(PyAny);
    | ------------------------- function or associated item `type_object` not found for this
    |
    = help: items from traits can only be used if the trait is in scope
help: the following trait is implemented but not in scope; perhaps add a `use` for it:
    |
237 |     use crate::type_object::PyTypeInfo;
    |

... hopefully this is enough for users to figure out how to upgrade.

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)))
});
}
}
6 changes: 3 additions & 3 deletions src/types/any.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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()))
}

Expand Down Expand Up @@ -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,
};
Expand Down
3 changes: 1 addition & 2 deletions src/types/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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.
Expand Down
Loading