use crate::conversion::IntoPyObject;
use crate::err::{self, PyErr, PyResult};
use crate::impl_::pycell::PyClassObject;
use crate::internal_tricks::ptr_from_ref;
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, PyTuple};
use crate::{
ffi, AsPyPointer, DowncastError, FromPyObject, PyAny, PyClass, PyClassInitializer, PyRef,
PyRefMut, PyTypeInfo, Python,
};
use crate::{gil, PyTypeCheck};
#[allow(deprecated)]
use crate::{IntoPy, ToPyObject};
use std::marker::PhantomData;
use std::mem::ManuallyDrop;
use std::ops::Deref;
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> Bound<'py, PyAny> {
#[inline]
#[track_caller]
pub unsafe fn from_owned_ptr(py: Python<'py>, ptr: *mut ffi::PyObject) -> Self {
Self(py, ManuallyDrop::new(Py::from_owned_ptr(py, ptr)))
}
#[inline]
pub unsafe fn from_owned_ptr_or_opt(py: Python<'py>, ptr: *mut ffi::PyObject) -> Option<Self> {
Py::from_owned_ptr_or_opt(py, ptr).map(|obj| Self(py, ManuallyDrop::new(obj)))
}
#[inline]
pub unsafe fn from_owned_ptr_or_err(
py: Python<'py>,
ptr: *mut ffi::PyObject,
) -> PyResult<Self> {
Py::from_owned_ptr_or_err(py, ptr).map(|obj| Self(py, ManuallyDrop::new(obj)))
}
pub(crate) unsafe fn from_owned_ptr_unchecked(
py: Python<'py>,
ptr: *mut ffi::PyObject,
) -> Self {
Self(py, ManuallyDrop::new(Py::from_owned_ptr_unchecked(ptr)))
}
#[inline]
#[track_caller]
pub unsafe fn from_borrowed_ptr(py: Python<'py>, ptr: *mut ffi::PyObject) -> Self {
Self(py, ManuallyDrop::new(Py::from_borrowed_ptr(py, ptr)))
}
#[inline]
pub unsafe fn from_borrowed_ptr_or_opt(
py: Python<'py>,
ptr: *mut ffi::PyObject,
) -> Option<Self> {
Py::from_borrowed_ptr_or_opt(py, ptr).map(|obj| Self(py, ManuallyDrop::new(obj)))
}
#[inline]
pub unsafe fn from_borrowed_ptr_or_err(
py: Python<'py>,
ptr: *mut ffi::PyObject,
) -> PyResult<Self> {
Py::from_borrowed_ptr_or_err(py, ptr).map(|obj| Self(py, ManuallyDrop::new(obj)))
}
#[inline]
pub(crate) unsafe fn ref_from_ptr<'a>(
_py: Python<'py>,
ptr: &'a *mut ffi::PyObject,
) -> &'a Self {
&*ptr_from_ref(ptr).cast::<Bound<'py, PyAny>>()
}
#[inline]
pub(crate) unsafe fn ref_from_ptr_or_opt<'a>(
_py: Python<'py>,
ptr: &'a *mut ffi::PyObject,
) -> &'a Option<Self> {
&*ptr_from_ref(ptr).cast::<Option<Bound<'py, PyAny>>>()
}
}
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.as_any().downcast_unchecked() }
}
#[inline]
pub fn into_super(self) -> Bound<'py, T::BaseType> {
unsafe { self.into_any().downcast_into_unchecked() }
}
#[inline]
pub(crate) fn get_class_object(&self) -> &PyClassObject<T> {
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 {} object>", name),
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> 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> {
unsafe { &*ptr_from_ref(self).cast::<Bound<'py, PyAny>>() }
}
#[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> {
Borrowed(
unsafe { NonNull::new_unchecked(self.as_ptr()) },
PhantomData,
self.py(),
)
}
#[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
}
}
unsafe impl<T> AsPyPointer for Bound<'_, T> {
#[inline]
fn as_ptr(&self) -> *mut ffi::PyObject {
self.1.as_ptr()
}
}
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)
}
}
impl<'a, 'py> Borrowed<'a, 'py, PyAny> {
#[inline]
#[track_caller]
pub unsafe fn from_ptr(py: Python<'py>, ptr: *mut ffi::PyObject) -> Self {
Self(
NonNull::new(ptr).unwrap_or_else(|| crate::err::panic_after_error(py)),
PhantomData,
py,
)
}
#[inline]
pub unsafe fn from_ptr_or_opt(py: Python<'py>, ptr: *mut ffi::PyObject) -> Option<Self> {
NonNull::new(ptr).map(|ptr| Self(ptr, PhantomData, py))
}
#[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(Self(ptr, PhantomData, py)),
)
}
#[inline]
pub(crate) unsafe fn from_ptr_unchecked(py: Python<'py>, ptr: *mut ffi::PyObject) -> Self {
Self(NonNull::new_unchecked(ptr), PhantomData, py)
}
#[inline]
pub(crate) fn downcast<T>(self) -> Result<Borrowed<'a, 'py, T>, DowncastError<'a, 'py>>
where
T: PyTypeCheck,
{
if T::type_check(&self) {
Ok(unsafe { self.downcast_unchecked() })
} else {
Err(DowncastError::new_from_borrowed(self, T::NAME))
}
}
#[inline]
pub(crate) unsafe fn downcast_unchecked<T>(self) -> Borrowed<'a, 'py, T> {
Borrowed(self.0, PhantomData, self.2)
}
}
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> 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 { &*ptr_from_ref(&self.0).cast() }
}
}
impl<T> Clone for Borrowed<'_, '_, T> {
#[inline]
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for Borrowed<'_, '_, T> {}
#[allow(deprecated)]
impl<T> ToPyObject for Borrowed<'_, '_, T> {
#[inline]
fn to_object(&self, py: Python<'_>) -> PyObject {
(*self).into_py(py)
}
}
#[allow(deprecated)]
impl<T> IntoPy<PyObject> for Borrowed<'_, '_, T> {
#[inline]
fn into_py(self, py: Python<'_>) -> PyObject {
self.to_owned().into_py(py)
}
}
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> {
unsafe { &*ptr_from_ref(self).cast::<Py<PyAny>>() }
}
#[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) -> &PyClassObject<T> {
let class_object = self.as_ptr().cast::<PyClassObject<T>>();
unsafe { &*class_object }
}
}
impl<T> Py<T> {
#[inline]
pub fn bind<'py>(&self, _py: Python<'py>) -> &Bound<'py, T> {
unsafe { &*ptr_from_ref(self).cast() }
}
#[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> {
Borrowed(self.0, PhantomData, py)
}
#[inline]
pub fn is<U: AsPyPointer>(&self, o: &U) -> bool {
self.as_ptr() == o.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.as_ptr());
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 {
unsafe { ffi::Py_None() == self.as_ptr() }
}
pub fn is_truthy(&self, py: Python<'_>) -> PyResult<bool> {
let v = unsafe { ffi::PyObject_IsTrue(self.as_ptr()) };
err::error_on_minusone(py, v)?;
Ok(v != 0)
}
pub fn extract<'a, 'py, D>(&'a self, py: Python<'py>) -> PyResult<D>
where
D: crate::conversion::FromPyObjectBound<'a, 'py>,
'py: 'a,
{
self.bind(py).as_any().extract()
}
pub fn getattr<'py, N>(&self, py: Python<'py>, attr_name: N) -> PyResult<PyObject>
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<PyObject>
where
A: IntoPyObject<'py, Target = PyTuple>,
{
self.bind(py)
.as_any()
.call(
args.into_pyobject(py).map_err(Into::into)?.into_bound(),
kwargs,
)
.map(Bound::unbind)
}
#[deprecated(since = "0.23.0", note = "renamed to `Py::call`")]
#[allow(deprecated)]
#[inline]
pub fn call_bound(
&self,
py: Python<'_>,
args: impl IntoPy<Py<PyTuple>>,
kwargs: Option<&Bound<'_, PyDict>>,
) -> PyResult<PyObject> {
self.call(py, args.into_py(py), kwargs)
}
pub fn call1<'py, N>(&self, py: Python<'py>, args: N) -> PyResult<PyObject>
where
N: IntoPyObject<'py, Target = PyTuple>,
{
self.bind(py)
.as_any()
.call1(args.into_pyobject(py).map_err(Into::into)?.into_bound())
.map(Bound::unbind)
}
pub fn call0(&self, py: Python<'_>) -> PyResult<PyObject> {
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<PyObject>
where
N: IntoPyObject<'py, Target = PyString>,
A: IntoPyObject<'py, Target = PyTuple>,
{
self.bind(py)
.as_any()
.call_method(
name,
args.into_pyobject(py).map_err(Into::into)?.into_bound(),
kwargs,
)
.map(Bound::unbind)
}
#[deprecated(since = "0.23.0", note = "renamed to `Py::call_method`")]
#[allow(deprecated)]
#[inline]
pub fn call_method_bound<N, A>(
&self,
py: Python<'_>,
name: N,
args: A,
kwargs: Option<&Bound<'_, PyDict>>,
) -> PyResult<PyObject>
where
N: IntoPy<Py<PyString>>,
A: IntoPy<Py<PyTuple>>,
{
self.call_method(py, name.into_py(py), args.into_py(py), kwargs)
}
pub fn call_method1<'py, N, A>(&self, py: Python<'py>, name: N, args: A) -> PyResult<PyObject>
where
N: IntoPyObject<'py, Target = PyString>,
A: IntoPyObject<'py, Target = PyTuple>,
{
self.bind(py)
.as_any()
.call_method1(
name,
args.into_pyobject(py).map_err(Into::into)?.into_bound(),
)
.map(Bound::unbind)
}
pub fn call_method0<'py, N>(&self, py: Python<'py>, name: N) -> PyResult<PyObject>
where
N: IntoPyObject<'py, Target = PyString>,
{
self.bind(py).as_any().call_method0(name).map(Bound::unbind)
}
#[inline]
#[track_caller]
pub unsafe fn from_owned_ptr(py: Python<'_>, ptr: *mut ffi::PyObject) -> Py<T> {
match NonNull::new(ptr) {
Some(nonnull_ptr) => Py(nonnull_ptr, PhantomData),
None => crate::err::panic_after_error(py),
}
}
#[inline]
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(Py(nonnull_ptr, PhantomData)),
None => Err(PyErr::fetch(py)),
}
}
#[inline]
pub unsafe fn from_owned_ptr_or_opt(_py: Python<'_>, ptr: *mut ffi::PyObject) -> Option<Self> {
NonNull::new(ptr).map(|nonnull_ptr| Py(nonnull_ptr, PhantomData))
}
pub(crate) unsafe fn from_owned_ptr_unchecked(ptr: *mut ffi::PyObject) -> Self {
Py(NonNull::new_unchecked(ptr), PhantomData)
}
#[inline]
#[track_caller]
pub unsafe fn from_borrowed_ptr(py: Python<'_>, ptr: *mut ffi::PyObject) -> Py<T> {
match Self::from_borrowed_ptr_or_opt(py, ptr) {
Some(slf) => slf,
None => crate::err::panic_after_error(py),
}
}
#[inline]
pub unsafe fn from_borrowed_ptr_or_err(
py: Python<'_>,
ptr: *mut ffi::PyObject,
) -> PyResult<Self> {
Self::from_borrowed_ptr_or_opt(py, ptr).ok_or_else(|| PyErr::fetch(py))
}
#[inline]
pub unsafe fn from_borrowed_ptr_or_opt(
_py: Python<'_>,
ptr: *mut ffi::PyObject,
) -> Option<Self> {
NonNull::new(ptr).map(|nonnull_ptr| {
ffi::Py_INCREF(ptr);
Py(nonnull_ptr, PhantomData)
})
}
unsafe fn from_non_null(ptr: NonNull<ffi::PyObject>) -> Self {
Self(ptr, PhantomData)
}
}
#[allow(deprecated)]
impl<T> ToPyObject for Py<T> {
#[inline]
fn to_object(&self, py: Python<'_>) -> PyObject {
self.clone_ref(py).into_any()
}
}
#[allow(deprecated)]
impl<T> IntoPy<PyObject> for Py<T> {
#[inline]
fn into_py(self, _py: Python<'_>) -> PyObject {
self.into_any()
}
}
#[allow(deprecated)]
impl<T> IntoPy<PyObject> for &'_ Py<T> {
#[inline]
fn into_py(self, py: Python<'_>) -> PyObject {
self.into_pyobject(py).unwrap().into_any().unbind()
}
}
#[allow(deprecated)]
impl<T> ToPyObject for Bound<'_, T> {
#[inline]
fn to_object(&self, py: Python<'_>) -> PyObject {
self.clone().into_py(py)
}
}
#[allow(deprecated)]
impl<T> IntoPy<PyObject> for Bound<'_, T> {
#[inline]
fn into_py(self, _py: Python<'_>) -> PyObject {
self.into_any().unbind()
}
}
#[allow(deprecated)]
impl<T> IntoPy<PyObject> for &Bound<'_, T> {
#[inline]
fn into_py(self, py: Python<'_>) -> PyObject {
self.into_pyobject(py).unwrap().into_any().unbind()
}
}
unsafe impl<T> crate::AsPyPointer for Py<T> {
#[inline]
fn as_ptr(&self) -> *mut ffi::PyObject {
self.0.as_ptr()
}
}
impl<T> std::convert::From<Py<T>> for PyObject
where
T: AsRef<PyAny>,
{
#[inline]
fn from(other: Py<T>) -> Self {
other.into_any()
}
}
impl<T> std::convert::From<Bound<'_, T>> for PyObject
where
T: AsRef<PyAny>,
{
#[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<'a, T> std::convert::From<PyRef<'a, T>> for Py<T>
where
T: PyClass,
{
fn from(pyref: PyRef<'a, T>) -> Self {
unsafe { Py::from_borrowed_ptr(pyref.py(), pyref.as_ptr()) }
}
}
impl<'a, T> std::convert::From<PyRefMut<'a, T>> for Py<T>
where
T: PyClass<Frozen = False>,
{
fn from(pyref: PyRefMut<'a, T>) -> Self {
unsafe { Py::from_borrowed_ptr(pyref.py(), pyref.as_ptr()) }
}
}
#[cfg(feature = "py-clone")]
impl<T> Clone for Py<T> {
#[track_caller]
fn clone(&self) -> Self {
unsafe {
gil::register_incref(self.0);
}
Self(self.0, PhantomData)
}
}
impl<T> Drop for Py<T> {
#[track_caller]
fn drop(&mut self) {
unsafe {
gil::register_decref(self.0);
}
}
}
impl<T> FromPyObject<'_> for Py<T>
where
T: PyTypeCheck,
{
fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult<Self> {
ob.extract::<Bound<'_, T>>().map(Bound::unbind)
}
}
impl<'py, T> FromPyObject<'py> for Bound<'py, T>
where
T: PyTypeCheck,
{
fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<Self> {
ob.downcast().cloned().map_err(Into::into)
}
}
impl<T> std::fmt::Display for Py<T>
where
T: PyTypeInfo,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Python::with_gil(|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()
}
}
pub type PyObject = Py<PyAny>;
impl PyObject {
#[inline]
pub fn downcast_bound<'py, T>(
&self,
py: Python<'py>,
) -> Result<&Bound<'py, T>, DowncastError<'_, 'py>>
where
T: PyTypeCheck,
{
self.bind(py).downcast()
}
#[inline]
pub unsafe fn downcast_bound_unchecked<'py, T>(&self, py: Python<'py>) -> &Bound<'py, T> {
self.bind(py).downcast_unchecked()
}
}
#[cfg(test)]
mod tests {
use super::{Bound, IntoPyObject, Py, PyObject};
use crate::tests::common::generate_unique_module_name;
use crate::types::{dict::IntoPyDict, PyAnyMethods, PyCapsule, PyDict, PyString};
use crate::{ffi, Borrowed, PyAny, PyResult, Python};
use pyo3_ffi::c_str;
use std::ffi::CStr;
#[test]
fn test_call() {
Python::with_gil(|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_for_non_existing_method() {
Python::with_gil(|py| {
let obj: PyObject = 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::with_gil(|py| {
let native = PyDict::new(py);
Py::from(native)
});
Python::with_gil(move |py| {
assert_eq!(dict.get_refcnt(py), 1);
});
}
#[test]
fn pyobject_from_py() {
Python::with_gil(|py| {
let dict: Py<PyDict> = PyDict::new(py).unbind();
let cnt = dict.get_refcnt(py);
let p: PyObject = dict.into();
assert_eq!(p.get_refcnt(py), cnt);
});
}
#[test]
fn attr() -> PyResult<()> {
use crate::types::PyModule;
Python::with_gil(|py| {
const CODE: &CStr = c_str!(
r#"
class A:
pass
a = A()
"#
);
let module =
PyModule::from_code(py, CODE, c_str!(""), &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::with_gil(|py| {
const CODE: &CStr = c_str!(
r#"
class A:
pass
a = A()
"#
);
let module =
PyModule::from_code(py, CODE, c_str!(""), &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::with_gil(|py| {
let instance: Py<PyAny> = py.eval(ffi::c_str!("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::with_gil(|py| {
let instance = py.eval(ffi::c_str!("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::with_gil(|py| {
let instance = py.eval(ffi::c_str!("object()"), None, None).unwrap();
let ptr = instance.as_ptr();
let instance: PyObject = instance.clone().unbind();
assert_eq!(instance.as_ptr(), ptr);
})
}
#[test]
fn test_debug_fmt() {
Python::with_gil(|py| {
let obj = "hello world".into_pyobject(py).unwrap();
assert_eq!(format!("{:?}", obj), "'hello world'");
});
}
#[test]
fn test_display_fmt() {
Python::with_gil(|py| {
let obj = "hello world".into_pyobject(py).unwrap();
assert_eq!(format!("{}", obj), "hello world");
});
}
#[test]
fn test_bound_as_any() {
Python::with_gil(|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::with_gil(|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::with_gil(|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 bound_from_borrowed_ptr_constructors() {
Python::with_gil(|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]
fn borrowed_ptr_constructors() {
Python::with_gil(|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::with_gil(|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);
});
}
#[cfg(feature = "macros")]
mod using_macros {
use super::*;
#[crate::pyclass(crate = "crate")]
struct SomeClass(i32);
#[test]
fn py_borrow_methods() {
Python::with_gil(|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::with_gil(|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::with_gil(|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::with_gil(|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::with_gil(|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());
})
}
}
}