use crate::err::{self, PyDowncastError, PyResult};
use crate::object::PyObject;
use crate::type_object::PyTypeInfo;
use crate::types::PyTuple;
use crate::{ffi, gil, Py, PyAny, PyCell, PyClass, PyNativeType, 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 {
match *self {
Some(ref t) => t.as_ptr(),
None => std::ptr::null_mut(),
}
}
}
impl<T> IntoPyPointer for Option<T>
where
T: IntoPyPointer,
{
#[inline]
fn into_ptr(self) -> *mut ffi::PyObject {
match self {
Some(t) => t.into_ptr(),
None => std::ptr::null_mut(),
}
}
}
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 FromPy<T>: Sized {
fn from_py(_: T, py: Python) -> Self;
}
pub trait IntoPy<T>: Sized {
fn into_py(self, py: Python) -> T;
}
impl<T, U> IntoPy<U> for T
where
U: FromPy<T>,
{
fn into_py(self, py: Python) -> U {
U::from_py(self, py)
}
}
impl<T> FromPy<T> for T {
fn from_py(t: T, _: Python) -> T {
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 {
match *self {
Some(ref val) => val.to_object(py),
None => py.None(),
}
}
}
impl<T> IntoPy<PyObject> for Option<T>
where
T: IntoPy<PyObject>,
{
fn into_py(self, py: Python) -> PyObject {
match self {
Some(val) => val.into_py(py),
None => py.None(),
}
}
}
impl ToPyObject for () {
fn to_object(&self, py: Python) -> PyObject {
py.None()
}
}
impl FromPy<()> for PyObject {
fn from_py(_: (), py: Python) -> Self {
py.None()
}
}
impl<'a, T> FromPy<&'a T> for PyObject
where
T: AsPyPointer,
{
#[inline]
fn from_py(other: &'a T, py: Python) -> PyObject {
unsafe { PyObject::from_borrowed_ptr(py, other.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 {
match T::extract(obj) {
Ok(v) => Ok(Some(v)),
Err(e) => Err(e),
}
}
}
}
pub trait PyTryFrom<'v>: Sized + PyNativeType {
fn try_from<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError>;
fn try_from_exact<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError>;
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> {
let value = value.into();
unsafe {
if T::is_instance(value) {
Ok(Self::try_from_unchecked(value))
} else {
Err(PyDowncastError)
}
}
}
fn try_from_exact<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError> {
let value = value.into();
unsafe {
if T::is_exact_instance(value) {
Ok(Self::try_from_unchecked(value))
} else {
Err(PyDowncastError)
}
}
}
#[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> {
let value = value.into();
unsafe {
if T::is_instance(value) {
Ok(Self::try_from_unchecked(value))
} else {
Err(PyDowncastError)
}
}
}
fn try_from_exact<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError> {
let value = value.into();
unsafe {
if T::is_exact_instance(value) {
Ok(Self::try_from_unchecked(value))
} else {
Err(PyDowncastError)
}
}
}
#[inline]
unsafe fn try_from_unchecked<V: Into<&'v PyAny>>(value: V) -> &'v Self {
Self::unchecked_downcast(value.into())
}
}
impl FromPy<()> for Py<PyTuple> {
fn from_py(_: (), py: Python) -> Py<PyTuple> {
Py::from_py(PyTuple::empty(py), py)
}
}
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 {
match Self::from_owned_ptr_or_opt(py, ptr) {
Some(s) => s,
None => 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> {
match Self::from_owned_ptr_or_opt(py, ptr) {
Some(s) => Ok(s),
None => Err(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 {
match Self::from_borrowed_ptr_or_opt(py, ptr) {
Some(s) => s,
None => 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> {
match Self::from_borrowed_ptr_or_opt(py, ptr) {
Some(s) => Ok(s),
None => Err(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);
}
}