use crate::err::PyResult;
use crate::ffi_ptr_ext::FfiPtrExt;
#[cfg(feature = "experimental-inspect")]
use crate::inspect::types::TypeInfo;
use crate::pyclass::boolean_struct::False;
use crate::types::any::PyAnyMethods;
use crate::types::{PyDict, PyString, PyTuple};
use crate::{ffi, Borrowed, Bound, Py, PyAny, PyClass, PyObject, PyRef, PyRefMut, Python};
#[cfg(feature = "gil-refs")]
use {
crate::{
err::{self, PyDowncastError},
gil, PyNativeType,
},
std::ptr::NonNull,
};
pub unsafe trait AsPyPointer {
fn as_ptr(&self) -> *mut ffi::PyObject;
}
pub trait ToPyObject {
fn to_object(&self, py: Python<'_>) -> PyObject;
}
#[cfg_attr(
diagnostic_namespace,
diagnostic::on_unimplemented(
message = "`{Self}` cannot be converted to a Python object",
note = "`IntoPy` is automatically implemented by the `#[pyclass]` macro",
note = "if you do not wish to have a corresponding Python type, implement it manually",
note = "if you do not own `{Self}` you can perform a manual conversion to one of the types in `pyo3::types::*`"
)
)]
pub trait IntoPy<T>: Sized {
fn into_py(self, py: Python<'_>) -> T;
#[cfg(feature = "experimental-inspect")]
fn type_output() -> TypeInfo {
TypeInfo::Any
}
#[doc(hidden)]
#[inline]
fn __py_call_vectorcall1<'py>(
self,
py: Python<'py>,
function: Borrowed<'_, 'py, PyAny>,
_: private::Token,
) -> PyResult<Bound<'py, PyAny>>
where
Self: IntoPy<Py<PyTuple>>,
{
#[inline]
fn inner<'py>(
py: Python<'py>,
function: Borrowed<'_, 'py, PyAny>,
args: Bound<'py, PyTuple>,
) -> PyResult<Bound<'py, PyAny>> {
unsafe {
ffi::PyObject_Call(function.as_ptr(), args.as_ptr(), std::ptr::null_mut())
.assume_owned_or_err(py)
}
}
inner(
py,
function,
<Self as IntoPy<Py<PyTuple>>>::into_py(self, py).into_bound(py),
)
}
#[doc(hidden)]
#[inline]
fn __py_call_vectorcall<'py>(
self,
py: Python<'py>,
function: Borrowed<'_, 'py, PyAny>,
kwargs: Option<Borrowed<'_, '_, PyDict>>,
_: private::Token,
) -> PyResult<Bound<'py, PyAny>>
where
Self: IntoPy<Py<PyTuple>>,
{
#[inline]
fn inner<'py>(
py: Python<'py>,
function: Borrowed<'_, 'py, PyAny>,
args: Bound<'py, PyTuple>,
kwargs: Option<Borrowed<'_, '_, PyDict>>,
) -> PyResult<Bound<'py, PyAny>> {
unsafe {
ffi::PyObject_Call(
function.as_ptr(),
args.as_ptr(),
kwargs.map_or_else(std::ptr::null_mut, |kwargs| kwargs.as_ptr()),
)
.assume_owned_or_err(py)
}
}
inner(
py,
function,
<Self as IntoPy<Py<PyTuple>>>::into_py(self, py).into_bound(py),
kwargs,
)
}
#[doc(hidden)]
#[inline]
fn __py_call_method_vectorcall1<'py>(
self,
_py: Python<'py>,
object: Borrowed<'_, 'py, PyAny>,
method_name: Borrowed<'_, 'py, PyString>,
_: private::Token,
) -> PyResult<Bound<'py, PyAny>>
where
Self: IntoPy<Py<PyTuple>>,
{
object
.getattr(method_name.to_owned())
.and_then(|method| method.call1(self))
}
}
pub(crate) mod private {
pub struct Token;
}
pub trait FromPyObject<'py>: Sized {
#[cfg(feature = "gil-refs")]
fn extract(ob: &'py PyAny) -> PyResult<Self> {
Self::extract_bound(&ob.as_borrowed())
}
fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<Self>;
#[cfg(feature = "experimental-inspect")]
fn type_input() -> TypeInfo {
TypeInfo::Any
}
}
mod from_py_object_bound_sealed {
pub trait Sealed {}
impl<'py, T> Sealed for T where T: super::FromPyObject<'py> {}
#[cfg(not(feature = "gil-refs"))]
impl Sealed for &'_ str {}
#[cfg(not(feature = "gil-refs"))]
impl Sealed for std::borrow::Cow<'_, str> {}
#[cfg(not(feature = "gil-refs"))]
impl Sealed for &'_ [u8] {}
#[cfg(not(feature = "gil-refs"))]
impl Sealed for std::borrow::Cow<'_, [u8]> {}
}
pub trait FromPyObjectBound<'a, 'py>: Sized + from_py_object_bound_sealed::Sealed {
fn from_py_object_bound(ob: Borrowed<'a, 'py, PyAny>) -> PyResult<Self>;
#[cfg(feature = "experimental-inspect")]
fn type_input() -> TypeInfo {
TypeInfo::Any
}
}
impl<'py, T> FromPyObjectBound<'_, 'py> for T
where
T: FromPyObject<'py>,
{
fn from_py_object_bound(ob: Borrowed<'_, 'py, PyAny>) -> PyResult<Self> {
Self::extract_bound(&ob)
}
#[cfg(feature = "experimental-inspect")]
fn type_input() -> TypeInfo {
<T as FromPyObject>::type_input()
}
}
impl<T: ?Sized + ToPyObject> ToPyObject for &'_ T {
#[inline]
fn to_object(&self, py: Python<'_>) -> PyObject {
<T as ToPyObject>::to_object(*self, py)
}
}
impl IntoPy<PyObject> for &'_ PyAny {
#[inline]
fn into_py(self, py: Python<'_>) -> PyObject {
unsafe { PyObject::from_borrowed_ptr(py, self.as_ptr()) }
}
}
impl<T> IntoPy<PyObject> for &'_ T
where
T: AsRef<PyAny>,
{
#[inline]
fn into_py(self, py: Python<'_>) -> PyObject {
unsafe { PyObject::from_borrowed_ptr(py, self.as_ref().as_ptr()) }
}
}
#[allow(deprecated)]
#[cfg(feature = "gil-refs")]
impl<'py, T> FromPyObject<'py> for &'py crate::PyCell<T>
where
T: PyClass,
{
fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult<Self> {
obj.clone().into_gil_ref().downcast().map_err(Into::into)
}
}
impl<T> FromPyObject<'_> for T
where
T: PyClass + Clone,
{
fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
let bound = obj.downcast::<Self>()?;
Ok(bound.try_borrow()?.clone())
}
}
impl<'py, T> FromPyObject<'py> for PyRef<'py, T>
where
T: PyClass,
{
fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult<Self> {
obj.downcast::<T>()?.try_borrow().map_err(Into::into)
}
}
impl<'py, T> FromPyObject<'py> for PyRefMut<'py, T>
where
T: PyClass<Frozen = False>,
{
fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult<Self> {
obj.downcast::<T>()?.try_borrow_mut().map_err(Into::into)
}
}
#[cfg(feature = "gil-refs")]
#[deprecated(since = "0.21.0")]
pub trait PyTryFrom<'v>: Sized + PyNativeType {
#[deprecated(
since = "0.21.0",
note = "use `value.downcast::<T>()` instead of `T::try_from(value)`"
)]
fn try_from<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError<'v>>;
#[deprecated(
since = "0.21.0",
note = "use `value.downcast_exact::<T>()` instead of `T::try_from_exact(value)`"
)]
fn try_from_exact<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError<'v>>;
#[deprecated(
since = "0.21.0",
note = "use `value.downcast_unchecked::<T>()` instead of `T::try_from_unchecked(value)`"
)]
unsafe fn try_from_unchecked<V: Into<&'v PyAny>>(value: V) -> &'v Self;
}
#[cfg(feature = "gil-refs")]
#[deprecated(since = "0.21.0")]
pub trait PyTryInto<T>: Sized {
#[deprecated(
since = "0.21.0",
note = "use `value.downcast()` instead of `value.try_into()`"
)]
fn try_into(&self) -> Result<&T, PyDowncastError<'_>>;
#[deprecated(
since = "0.21.0",
note = "use `value.downcast()` instead of `value.try_into_exact()`"
)]
fn try_into_exact(&self) -> Result<&T, PyDowncastError<'_>>;
}
#[cfg(feature = "gil-refs")]
#[allow(deprecated)]
mod implementations {
use super::*;
use crate::type_object::PyTypeInfo;
impl<U> PyTryInto<U> for PyAny
where
U: for<'v> PyTryFrom<'v>,
{
fn try_into(&self) -> Result<&U, PyDowncastError<'_>> {
<U as PyTryFrom<'_>>::try_from(self)
}
fn try_into_exact(&self) -> Result<&U, PyDowncastError<'_>> {
U::try_from_exact(self)
}
}
impl<'v, T> PyTryFrom<'v> for T
where
T: PyTypeInfo<AsRefTarget = Self> + PyNativeType,
{
fn try_from<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError<'v>> {
value.into().downcast()
}
fn try_from_exact<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError<'v>> {
value.into().downcast_exact()
}
#[inline]
unsafe fn try_from_unchecked<V: Into<&'v PyAny>>(value: V) -> &'v Self {
value.into().downcast_unchecked()
}
}
impl<'v, T> PyTryFrom<'v> for crate::PyCell<T>
where
T: 'v + PyClass,
{
fn try_from<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError<'v>> {
value.into().downcast()
}
fn try_from_exact<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError<'v>> {
let value = value.into();
unsafe {
if T::is_exact_type_of(value) {
Ok(Self::try_from_unchecked(value))
} else {
Err(PyDowncastError::new(value, T::NAME))
}
}
}
#[inline]
unsafe fn try_from_unchecked<V: Into<&'v PyAny>>(value: V) -> &'v Self {
value.into().downcast_unchecked()
}
}
}
impl IntoPy<Py<PyTuple>> for () {
fn into_py(self, py: Python<'_>) -> Py<PyTuple> {
PyTuple::empty_bound(py).unbind()
}
#[inline]
fn __py_call_vectorcall1<'py>(
self,
py: Python<'py>,
function: Borrowed<'_, 'py, PyAny>,
_: private::Token,
) -> PyResult<Bound<'py, PyAny>> {
unsafe { ffi::compat::PyObject_CallNoArgs(function.as_ptr()).assume_owned_or_err(py) }
}
#[inline]
fn __py_call_vectorcall<'py>(
self,
py: Python<'py>,
function: Borrowed<'_, 'py, PyAny>,
kwargs: Option<Borrowed<'_, '_, PyDict>>,
_: private::Token,
) -> PyResult<Bound<'py, PyAny>> {
unsafe {
match kwargs {
Some(kwargs) => ffi::PyObject_Call(
function.as_ptr(),
PyTuple::empty_bound(py).as_ptr(),
kwargs.as_ptr(),
)
.assume_owned_or_err(py),
None => ffi::compat::PyObject_CallNoArgs(function.as_ptr()).assume_owned_or_err(py),
}
}
}
#[inline]
#[allow(clippy::used_underscore_binding)]
fn __py_call_method_vectorcall1<'py>(
self,
py: Python<'py>,
object: Borrowed<'_, 'py, PyAny>,
method_name: Borrowed<'_, 'py, PyString>,
_: private::Token,
) -> PyResult<Bound<'py, PyAny>> {
unsafe {
ffi::compat::PyObject_CallMethodNoArgs(object.as_ptr(), method_name.as_ptr())
.assume_owned_or_err(py)
}
}
}
#[cfg(feature = "gil-refs")]
#[deprecated(since = "0.21.0")]
pub unsafe trait FromPyPointer<'p>: Sized {
#[deprecated(
since = "0.21.0",
note = "use `Py::from_owned_ptr_or_opt(py, ptr)` or `Bound::from_owned_ptr_or_opt(py, ptr)` instead"
)]
unsafe fn from_owned_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<&'p Self>;
#[deprecated(
since = "0.21.0",
note = "use `Py::from_owned_ptr(py, ptr)` or `Bound::from_owned_ptr(py, ptr)` instead"
)]
unsafe fn from_owned_ptr_or_panic(py: Python<'p>, ptr: *mut ffi::PyObject) -> &'p Self {
#[allow(deprecated)]
Self::from_owned_ptr_or_opt(py, ptr).unwrap_or_else(|| err::panic_after_error(py))
}
#[deprecated(
since = "0.21.0",
note = "use `Py::from_owned_ptr(py, ptr)` or `Bound::from_owned_ptr(py, ptr)` instead"
)]
unsafe fn from_owned_ptr(py: Python<'p>, ptr: *mut ffi::PyObject) -> &'p Self {
#[allow(deprecated)]
Self::from_owned_ptr_or_panic(py, ptr)
}
#[deprecated(
since = "0.21.0",
note = "use `Py::from_owned_ptr_or_err(py, ptr)` or `Bound::from_owned_ptr_or_err(py, ptr)` instead"
)]
unsafe fn from_owned_ptr_or_err(py: Python<'p>, ptr: *mut ffi::PyObject) -> PyResult<&'p Self> {
#[allow(deprecated)]
Self::from_owned_ptr_or_opt(py, ptr).ok_or_else(|| err::PyErr::fetch(py))
}
#[deprecated(
since = "0.21.0",
note = "use `Py::from_borrowed_ptr_or_opt(py, ptr)` or `Bound::from_borrowed_ptr_or_opt(py, ptr)` instead"
)]
unsafe fn from_borrowed_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject)
-> Option<&'p Self>;
#[deprecated(
since = "0.21.0",
note = "use `Py::from_borrowed_ptr(py, ptr)` or `Bound::from_borrowed_ptr(py, ptr)` instead"
)]
unsafe fn from_borrowed_ptr_or_panic(py: Python<'p>, ptr: *mut ffi::PyObject) -> &'p Self {
#[allow(deprecated)]
Self::from_borrowed_ptr_or_opt(py, ptr).unwrap_or_else(|| err::panic_after_error(py))
}
#[deprecated(
since = "0.21.0",
note = "use `Py::from_borrowed_ptr(py, ptr)` or `Bound::from_borrowed_ptr(py, ptr)` instead"
)]
unsafe fn from_borrowed_ptr(py: Python<'p>, ptr: *mut ffi::PyObject) -> &'p Self {
#[allow(deprecated)]
Self::from_borrowed_ptr_or_panic(py, ptr)
}
#[deprecated(
since = "0.21.0",
note = "use `Py::from_borrowed_ptr_or_err(py, ptr)` or `Bound::from_borrowed_ptr_or_err(py, ptr)` instead"
)]
unsafe fn from_borrowed_ptr_or_err(
py: Python<'p>,
ptr: *mut ffi::PyObject,
) -> PyResult<&'p Self> {
#[allow(deprecated)]
Self::from_borrowed_ptr_or_opt(py, ptr).ok_or_else(|| err::PyErr::fetch(py))
}
}
#[cfg(feature = "gil-refs")]
#[allow(deprecated)]
unsafe impl<'p, T> FromPyPointer<'p> for T
where
T: 'p + crate::PyNativeType,
{
unsafe fn from_owned_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<&'p Self> {
gil::register_owned(py, NonNull::new(ptr)?);
Some(&*(ptr as *mut Self))
}
unsafe fn from_borrowed_ptr_or_opt(
_py: Python<'p>,
ptr: *mut ffi::PyObject,
) -> Option<&'p Self> {
NonNull::new(ptr as *mut Self).map(|p| &*p.as_ptr())
}
}
mod test_no_clone {}
#[cfg(test)]
mod tests {
#[cfg(feature = "gil-refs")]
#[allow(deprecated)]
mod deprecated {
use super::super::PyTryFrom;
use crate::types::{IntoPyDict, PyAny, PyDict, PyList};
use crate::{Python, ToPyObject};
#[test]
fn test_try_from() {
Python::with_gil(|py| {
let list: &PyAny = vec![3, 6, 5, 4, 7].to_object(py).into_ref(py);
let dict: &PyAny = vec![("reverse", true)].into_py_dict(py).as_ref();
assert!(<PyList as PyTryFrom<'_>>::try_from(list).is_ok());
assert!(<PyDict as PyTryFrom<'_>>::try_from(dict).is_ok());
assert!(<PyAny as PyTryFrom<'_>>::try_from(list).is_ok());
assert!(<PyAny as PyTryFrom<'_>>::try_from(dict).is_ok());
});
}
#[test]
fn test_try_from_exact() {
Python::with_gil(|py| {
let list: &PyAny = vec![3, 6, 5, 4, 7].to_object(py).into_ref(py);
let dict: &PyAny = vec![("reverse", true)].into_py_dict(py).as_ref();
assert!(PyList::try_from_exact(list).is_ok());
assert!(PyDict::try_from_exact(dict).is_ok());
assert!(PyAny::try_from_exact(list).is_err());
assert!(PyAny::try_from_exact(dict).is_err());
});
}
#[test]
fn test_try_from_unchecked() {
Python::with_gil(|py| {
let list = PyList::new(py, [1, 2, 3]);
let val = unsafe { <PyList as PyTryFrom>::try_from_unchecked(list.as_ref()) };
assert!(list.is(val));
});
}
}
}