#![warn(clippy::undocumented_unsafe_blocks)]
use crate::call::PyCallArgs;
use crate::conversion::IntoPyObject;
use crate::err::{PyErr, PyResult};
use crate::impl_::pyclass::PyClassImpl;
#[cfg(feature = "experimental-inspect")]
use crate::inspect::PyStaticExpr;
use crate::pycell::impl_::PyClassObjectLayout;
use crate::pycell::{PyBorrowError, PyBorrowMutError};
use crate::pyclass::boolean_struct::{False, True};
use crate::types::{any::PyAnyMethods, string::PyStringMethods, typeobject::PyTypeMethods};
use crate::types::{DerefToPyAny, PyDict, PyString};
#[allow(deprecated)]
use crate::DowncastError;
use crate::{
ffi, CastError, CastIntoError, FromPyObject, PyAny, PyClass, PyClassInitializer, PyRef,
PyRefMut, PyTypeInfo, Python,
};
use crate::{internal::state, PyTypeCheck};
use std::marker::PhantomData;
use std::mem::ManuallyDrop;
use std::ops::Deref;
use std::ptr;
use std::ptr::NonNull;
pub trait BoundObject<'py, T>: bound_object_sealed::Sealed {
type Any: BoundObject<'py, PyAny>;
fn as_borrowed(&self) -> Borrowed<'_, 'py, T>;
fn into_bound(self) -> Bound<'py, T>;
fn into_any(self) -> Self::Any;
fn into_ptr(self) -> *mut ffi::PyObject;
fn as_ptr(&self) -> *mut ffi::PyObject;
fn unbind(self) -> Py<T>;
}
mod bound_object_sealed {
pub unsafe trait Sealed {}
unsafe impl<T> Sealed for super::Bound<'_, T> {}
unsafe impl<T> Sealed for super::Borrowed<'_, '_, T> {}
}
#[doc = concat!("[the guide](https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/types.html#boundpy-t)")]
#[repr(transparent)]
pub struct Bound<'py, T>(Python<'py>, ManuallyDrop<Py<T>>);
impl<'py, T> Bound<'py, T>
where
T: PyClass,
{
pub fn new(
py: Python<'py>,
value: impl Into<PyClassInitializer<T>>,
) -> PyResult<Bound<'py, T>> {
value.into().create_class_object(py)
}
}
impl<'py, T> Bound<'py, T> {
#[inline]
pub fn cast<U>(&self) -> Result<&Bound<'py, U>, CastError<'_, 'py>>
where
U: PyTypeCheck,
{
#[inline]
fn inner<'a, 'py, U>(
any: &'a Bound<'py, PyAny>,
) -> Result<&'a Bound<'py, U>, CastError<'a, 'py>>
where
U: PyTypeCheck,
{
if U::type_check(any) {
Ok(unsafe { any.cast_unchecked() })
} else {
Err(CastError::new(
any.as_borrowed(),
U::classinfo_object(any.py()),
))
}
}
inner(self.as_any())
}
#[inline]
pub fn cast_into<U>(self) -> Result<Bound<'py, U>, CastIntoError<'py>>
where
U: PyTypeCheck,
{
#[inline]
fn inner<U>(any: Bound<'_, PyAny>) -> Result<Bound<'_, U>, CastIntoError<'_>>
where
U: PyTypeCheck,
{
if U::type_check(&any) {
Ok(unsafe { any.cast_into_unchecked() })
} else {
let to = U::classinfo_object(any.py());
Err(CastIntoError::new(any, to))
}
}
inner(self.into_any())
}
#[inline]
pub fn cast_exact<U>(&self) -> Result<&Bound<'py, U>, CastError<'_, 'py>>
where
U: PyTypeInfo,
{
#[inline]
fn inner<'a, 'py, U>(
any: &'a Bound<'py, PyAny>,
) -> Result<&'a Bound<'py, U>, CastError<'a, 'py>>
where
U: PyTypeInfo,
{
if any.is_exact_instance_of::<U>() {
Ok(unsafe { any.cast_unchecked() })
} else {
Err(CastError::new(
any.as_borrowed(),
U::type_object(any.py()).into_any(),
))
}
}
inner(self.as_any())
}
#[inline]
pub fn cast_into_exact<U>(self) -> Result<Bound<'py, U>, CastIntoError<'py>>
where
U: PyTypeInfo,
{
#[inline]
fn inner<U>(any: Bound<'_, PyAny>) -> Result<Bound<'_, U>, CastIntoError<'_>>
where
U: PyTypeInfo,
{
if any.is_exact_instance_of::<U>() {
Ok(unsafe { any.cast_into_unchecked() })
} else {
let to = U::type_object(any.py()).into_any();
Err(CastIntoError::new(any, to))
}
}
inner(self.into_any())
}
#[inline]
pub unsafe fn cast_unchecked<U>(&self) -> &Bound<'py, U> {
unsafe { NonNull::from(self).cast().as_ref() }
}
#[inline]
pub unsafe fn cast_into_unchecked<U>(self) -> Bound<'py, U> {
unsafe { std::mem::transmute(self) }
}
}
impl<'py> Bound<'py, PyAny> {
#[inline]
#[track_caller]
pub unsafe fn from_owned_ptr(py: Python<'py>, ptr: *mut ffi::PyObject) -> Self {
let non_null = NonNull::new(ptr).unwrap_or_else(|| panic_on_null(py));
unsafe { Py::from_non_null(non_null) }.into_bound(py)
}
#[inline]
pub unsafe fn from_owned_ptr_or_opt(py: Python<'py>, ptr: *mut ffi::PyObject) -> Option<Self> {
NonNull::new(ptr).map(|nonnull_ptr| {
unsafe { Py::from_non_null(nonnull_ptr) }.into_bound(py)
})
}
#[inline]
pub unsafe fn from_owned_ptr_or_err(
py: Python<'py>,
ptr: *mut ffi::PyObject,
) -> PyResult<Self> {
match NonNull::new(ptr) {
Some(nonnull_ptr) => Ok(
unsafe { Py::from_non_null(nonnull_ptr) }.into_bound(py),
),
None => Err(PyErr::fetch(py)),
}
}
pub(crate) unsafe fn from_owned_ptr_unchecked(
py: Python<'py>,
ptr: *mut ffi::PyObject,
) -> Self {
unsafe { Py::from_non_null(NonNull::new_unchecked(ptr)) }.into_bound(py)
}
#[inline]
#[track_caller]
pub unsafe fn from_borrowed_ptr(py: Python<'py>, ptr: *mut ffi::PyObject) -> Self {
let non_null = NonNull::new(ptr).unwrap_or_else(|| panic_on_null(py));
unsafe { Py::from_borrowed_non_null(py, non_null) }.into_bound(py)
}
#[inline]
pub unsafe fn from_borrowed_ptr_or_opt(
py: Python<'py>,
ptr: *mut ffi::PyObject,
) -> Option<Self> {
NonNull::new(ptr).map(|nonnull_ptr| {
unsafe { Py::from_borrowed_non_null(py, nonnull_ptr) }.into_bound(py)
})
}
#[inline]
pub unsafe fn from_borrowed_ptr_or_err(
py: Python<'py>,
ptr: *mut ffi::PyObject,
) -> PyResult<Self> {
match NonNull::new(ptr) {
Some(nonnull_ptr) => Ok(
unsafe { Py::from_borrowed_non_null(py, nonnull_ptr) }.into_bound(py),
),
None => Err(PyErr::fetch(py)),
}
}
#[inline]
pub(crate) unsafe fn ref_from_ptr<'a>(
_py: Python<'py>,
ptr: &'a *mut ffi::PyObject,
) -> &'a Self {
let ptr = NonNull::from(ptr).cast();
unsafe { ptr.as_ref() }
}
#[inline]
pub(crate) unsafe fn ref_from_ptr_or_opt<'a>(
_py: Python<'py>,
ptr: &'a *mut ffi::PyObject,
) -> &'a Option<Self> {
let ptr = NonNull::from(ptr).cast();
unsafe { ptr.as_ref() }
}
pub(crate) unsafe fn ref_from_non_null<'a>(
_py: Python<'py>,
ptr: &'a NonNull<ffi::PyObject>,
) -> &'a Self {
let ptr = NonNull::from(ptr).cast();
unsafe { ptr.as_ref() }
}
}
impl<'py, T> Bound<'py, T>
where
T: PyClass,
{
#[inline]
#[track_caller]
pub fn borrow(&self) -> PyRef<'py, T> {
PyRef::borrow(self)
}
#[inline]
#[track_caller]
pub fn borrow_mut(&self) -> PyRefMut<'py, T>
where
T: PyClass<Frozen = False>,
{
PyRefMut::borrow(self)
}
#[inline]
pub fn try_borrow(&self) -> Result<PyRef<'py, T>, PyBorrowError> {
PyRef::try_borrow(self)
}
#[inline]
pub fn try_borrow_mut(&self) -> Result<PyRefMut<'py, T>, PyBorrowMutError>
where
T: PyClass<Frozen = False>,
{
PyRefMut::try_borrow(self)
}
#[inline]
pub fn get(&self) -> &T
where
T: PyClass<Frozen = True> + Sync,
{
self.1.get()
}
#[inline]
pub fn as_super(&self) -> &Bound<'py, T::BaseType> {
unsafe { self.cast_unchecked() }
}
#[inline]
pub fn into_super(self) -> Bound<'py, T::BaseType> {
unsafe { self.cast_into_unchecked() }
}
#[inline]
pub(crate) fn get_class_object(&self) -> &<T as PyClassImpl>::Layout {
self.1.get_class_object()
}
}
impl<T> std::fmt::Debug for Bound<'_, T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
let any = self.as_any();
python_format(any, any.repr(), f)
}
}
impl<T> std::fmt::Display for Bound<'_, T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
let any = self.as_any();
python_format(any, any.str(), f)
}
}
fn python_format(
any: &Bound<'_, PyAny>,
format_result: PyResult<Bound<'_, PyString>>,
f: &mut std::fmt::Formatter<'_>,
) -> Result<(), std::fmt::Error> {
match format_result {
Result::Ok(s) => return f.write_str(&s.to_string_lossy()),
Result::Err(err) => err.write_unraisable(any.py(), Some(any)),
}
match any.get_type().name() {
Result::Ok(name) => std::write!(f, "<unprintable {name} object>"),
Result::Err(_err) => f.write_str("<unprintable object>"),
}
}
impl<'py, T> Deref for Bound<'py, T>
where
T: DerefToPyAny,
{
type Target = Bound<'py, PyAny>;
#[inline]
fn deref(&self) -> &Bound<'py, PyAny> {
self.as_any()
}
}
impl<'py, T> AsRef<Bound<'py, PyAny>> for Bound<'py, T> {
#[inline]
fn as_ref(&self) -> &Bound<'py, PyAny> {
self.as_any()
}
}
impl<T> AsRef<Py<PyAny>> for Bound<'_, T> {
#[inline]
fn as_ref(&self) -> &Py<PyAny> {
self.as_any().as_unbound()
}
}
impl<T> Clone for Bound<'_, T> {
#[inline]
fn clone(&self) -> Self {
Self(self.0, ManuallyDrop::new(self.1.clone_ref(self.0)))
}
}
impl<T> Drop for Bound<'_, T> {
#[inline]
fn drop(&mut self) {
unsafe { ffi::Py_DECREF(self.as_ptr()) }
}
}
impl<'py, T> Bound<'py, T> {
#[inline]
pub fn py(&self) -> Python<'py> {
self.0
}
#[inline]
pub fn as_ptr(&self) -> *mut ffi::PyObject {
self.1.as_ptr()
}
#[inline]
pub fn into_ptr(self) -> *mut ffi::PyObject {
ManuallyDrop::new(self).as_ptr()
}
#[inline]
pub fn as_any(&self) -> &Bound<'py, PyAny> {
let ptr = NonNull::from(self).cast();
unsafe { ptr.as_ref() }
}
#[inline]
pub fn into_any(self) -> Bound<'py, PyAny> {
Bound(self.0, ManuallyDrop::new(self.unbind().into_any()))
}
#[inline]
pub fn as_borrowed<'a>(&'a self) -> Borrowed<'a, 'py, T> {
unsafe { Borrowed::from_non_null(self.py(), (self.1).0).cast_unchecked() }
}
#[inline]
pub fn unbind(self) -> Py<T> {
let non_null = (ManuallyDrop::new(self).1).0;
unsafe { Py::from_non_null(non_null) }
}
#[inline]
pub fn as_unbound(&self) -> &Py<T> {
&self.1
}
}
impl<'py, T> BoundObject<'py, T> for Bound<'py, T> {
type Any = Bound<'py, PyAny>;
fn as_borrowed(&self) -> Borrowed<'_, 'py, T> {
Bound::as_borrowed(self)
}
fn into_bound(self) -> Bound<'py, T> {
self
}
fn into_any(self) -> Self::Any {
self.into_any()
}
fn into_ptr(self) -> *mut ffi::PyObject {
self.into_ptr()
}
fn as_ptr(&self) -> *mut ffi::PyObject {
self.as_ptr()
}
fn unbind(self) -> Py<T> {
self.unbind()
}
}
#[repr(transparent)]
pub struct Borrowed<'a, 'py, T>(NonNull<ffi::PyObject>, PhantomData<&'a Py<T>>, Python<'py>);
impl<'a, 'py, T> Borrowed<'a, 'py, T> {
pub fn to_owned(self) -> Bound<'py, T> {
(*self).clone()
}
#[inline]
pub fn as_ptr(self) -> *mut ffi::PyObject {
self.0.as_ptr()
}
pub(crate) fn to_any(self) -> Borrowed<'a, 'py, PyAny> {
Borrowed(self.0, PhantomData, self.2)
}
pub fn extract<O>(self) -> Result<O, O::Error>
where
O: FromPyObject<'a, 'py>,
{
FromPyObject::extract(self.to_any())
}
#[inline]
pub fn cast<U>(self) -> Result<Borrowed<'a, 'py, U>, CastError<'a, 'py>>
where
U: PyTypeCheck,
{
fn inner<'a, 'py, U>(
any: Borrowed<'a, 'py, PyAny>,
) -> Result<Borrowed<'a, 'py, U>, CastError<'a, 'py>>
where
U: PyTypeCheck,
{
if U::type_check(&any) {
Ok(unsafe { any.cast_unchecked() })
} else {
Err(CastError::new(any, U::classinfo_object(any.py())))
}
}
inner(self.to_any())
}
#[inline]
pub fn cast_exact<U>(self) -> Result<Borrowed<'a, 'py, U>, CastError<'a, 'py>>
where
U: PyTypeInfo,
{
fn inner<'a, 'py, U>(
any: Borrowed<'a, 'py, PyAny>,
) -> Result<Borrowed<'a, 'py, U>, CastError<'a, 'py>>
where
U: PyTypeInfo,
{
if any.is_exact_instance_of::<U>() {
Ok(unsafe { any.cast_unchecked() })
} else {
Err(CastError::new(any, U::classinfo_object(any.py())))
}
}
inner(self.to_any())
}
#[inline]
pub unsafe fn cast_unchecked<U>(self) -> Borrowed<'a, 'py, U> {
Borrowed(self.0, PhantomData, self.2)
}
}
impl<'a, T: PyClass> Borrowed<'a, '_, T> {
#[inline]
pub(crate) fn get_class_object(self) -> &'a <T as PyClassImpl>::Layout {
unsafe { &*self.as_ptr().cast::<<T as PyClassImpl>::Layout>() }
}
}
impl<'a, 'py> Borrowed<'a, 'py, PyAny> {
#[inline]
#[track_caller]
pub unsafe fn from_ptr(py: Python<'py>, ptr: *mut ffi::PyObject) -> Self {
let non_null = NonNull::new(ptr).unwrap_or_else(|| panic_on_null(py));
unsafe { Self::from_non_null(py, non_null) }
}
#[inline]
pub unsafe fn from_ptr_or_opt(py: Python<'py>, ptr: *mut ffi::PyObject) -> Option<Self> {
NonNull::new(ptr).map(|ptr|
unsafe { Self::from_non_null(py, ptr) })
}
#[inline]
pub unsafe fn from_ptr_or_err(py: Python<'py>, ptr: *mut ffi::PyObject) -> PyResult<Self> {
NonNull::new(ptr).map_or_else(
|| Err(PyErr::fetch(py)),
|ptr| {
Ok(
unsafe { Self::from_non_null(py, ptr) },
)
},
)
}
#[inline]
pub(crate) unsafe fn from_ptr_unchecked(py: Python<'py>, ptr: *mut ffi::PyObject) -> Self {
unsafe { Self::from_non_null(py, NonNull::new_unchecked(ptr)) }
}
#[inline]
pub(crate) unsafe fn from_non_null(py: Python<'py>, ptr: NonNull<ffi::PyObject>) -> Self {
Self(ptr, PhantomData, py)
}
}
impl<'a, 'py, T> From<&'a Bound<'py, T>> for Borrowed<'a, 'py, T> {
#[inline]
fn from(instance: &'a Bound<'py, T>) -> Self {
instance.as_borrowed()
}
}
impl<T> AsRef<Py<PyAny>> for Borrowed<'_, '_, T> {
#[inline]
fn as_ref(&self) -> &Py<PyAny> {
self.as_any().as_unbound()
}
}
impl<T> std::fmt::Debug for Borrowed<'_, '_, T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Bound::fmt(self, f)
}
}
impl<'py, T> Deref for Borrowed<'_, 'py, T> {
type Target = Bound<'py, T>;
#[inline]
fn deref(&self) -> &Bound<'py, T> {
unsafe { Bound::ref_from_non_null(self.2, &self.0).cast_unchecked() }
}
}
impl<T> Clone for Borrowed<'_, '_, T> {
#[inline]
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for Borrowed<'_, '_, T> {}
impl<'a, 'py, T> BoundObject<'py, T> for Borrowed<'a, 'py, T> {
type Any = Borrowed<'a, 'py, PyAny>;
fn as_borrowed(&self) -> Borrowed<'a, 'py, T> {
*self
}
fn into_bound(self) -> Bound<'py, T> {
(*self).to_owned()
}
fn into_any(self) -> Self::Any {
self.to_any()
}
fn into_ptr(self) -> *mut ffi::PyObject {
(*self).to_owned().into_ptr()
}
fn as_ptr(&self) -> *mut ffi::PyObject {
(*self).as_ptr()
}
fn unbind(self) -> Py<T> {
(*self).to_owned().unbind()
}
}
#[doc = concat!("[guide entry](https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/class.html#bound-and-interior-mutability)")]
#[repr(transparent)]
pub struct Py<T>(NonNull<ffi::PyObject>, PhantomData<T>);
#[cfg(feature = "nightly")]
unsafe impl<T> crate::marker::Ungil for Py<T> {}
unsafe impl<T> Send for Py<T> {}
unsafe impl<T> Sync for Py<T> {}
impl<T> Py<T>
where
T: PyClass,
{
pub fn new(py: Python<'_>, value: impl Into<PyClassInitializer<T>>) -> PyResult<Py<T>> {
Bound::new(py, value).map(Bound::unbind)
}
}
impl<T> Py<T> {
#[inline]
pub fn as_ptr(&self) -> *mut ffi::PyObject {
self.0.as_ptr()
}
#[inline]
pub fn into_ptr(self) -> *mut ffi::PyObject {
ManuallyDrop::new(self).0.as_ptr()
}
#[inline]
pub fn as_any(&self) -> &Py<PyAny> {
let ptr = NonNull::from(self).cast();
unsafe { ptr.as_ref() }
}
#[inline]
pub fn into_any(self) -> Py<PyAny> {
unsafe { Py::from_non_null(ManuallyDrop::new(self).0) }
}
}
impl<T> Py<T>
where
T: PyClass,
{
#[inline]
#[track_caller]
pub fn borrow<'py>(&'py self, py: Python<'py>) -> PyRef<'py, T> {
self.bind(py).borrow()
}
#[inline]
#[track_caller]
pub fn borrow_mut<'py>(&'py self, py: Python<'py>) -> PyRefMut<'py, T>
where
T: PyClass<Frozen = False>,
{
self.bind(py).borrow_mut()
}
#[inline]
pub fn try_borrow<'py>(&'py self, py: Python<'py>) -> Result<PyRef<'py, T>, PyBorrowError> {
self.bind(py).try_borrow()
}
#[inline]
pub fn try_borrow_mut<'py>(
&'py self,
py: Python<'py>,
) -> Result<PyRefMut<'py, T>, PyBorrowMutError>
where
T: PyClass<Frozen = False>,
{
self.bind(py).try_borrow_mut()
}
#[inline]
pub fn get(&self) -> &T
where
T: PyClass<Frozen = True> + Sync,
{
unsafe { &*self.get_class_object().get_ptr() }
}
#[inline]
pub(crate) fn get_class_object(&self) -> &<T as PyClassImpl>::Layout {
let class_object = self.as_ptr().cast::<<T as PyClassImpl>::Layout>();
unsafe { &*class_object }
}
}
impl<T> Py<T> {
#[inline]
pub fn bind<'py>(&self, _py: Python<'py>) -> &Bound<'py, T> {
unsafe { NonNull::from(self).cast().as_ref() }
}
#[inline]
pub fn into_bound(self, py: Python<'_>) -> Bound<'_, T> {
Bound(py, ManuallyDrop::new(self))
}
#[inline]
pub fn bind_borrowed<'a, 'py>(&'a self, py: Python<'py>) -> Borrowed<'a, 'py, T> {
let borrowed = unsafe { Borrowed::from_non_null(py, self.0) };
unsafe { borrowed.cast_unchecked() }
}
#[inline]
pub fn is<U: AsRef<Py<PyAny>>>(&self, o: U) -> bool {
ptr::eq(self.as_ptr(), o.as_ref().as_ptr())
}
#[inline]
pub fn get_refcnt(&self, _py: Python<'_>) -> isize {
unsafe { ffi::Py_REFCNT(self.0.as_ptr()) }
}
#[inline]
pub fn clone_ref(&self, _py: Python<'_>) -> Py<T> {
unsafe { ffi::Py_INCREF(self.0.as_ptr()) };
unsafe { Self::from_non_null(self.0) }
}
#[inline]
pub fn drop_ref(self, py: Python<'_>) {
let _ = self.into_bound(py);
}
pub fn is_none(&self, py: Python<'_>) -> bool {
self.bind(py).as_any().is_none()
}
pub fn is_truthy(&self, py: Python<'_>) -> PyResult<bool> {
self.bind(py).as_any().is_truthy()
}
pub fn extract<'a, 'py, D>(&'a self, py: Python<'py>) -> Result<D, D::Error>
where
D: FromPyObject<'a, 'py>,
{
self.bind_borrowed(py).extract()
}
pub fn getattr<'py, N>(&self, py: Python<'py>, attr_name: N) -> PyResult<Py<PyAny>>
where
N: IntoPyObject<'py, Target = PyString>,
{
self.bind(py).as_any().getattr(attr_name).map(Bound::unbind)
}
pub fn setattr<'py, N, V>(&self, py: Python<'py>, attr_name: N, value: V) -> PyResult<()>
where
N: IntoPyObject<'py, Target = PyString>,
V: IntoPyObject<'py>,
{
self.bind(py).as_any().setattr(attr_name, value)
}
pub fn call<'py, A>(
&self,
py: Python<'py>,
args: A,
kwargs: Option<&Bound<'py, PyDict>>,
) -> PyResult<Py<PyAny>>
where
A: PyCallArgs<'py>,
{
self.bind(py).as_any().call(args, kwargs).map(Bound::unbind)
}
pub fn call1<'py, A>(&self, py: Python<'py>, args: A) -> PyResult<Py<PyAny>>
where
A: PyCallArgs<'py>,
{
self.bind(py).as_any().call1(args).map(Bound::unbind)
}
pub fn call0(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
self.bind(py).as_any().call0().map(Bound::unbind)
}
pub fn call_method<'py, N, A>(
&self,
py: Python<'py>,
name: N,
args: A,
kwargs: Option<&Bound<'py, PyDict>>,
) -> PyResult<Py<PyAny>>
where
N: IntoPyObject<'py, Target = PyString>,
A: PyCallArgs<'py>,
{
self.bind(py)
.as_any()
.call_method(name, args, kwargs)
.map(Bound::unbind)
}
pub fn call_method1<'py, N, A>(&self, py: Python<'py>, name: N, args: A) -> PyResult<Py<PyAny>>
where
N: IntoPyObject<'py, Target = PyString>,
A: PyCallArgs<'py>,
{
self.bind(py)
.as_any()
.call_method1(name, args)
.map(Bound::unbind)
}
pub fn call_method0<'py, N>(&self, py: Python<'py>, name: N) -> PyResult<Py<PyAny>>
where
N: IntoPyObject<'py, Target = PyString>,
{
self.bind(py).as_any().call_method0(name).map(Bound::unbind)
}
#[inline]
#[track_caller]
#[deprecated(note = "use `Bound::from_owned_ptr` instead", since = "0.28.0")]
pub unsafe fn from_owned_ptr(py: Python<'_>, ptr: *mut ffi::PyObject) -> Py<T> {
match NonNull::new(ptr) {
Some(nonnull_ptr) => {
unsafe { Self::from_non_null(nonnull_ptr) }
}
None => panic_on_null(py),
}
}
#[inline]
#[deprecated(note = "use `Bound::from_owned_ptr_or_err` instead", since = "0.28.0")]
pub unsafe fn from_owned_ptr_or_err(
py: Python<'_>,
ptr: *mut ffi::PyObject,
) -> PyResult<Py<T>> {
match NonNull::new(ptr) {
Some(nonnull_ptr) => Ok(
unsafe { Self::from_non_null(nonnull_ptr) },
),
None => Err(PyErr::fetch(py)),
}
}
#[inline]
#[deprecated(note = "use `Bound::from_owned_ptr_or_opt` instead", since = "0.28.0")]
pub unsafe fn from_owned_ptr_or_opt(_py: Python<'_>, ptr: *mut ffi::PyObject) -> Option<Self> {
NonNull::new(ptr).map(|nonnull_ptr| {
unsafe { Self::from_non_null(nonnull_ptr) }
})
}
#[inline]
#[track_caller]
#[deprecated(note = "use `Borrowed::from_borrowed_ptr` instead", since = "0.28.0")]
pub unsafe fn from_borrowed_ptr(py: Python<'_>, ptr: *mut ffi::PyObject) -> Py<T> {
#[allow(deprecated)]
unsafe { Self::from_borrowed_ptr_or_opt(py, ptr) }.unwrap_or_else(|| panic_on_null(py))
}
#[inline]
#[deprecated(
note = "use `Borrowed::from_borrowed_ptr_or_err` instead",
since = "0.28.0"
)]
pub unsafe fn from_borrowed_ptr_or_err(
py: Python<'_>,
ptr: *mut ffi::PyObject,
) -> PyResult<Self> {
#[allow(deprecated)]
unsafe { Self::from_borrowed_ptr_or_opt(py, ptr) }.ok_or_else(|| PyErr::fetch(py))
}
#[inline]
#[deprecated(
note = "use `Borrowed::from_borrowed_ptr_or_opt` instead",
since = "0.28.0"
)]
pub unsafe fn from_borrowed_ptr_or_opt(
_py: Python<'_>,
ptr: *mut ffi::PyObject,
) -> Option<Self> {
NonNull::new(ptr).map(|nonnull_ptr| {
unsafe { ffi::Py_INCREF(ptr) };
unsafe { Self::from_non_null(nonnull_ptr) }
})
}
#[inline(always)]
unsafe fn from_non_null(ptr: NonNull<ffi::PyObject>) -> Self {
Self(ptr, PhantomData)
}
#[inline(always)]
unsafe fn from_borrowed_non_null(_py: Python<'_>, ptr: NonNull<ffi::PyObject>) -> Self {
unsafe { ffi::Py_INCREF(ptr.as_ptr()) };
unsafe { Self::from_non_null(ptr) }
}
}
impl<T> AsRef<Py<PyAny>> for Py<T> {
#[inline]
fn as_ref(&self) -> &Py<PyAny> {
self.as_any()
}
}
impl<T> std::convert::From<Py<T>> for Py<PyAny>
where
T: DerefToPyAny,
{
#[inline]
fn from(other: Py<T>) -> Self {
other.into_any()
}
}
impl<T> std::convert::From<Bound<'_, T>> for Py<PyAny>
where
T: DerefToPyAny,
{
#[inline]
fn from(other: Bound<'_, T>) -> Self {
other.into_any().unbind()
}
}
impl<T> std::convert::From<Bound<'_, T>> for Py<T> {
#[inline]
fn from(other: Bound<'_, T>) -> Self {
other.unbind()
}
}
impl<T> std::convert::From<Borrowed<'_, '_, T>> for Py<T> {
fn from(value: Borrowed<'_, '_, T>) -> Self {
value.unbind()
}
}
impl<'py, T> std::convert::From<PyRef<'py, T>> for Py<T>
where
T: PyClass,
{
fn from(pyref: PyRef<'py, T>) -> Self {
unsafe { Bound::from_borrowed_ptr(pyref.py(), pyref.as_ptr()).cast_into_unchecked() }
.unbind()
}
}
impl<'py, T> std::convert::From<PyRefMut<'py, T>> for Py<T>
where
T: PyClass<Frozen = False>,
{
fn from(pyref: PyRefMut<'py, T>) -> Self {
unsafe { Bound::from_borrowed_ptr(pyref.py(), pyref.as_ptr()).cast_into_unchecked() }
.unbind()
}
}
#[cfg(feature = "py-clone")]
impl<T> Clone for Py<T> {
#[track_caller]
#[inline]
fn clone(&self) -> Self {
#[track_caller]
#[inline]
fn try_incref(obj: NonNull<ffi::PyObject>) {
use crate::internal::state::thread_is_attached;
if thread_is_attached() {
unsafe { ffi::Py_INCREF(obj.as_ptr()) }
} else {
incref_failed()
}
}
#[cold]
#[track_caller]
fn incref_failed() -> ! {
panic!("Cannot clone pointer into Python heap without the thread being attached.");
}
try_incref(self.0);
Self(self.0, PhantomData)
}
}
impl<T> Drop for Py<T> {
#[inline]
fn drop(&mut self) {
#[inline]
fn inner(obj: NonNull<ffi::PyObject>) {
use crate::internal::state::thread_is_attached;
if thread_is_attached() {
unsafe { ffi::Py_DECREF(obj.as_ptr()) }
} else {
drop_slow(obj)
}
}
#[cold]
fn drop_slow(obj: NonNull<ffi::PyObject>) {
unsafe {
state::register_decref(obj);
}
}
inner(self.0)
}
}
impl<'a, 'py, T> FromPyObject<'a, 'py> for Py<T>
where
T: PyTypeCheck + 'a,
{
type Error = CastError<'a, 'py>;
#[cfg(feature = "experimental-inspect")]
const INPUT_TYPE: PyStaticExpr = T::TYPE_HINT;
fn extract(ob: Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
ob.extract::<Bound<'py, T>>().map(Bound::unbind)
}
}
impl<'a, 'py, T> FromPyObject<'a, 'py> for Bound<'py, T>
where
T: PyTypeCheck + 'a,
{
type Error = CastError<'a, 'py>;
#[cfg(feature = "experimental-inspect")]
const INPUT_TYPE: PyStaticExpr = T::TYPE_HINT;
fn extract(ob: Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
ob.cast().map(Borrowed::to_owned)
}
}
impl<T> std::fmt::Display for Py<T>
where
T: PyTypeInfo,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Python::attach(|py| std::fmt::Display::fmt(self.bind(py), f))
}
}
impl<T> std::fmt::Debug for Py<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("Py").field(&self.0.as_ptr()).finish()
}
}
impl Py<PyAny> {
#[deprecated(since = "0.27.0", note = "use `Py::cast_bound` instead")]
#[inline]
#[allow(deprecated)]
pub fn downcast_bound<'py, T>(
&self,
py: Python<'py>,
) -> Result<&Bound<'py, T>, DowncastError<'_, 'py>>
where
T: PyTypeCheck,
{
#[allow(deprecated)]
self.bind(py).downcast()
}
#[deprecated(since = "0.27.0", note = "use `Py::cast_bound_unchecked` instead")]
#[inline]
pub unsafe fn downcast_bound_unchecked<'py, T>(&self, py: Python<'py>) -> &Bound<'py, T> {
unsafe { self.cast_bound_unchecked(py) }
}
}
impl<T> Py<T> {
pub fn cast_bound<'py, U>(&self, py: Python<'py>) -> Result<&Bound<'py, U>, CastError<'_, 'py>>
where
U: PyTypeCheck,
{
self.bind(py).cast()
}
#[inline]
pub unsafe fn cast_bound_unchecked<'py, U>(&self, py: Python<'py>) -> &Bound<'py, U> {
unsafe { self.bind(py).cast_unchecked() }
}
}
#[track_caller]
#[cold]
fn panic_on_null(py: Python<'_>) -> ! {
if let Some(err) = PyErr::take(py) {
err.write_unraisable(py, None);
}
panic!("PyObject pointer is null");
}
#[cfg(test)]
mod tests {
use super::{Bound, IntoPyObject, Py};
#[cfg(all(feature = "macros", Py_3_8, panic = "unwind"))]
use crate::exceptions::PyValueError;
use crate::test_utils::generate_unique_module_name;
#[cfg(all(feature = "macros", Py_3_8, panic = "unwind"))]
use crate::test_utils::UnraisableCapture;
use crate::types::{dict::IntoPyDict, PyAnyMethods, PyCapsule, PyDict, PyString};
use crate::{ffi, Borrowed, IntoPyObjectExt, PyAny, PyResult, Python};
use std::ffi::CStr;
#[test]
fn test_call() {
Python::attach(|py| {
let obj = py.get_type::<PyDict>().into_pyobject(py).unwrap();
let assert_repr = |obj: Bound<'_, PyAny>, expected: &str| {
assert_eq!(obj.repr().unwrap(), expected);
};
assert_repr(obj.call0().unwrap(), "{}");
assert_repr(obj.call1(()).unwrap(), "{}");
assert_repr(obj.call((), None).unwrap(), "{}");
assert_repr(obj.call1(((('x', 1),),)).unwrap(), "{'x': 1}");
assert_repr(
obj.call((), Some(&[('x', 1)].into_py_dict(py).unwrap()))
.unwrap(),
"{'x': 1}",
);
})
}
#[test]
fn test_call_tuple_ref() {
let assert_repr = |obj: &Bound<'_, PyAny>, expected: &str| {
use crate::prelude::PyStringMethods;
assert_eq!(
obj.repr()
.unwrap()
.to_cow()
.unwrap()
.trim_matches(|c| c == '{' || c == '}'),
expected.trim_matches(|c| c == ',' || c == ' ')
);
};
macro_rules! tuple {
($py:ident, $($key: literal => $value: literal),+) => {
let ty_obj = $py.get_type::<PyDict>().into_pyobject($py).unwrap();
assert!(ty_obj.call1(&(($(($key),)+),)).is_err());
let obj = ty_obj.call1(&(($(($key, i32::from($value)),)+),)).unwrap();
assert_repr(&obj, concat!($("'", $key, "'", ": ", stringify!($value), ", ",)+));
assert!(obj.call_method1("update", &(($(($key),)+),)).is_err());
obj.call_method1("update", &(($((i32::from($value), $key),)+),)).unwrap();
assert_repr(&obj, concat!(
concat!($("'", $key, "'", ": ", stringify!($value), ", ",)+),
concat!($(stringify!($value), ": ", "'", $key, "'", ", ",)+)
));
};
}
Python::attach(|py| {
tuple!(py, "a" => 1);
tuple!(py, "a" => 1, "b" => 2);
tuple!(py, "a" => 1, "b" => 2, "c" => 3);
tuple!(py, "a" => 1, "b" => 2, "c" => 3, "d" => 4);
tuple!(py, "a" => 1, "b" => 2, "c" => 3, "d" => 4, "e" => 5);
tuple!(py, "a" => 1, "b" => 2, "c" => 3, "d" => 4, "e" => 5, "f" => 6);
tuple!(py, "a" => 1, "b" => 2, "c" => 3, "d" => 4, "e" => 5, "f" => 6, "g" => 7);
tuple!(py, "a" => 1, "b" => 2, "c" => 3, "d" => 4, "e" => 5, "f" => 6, "g" => 7, "h" => 8);
tuple!(py, "a" => 1, "b" => 2, "c" => 3, "d" => 4, "e" => 5, "f" => 6, "g" => 7, "h" => 8, "i" => 9);
tuple!(py, "a" => 1, "b" => 2, "c" => 3, "d" => 4, "e" => 5, "f" => 6, "g" => 7, "h" => 8, "i" => 9, "j" => 10, "k" => 11);
tuple!(py, "a" => 1, "b" => 2, "c" => 3, "d" => 4, "e" => 5, "f" => 6, "g" => 7, "h" => 8, "i" => 9, "j" => 10, "k" => 11, "l" => 12);
})
}
#[test]
fn test_call_for_non_existing_method() {
Python::attach(|py| {
let obj: Py<PyAny> = PyDict::new(py).into();
assert!(obj.call_method0(py, "asdf").is_err());
assert!(obj
.call_method(py, "nonexistent_method", (1,), None)
.is_err());
assert!(obj.call_method0(py, "nonexistent_method").is_err());
assert!(obj.call_method1(py, "nonexistent_method", (1,)).is_err());
});
}
#[test]
fn py_from_dict() {
let dict: Py<PyDict> = Python::attach(|py| {
let native = PyDict::new(py);
Py::from(native)
});
Python::attach(move |py| {
assert_eq!(dict.get_refcnt(py), 1);
});
}
#[test]
fn pyobject_from_py() {
Python::attach(|py| {
let dict: Py<PyDict> = PyDict::new(py).unbind();
let cnt = dict.get_refcnt(py);
let p: Py<PyAny> = dict.into();
assert_eq!(p.get_refcnt(py), cnt);
});
}
#[test]
fn attr() -> PyResult<()> {
use crate::types::PyModule;
Python::attach(|py| {
const CODE: &CStr = cr#"
class A:
pass
a = A()
"#;
let module = PyModule::from_code(py, CODE, c"", &generate_unique_module_name(""))?;
let instance: Py<PyAny> = module.getattr("a")?.into();
instance.getattr(py, "foo").unwrap_err();
instance.setattr(py, "foo", "bar")?;
assert!(instance
.getattr(py, "foo")?
.bind(py)
.eq(PyString::new(py, "bar"))?);
instance.getattr(py, "foo")?;
Ok(())
})
}
#[test]
fn pystring_attr() -> PyResult<()> {
use crate::types::PyModule;
Python::attach(|py| {
const CODE: &CStr = cr#"
class A:
pass
a = A()
"#;
let module = PyModule::from_code(py, CODE, c"", &generate_unique_module_name(""))?;
let instance: Py<PyAny> = module.getattr("a")?.into();
let foo = crate::intern!(py, "foo");
let bar = crate::intern!(py, "bar");
instance.getattr(py, foo).unwrap_err();
instance.setattr(py, foo, bar)?;
assert!(instance.getattr(py, foo)?.bind(py).eq(bar)?);
Ok(())
})
}
#[test]
fn invalid_attr() -> PyResult<()> {
Python::attach(|py| {
let instance: Py<PyAny> = py.eval(c"object()", None, None)?.into();
instance.getattr(py, "foo").unwrap_err();
instance.setattr(py, "foo", "bar").unwrap_err();
Ok(())
})
}
#[test]
fn test_py2_from_py_object() {
Python::attach(|py| {
let instance = py.eval(c"object()", None, None).unwrap();
let ptr = instance.as_ptr();
let instance: Bound<'_, PyAny> = instance.extract().unwrap();
assert_eq!(instance.as_ptr(), ptr);
})
}
#[test]
fn test_py2_into_py_object() {
Python::attach(|py| {
let instance = py.eval(c"object()", None, None).unwrap();
let ptr = instance.as_ptr();
let instance: Py<PyAny> = instance.clone().unbind();
assert_eq!(instance.as_ptr(), ptr);
})
}
#[test]
fn test_debug_fmt() {
Python::attach(|py| {
let obj = "hello world".into_pyobject(py).unwrap();
assert_eq!(format!("{obj:?}"), "'hello world'");
});
}
#[test]
fn test_display_fmt() {
Python::attach(|py| {
let obj = "hello world".into_pyobject(py).unwrap();
assert_eq!(format!("{obj}"), "hello world");
});
}
#[test]
fn test_bound_as_any() {
Python::attach(|py| {
let obj = PyString::new(py, "hello world");
let any = obj.as_any();
assert_eq!(any.as_ptr(), obj.as_ptr());
});
}
#[test]
fn test_bound_into_any() {
Python::attach(|py| {
let obj = PyString::new(py, "hello world");
let any = obj.clone().into_any();
assert_eq!(any.as_ptr(), obj.as_ptr());
});
}
#[test]
fn test_bound_py_conversions() {
Python::attach(|py| {
let obj: Bound<'_, PyString> = PyString::new(py, "hello world");
let obj_unbound: &Py<PyString> = obj.as_unbound();
let _: &Bound<'_, PyString> = obj_unbound.bind(py);
let obj_unbound: Py<PyString> = obj.unbind();
let obj: Bound<'_, PyString> = obj_unbound.into_bound(py);
assert_eq!(obj, "hello world");
});
}
#[test]
fn test_borrowed_identity() {
Python::attach(|py| {
let yes = true.into_pyobject(py).unwrap();
let no = false.into_pyobject(py).unwrap();
assert!(yes.is(yes));
assert!(!yes.is(no));
});
}
#[test]
#[expect(
clippy::undocumented_unsafe_blocks,
reason = "Doing evil things to try to make `Bound` blow up"
)]
fn bound_from_borrowed_ptr_constructors() {
Python::attach(|py| {
fn check_drop<'py>(
py: Python<'py>,
method: impl FnOnce(*mut ffi::PyObject) -> Bound<'py, PyAny>,
) {
let mut dropped = false;
let capsule = PyCapsule::new_with_destructor(
py,
(&mut dropped) as *mut _ as usize,
None,
|ptr, _| unsafe { std::ptr::write(ptr as *mut bool, true) },
)
.unwrap();
let bound = method(capsule.as_ptr());
assert!(!dropped);
drop(capsule);
assert!(!dropped);
drop(bound);
assert!(dropped);
}
check_drop(py, |ptr| unsafe { Bound::from_borrowed_ptr(py, ptr) });
check_drop(py, |ptr| unsafe {
Bound::from_borrowed_ptr_or_opt(py, ptr).unwrap()
});
check_drop(py, |ptr| unsafe {
Bound::from_borrowed_ptr_or_err(py, ptr).unwrap()
});
})
}
#[test]
#[expect(
clippy::undocumented_unsafe_blocks,
reason = "Doing evil things to try to make `Borrowed` blow up"
)]
fn borrowed_ptr_constructors() {
Python::attach(|py| {
fn check_drop<'py>(
py: Python<'py>,
method: impl FnOnce(&*mut ffi::PyObject) -> Borrowed<'_, 'py, PyAny>,
) {
let mut dropped = false;
let capsule = PyCapsule::new_with_destructor(
py,
(&mut dropped) as *mut _ as usize,
None,
|ptr, _| unsafe { std::ptr::write(ptr as *mut bool, true) },
)
.unwrap();
let ptr = &capsule.as_ptr();
let _borrowed = method(ptr);
assert!(!dropped);
drop(capsule);
assert!(dropped);
}
check_drop(py, |&ptr| unsafe { Borrowed::from_ptr(py, ptr) });
check_drop(py, |&ptr| unsafe {
Borrowed::from_ptr_or_opt(py, ptr).unwrap()
});
check_drop(py, |&ptr| unsafe {
Borrowed::from_ptr_or_err(py, ptr).unwrap()
});
})
}
#[test]
fn explicit_drop_ref() {
Python::attach(|py| {
let object: Py<PyDict> = PyDict::new(py).unbind();
let object2 = object.clone_ref(py);
assert_eq!(object.as_ptr(), object2.as_ptr());
assert_eq!(object.get_refcnt(py), 2);
object.drop_ref(py);
assert_eq!(object2.get_refcnt(py), 1);
object2.drop_ref(py);
});
}
#[test]
fn test_py_is_truthy() {
Python::attach(|py| {
let yes = true.into_py_any(py).unwrap();
let no = false.into_py_any(py).unwrap();
assert!(yes.is_truthy(py).unwrap());
assert!(!no.is_truthy(py).unwrap());
});
}
#[cfg(all(feature = "macros", Py_3_8, panic = "unwind"))]
#[test]
fn test_constructors_panic_on_null() {
Python::attach(|py| {
const NULL: *mut ffi::PyObject = std::ptr::null_mut();
#[expect(deprecated, reason = "Py<T> constructors")]
for constructor in unsafe {
[
(|py| {
Py::<PyAny>::from_owned_ptr(py, NULL);
}) as fn(Python<'_>),
(|py| {
Py::<PyAny>::from_borrowed_ptr(py, NULL);
}) as fn(Python<'_>),
(|py| {
Bound::from_owned_ptr(py, NULL);
}) as fn(Python<'_>),
(|py| {
Bound::from_borrowed_ptr(py, NULL);
}) as fn(Python<'_>),
(|py| {
Borrowed::from_ptr(py, NULL);
}) as fn(Python<'_>),
]
} {
UnraisableCapture::enter(py, |capture| {
let result = std::panic::catch_unwind(|| {
constructor(py);
});
assert_eq!(
result.unwrap_err().downcast_ref::<&str>(),
Some(&"PyObject pointer is null")
);
assert!(capture.take_capture().is_none());
PyValueError::new_err("error").restore(py);
let result = std::panic::catch_unwind(|| {
constructor(py);
});
assert_eq!(
result.unwrap_err().downcast_ref::<&str>(),
Some(&"PyObject pointer is null")
);
assert!(capture.take_capture().is_some_and(|(err, obj)| {
err.is_instance_of::<PyValueError>(py) && obj.is_none()
}));
});
}
});
}
#[cfg(feature = "macros")]
mod using_macros {
use super::*;
#[crate::pyclass(crate = "crate")]
struct SomeClass(i32);
#[test]
fn py_borrow_methods() {
Python::attach(|py| {
let instance = Py::new(py, SomeClass(0)).unwrap();
assert_eq!(instance.borrow(py).0, 0);
assert_eq!(instance.try_borrow(py).unwrap().0, 0);
assert_eq!(instance.borrow_mut(py).0, 0);
assert_eq!(instance.try_borrow_mut(py).unwrap().0, 0);
instance.borrow_mut(py).0 = 123;
assert_eq!(instance.borrow(py).0, 123);
assert_eq!(instance.try_borrow(py).unwrap().0, 123);
assert_eq!(instance.borrow_mut(py).0, 123);
assert_eq!(instance.try_borrow_mut(py).unwrap().0, 123);
})
}
#[test]
fn bound_borrow_methods() {
Python::attach(|py| {
let instance = Bound::new(py, SomeClass(0)).unwrap();
assert_eq!(instance.borrow().0, 0);
assert_eq!(instance.try_borrow().unwrap().0, 0);
assert_eq!(instance.borrow_mut().0, 0);
assert_eq!(instance.try_borrow_mut().unwrap().0, 0);
instance.borrow_mut().0 = 123;
assert_eq!(instance.borrow().0, 123);
assert_eq!(instance.try_borrow().unwrap().0, 123);
assert_eq!(instance.borrow_mut().0, 123);
assert_eq!(instance.try_borrow_mut().unwrap().0, 123);
})
}
#[crate::pyclass(frozen, crate = "crate")]
struct FrozenClass(i32);
#[test]
fn test_frozen_get() {
Python::attach(|py| {
for i in 0..10 {
let instance = Py::new(py, FrozenClass(i)).unwrap();
assert_eq!(instance.get().0, i);
assert_eq!(instance.bind(py).get().0, i);
}
})
}
#[crate::pyclass(crate = "crate", subclass)]
struct BaseClass;
trait MyClassMethods<'py>: Sized {
fn pyrepr_by_ref(&self) -> PyResult<String>;
fn pyrepr_by_val(self) -> PyResult<String> {
self.pyrepr_by_ref()
}
}
impl<'py> MyClassMethods<'py> for Bound<'py, BaseClass> {
fn pyrepr_by_ref(&self) -> PyResult<String> {
self.call_method0("__repr__")?.extract()
}
}
#[crate::pyclass(crate = "crate", extends = BaseClass)]
struct SubClass;
#[test]
fn test_as_super() {
Python::attach(|py| {
let obj = Bound::new(py, (SubClass, BaseClass)).unwrap();
let _: &Bound<'_, BaseClass> = obj.as_super();
let _: &Bound<'_, PyAny> = obj.as_super().as_super();
assert!(obj.as_super().pyrepr_by_ref().is_ok());
})
}
#[test]
fn test_into_super() {
Python::attach(|py| {
let obj = Bound::new(py, (SubClass, BaseClass)).unwrap();
let _: Bound<'_, BaseClass> = obj.clone().into_super();
let _: Bound<'_, PyAny> = obj.clone().into_super().into_super();
assert!(obj.into_super().pyrepr_by_val().is_ok());
})
}
}
}