diff --git a/README.md b/README.md index 9da474bd..356ac9bd 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,8 @@ Supported Python versions: * Python 3.5 and up Supported Rust version: -* Rust 1.15.1 or later -* On Windows, we require rustc 1.15.0-nightly +* Rust 1.17.0-nightly or later +* On Windows, we require rustc 1.17.0-nightly # Usage @@ -64,7 +64,7 @@ On Windows, you will need to rename the output from \*.dll to \*.pyd. name = "rust2py" crate-type = ["cdylib"] -[dependencies.cpython] +[dependencies.pyo3] version = "0.1" features = ["extension-module"] ``` @@ -101,4 +101,4 @@ fn sum_as_string_py(_: Python, a:i64, b:i64) -> PyResult { For `setup.py` integration, see https://github.com/PyO3/setuptools-rust -** This is fork of rust-cpython project https://github.com/dgrunwald/rust-cpython ** +**This is fork of rust-cpython project https://github.com/dgrunwald/rust-cpython** diff --git a/pyo3cls/src/py_class.rs b/pyo3cls/src/py_class.rs index 773b93cd..e1fbb05a 100644 --- a/pyo3cls/src/py_class.rs +++ b/pyo3cls/src/py_class.rs @@ -34,7 +34,6 @@ pub fn build_py_class(ast: &mut syn::DeriveInput) -> Tokens { impl_to_py_object(&ast.ident).to_tokens(&mut tokens); impl_from_py_object(&ast.ident).to_tokens(&mut tokens); impl_python_object(&ast.ident).to_tokens(&mut tokens); - impl_checked_downcast(&ast.ident).to_tokens(&mut tokens); let dummy_const = syn::Ident::new(format!("_IMPL_PYO3_CLS_{}", ast.ident)); quote! { @@ -106,42 +105,14 @@ fn impl_storage(cls: &syn::Ident, base: &syn::Ident, fields: &Vec) - #accessors - impl _pyo3::PythonObjectWithTypeObject for #cls { + impl _pyo3::class::typeob::PyTypeObjectInfo for #cls { #[inline] fn type_name() -> &'static str { #cls_name } #[inline] - fn type_object(py: _pyo3::Python) -> _pyo3::PyType { - unsafe { <#cls as _pyo3::class::PyTypeObject>::initialized(py, None) } - } - } - - impl _pyo3::class::PyTypeObject for #cls { - - fn add_to_module(py: _pyo3::Python, module: &_pyo3::PyModule) -> _pyo3::PyResult<()> { - let ty = unsafe { #cls::initialized(py, module.name(py).ok()) }; - module.add(py, stringify!(#cls), ty) - } - - #[inline] - unsafe fn type_obj() -> &'static mut _pyo3::ffi::PyTypeObject { + fn type_object() -> &'static mut _pyo3::ffi::PyTypeObject { static mut TYPE_OBJECT: _pyo3::ffi::PyTypeObject = _pyo3::ffi::PyTypeObject_INIT; - &mut TYPE_OBJECT - } - - unsafe fn initialized(py: _pyo3::Python, module_name: Option<&str>) -> _pyo3::PyType { - let mut ty = #cls::type_obj(); - - if (ty.tp_flags & _pyo3::ffi::Py_TPFLAGS_READY) != 0 { - _pyo3::PyType::from_type_ptr(py, ty) - } else { - // automatically initialize the class on-demand - _pyo3::class::typeob::initialize_type::<#cls>( - py, module_name, #cls_name, ty).expect( - concat!("An error occurred while initializing class ", - stringify!(#cls))); - _pyo3::PyType::from_type_ptr(py, ty) - } + unsafe { &mut TYPE_OBJECT } } } @@ -258,29 +229,3 @@ fn impl_python_object(cls: &syn::Ident) -> Tokens { } } } - -fn impl_checked_downcast(cls: &syn::Ident) -> Tokens { - quote! { - impl _pyo3::PythonObjectWithCheckedDowncast for #cls { - #[inline] - fn downcast_from<'p>(py: _pyo3::Python<'p>, obj: _pyo3::PyObject) - -> Result<#cls, _pyo3::PythonObjectDowncastError<'p>> { - if py.get_type::<#cls>().is_instance(py, &obj) { - Ok(#cls { _unsafe_inner: obj }) - } else { - Err(_pyo3::PythonObjectDowncastError(py)) - } - } - - #[inline] - fn downcast_borrow_from<'a, 'p>(py: _pyo3::Python<'p>, obj: &'a _pyo3::PyObject) - -> Result<&'a #cls, _pyo3::PythonObjectDowncastError<'p>> { - if py.get_type::<#cls>().is_instance(py, obj) { - unsafe { Ok(std::mem::transmute(obj)) } - } else { - Err(_pyo3::PythonObjectDowncastError(py)) - } - } - } - } -} diff --git a/pyo3cls/src/py_method.rs b/pyo3cls/src/py_method.rs index 1be85f55..f01e5870 100644 --- a/pyo3cls/src/py_method.rs +++ b/pyo3cls/src/py_method.rs @@ -473,9 +473,13 @@ fn impl_arg_param(arg: &Arg, spec: &Vec, body: &Tokens) -> Tokens { quote! { match match _iter.next().unwrap().as_ref() { Some(obj) => { - match <#opt_ty as _pyo3::FromPyObject>::extract(py, obj) { - Ok(obj) => Ok(Some(obj)), - Err(e) => Err(e), + if obj == &py.None() { + Ok(#default) + } else { + match <#opt_ty as _pyo3::FromPyObject>::extract(py, obj) { + Ok(obj) => Ok(Some(obj)), + Err(e) => Err(e) + } } }, None => Ok(#default) @@ -488,9 +492,13 @@ fn impl_arg_param(arg: &Arg, spec: &Vec, body: &Tokens) -> Tokens { quote! { match match _iter.next().unwrap().as_ref() { Some(obj) => { - match <#ty as _pyo3::FromPyObject>::extract(py, obj) { - Ok(obj) => Ok(obj), - Err(e) => Err(e), + if obj == &py.None() { + Ok(#default) + } else { + match <#ty as _pyo3::FromPyObject>::extract(py, obj) { + Ok(obj) => Ok(obj), + Err(e) => Err(e), + } } }, None => Ok(#default) diff --git a/src/class/typeob.rs b/src/class/typeob.rs index e303214f..46446e5e 100644 --- a/src/class/typeob.rs +++ b/src/class/typeob.rs @@ -5,19 +5,45 @@ use std::ffi::CString; use std::collections::HashMap; use ::{ffi, class, PyErr, Python, PyResult, PythonObject}; -use objects::{PyType, PyModule}; +use objects::PyType; use callback::AbortOnDrop; use class::{BaseObject, PyMethodDefType}; -pub trait PyTypeObject : BaseObject + PythonObject { +/// Trait implemented by Python object types that have a corresponding type object. +pub trait PyTypeObject { - fn add_to_module(py: Python, module: &PyModule) -> PyResult<()>; + /// Retrieves the type object for this Python object type. + fn type_object(py: Python) -> PyType; - unsafe fn type_obj() -> &'static mut ffi::PyTypeObject; +} - unsafe fn initialized(py: Python, module_name: Option<&str>) -> PyType; +/// Trait implemented by object that generated by py::class macro +pub trait PyTypeObjectInfo { + fn type_name() -> &'static str; + + fn type_object() -> &'static mut ffi::PyTypeObject; + +} + + +impl PyTypeObject for T where T: BaseObject + PythonObject + PyTypeObjectInfo { + + #[inline] + fn type_object(py: Python) -> PyType { + let mut ty = ::type_object(); + + if (ty.tp_flags & ffi::Py_TPFLAGS_READY) != 0 { + unsafe { PyType::from_type_ptr(py, ty) } + } else { + // automatically initialize the class on-demand + initialize_type::(py, None, ::type_name(), ty).expect( + format!("An error occurred while initializing class {}", + ::type_name()).as_ref()); + unsafe { PyType::from_type_ptr(py, ty) } + } + } } pub fn initialize_type(py: Python, module_name: Option<&str>, type_name: &str, diff --git a/src/conversion.rs b/src/conversion.rs index 4436ac7e..4ea3bad6 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -85,6 +85,7 @@ pub trait ToPyTuple { } py_impl_to_py_object_for_python_object!(PyObject); +py_impl_from_py_object_for_python_object!(PyObject); /// FromPyObject is implemented by various types that can be extracted from a Python object. /// @@ -111,10 +112,6 @@ pub trait FromPyObject<'source> : Sized { fn extract(py: Python, obj: &'source PyObject) -> PyResult; } - -py_impl_from_py_object_for_python_object!(PyObject); - - pub trait RefFromPyObject { fn with_extracted(py: Python, obj: &PyObject, f: F) -> PyResult where F: FnOnce(&Self) -> R; @@ -134,29 +131,32 @@ impl RefFromPyObject for T } } -/* -impl <'prepared, T> ExtractPyObject<'prepared> for T -where T: PythonObjectWithCheckedDowncast -{ - type Prepared = PyObject; - +/// Identity conversion: allows using existing `PyObject` instances where +/// `T: ToPyObject` is expected. +/*impl ToPyObject for T where T: PythonObject { #[inline] - fn prepare_extract(py: Python, obj: &PyObject) -> PyResult { - Ok(obj.clone_ref(py)) + fn to_py_object(&self, py: Python) -> PyObject { + PyClone::clone_ref(self, py).into_object() } #[inline] - fn extract(py: Python, obj: &'prepared Self::Prepared) -> PyResult { - Ok(try!(obj.clone_ref(py).cast_into(py))) + default fn into_py_object(self, _py: Python) -> PyObject { + self.into_object() } -} -*/ + + #[inline] + default fn with_borrowed_ptr(&self, _py: Python, f: F) -> R + where F: FnOnce(*mut ffi::PyObject) -> R + { + f(PythonObject::as_object(self).as_ptr()) + } +}*/ // ToPyObject for references impl <'a, T: ?Sized> ToPyObject for &'a T where T: ToPyObject { #[inline] - fn to_py_object(&self, py: Python) -> PyObject { + default fn to_py_object(&self, py: Python) -> PyObject { ::to_py_object(*self, py) } @@ -194,14 +194,9 @@ impl ToPyObject for Option where T: ToPyObject { /// `()` is converted to Python `None`. impl ToPyObject for () { - fn to_py_object(&self, py: Python) -> PyObject { py.None() } - - fn into_py_object(self, py: Python) -> PyObject { - py.None() - } } @@ -217,32 +212,3 @@ impl <'source, T> FromPyObject<'source> for Option where T: FromPyObject<'sou } } } - -/* -impl <'prepared, T> ExtractPyObject<'prepared> for Option -where T: ExtractPyObject<'prepared> -{ - type Prepared = Option; - - fn prepare_extract(py: Python, obj: &PyObject) -> PyResult { - if obj.as_ptr() == unsafe { ffi::Py_None() } { - Ok(None) - } else { - Ok(Some(try!(T::prepare_extract(py, obj)))) - } - } - - fn extract(py: Python, obj: &'prepared Self::Prepared) -> PyResult> { - match *obj { - Some(ref inner) => { - match T::extract(py, inner) { - Ok(v) => Ok(Some(v)), - Err(e) => Err(e) - } - }, - None => Ok(None) - } - } -} -*/ - diff --git a/src/err.rs b/src/err.rs index d99fb8cd..47f9f7a3 100644 --- a/src/err.rs +++ b/src/err.rs @@ -17,13 +17,13 @@ // DEALINGS IN THE SOFTWARE. use std; -use python::{PythonObject, ToPythonPointer, Python, PythonObjectDowncastError, - PythonObjectWithTypeObject, PyClone, PyDrop}; +use python::{PythonObject, ToPythonPointer, Python, PythonObjectDowncastError, PyClone, PyDrop}; use objects::{PyObject, PyType, exc}; use ffi; use libc; use std::ptr; use libc::c_char; +use class::PyTypeObject; use conversion::{ToPyObject, ToPyTuple}; use std::ffi::CString; @@ -46,7 +46,7 @@ use pyo3::{Python, PyDict}; py_exception!(mymodule, CustomError); fn main() { - let gil = Python::acquire_gil(); +let gil = Python::acquire_gil(); let py = gil.python(); let ctx = PyDict::new(py); @@ -72,31 +72,11 @@ macro_rules! py_exception { } } - impl $crate::PythonObjectWithCheckedDowncast for $name { + impl $crate::PyTypeObject for $name { #[inline] - fn downcast_from<'p>(py: $crate::Python<'p>, obj: $crate::PyObject) - -> Result<$name, $crate::PythonObjectDowncastError<'p>> - { - if <$name as $crate::PythonObjectWithTypeObject>::type_object(py).is_instance(py, &obj) { - Ok(unsafe { $crate::PythonObject::unchecked_downcast_from(obj) }) - } else { - Err($crate::PythonObjectDowncastError(py)) - } + fn type_name() -> &'static str { + "$name" } - - #[inline] - fn downcast_borrow_from<'a, 'p>(py: $crate::Python<'p>, obj: &'a $crate::PyObject) - -> Result<&'a $name, $crate::PythonObjectDowncastError<'p>> - { - if <$name as $crate::PythonObjectWithTypeObject>::type_object(py).is_instance(py, obj) { - Ok(unsafe { $crate::PythonObject::unchecked_downcast_borrow_from(obj) }) - } else { - Err($crate::PythonObjectDowncastError(py)) - } - } - } - - impl $crate::PythonObjectWithTypeObject for $name { #[inline] fn type_object(py: $crate::Python) -> $crate::PyType { unsafe { @@ -153,7 +133,7 @@ impl PyErr { /// Example: /// `return Err(PyErr::new::(py, "Error message"));` pub fn new(py: Python, value: V) -> PyErr - where T: PythonObjectWithTypeObject, V: ToPyObject + where T: PyTypeObject, V: ToPyObject { PyErr::new_helper(py, py.get_type::(), value.to_py_object(py).into_object()) } diff --git a/src/lib.rs b/src/lib.rs index 8d1dcc26..af05e19b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -93,7 +93,8 @@ pub mod ffi; pub use ffi::{Py_ssize_t, Py_hash_t}; pub use err::{PyErr, PyResult}; pub use objects::*; -pub use python::{Python, PythonObject, PythonObjectWithCheckedDowncast, PythonObjectDowncastError, PythonObjectWithTypeObject, PyClone, PyDrop}; +pub use python::{Python, PythonObject, + PythonObjectWithCheckedDowncast, PythonObjectDowncastError, PyClone, PyDrop}; pub use pythonrun::{GILGuard, GILProtected, prepare_freethreaded_python}; pub use conversion::{FromPyObject, RefFromPyObject, ToPyObject, ToPyTuple}; pub use class::{CompareOp}; diff --git a/src/objects/boolobject.rs b/src/objects/boolobject.rs index 226a2f6f..fcbfbe2e 100644 --- a/src/objects/boolobject.rs +++ b/src/objects/boolobject.rs @@ -25,7 +25,6 @@ impl PyBool { /// Converts a rust `bool` to a Python `bool`. impl ToPyObject for bool { - #[inline] fn to_py_object(&self, py: Python) -> PyObject { PyBool::get(py, *self).into_object() diff --git a/src/objects/exc.rs b/src/objects/exc.rs index c1aa7775..82a1ee5d 100644 --- a/src/objects/exc.rs +++ b/src/objects/exc.rs @@ -22,7 +22,7 @@ use std::os::raw::c_char; use std::{self, mem, ops}; use std::ffi::CStr; use ffi; -use python::{Python, PythonObject, PythonObjectWithCheckedDowncast, PythonObjectDowncastError, PythonObjectWithTypeObject}; +use python::{Python, PythonObject}; use err::{self, PyResult}; use super::object::PyObject; use super::tuple::PyTuple; @@ -34,37 +34,9 @@ macro_rules! exc_type( pyobject_newtype!($name); - impl PythonObjectWithCheckedDowncast for $name { + impl $crate::class::typeob::PyTypeObject for $name { #[inline] - fn downcast_from<'p>(py: Python<'p>, obj : PyObject) - -> Result<$name, PythonObjectDowncastError<'p>> - { - unsafe { - if ffi::PyObject_TypeCheck(obj.as_ptr(), ffi::$exc_name as *mut ffi::PyTypeObject) != 0 { - Ok(PythonObject::unchecked_downcast_from(obj)) - } else { - Err(PythonObjectDowncastError(py)) - } - } - } - - #[inline] - fn downcast_borrow_from<'a, 'p>(py: Python<'p>, obj: &'a PyObject) - -> Result<&'a $name, PythonObjectDowncastError<'p>> - { - unsafe { - if ffi::PyObject_TypeCheck(obj.as_ptr(), ffi::$exc_name as *mut ffi::PyTypeObject) != 0 { - Ok(PythonObject::unchecked_downcast_borrow_from(obj)) - } else { - Err(PythonObjectDowncastError(py)) - } - } - } - } - - impl PythonObjectWithTypeObject for $name { - #[inline] - fn type_object(py: Python) -> PyType { + fn type_object(py: Python) -> $crate::PyType { unsafe { PyType::from_type_ptr(py, ffi::$exc_name as *mut ffi::PyTypeObject) } } } diff --git a/src/objects/iterator.rs b/src/objects/iterator.rs index 949210fe..299b6200 100644 --- a/src/objects/iterator.rs +++ b/src/objects/iterator.rs @@ -36,7 +36,7 @@ impl <'p> PyIterator<'p> { if unsafe { ffi::PyIter_Check(obj.as_ptr()) != 0 } { Ok(PyIterator { py: py, iter: obj }) } else { - Err(PythonObjectDowncastError(py)) + Err(PythonObjectDowncastError(py, None)) } } diff --git a/src/objects/mod.rs b/src/objects/mod.rs index 44673d33..fd75a17d 100644 --- a/src/objects/mod.rs +++ b/src/objects/mod.rs @@ -72,7 +72,21 @@ macro_rules! pyobject_newtype( if ::ffi::$checkfunction(obj.as_ptr()) != 0 { Ok($name(obj)) } else { - Err(::python::PythonObjectDowncastError(py)) + Err(::python::PythonObjectDowncastError(py, None)) + } + } + } + + #[inline] + fn downcast_from_with_msg<'p>( + py: ::python::Python<'p>, + obj: ::objects::object::PyObject, msg: &'p str) + -> Result<$name, ::python::PythonObjectDowncastError<'p>> { + unsafe { + if ::ffi::$checkfunction(obj.as_ptr()) != 0 { + Ok($name(obj)) + } else { + Err(::python::PythonObjectDowncastError(py, Some(msg))) } } } @@ -83,7 +97,7 @@ macro_rules! pyobject_newtype( if ::ffi::$checkfunction(obj.as_ptr()) != 0 { Ok(::std::mem::transmute(obj)) } else { - Err(::python::PythonObjectDowncastError(py)) + Err(::python::PythonObjectDowncastError(py, None)) } } } @@ -92,10 +106,14 @@ macro_rules! pyobject_newtype( ($name: ident, $checkfunction: ident, $typeobject: ident) => ( pyobject_newtype!($name, $checkfunction); - impl ::python::PythonObjectWithTypeObject for $name { + impl $crate::class::typeob::PyTypeObjectInfo for $name { #[inline] - fn type_object(py: ::python::Python) -> ::objects::typeobject::PyType { - unsafe { ::objects::typeobject::PyType::from_type_ptr(py, &mut ::ffi::$typeobject) } + fn type_name() -> &'static str { + stringify!($name) + } + #[inline] + fn type_object() -> &'static mut $crate::ffi::PyTypeObject { + unsafe { &mut $crate::ffi::$typeobject } } } ); diff --git a/src/objects/module.rs b/src/objects/module.rs index bf5748d0..3ed916c9 100644 --- a/src/objects/module.rs +++ b/src/objects/module.rs @@ -22,8 +22,7 @@ use std::os::raw::c_char; use python::{Python, PythonObject}; use objectprotocol::ObjectProtocol; use conversion::{ToPyObject, ToPyTuple}; -use objects::{PyObject, PyDict, exc}; -use class::PyTypeObject; +use objects::{PyObject, PyDict, PyType, exc}; use err::{self, PyResult, PyErr}; use std::ffi::{CStr, CString}; @@ -111,10 +110,23 @@ impl PyModule { /// sets `new_type.__module__` to this module's name, /// and adds the type to this module. pub fn add_class<'p, T>(&self, py: Python<'p>) -> PyResult<()> - where T: PyTypeObject + where T: ::class::BaseObject + PythonObject + ::class::typeob::PyTypeObjectInfo { - T::add_to_module(py, self) + let mut ty = ::type_object(); + let type_name = ::type_name(); + + let ty = if (ty.tp_flags & ffi::Py_TPFLAGS_READY) != 0 { + unsafe { PyType::from_type_ptr(py, ty) } + } else { + // automatically initialize the class on-demand + let name = self.name(py)?; + ::class::typeob::initialize_type::( + py, Some(name), type_name, ty).expect( + format!("An error occurred while initializing class {}", + ::type_name()).as_ref()); + unsafe { PyType::from_type_ptr(py, ty) } + }; + + self.add(py, type_name, ty) } } - - diff --git a/src/objects/object.rs b/src/objects/object.rs index 0ecc9f23..490597a1 100644 --- a/src/objects/object.rs +++ b/src/objects/object.rs @@ -18,7 +18,7 @@ use std::{mem, ptr}; use ffi; -use python::{Python, PythonObject, PythonObjectWithCheckedDowncast, PythonObjectWithTypeObject, PythonObjectDowncastError}; +use python::{Python, PythonObject, PythonObjectWithCheckedDowncast, PythonObjectDowncastError}; use objects::PyType; use err::PyResult; @@ -122,7 +122,7 @@ impl PythonObjectWithCheckedDowncast for PyObject { } } -impl PythonObjectWithTypeObject for PyObject { +impl ::class::PyTypeObject for PyObject { #[inline] fn type_object(py: Python) -> PyType { unsafe { PyType::from_type_ptr(py, &mut ffi::PyBaseObject_Type) } diff --git a/src/objects/slice.rs b/src/objects/slice.rs index 3e651636..15c8fef1 100644 --- a/src/objects/slice.rs +++ b/src/objects/slice.rs @@ -74,7 +74,6 @@ impl PySlice { } impl ToPyObject for PySliceIndices { - fn to_py_object(&self, py: Python) -> PyObject { PySlice::new(py, self.start, self.stop, self.step).into_object() } diff --git a/src/objects/tuple.rs b/src/objects/tuple.rs index d6586a03..f7839414 100644 --- a/src/objects/tuple.rs +++ b/src/objects/tuple.rs @@ -117,11 +117,11 @@ macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+ ]).into_object() } - fn into_py_object(self, py: Python) -> PyObject { - PyTuple::new(py, &[ - $(py_coerce_expr!(self.$n.into_py_object(py)).into_object(),)+ - ]).into_object() - } + //fn into_py_object(self, py: Python) -> PyObject { + // PyTuple::new(py, &[ + // $(py_coerce_expr!(self.$n.into_py_object(py)).into_object(),)+ + // ]).into_object() + //} } impl <$($T: ToPyObject),+> ToPyTuple for ($($T,)+) { @@ -193,7 +193,6 @@ impl ToPyTuple for NoArgs { } } - /// Converts `()` to an empty Python tuple. impl ToPyTuple for () { diff --git a/src/python.rs b/src/python.rs index 8bc5e7fa..98111ef3 100644 --- a/src/python.rs +++ b/src/python.rs @@ -21,6 +21,7 @@ use std::ffi::CString; use std::marker::PhantomData; use std::os::raw::c_int; use ffi; +use class::PyTypeObject; use objects::{PyObject, PyType, PyBool, PyDict, PyModule}; use err::{self, PyErr, PyResult}; use pythonrun::GILGuard; @@ -39,7 +40,7 @@ use pythonrun::GILGuard; pub struct Python<'p>(PhantomData<&'p GILGuard>); /// Trait implemented by all Python object types. -pub trait PythonObject : ::conversion::ToPyObject + Send + Sized + 'static { +pub trait PythonObject : Send + Sized + 'static { /// Casts the Python object to PyObject. fn as_object(&self) -> &PyObject; @@ -56,26 +57,51 @@ pub trait PythonObject : ::conversion::ToPyObject + Send + Sized + 'static { } // Marker type that indicates an error while downcasting -pub struct PythonObjectDowncastError<'p>(pub Python<'p>); +pub struct PythonObjectDowncastError<'p>(pub Python<'p>, pub Option<&'p str>); /// Trait implemented by Python object types that allow a checked downcast. pub trait PythonObjectWithCheckedDowncast : PythonObject { /// Cast from PyObject to a concrete Python object type. fn downcast_from<'p>(Python<'p>, PyObject) -> Result>; + /// Cast from PyObject to a concrete Python object type. + fn downcast_from_with_msg<'p>( + Python<'p>, PyObject, msg: &'p str) -> Result>; + /// Cast from PyObject to a concrete Python object type. fn downcast_borrow_from<'a, 'p>(Python<'p>, &'a PyObject) -> Result<&'a Self, PythonObjectDowncastError<'p>>; } -/// Trait implemented by Python object types that have a corresponding type object. -pub trait PythonObjectWithTypeObject : PythonObjectWithCheckedDowncast { +impl PythonObjectWithCheckedDowncast for T where T: PyTypeObject + PythonObject { + #[inline] + default fn downcast_from<'p>(py: Python<'p>, obj: PyObject) + -> Result> { + if T::type_object(py).is_instance(py, &obj) { + Ok( unsafe { T::unchecked_downcast_from(obj) }) + } else { + Err(PythonObjectDowncastError(py, None)) + } + } - /// Type name - fn type_name() -> &'static str { "Unknown" } - - /// Retrieves the type object for this Python object type. - fn type_object(Python) -> PyType; + #[inline] + default fn downcast_from_with_msg<'p>(py: Python<'p>, obj: PyObject, msg: &'p str) + -> Result> { + if T::type_object(py).is_instance(py, &obj) { + Ok( unsafe { T::unchecked_downcast_from(obj) }) + } else { + Err(PythonObjectDowncastError(py, Some(msg))) + } + } + #[inline] + default fn downcast_borrow_from<'a, 'p>(py: Python<'p>, obj: &'a PyObject) + -> Result<&'a T, PythonObjectDowncastError<'p>> { + if T::type_object(py).is_instance(py, obj) { + unsafe { Ok( T::unchecked_downcast_borrow_from(obj)) } + } else { + Err(PythonObjectDowncastError(py, None)) + } + } } pub trait PyClone : Sized { @@ -298,7 +324,7 @@ impl<'p> Python<'p> { } /// Gets the Python type object for type T. - pub fn get_type(self) -> PyType where T: PythonObjectWithTypeObject { + pub fn get_type(self) -> PyType where T: PyTypeObject { T::type_object(self) }