use crate::err::{self, PyDowncastError, PyResult};
use crate::object::PyObject;
use crate::type_object::PyTypeInfo;
use crate::types::PyAny;
use crate::types::PyTuple;
use crate::{ffi, gil, Py, 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;
}
impl<T> ToBorrowedObject for T
where
T: ToPyObject,
{
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
}
}
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>,
{
default 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 T
where
T: PyTryFrom<'a>,
{
#[inline]
default fn extract(ob: &'a PyAny) -> PyResult<&'a T> {
Ok(T::try_from(ob)?)
}
}
impl<'a, T> FromPyObject<'a> for &'a mut T
where
T: PyTryFrom<'a>,
{
#[inline]
default fn extract(ob: &'a PyAny) -> PyResult<&'a mut T> {
Ok(T::try_from_mut(ob)?)
}
}
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 PyTryInto<T>: Sized {
fn try_into(&self) -> Result<&T, PyDowncastError>;
fn try_into_exact(&self) -> Result<&T, PyDowncastError>;
fn try_into_mut(&self) -> Result<&mut T, PyDowncastError>;
fn try_into_mut_exact(&self) -> Result<&mut T, PyDowncastError>;
}
pub trait PyTryFrom<'v>: Sized {
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>;
fn try_from_mut<V: Into<&'v PyAny>>(value: V) -> Result<&'v mut Self, PyDowncastError>;
fn try_from_mut_exact<V: Into<&'v PyAny>>(value: V) -> Result<&'v mut Self, PyDowncastError>;
unsafe fn try_from_unchecked<V: Into<&'v PyAny>>(value: V) -> &'v Self;
#[allow(clippy::mut_from_ref)]
unsafe fn try_from_mut_unchecked<V: Into<&'v PyAny>>(value: V) -> &'v mut Self;
}
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)
}
fn try_into_mut(&self) -> Result<&mut U, PyDowncastError> {
U::try_from_mut(self)
}
fn try_into_mut_exact(&self) -> Result<&mut U, PyDowncastError> {
U::try_from_mut_exact(self)
}
}
impl<'v, T> PyTryFrom<'v> for T
where
T: PyTypeInfo,
{
fn try_from<V: Into<&'v PyAny>>(value: V) -> Result<&'v T, PyDowncastError> {
let value = value.into();
unsafe {
if T::is_instance(value) {
Ok(PyTryFrom::try_from_unchecked(value))
} else {
Err(PyDowncastError)
}
}
}
fn try_from_exact<V: Into<&'v PyAny>>(value: V) -> Result<&'v T, PyDowncastError> {
let value = value.into();
unsafe {
if T::is_exact_instance(value) {
Ok(PyTryFrom::try_from_unchecked(value))
} else {
Err(PyDowncastError)
}
}
}
fn try_from_mut<V: Into<&'v PyAny>>(value: V) -> Result<&'v mut T, PyDowncastError> {
let value = value.into();
unsafe {
if T::is_instance(value) {
Ok(PyTryFrom::try_from_mut_unchecked(value))
} else {
Err(PyDowncastError)
}
}
}
fn try_from_mut_exact<V: Into<&'v PyAny>>(value: V) -> Result<&'v mut T, PyDowncastError> {
let value = value.into();
unsafe {
if T::is_exact_instance(value) {
Ok(PyTryFrom::try_from_mut_unchecked(value))
} else {
Err(PyDowncastError)
}
}
}
#[inline]
unsafe fn try_from_unchecked<V: Into<&'v PyAny>>(value: V) -> &'v T {
let value = value.into();
let ptr = if T::OFFSET == 0 {
value as *const _ as *const u8 as *const T
} else {
(value.as_ptr() as *const u8).offset(T::OFFSET) as *const T
};
&*ptr
}
#[inline]
unsafe fn try_from_mut_unchecked<V: Into<&'v PyAny>>(value: V) -> &'v mut T {
let value = value.into();
let ptr = if T::OFFSET == 0 {
value as *const _ as *mut u8 as *mut T
} else {
(value.as_ptr() as *mut u8).offset(T::OFFSET) as *mut T
};
&mut *ptr
}
}
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<Self>;
unsafe fn from_owned_ptr_or_panic(py: Python<'p>, ptr: *mut ffi::PyObject) -> Self {
match Self::from_owned_ptr_or_opt(py, ptr) {
Some(s) => s,
None => err::panic_after_error(),
}
}
unsafe fn from_owned_ptr(py: Python<'p>, ptr: *mut ffi::PyObject) -> Self {
Self::from_owned_ptr_or_panic(py, ptr)
}
unsafe fn from_owned_ptr_or_err(py: Python<'p>, ptr: *mut ffi::PyObject) -> PyResult<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<Self>;
unsafe fn from_borrowed_ptr_or_panic(py: Python<'p>, ptr: *mut ffi::PyObject) -> Self {
match Self::from_borrowed_ptr_or_opt(py, ptr) {
Some(s) => s,
None => err::panic_after_error(),
}
}
unsafe fn from_borrowed_ptr(py: Python<'p>, ptr: *mut ffi::PyObject) -> Self {
Self::from_borrowed_ptr_or_panic(py, ptr)
}
unsafe fn from_borrowed_ptr_or_err(py: Python<'p>, ptr: *mut ffi::PyObject) -> PyResult<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 &'p T
where
T: PyTypeInfo,
{
unsafe fn from_owned_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<Self> {
NonNull::new(ptr).map(|p| py.unchecked_downcast(gil::register_owned(py, p)))
}
unsafe fn from_borrowed_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<Self> {
NonNull::new(ptr).map(|p| py.unchecked_downcast(gil::register_borrowed(py, p)))
}
}
unsafe impl<'p, T> FromPyPointer<'p> for &'p mut T
where
T: PyTypeInfo,
{
unsafe fn from_owned_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<Self> {
NonNull::new(ptr).map(|p| py.unchecked_mut_downcast(gil::register_owned(py, p)))
}
unsafe fn from_borrowed_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<Self> {
NonNull::new(ptr).map(|p| py.unchecked_mut_downcast(gil::register_borrowed(py, p)))
}
}
#[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);
}
}