use crate::err::{self, PyDowncastError, PyResult};
use crate::type_object::PyTypeInfo;
use crate::types::PyTuple;
use crate::{
ffi, gil, Py, PyAny, PyCell, PyClass, PyNativeType, PyObject, PyRef, PyRefMut, Python,
};
use std::ptr::NonNull;
pub trait AsPyPointer {
fn as_ptr(&self) -> *mut ffi::PyObject;
}
pub trait IntoPyPointer {
fn into_ptr(self) -> *mut ffi::PyObject;
}
impl<T> AsPyPointer for Option<T>
where
T: AsPyPointer,
{
#[inline]
fn as_ptr(&self) -> *mut ffi::PyObject {
self.as_ref()
.map_or_else(std::ptr::null_mut, |t| t.into_ptr())
}
}
impl<T> IntoPyPointer for Option<T>
where
T: IntoPyPointer,
{
#[inline]
fn into_ptr(self) -> *mut ffi::PyObject {
self.map_or_else(std::ptr::null_mut, |t| t.into_ptr())
}
}
impl<'a, T> IntoPyPointer for &'a T
where
T: AsPyPointer,
{
fn into_ptr(self) -> *mut ffi::PyObject {
let ptr = self.as_ptr();
if !ptr.is_null() {
unsafe {
ffi::Py_INCREF(ptr);
}
}
ptr
}
}
pub trait ToPyObject {
fn to_object(&self, py: Python) -> PyObject;
}
pub trait ToBorrowedObject: ToPyObject {
fn with_borrowed_ptr<F, R>(&self, py: Python, f: F) -> R
where
F: FnOnce(*mut ffi::PyObject) -> R,
{
let ptr = self.to_object(py).into_ptr();
let result = f(ptr);
unsafe {
ffi::Py_XDECREF(ptr);
}
result
}
}
impl<T> ToBorrowedObject for T
where
T: ToPyObject,
{
#[cfg(feature = "nightly")]
default fn with_borrowed_ptr<F, R>(&self, py: Python, f: F) -> R
where
F: FnOnce(*mut ffi::PyObject) -> R,
{
let ptr = self.to_object(py).into_ptr();
let result = f(ptr);
unsafe {
ffi::Py_XDECREF(ptr);
}
result
}
}
#[cfg(feature = "nightly")]
impl<T> ToBorrowedObject for T
where
T: ToPyObject + AsPyPointer,
{
fn with_borrowed_ptr<F, R>(&self, _py: Python, f: F) -> R
where
F: FnOnce(*mut ffi::PyObject) -> R,
{
f(self.as_ptr())
}
}
pub trait IntoPy<T>: Sized {
fn into_py(self, py: Python) -> T;
}
pub trait FromPyObject<'source>: Sized {
fn extract(ob: &'source PyAny) -> PyResult<Self>;
}
impl<'a, T: ?Sized> ToPyObject for &'a T
where
T: ToPyObject,
{
#[inline]
fn to_object(&self, py: Python) -> PyObject {
<T as ToPyObject>::to_object(*self, py)
}
}
impl<T> ToPyObject for Option<T>
where
T: ToPyObject,
{
fn to_object(&self, py: Python) -> PyObject {
self.as_ref()
.map_or_else(|| py.None(), |val| val.to_object(py))
}
}
impl<T> IntoPy<PyObject> for Option<T>
where
T: IntoPy<PyObject>,
{
fn into_py(self, py: Python) -> PyObject {
self.map_or_else(|| py.None(), |val| val.into_py(py))
}
}
impl ToPyObject for () {
fn to_object(&self, py: Python) -> PyObject {
py.None()
}
}
impl IntoPy<PyObject> for () {
fn into_py(self, py: Python) -> PyObject {
py.None()
}
}
impl<T> IntoPy<PyObject> for &'_ T
where
T: AsPyPointer,
{
#[inline]
fn into_py(self, py: Python) -> PyObject {
unsafe { PyObject::from_borrowed_ptr(py, self.as_ptr()) }
}
}
impl<'a, T> FromPyObject<'a> for &'a PyCell<T>
where
T: PyClass,
{
fn extract(obj: &'a PyAny) -> PyResult<Self> {
PyTryFrom::try_from(obj).map_err(Into::into)
}
}
impl<'a, T> FromPyObject<'a> for T
where
T: PyClass + Clone,
{
fn extract(obj: &'a PyAny) -> PyResult<Self> {
let cell: &PyCell<Self> = PyTryFrom::try_from(obj)?;
Ok(unsafe { cell.try_borrow_unguarded()?.clone() })
}
}
impl<'a, T> FromPyObject<'a> for PyRef<'a, T>
where
T: PyClass,
{
fn extract(obj: &'a PyAny) -> PyResult<Self> {
let cell: &PyCell<T> = PyTryFrom::try_from(obj)?;
cell.try_borrow().map_err(Into::into)
}
}
impl<'a, T> FromPyObject<'a> for PyRefMut<'a, T>
where
T: PyClass,
{
fn extract(obj: &'a PyAny) -> PyResult<Self> {
let cell: &PyCell<T> = PyTryFrom::try_from(obj)?;
cell.try_borrow_mut().map_err(Into::into)
}
}
impl<'a, T> FromPyObject<'a> for Option<T>
where
T: FromPyObject<'a>,
{
fn extract(obj: &'a PyAny) -> PyResult<Self> {
if obj.as_ptr() == unsafe { ffi::Py_None() } {
Ok(None)
} else {
T::extract(obj).map(Some)
}
}
}
pub trait PyTryFrom<'v>: Sized + PyNativeType {
fn try_from<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError<'v>>;
fn try_from_exact<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError<'v>>;
unsafe fn try_from_unchecked<V: Into<&'v PyAny>>(value: V) -> &'v Self;
}
pub trait PyTryInto<T>: Sized {
fn try_into(&self) -> Result<&T, PyDowncastError>;
fn try_into_exact(&self) -> Result<&T, PyDowncastError>;
}
impl<U> PyTryInto<U> for PyAny
where
U: for<'v> PyTryFrom<'v>,
{
fn try_into(&self) -> Result<&U, PyDowncastError> {
U::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 + PyNativeType,
{
fn try_from<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError<'v>> {
let value = value.into();
unsafe {
if T::is_instance(value) {
Ok(Self::try_from_unchecked(value))
} else {
Err(PyDowncastError::new(value, T::NAME))
}
}
}
fn try_from_exact<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError<'v>> {
let value = value.into();
unsafe {
if T::is_exact_instance(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 {
Self::unchecked_downcast(value.into())
}
}
impl<'v, T> PyTryFrom<'v> for PyCell<T>
where
T: 'v + PyClass,
{
fn try_from<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError<'v>> {
let value = value.into();
unsafe {
if T::is_instance(value) {
Ok(Self::try_from_unchecked(value))
} else {
Err(PyDowncastError::new(value, T::NAME))
}
}
}
fn try_from_exact<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError<'v>> {
let value = value.into();
unsafe {
if T::is_exact_instance(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 {
Self::unchecked_downcast(value.into())
}
}
impl IntoPy<Py<PyTuple>> for () {
fn into_py(self, py: Python) -> Py<PyTuple> {
PyTuple::empty(py).into()
}
}
pub unsafe trait FromPyPointer<'p>: Sized {
unsafe fn from_owned_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<&'p Self>;
unsafe fn from_owned_ptr_or_panic(py: Python<'p>, ptr: *mut ffi::PyObject) -> &'p Self {
Self::from_owned_ptr_or_opt(py, ptr).unwrap_or_else(|| err::panic_after_error(py))
}
unsafe fn from_owned_ptr(py: Python<'p>, ptr: *mut ffi::PyObject) -> &'p Self {
Self::from_owned_ptr_or_panic(py, ptr)
}
unsafe fn from_owned_ptr_or_err(py: Python<'p>, ptr: *mut ffi::PyObject) -> PyResult<&'p Self> {
Self::from_owned_ptr_or_opt(py, ptr).ok_or_else(|| err::PyErr::fetch(py))
}
unsafe fn from_borrowed_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject)
-> Option<&'p Self>;
unsafe fn from_borrowed_ptr_or_panic(py: Python<'p>, ptr: *mut ffi::PyObject) -> &'p Self {
Self::from_borrowed_ptr_or_opt(py, ptr).unwrap_or_else(|| err::panic_after_error(py))
}
unsafe fn from_borrowed_ptr(py: Python<'p>, ptr: *mut ffi::PyObject) -> &'p Self {
Self::from_borrowed_ptr_or_panic(py, ptr)
}
unsafe fn from_borrowed_ptr_or_err(
py: Python<'p>,
ptr: *mut ffi::PyObject,
) -> PyResult<&'p Self> {
Self::from_borrowed_ptr_or_opt(py, ptr).ok_or_else(|| err::PyErr::fetch(py))
}
}
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())
}
}
#[cfg(test)]
mod test {
use crate::types::PyList;
use crate::Python;
use super::PyTryFrom;
#[test]
fn test_try_from_unchecked() {
let gil = Python::acquire_gil();
let py = gil.python();
let list = PyList::new(py, &[1, 2, 3]);
let val = unsafe { <PyList as PyTryFrom>::try_from_unchecked(list.as_ref()) };
assert_eq!(list, val);
}
}