Skip to content

Commit

Permalink
reintroduce PyTypeInfo methods (#4484)
Browse files Browse the repository at this point in the history
  • Loading branch information
Icxolu committed Aug 24, 2024
1 parent 7aa2101 commit 07da500
Show file tree
Hide file tree
Showing 15 changed files with 64 additions and 50 deletions.
2 changes: 1 addition & 1 deletion guide/src/class/numeric.md
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ fn my_module(m: &Bound<'_, PyModule>) -> PyResult<()> {
# fn main() -> PyResult<()> {
# Python::with_gil(|py| -> PyResult<()> {
# let globals = PyModule::import(py, "__main__")?.dict();
# globals.set_item("Number", Number::type_object_bound(py))?;
# globals.set_item("Number", Number::type_object(py))?;
#
# py.run(SCRIPT, Some(&globals), None)?;
# Ok(())
Expand Down
19 changes: 8 additions & 11 deletions src/err/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ impl PyErr {
{
PyErr::from_state(PyErrState::Lazy(Box::new(move |py| {
PyErrStateLazyFnOutput {
ptype: T::type_object_bound(py).into(),
ptype: T::type_object(py).into(),
pvalue: args.arguments(py),
}
})))
Expand Down Expand Up @@ -215,7 +215,7 @@ impl PyErr {
/// assert_eq!(err.to_string(), "TypeError: some type error");
///
/// // Case #2: Exception type
/// let err = PyErr::from_value(PyTypeError::type_object_bound(py).into_any());
/// let err = PyErr::from_value(PyTypeError::type_object(py).into_any());
/// assert_eq!(err.to_string(), "TypeError: ");
///
/// // Case #3: Invalid exception value
Expand Down Expand Up @@ -587,7 +587,7 @@ impl PyErr {
where
T: PyTypeInfo,
{
self.is_instance(py, &T::type_object_bound(py))
self.is_instance(py, &T::type_object(py))
}

/// Writes the error back to the Python interpreter's global state.
Expand Down Expand Up @@ -1187,21 +1187,18 @@ mod tests {
fn test_pyerr_matches() {
Python::with_gil(|py| {
let err = PyErr::new::<PyValueError, _>("foo");
assert!(err.matches(py, PyValueError::type_object_bound(py)));
assert!(err.matches(py, PyValueError::type_object(py)));

assert!(err.matches(
py,
(
PyValueError::type_object_bound(py),
PyTypeError::type_object_bound(py)
)
(PyValueError::type_object(py), PyTypeError::type_object(py))
));

assert!(!err.matches(py, PyTypeError::type_object_bound(py)));
assert!(!err.matches(py, PyTypeError::type_object(py)));

// String is not a valid exception class, so we should get a TypeError
let err: PyErr = PyErr::from_type(crate::types::PyString::type_object_bound(py), "foo");
assert!(err.matches(py, PyTypeError::type_object_bound(py)));
let err: PyErr = PyErr::from_type(crate::types::PyString::type_object(py), "foo");
assert!(err.matches(py, PyTypeError::type_object(py)));
})
}

Expand Down
2 changes: 1 addition & 1 deletion src/impl_/pymodule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ impl<T> AddTypeToModule<T> {

impl<T: PyTypeInfo> PyAddToModule for AddTypeToModule<T> {
fn add_to_module(&'static self, module: &Bound<'_, PyModule>) -> PyResult<()> {
module.add(T::NAME, T::type_object_bound(module.py()))
module.add(T::NAME, T::type_object(module.py()))
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/marker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -685,7 +685,7 @@ impl<'py> Python<'py> {
where
T: PyTypeInfo,
{
T::type_object_bound(self)
T::type_object(self)
}

/// Deprecated name for [`Python::get_type`].
Expand Down
2 changes: 1 addition & 1 deletion src/tests/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ mod inner {
$crate::tests::common::CatchWarnings::enter($py, |w| {
use $crate::types::{PyListMethods, PyStringMethods};
$body;
let expected_warnings = [$((<$category as $crate::type_object::PyTypeInfo>::type_object_bound($py), $message)),+];
let expected_warnings = [$((<$category as $crate::type_object::PyTypeInfo>::type_object($py), $message)),+];
assert_eq!(w.len(), expected_warnings.len());
for (warning, (category, message)) in w.iter().zip(expected_warnings) {

Expand Down
29 changes: 25 additions & 4 deletions src/type_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ pub unsafe trait PyTypeInfo: Sized {

/// Returns the safe abstraction over the type object.
#[inline]
fn type_object_bound(py: Python<'_>) -> Bound<'_, PyType> {
fn type_object(py: Python<'_>) -> Bound<'_, PyType> {
// Making the borrowed object `Bound` is necessary for soundness reasons. It's an extreme
// edge case, but arbitrary Python code _could_ change the __class__ of an object and cause
// the type object to be freed.
Expand All @@ -61,17 +61,38 @@ pub unsafe trait PyTypeInfo: Sized {
}
}

/// Deprecated name for [`PyTypeInfo::type_object`].
#[deprecated(since = "0.23.0", note = "renamed to `PyTypeInfo::type_object`")]
#[inline]
fn type_object_bound(py: Python<'_>) -> Bound<'_, PyType> {
Self::type_object(py)
}

/// Checks if `object` is an instance of this type or a subclass of this type.
#[inline]
fn is_type_of_bound(object: &Bound<'_, PyAny>) -> bool {
fn is_type_of(object: &Bound<'_, PyAny>) -> bool {
unsafe { ffi::PyObject_TypeCheck(object.as_ptr(), Self::type_object_raw(object.py())) != 0 }
}

/// Deprecated name for [`PyTypeInfo::is_type_of`].
#[deprecated(since = "0.23.0", note = "renamed to `PyTypeInfo::is_type_of`")]
#[inline]
fn is_type_of_bound(object: &Bound<'_, PyAny>) -> bool {
Self::is_type_of(object)
}

/// Checks if `object` is an instance of this type.
#[inline]
fn is_exact_type_of_bound(object: &Bound<'_, PyAny>) -> bool {
fn is_exact_type_of(object: &Bound<'_, PyAny>) -> bool {
unsafe { ffi::Py_TYPE(object.as_ptr()) == Self::type_object_raw(object.py()) }
}

/// Deprecated name for [`PyTypeInfo::is_exact_type_of`].
#[deprecated(since = "0.23.0", note = "renamed to `PyTypeInfo::is_exact_type_of`")]
#[inline]
fn is_exact_type_of_bound(object: &Bound<'_, PyAny>) -> bool {
Self::is_exact_type_of(object)
}
}

/// Implemented by types which can be used as a concrete Python type inside `Py<T>` smart pointers.
Expand All @@ -93,7 +114,7 @@ where

#[inline]
fn type_check(object: &Bound<'_, PyAny>) -> bool {
T::is_type_of_bound(object)
T::is_type_of(object)
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/types/any.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1430,12 +1430,12 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {

#[inline]
fn is_instance_of<T: PyTypeInfo>(&self) -> bool {
T::is_type_of_bound(self)
T::is_type_of(self)
}

#[inline]
fn is_exact_instance_of<T: PyTypeInfo>(&self) -> bool {
T::is_exact_type_of_bound(self)
T::is_exact_type_of(self)
}

fn contains<V>(&self, value: V) -> PyResult<bool>
Expand Down Expand Up @@ -1917,7 +1917,7 @@ class SimpleClass:
#[test]
fn test_is_callable() {
Python::with_gil(|py| {
assert!(PyList::type_object_bound(py).is_callable());
assert!(PyList::type_object(py).is_callable());

let not_callable = 5.to_object(py).into_bound(py);
assert!(!not_callable.is_callable());
Expand Down
2 changes: 1 addition & 1 deletion src/types/code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ mod tests {
#[test]
fn test_type_object() {
Python::with_gil(|py| {
assert_eq!(PyCode::type_object_bound(py).name().unwrap(), "code");
assert_eq!(PyCode::type_object(py).name().unwrap(), "code");
})
}
}
8 changes: 4 additions & 4 deletions src/types/ellipsis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,13 @@ unsafe impl PyTypeInfo for PyEllipsis {
}

#[inline]
fn is_type_of_bound(object: &Bound<'_, PyAny>) -> bool {
fn is_type_of(object: &Bound<'_, PyAny>) -> bool {
// ellipsis is not usable as a base type
Self::is_exact_type_of_bound(object)
Self::is_exact_type_of(object)
}

#[inline]
fn is_exact_type_of_bound(object: &Bound<'_, PyAny>) -> bool {
fn is_exact_type_of(object: &Bound<'_, PyAny>) -> bool {
object.is(&**Self::get(object.py()))
}
}
Expand All @@ -67,7 +67,7 @@ mod tests {
Python::with_gil(|py| {
assert!(PyEllipsis::get(py)
.get_type()
.is(&PyEllipsis::type_object_bound(py)));
.is(&PyEllipsis::type_object(py)));
})
}

Expand Down
4 changes: 2 additions & 2 deletions src/types/mapping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ impl PyMapping {
/// library). This is equivalent to `collections.abc.Mapping.register(T)` in Python.
/// This registration is required for a pyclass to be downcastable from `PyAny` to `PyMapping`.
pub fn register<T: PyTypeInfo>(py: Python<'_>) -> PyResult<()> {
let ty = T::type_object_bound(py);
let ty = T::type_object(py);
get_mapping_abc(py)?.call_method1("register", (ty,))?;
Ok(())
}
Expand Down Expand Up @@ -172,7 +172,7 @@ impl PyTypeCheck for PyMapping {
fn type_check(object: &Bound<'_, PyAny>) -> bool {
// Using `is_instance` for `collections.abc.Mapping` is slow, so provide
// optimized case dict as a well-known mapping
PyDict::is_type_of_bound(object)
PyDict::is_type_of(object)
|| get_mapping_abc(object.py())
.and_then(|abc| object.is_instance(abc))
.unwrap_or_else(|err| {
Expand Down
10 changes: 4 additions & 6 deletions src/types/none.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,13 @@ unsafe impl PyTypeInfo for PyNone {
}

#[inline]
fn is_type_of_bound(object: &Bound<'_, PyAny>) -> bool {
fn is_type_of(object: &Bound<'_, PyAny>) -> bool {
// NoneType is not usable as a base type
Self::is_exact_type_of_bound(object)
Self::is_exact_type_of(object)
}

#[inline]
fn is_exact_type_of_bound(object: &Bound<'_, PyAny>) -> bool {
fn is_exact_type_of(object: &Bound<'_, PyAny>) -> bool {
object.is(&**Self::get(object.py()))
}
}
Expand Down Expand Up @@ -79,9 +79,7 @@ mod tests {
#[test]
fn test_none_type_object_consistent() {
Python::with_gil(|py| {
assert!(PyNone::get(py)
.get_type()
.is(&PyNone::type_object_bound(py)));
assert!(PyNone::get(py).get_type().is(&PyNone::type_object(py)));
})
}

Expand Down
8 changes: 4 additions & 4 deletions src/types/notimplemented.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,13 @@ unsafe impl PyTypeInfo for PyNotImplemented {
}

#[inline]
fn is_type_of_bound(object: &Bound<'_, PyAny>) -> bool {
fn is_type_of(object: &Bound<'_, PyAny>) -> bool {
// NotImplementedType is not usable as a base type
Self::is_exact_type_of_bound(object)
Self::is_exact_type_of(object)
}

#[inline]
fn is_exact_type_of_bound(object: &Bound<'_, PyAny>) -> bool {
fn is_exact_type_of(object: &Bound<'_, PyAny>) -> bool {
object.is(&**Self::get(object.py()))
}
}
Expand All @@ -70,7 +70,7 @@ mod tests {
Python::with_gil(|py| {
assert!(PyNotImplemented::get(py)
.get_type()
.is(&PyNotImplemented::type_object_bound(py)));
.is(&PyNotImplemented::type_object(py)));
})
}

Expand Down
10 changes: 4 additions & 6 deletions src/types/pysuper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,10 @@ impl PySuper {
ty: &Bound<'py, PyType>,
obj: &Bound<'py, PyAny>,
) -> PyResult<Bound<'py, PySuper>> {
PySuper::type_object_bound(ty.py())
.call1((ty, obj))
.map(|any| {
// Safety: super() always returns instance of super
unsafe { any.downcast_into_unchecked() }
})
PySuper::type_object(ty.py()).call1((ty, obj)).map(|any| {
// Safety: super() always returns instance of super
unsafe { any.downcast_into_unchecked() }
})
}

/// Deprecated name for [`PySuper::new`].
Expand Down
6 changes: 3 additions & 3 deletions src/types/sequence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ impl PySequence {
/// library). This is equivalent to `collections.abc.Sequence.register(T)` in Python.
/// This registration is required for a pyclass to be downcastable from `PyAny` to `PySequence`.
pub fn register<T: PyTypeInfo>(py: Python<'_>) -> PyResult<()> {
let ty = T::type_object_bound(py);
let ty = T::type_object(py);
get_sequence_abc(py)?.call_method1("register", (ty,))?;
Ok(())
}
Expand Down Expand Up @@ -362,8 +362,8 @@ impl PyTypeCheck for PySequence {
fn type_check(object: &Bound<'_, PyAny>) -> bool {
// Using `is_instance` for `collections.abc.Sequence` is slow, so provide
// optimized cases for list and tuples as common well-known sequences
PyList::is_type_of_bound(object)
|| PyTuple::is_type_of_bound(object)
PyList::is_type_of(object)
|| PyTuple::is_type_of(object)
|| get_sequence_abc(object.py())
.and_then(|abc| object.is_instance(abc))
.unwrap_or_else(|err| {
Expand Down
4 changes: 2 additions & 2 deletions src/types/typeobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ impl PyType {
/// Creates a new type object.
#[inline]
pub fn new<T: PyTypeInfo>(py: Python<'_>) -> Bound<'_, PyType> {
T::type_object_bound(py)
T::type_object(py)
}

/// Deprecated name for [`PyType::new`].
Expand Down Expand Up @@ -202,7 +202,7 @@ impl<'py> PyTypeMethods<'py> for Bound<'py, PyType> {
where
T: PyTypeInfo,
{
self.is_subclass(&T::type_object_bound(self.py()))
self.is_subclass(&T::type_object(self.py()))
}

fn mro(&self) -> Bound<'py, PyTuple> {
Expand Down

0 comments on commit 07da500

Please sign in to comment.