Skip to content

Commit

Permalink
cleanup: deprecate PyTypeObject trait
Browse files Browse the repository at this point in the history
  • Loading branch information
davidhewitt committed Apr 9, 2022
1 parent d13a498 commit b998ead
Show file tree
Hide file tree
Showing 20 changed files with 124 additions and 73 deletions.
6 changes: 3 additions & 3 deletions Architecture.md
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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Expand Up @@ -6,6 +6,12 @@ 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]

### Changed

- Move `PyTypeObject::type_object` method to `PyTypeInfo` trait, and deprecate `PyTypeObject` trait. [#2284](https://github.com/PyO3/pyo3/pull/2284)

## [0.16.3] - 2022-04-05

### Packaging
Expand Down
3 changes: 1 addition & 2 deletions guide/src/class.md
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 @@ -999,7 +999,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
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
33 changes: 33 additions & 0 deletions guide/src/migration.md
Expand Up @@ -3,6 +3,39 @@
This guide can help you upgrade code through breaking changes from one PyO3 version to the next.
For a detailed list of all changes, see the [CHANGELOG](changelog.md).

## from 0.16.* to 0.17

### `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
@@ -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
@@ -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
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
@@ -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
Expand Up @@ -4,7 +4,7 @@ use crate::{
impl_::freelist::FreeList,
pycell::PyCellLayout,
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;

/// This handles following two situations:
/// 1. In case `T` is `Send`, stub `ThreadChecker` is used and does nothing.
Expand Down
7 changes: 4 additions & 3 deletions src/marker.rs
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
55 changes: 37 additions & 18 deletions src/type_object.rs
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 {}

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,25 @@ 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 crate::Python;
use super::PyTypeObject;
use crate::types::{PyList, PyType};

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
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
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

0 comments on commit b998ead

Please sign in to comment.