diff --git a/pyo3cls/src/defs.rs b/pyo3cls/src/defs.rs index b2478b02..f542372d 100644 --- a/pyo3cls/src/defs.rs +++ b/pyo3cls/src/defs.rs @@ -17,10 +17,11 @@ pub const OBJECT: Proto = Proto { MethodProto::Binary { name: "__getattr__", arg: "Name", - pyres: true, + //pyres: true, + pyres: false, proto: "_pyo3::class::basic::PyObjectGetAttrProtocol"}, MethodProto::Ternary { - name: "__getattr__", + name: "__setattr__", arg1: "Name", arg2: "Value", pyres: true, diff --git a/pyo3cls/src/func.rs b/pyo3cls/src/func.rs index 053d89e8..a873b496 100644 --- a/pyo3cls/src/func.rs +++ b/pyo3cls/src/func.rs @@ -4,6 +4,7 @@ use quote::Tokens; // TODO: // Add lifetime support for args with Rptr +#[derive(Debug)] pub enum MethodProto { Unary{name: &'static str, pyres: bool, proto: &'static str, }, Binary{name: &'static str, arg: &'static str, pyres: bool, proto: &'static str}, @@ -58,12 +59,13 @@ pub fn impl_method_proto(cls: &Box, MethodProto::Binary{name: _, arg, pyres, proto} => { let p = syn::Ident::from(proto); let arg_name = syn::Ident::from(arg); - let arg_ty = get_arg_ty(sig, 2); + //let arg_ty = get_arg_ty(sig, 2); + let arg_ty = get_arg_ty(sig, 1); let succ = get_res_success(ty); if pyres { quote! { - impl #p for #cls { + impl<'a'> #p<'a> for #cls { type #arg_name = #arg_ty; type Success = #succ; type Result = #ty; @@ -71,7 +73,7 @@ pub fn impl_method_proto(cls: &Box, } } else { quote! { - impl #p for #cls { + impl<'a> #p<'a> for #cls { type #arg_name = #arg_ty; type Result = #ty; } diff --git a/pyo3cls/src/method.rs b/pyo3cls/src/method.rs index a54cdde4..9a77e38b 100644 --- a/pyo3cls/src/method.rs +++ b/pyo3cls/src/method.rs @@ -43,7 +43,8 @@ impl<'a> FnSpec<'a> { let (fn_type, fn_attrs) = parse_attributes(meth_attrs); //let mut has_self = false; - let mut py = false; + //let mut py = false; + let mut py = true; let mut arguments = Vec::new(); for input in sig.decl.inputs[1..].iter() { @@ -74,7 +75,6 @@ impl<'a> FnSpec<'a> { } } - FnSpec { tp: fn_type, attrs: fn_attrs, diff --git a/pyo3cls/src/py_class.rs b/pyo3cls/src/py_class.rs index e1fbb05a..0abe1169 100644 --- a/pyo3cls/src/py_class.rs +++ b/pyo3cls/src/py_class.rs @@ -10,22 +10,8 @@ pub fn build_py_class(ast: &mut syn::DeriveInput) -> Tokens { let mut tokens = Tokens::new(); match ast.body { - syn::Body::Struct(syn::VariantData::Struct(ref mut data)) => { - impl_storage(&ast.ident, &base, data).to_tokens(&mut tokens); - - let tt = quote! { - struct Test { - _unsafe_inner: PyObject - } - }; - let t = syn::parse_item(tt.as_str()).unwrap(); - match t.node { - syn::ItemKind::Struct(syn::VariantData::Struct(fields), _) => { - data.clear(); - data.extend(fields); - } - _ => panic!("something is worng"), - } + syn::Body::Struct(syn::VariantData::Struct(_)) => { + impl_storage(&ast.ident, &base).to_tokens(&mut tokens); }, _ => panic!("#[class] can only be used with notmal structs"), @@ -49,63 +35,25 @@ pub fn build_py_class(ast: &mut syn::DeriveInput) -> Tokens { } } -fn impl_storage(cls: &syn::Ident, base: &syn::Ident, fields: &Vec) -> Tokens { - let names: Vec = fields.iter() - .map(|f| f.ident.as_ref().unwrap().clone()).collect(); - let values: Vec = fields.iter() - .map(|f| f.ident.as_ref().unwrap().clone()).collect(); - //let types: Vec = fields.iter().map(|f| f.ty.clone()).collect(); - - let storage = syn::Ident::from(format!("{}_Storage", cls).as_str()); - - let mut accessors = Tokens::new(); - for field in fields.iter() { - let name = &field.ident.as_ref().unwrap(); - let name_mut = syn::Ident::from(format!("{}_mut", name.as_ref())); - let ty = &field.ty; - - let accessor = quote!{ - impl #cls { - fn #name<'a>(&'a self, py: _pyo3::Python<'a>) -> &'a #ty { - unsafe { - let ptr = (self._unsafe_inner.as_ptr() as *const u8) - .offset(base_offset() as isize) as *const #storage; - &(*ptr).#name - } - } - fn #name_mut<'a>(&'a self, py: _pyo3::Python<'a>) -> &'a mut #ty { - unsafe { - let ptr = (self._unsafe_inner.as_ptr() as *const u8) - .offset(base_offset() as isize) as *mut #storage; - &mut (*ptr).#name - } - } - } - }; - accessor.to_tokens(&mut accessors); - } - +fn impl_storage(cls: &syn::Ident, base: &syn::Ident) -> Tokens { let cls_name = quote! { #cls }.as_str().to_string(); quote! { - pub struct #storage { - #(#fields),* - } - - impl #cls { - fn create_instance(py: _pyo3::Python, #(#fields),*) -> _pyo3::PyResult<#cls> { - let obj = try!(unsafe { - <#cls as _pyo3::class::BaseObject>::alloc( - py, &py.get_type::<#cls>(), - #storage { #(#names: #values),*})}); - - return Ok(#cls { _unsafe_inner: obj }); - } - } - - #accessors - impl _pyo3::class::typeob::PyTypeObjectInfo for #cls { + #[inline] + fn size() -> usize { + Self::offset() + std::mem::size_of::<#cls>() + } + + #[inline] + fn offset() -> usize { + let align = std::mem::align_of::<#cls>(); + let bs = <#base as _pyo3::class::BaseObject>::size(); + + // round base_size up to next multiple of align + (bs + align - 1) / align * align + } + #[inline] fn type_name() -> &'static str { #cls_name } @@ -117,8 +65,8 @@ fn impl_storage(cls: &syn::Ident, base: &syn::Ident, fields: &Vec) - } #[inline] - fn base_offset() -> usize { - let align = std::mem::align_of::<#storage>(); + fn offset() -> usize { + let align = std::mem::align_of::<#cls>(); let bs = <#base as _pyo3::class::BaseObject>::size(); // round base_size up to next multiple of align @@ -126,30 +74,46 @@ fn impl_storage(cls: &syn::Ident, base: &syn::Ident, fields: &Vec) - } impl _pyo3::class::BaseObject for #cls { - type Type = #storage; + type Type = #cls; #[inline] fn size() -> usize { - base_offset() + std::mem::size_of::() + offset() + std::mem::size_of::() } - unsafe fn alloc(py: _pyo3::Python, ty: &_pyo3::PyType, - value: Self::Type) -> _pyo3::PyResult<_pyo3::PyObject> + unsafe fn alloc(py: _pyo3::Python, + value: Self::Type) -> _pyo3::PyResult<*mut _pyo3::ffi::PyObject> { - let obj = try!(<#base as _pyo3::class::BaseObject>::alloc(py, ty, ())); + let ty = py.get_type::(); + let obj = ffi::PyType_GenericAlloc(ty.as_type_ptr(), 0); - let ptr = (obj.as_ptr() as *mut u8) - .offset(base_offset() as isize) as *mut Self::Type; + if obj.is_null() { + return Err(PyErr::fetch(py)) + } + + let ptr = (obj as *mut u8).offset(offset() as isize) as *mut Self::Type; std::ptr::write(ptr, value); Ok(obj) } unsafe fn dealloc(py: _pyo3::Python, obj: *mut _pyo3::ffi::PyObject) { - let ptr = (obj as *mut u8).offset(base_offset() as isize) as *mut Self::Type; + let ptr = (obj as *mut u8).offset(offset() as isize) as *mut Self::Type; std::ptr::drop_in_place(ptr); - <#base as _pyo3::class::BaseObject>::dealloc(py, obj) + // Unfortunately, there is no PyType_GenericFree, so + // we have to manually un-do the work of PyType_GenericAlloc: + let ty = _pyo3::ffi::Py_TYPE(obj); + if _pyo3::ffi::PyType_IS_GC(ty) != 0 { + _pyo3::ffi::PyObject_GC_Del(obj as *mut c_void); + } else { + _pyo3::ffi::PyObject_Free(obj as *mut c_void); + } + // For heap types, PyType_GenericAlloc calls INCREF on the type objects, + // so we need to call DECREF here: + if _pyo3::ffi::PyType_HasFeature(ty, _pyo3::ffi::Py_TPFLAGS_HEAPTYPE) != 0 { + _pyo3::ffi::Py_DECREF(ty as *mut _pyo3::ffi::PyObject); + } } } } @@ -182,19 +146,12 @@ fn impl_to_py_object(cls: &syn::Ident) -> Tokens { fn impl_from_py_object(cls: &syn::Ident) -> Tokens { quote! { - impl <'source> _pyo3::FromPyObject<'source> for #cls { + impl <'source> _pyo3::FromPyObj<'source> for &'source #cls { #[inline] - fn extract(py: _pyo3::Python, obj: &'source _pyo3::PyObject) - -> _pyo3::PyResult<#cls> { - Ok(obj.clone_ref(py).cast_into::<#cls>(py)?) - } - } - - impl <'source> _pyo3::FromPyObject<'source> for &'source #cls { - #[inline] - fn extract(py: _pyo3::Python, obj: &'source _pyo3::PyObject) - -> _pyo3::PyResult<&'source #cls> { - Ok(obj.cast_as::<#cls>(py)?) + fn extr(py: &'source _pyo3::Py<'source, S>) -> _pyo3::PyResult<&'source #cls> + where S: _pyo3::class::typeob::PyTypeObjectInfo + { + Ok(py.cast_as::<#cls>()?) } } } @@ -205,26 +162,54 @@ fn impl_python_object(cls: &syn::Ident) -> Tokens { impl _pyo3::PythonObject for #cls { #[inline] fn as_object(&self) -> &_pyo3::PyObject { - &self._unsafe_inner + unimplemented!(); + /*unsafe { + let py = Python::assume_gil_acquired(); + + let ty = cls::type_object(py); + let align = std::mem::align_of::(); + let bs = ::size(); + + // round base_size up to next multiple of align + let offset = (bs + align - 1) / align * align; + + let ptr = (self as *mut u8).offset(-1(offset as isize)) as *mut ffi::PyObject; + + Ok(PyObject::from_owned_ptr(py, ptr)) + }*/ } #[inline] fn into_object(self) -> _pyo3::PyObject { - self._unsafe_inner + unsafe { + let py = Python::assume_gil_acquired(); + + let ty = #cls::type_object(py); + let align = std::mem::align_of::<#cls>(); + let bs = <#cls as BaseObject>::size(); + + // round base_size up to next multiple of align + let offset = (bs + align - 1) / align * align; + + let ptr = (&self as *const _ as *mut u8).offset( + -(offset as isize)) as *mut ffi::PyObject; + + PyObject::from_borrowed_ptr(py, ptr) + } } /// Unchecked downcast from PyObject to Self. /// Undefined behavior if the input object does not have the expected type. #[inline] unsafe fn unchecked_downcast_from(obj: _pyo3::PyObject) -> Self { - #cls { _unsafe_inner: obj } + unimplemented!(); } /// Unchecked downcast from PyObject to Self. /// Undefined behavior if the input object does not have the expected type. #[inline] - unsafe fn unchecked_downcast_borrow_from<'a>(obj: &'a _pyo3::PyObject) -> &'a Self { - std::mem::transmute(obj) + unsafe fn unchecked_downcast_borrow_from<'b>(obj: &'b _pyo3::PyObject) -> &'b Self { + unimplemented!(); } } } diff --git a/pyo3cls/src/py_method.rs b/pyo3cls/src/py_method.rs index 37077a58..36b840a1 100644 --- a/pyo3cls/src/py_method.rs +++ b/pyo3cls/src/py_method.rs @@ -9,9 +9,11 @@ pub fn gen_py_method<'a>(cls: &Box, name: &syn::Ident, sig: &mut syn::MethodSig, meth_attrs: &mut Vec) -> Tokens { check_generic(name, sig); + println!("====0"); let spec = FnSpec::parse(name, sig, meth_attrs); + println!("====1"); match spec.tp { FnType::Fn => impl_py_method_def(name, &impl_wrap(cls, name, &spec)), diff --git a/pyo3cls/src/py_proto.rs b/pyo3cls/src/py_proto.rs index a8101ec1..abf437a1 100644 --- a/pyo3cls/src/py_proto.rs +++ b/pyo3cls/src/py_proto.rs @@ -71,6 +71,7 @@ fn impl_proto_impl(ty: &Box, impls: &mut Vec, proto: &de syn::ImplItemKind::Method(ref mut sig, _) => { for m in proto.methods { if m.eq(iimpl.ident.as_ref()) { + println!("test: {:?} {:?}", m, ty); impl_method_proto(ty, sig, m).to_tokens(&mut tokens); } } diff --git a/src/callback.rs b/src/callback.rs index 1fed6aa2..d70b35b5 100644 --- a/src/callback.rs +++ b/src/callback.rs @@ -196,7 +196,36 @@ impl CallbackConverter for HashConverter pub unsafe fn handle_callback(location: &str, _c: C, f: F) -> C::R - where F: FnOnce(Python) -> PyResult, + where F: for<'p> FnOnce(Python<'p>) -> PyResult, + F: panic::UnwindSafe, + C: CallbackConverter +{ + let guard = AbortOnDrop(location); + let ret = panic::catch_unwind(|| { + let py = Python::assume_gil_acquired(); + match f(py) { + Ok(val) => { + C::convert(val, py) + } + Err(e) => { + e.restore(py); + C::error_value() + } + } + }); + let ret = match ret { + Ok(r) => r, + Err(ref err) => { + handle_panic(Python::assume_gil_acquired(), err); + C::error_value() + } + }; + mem::forget(guard); + ret +} + +pub unsafe fn handle_callback2<'p, F, T, C>(location: &str, _c: C, f: F) -> C::R + where F: FnOnce(Python<'p>) -> PyResult, F: panic::UnwindSafe, C: CallbackConverter { diff --git a/src/class/basic.rs b/src/class/basic.rs index 807319bc..b888d9a8 100644 --- a/src/class/basic.rs +++ b/src/class/basic.rs @@ -16,6 +16,7 @@ use conversion::{ToPyObject, FromPyObject}; use callback::{handle_callback, PyObjectCallbackConverter, HashConverter, UnitCallbackConverter, BoolCallbackConverter}; use class::methods::PyMethodDef; +use ::{Py, PyPtr}; // classmethod // staticmethod @@ -25,79 +26,84 @@ use class::methods::PyMethodDef; /// Object customization #[allow(unused_variables)] -pub trait PyObjectProtocol: PythonObject { +pub trait PyObjectProtocol<'a>: PythonObject { - fn __getattr__(&self, py: Python, name: Self::Name) - -> Self::Result where Self: PyObjectGetAttrProtocol { unimplemented!() } + fn __getattr__(&'a self, name: Self::Name) + -> Self::Result where Self: PyObjectGetAttrProtocol<'a> + { unimplemented!() } fn __setattr__(&self, py: Python, name: Self::Name, value: Self::Value) - -> Self::Result where Self: PyObjectSetAttrProtocol { unimplemented!() } + -> Self::Result where Self: PyObjectSetAttrProtocol<'a> { unimplemented!() } fn __delattr__(&self, py: Python, name: Self::Name) - -> Self::Result where Self: PyObjectDelAttrProtocol { unimplemented!() } + -> Self::Result where Self: PyObjectDelAttrProtocol<'a> { unimplemented!() } fn __str__(&self, py: Python) - -> Self::Result where Self: PyObjectStrProtocol { unimplemented!() } + -> Self::Result where Self: PyObjectStrProtocol<'a> { unimplemented!() } fn __repr__(&self, py: Python) - -> Self::Result where Self: PyObjectReprProtocol { unimplemented!() } + -> Self::Result where Self: PyObjectReprProtocol<'a> { unimplemented!() } fn __format__(&self, py: Python, format_spec: Self::Format) - -> Self::Result where Self: PyObjectFormatProtocol { unimplemented!() } + -> Self::Result where Self: PyObjectFormatProtocol<'a> { unimplemented!() } fn __hash__(&self, py: Python) - -> Self::Result where Self: PyObjectHashProtocol { unimplemented!() } + -> Self::Result where Self: PyObjectHashProtocol<'a> { unimplemented!() } - fn __bool__(&self, py: Python) - -> Self::Result where Self: PyObjectBoolProtocol { unimplemented!() } + fn __bool__(&self) -> Self::Result where Self: PyObjectBoolProtocol<'a> { unimplemented!() } fn __bytes__(&self, py: Python) - -> Self::Result where Self: PyObjectBytesProtocol { unimplemented!() } + -> Self::Result where Self: PyObjectBytesProtocol<'a> { unimplemented!() } fn __richcmp__(&self, py: Python, other: Self::Other, op: CompareOp) - -> Self::Result where Self: PyObjectRichcmpProtocol { unimplemented!() } + -> Self::Result where Self: PyObjectRichcmpProtocol<'a> { unimplemented!() } } -pub trait PyObjectGetAttrProtocol: PyObjectProtocol { - type Name: for<'a> FromPyObject<'a>; - type Success: ToPyObject; - type Result: Into>; -} -pub trait PyObjectSetAttrProtocol: PyObjectProtocol { - type Name: for<'a> FromPyObject<'a>; - type Value: for<'a> FromPyObject<'a>; +pub trait PyObjectGetAttrProtocol<'a>: PyObjectProtocol<'a> { + type Name: ::FromPyObj<'a> + ::class::typeob::PyTypeObjectInfo; type Result: Into>; } -pub trait PyObjectDelAttrProtocol: PyObjectProtocol { - type Name: for<'a> FromPyObject<'a>; + +//pub trait PyObjectGetAttrProtocol: PyObjectProtocol + ::BaseObject + ::PyTypeObject { +// type Name: for<'a> ::FromPyObj<'a>; +// type Success: ToPyObject; +// type Result: Into>; +//} +pub trait PyObjectSetAttrProtocol<'a>: PyObjectProtocol<'a> { + type Name: FromPyObject<'a> + ::class::typeob::PyTypeObjectInfo + ::class::typeob::PyTypeObject + ::PythonObject; + type Value: FromPyObject<'a>; type Result: Into>; } -pub trait PyObjectStrProtocol: PyObjectProtocol { +pub trait PyObjectDelAttrProtocol<'a>: PyObjectProtocol<'a> { + type Name: FromPyObject<'a>; + type Result: Into>; +} +pub trait PyObjectStrProtocol<'a>: PyObjectProtocol<'a> { type Success: ToPyObject; type Result: Into>; } -pub trait PyObjectReprProtocol: PyObjectProtocol { +pub trait PyObjectReprProtocol<'a>: PyObjectProtocol<'a> { type Success: ToPyObject; type Result: Into>; } -pub trait PyObjectFormatProtocol: PyObjectProtocol { - type Format: for<'a> FromPyObject<'a>; +pub trait PyObjectFormatProtocol<'a>: PyObjectProtocol<'a> { + type Format: FromPyObject<'a>; type Success: ToPyObject; type Result: Into>; } -pub trait PyObjectHashProtocol: PyObjectProtocol { +pub trait PyObjectHashProtocol<'a>: PyObjectProtocol<'a> { type Result: Into>; } -pub trait PyObjectBoolProtocol: PyObjectProtocol { +pub trait PyObjectBoolProtocol<'a>: PyObjectProtocol<'a> { type Result: Into>; } -pub trait PyObjectBytesProtocol: PyObjectProtocol { +pub trait PyObjectBytesProtocol<'a>: PyObjectProtocol<'a> { type Success: ToPyObject; type Result: Into>; } -pub trait PyObjectRichcmpProtocol: PyObjectProtocol { - type Other: for<'a> FromPyObject<'a>; +pub trait PyObjectRichcmpProtocol<'a>: PyObjectProtocol<'a> { + type Other: FromPyObject<'a>; type Success: ToPyObject; type Result: Into>; } @@ -121,7 +127,7 @@ impl PyObjectProtocolImpl for T { } } -impl PyObjectProtocolImpl for T where T: PyObjectProtocol { +impl<'a, T> PyObjectProtocolImpl for T where T: PyObjectProtocol<'a> { #[inline] fn methods() -> Vec { let mut methods = Vec::new(); @@ -135,6 +141,8 @@ impl PyObjectProtocolImpl for T where T: PyObjectProtocol { methods } fn tp_as_object(type_object: &mut ffi::PyTypeObject) { + println!("getattr: {:?}", Self::tp_getattro()); + type_object.tp_str = Self::tp_str(); type_object.tp_repr = Self::tp_repr(); type_object.tp_hash = Self::tp_hash(); @@ -156,8 +164,7 @@ trait PyObjectGetAttrProtocolImpl { fn tp_getattro() -> Option; } -impl PyObjectGetAttrProtocolImpl for T - where T: PyObjectProtocol +impl<'a, T> PyObjectGetAttrProtocolImpl for T where T: PyObjectProtocol<'a> { #[inline] default fn tp_getattro() -> Option { @@ -165,11 +172,55 @@ impl PyObjectGetAttrProtocolImpl for T } } -impl PyObjectGetAttrProtocolImpl for T where T: PyObjectGetAttrProtocol +use python::PyClone; +use callback::CallbackConverter; + + +impl<'a, T> PyObjectGetAttrProtocolImpl for T + where T: PyObjectGetAttrProtocol<'a> + ::class::typeob::PyTypeObjectInfo { #[inline] fn tp_getattro() -> Option { - py_binary_func_!(PyObjectGetAttrProtocol, T::__getattr__, PyObjectCallbackConverter) + //py_binary_func_!(PyObjectGetAttrProtocol, T::__getattr__, PyObjectCallbackConverter) + unsafe extern "C" fn wrap<'a, T>(slf: *mut ffi::PyObject, + arg: *mut ffi::PyObject) -> *mut ffi::PyObject + where T: PyObjectGetAttrProtocol<'a> + ::class::typeob::PyTypeObjectInfo + { + const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()"); + { + println!("GETATTRO callback"); + let py = Python::assume_gil_acquired(); + + //::callback::handle_callback2(LOCATION, PyObjectCallbackConverter, |py| { + let arg1 = ::pyptr::from_borrowed_ptr(py, arg.clone()); + let name: &Py = {&arg1 as *const _}.as_ref().unwrap(); + let ret = match name.extr() { + Ok(name) => { + let slf: Py = ::pyptr::from_borrowed_ptr(py, slf); + let slf1: &Py = {&slf as *const _}.as_ref().unwrap(); + let res = slf1.as_ref().__getattr__(name).into(); + res + } + Err(e) => Err(e), + }; + println!("GETATTRO callback 3 {:?}", ret); + + //$crate::PyDrop::release_ref(arg, py); + //$crate::PyDrop::release_ref(slf, py); + //res + match ret { + Ok(val) => { + PyObjectCallbackConverter::convert(val, py) + } + Err(e) => { + e.restore(py); + //PyObjectCallbackConverter::error_value() + ::ptr::null_mut() + } + } + } + } + Some(wrap::) } } @@ -178,7 +229,7 @@ trait PyObjectSetAttrProtocolImpl { fn tp_setattro() -> Option; } -impl PyObjectSetAttrProtocolImpl for T where T: PyObjectProtocol +impl<'a, T> PyObjectSetAttrProtocolImpl for T where T: PyObjectProtocol<'a> { #[inline] default fn tp_setattro() -> Option { @@ -186,7 +237,7 @@ impl PyObjectSetAttrProtocolImpl for T where T: PyObjectProtocol } } -impl PyObjectSetAttrProtocolImpl for T where T: PyObjectSetAttrProtocol +/*impl PyObjectSetAttrProtocolImpl for T where T: PyObjectSetAttrProtocol { #[inline] fn tp_setattro() -> Option { @@ -225,14 +276,14 @@ impl PyObjectSetAttrProtocolImpl for T where T: PyObjectSetAttrProtocol } Some(wrap::) } -} +}*/ trait PyObjectDelAttrProtocolImpl { fn tp_delattro() -> Option; } -impl PyObjectDelAttrProtocolImpl for T where T: PyObjectProtocol +impl<'a, T> PyObjectDelAttrProtocolImpl for T where T: PyObjectProtocol<'a> { #[inline] default fn tp_delattro() -> Option { @@ -240,7 +291,7 @@ impl PyObjectDelAttrProtocolImpl for T where T: PyObjectProtocol } } -impl PyObjectDelAttrProtocolImpl for T where T: PyObjectDelAttrProtocol +/*impl PyObjectDelAttrProtocolImpl for T where T: PyObjectDelAttrProtocol { #[inline] default fn tp_delattro() -> Option { @@ -273,10 +324,10 @@ impl PyObjectDelAttrProtocolImpl for T where T: PyObjectDelAttrProtocol } Some(wrap::) } -} +}*/ -impl PyObjectDelAttrProtocolImpl for T +/*impl PyObjectDelAttrProtocolImpl for T where T: PyObjectSetAttrProtocol + PyObjectDelAttrProtocol { #[inline] @@ -318,50 +369,50 @@ impl PyObjectDelAttrProtocolImpl for T } Some(wrap::) } -} +}*/ trait PyObjectStrProtocolImpl { fn tp_str() -> Option; } -impl PyObjectStrProtocolImpl for T where T: PyObjectProtocol +impl<'a, T> PyObjectStrProtocolImpl for T where T: PyObjectProtocol<'a> { #[inline] default fn tp_str() -> Option { None } } -impl PyObjectStrProtocolImpl for T where T: PyObjectStrProtocol +/*impl PyObjectStrProtocolImpl for T where T: PyObjectStrProtocol { #[inline] fn tp_str() -> Option { py_unary_func_!(PyObjectStrProtocol, T::__str__, PyObjectCallbackConverter) } -} +}*/ trait PyObjectReprProtocolImpl { fn tp_repr() -> Option; } -impl PyObjectReprProtocolImpl for T where T: PyObjectProtocol +impl<'a, T> PyObjectReprProtocolImpl for T where T: PyObjectProtocol<'a> { #[inline] default fn tp_repr() -> Option { None } } -impl PyObjectReprProtocolImpl for T where T: PyObjectReprProtocol +/*impl PyObjectReprProtocolImpl for T where T: PyObjectReprProtocol { #[inline] fn tp_repr() -> Option { py_unary_func_!(PyObjectReprProtocol, T::__repr__, PyObjectCallbackConverter) } -} +}*/ #[doc(hidden)] pub trait PyObjectFormatProtocolImpl { fn __format__() -> Option; } -impl PyObjectFormatProtocolImpl for T where T: PyObjectProtocol +impl<'a, T> PyObjectFormatProtocolImpl for T where T: PyObjectProtocol<'a> { #[inline] default fn __format__() -> Option { @@ -370,10 +421,10 @@ impl PyObjectFormatProtocolImpl for T where T: PyObjectProtocol } #[doc(hidden)] -pub trait PyObjectBytesProtocolImpl { + pub trait PyObjectBytesProtocolImpl { fn __bytes__() -> Option; } -impl PyObjectBytesProtocolImpl for T where T: PyObjectProtocol +impl<'a, T> PyObjectBytesProtocolImpl for T where T: PyObjectProtocol<'a> { #[inline] default fn __bytes__() -> Option { @@ -385,50 +436,63 @@ impl PyObjectBytesProtocolImpl for T where T: PyObjectProtocol trait PyObjectHashProtocolImpl { fn tp_hash() -> Option; } -impl PyObjectHashProtocolImpl for T where T: PyObjectProtocol +impl<'a, T> PyObjectHashProtocolImpl for T where T: PyObjectProtocol<'a> { #[inline] default fn tp_hash() -> Option { None } } -impl PyObjectHashProtocolImpl for T where T: PyObjectHashProtocol +/*impl PyObjectHashProtocolImpl for T where T: PyObjectHashProtocol { #[inline] fn tp_hash() -> Option { py_unary_func_!(PyObjectHashProtocol, T::__hash__, HashConverter, ffi::Py_hash_t) } -} +}*/ trait PyObjectBoolProtocolImpl { fn nb_bool() -> Option; } -impl PyObjectBoolProtocolImpl for T where T: PyObjectProtocol +impl<'a, T> PyObjectBoolProtocolImpl for T where T: PyObjectProtocol<'a> { #[inline] default fn nb_bool() -> Option { None } } -impl PyObjectBoolProtocolImpl for T where T: PyObjectBoolProtocol +/*impl PyObjectBoolProtocolImpl for T + where T: PyObjectBoolProtocol + ::BaseObject + ::PyTypeObject { #[inline] fn nb_bool() -> Option { - py_unary_func_!(PyObjectBoolProtocol, T::__bool__, BoolCallbackConverter, c_int) + //py_unary_func_2!(PyObjectBoolProtocol, T::__bool__, BoolCallbackConverter, c_int) + unsafe extern "C" fn wrap(slf: *mut ffi::PyObject) -> c_int + where T: PyObjectBoolProtocol + { + const LOCATION: &'static str = concat!(stringify!(T), ".", stringify!($f), "()"); + ::callback::handle_callback(LOCATION, BoolCallbackConverter, |py| { + let slf: Py = ::pyptr::from_borrowed_ptr(py, slf); + let ret = slf.__bool__().into(); + //$crate::PyDrop::release_ref(slf, py); + ret + }) + } + Some(wrap::) } -} +}*/ trait PyObjectRichcmpProtocolImpl { fn tp_richcompare() -> Option; } -impl PyObjectRichcmpProtocolImpl for T where T: PyObjectProtocol +impl<'a, T> PyObjectRichcmpProtocolImpl for T where T: PyObjectProtocol<'a> { #[inline] default fn tp_richcompare() -> Option { None } } -impl PyObjectRichcmpProtocolImpl for T where T: PyObjectRichcmpProtocol +/*impl PyObjectRichcmpProtocolImpl for T where T: PyObjectRichcmpProtocol { #[inline] fn tp_richcompare() -> Option { @@ -456,7 +520,7 @@ impl PyObjectRichcmpProtocolImpl for T where T: PyObjectRichcmpProtocol } Some(wrap::) } -} +}*/ fn extract_op(py: Python, op: c_int) -> PyResult { diff --git a/src/class/macros.rs b/src/class/macros.rs index a2246a8b..82e2461c 100644 --- a/src/class/macros.rs +++ b/src/class/macros.rs @@ -44,6 +44,28 @@ macro_rules! py_unary_func_ { }} } +#[macro_export] + #[doc(hidden)] + macro_rules! py_unary_func_2 { + ($trait:ident, $class:ident :: $f:ident, $conv:expr) => { + py_unary_func_2!($trait, $class::$f, $conv, *mut $crate::ffi::PyObject); + }; + ($trait:ident, $class:ident :: $f:ident, $conv:expr, $res_type:ty) => {{ + unsafe extern "C" fn wrap(slf: *mut $crate::ffi::PyObject) -> $res_type + where T: $trait + { + const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()"); + $crate::callback::handle_callback(LOCATION, $conv, |py| { + let slf: $crate::Py = $crate::Py::from_borrowed_ptr(py, slf); + let ret = slf.$f(py).into(); + //$crate::PyDrop::release_ref(slf, py); + ret + }) + } + Some(wrap::<$class>) + }} +} + #[macro_export] #[doc(hidden)] macro_rules! py_len_func { diff --git a/src/class/mod.rs b/src/class/mod.rs index b759f3f5..6ff92dfe 100644 --- a/src/class/mod.rs +++ b/src/class/mod.rs @@ -37,9 +37,9 @@ pub static NO_METHODS: &'static [&'static str] = &[]; pub static NO_PY_METHODS: &'static [PyMethodDefType] = &[]; use ffi; -use err::{self, PyResult}; -use objects::{PyObject, PyType}; -use python::{Python, PythonObject}; +use err::{PyErr, PyResult}; +use objects::PyObject; +use python::Python; #[derive(Debug)] @@ -54,7 +54,7 @@ pub enum CompareOp { /// A PythonObject that is usable as a base type for #[class] -pub trait BaseObject : PythonObject { +pub trait BaseObject { /// Gets the size of the object, in bytes. fn size() -> usize; @@ -64,7 +64,7 @@ pub trait BaseObject : PythonObject { /// and initializes it using init_val. /// `ty` must be derived from the Self type, and the resulting object /// must be of type `ty`. - unsafe fn alloc(py: Python, ty: &PyType, val: Self::Type) -> PyResult; + unsafe fn alloc(py: Python, val: Self::Type) -> PyResult<*mut ffi::PyObject>; /// Calls the rust destructor for the object and frees the memory /// (usually by calling ptr->ob_type->tp_free). @@ -81,9 +81,16 @@ impl BaseObject for PyObject { type Type = (); - unsafe fn alloc(py: Python, ty: &PyType, _init_val: ()) -> PyResult { + unsafe fn alloc(py: Python, _val: ()) -> PyResult<*mut ffi::PyObject> { + let ty = py.get_type::(); let ptr = ffi::PyType_GenericAlloc(ty.as_type_ptr(), 0); - err::result_from_owned_ptr(py, ptr) + println!("alloc PyObject {}", ffi::Py_REFCNT(ptr)); + //err::result_from_owned_ptr(py, ptr) + if ptr.is_null() { + return Err(PyErr::fetch(py)) + } else { + Ok(ptr) + } } unsafe fn dealloc(_py: Python, obj: *mut ffi::PyObject) { diff --git a/src/class/typeob.rs b/src/class/typeob.rs index 391a2762..45fca395 100644 --- a/src/class/typeob.rs +++ b/src/class/typeob.rs @@ -4,7 +4,7 @@ use std::mem; use std::ffi::CString; use std::collections::HashMap; -use ::{ffi, class, PyErr, Python, PyResult, PythonObject}; +use ::{ffi, class, PyErr, Python, PyResult, PythonObject, PyPtr, Py}; use objects::PyType; use callback::AbortOnDrop; use class::{BaseObject, PyMethodDefType}; @@ -21,6 +21,10 @@ pub trait PyTypeObject { /// Trait implemented by object that generated by py::class macro pub trait PyTypeObjectInfo { + fn size() -> usize; + + fn offset() -> usize; + fn type_name() -> &'static str; fn type_object() -> &'static mut ffi::PyTypeObject; @@ -28,7 +32,31 @@ pub trait PyTypeObjectInfo { } -impl PyTypeObject for T where T: BaseObject + PythonObject + PyTypeObjectInfo { +impl<'a, T: ?Sized> PyTypeObjectInfo for &'a T where T: PyTypeObjectInfo { + + #[inline] + default fn size() -> usize { + ::size() + } + + #[inline] + default fn offset() -> usize { + ::offset() + } + + #[inline] + default fn type_name() -> &'static str { + ::type_name() + } + + #[inline] + default fn type_object() -> &'static mut ffi::PyTypeObject { + ::type_object() + } +} + + +impl PyTypeObject for T where T: BaseObject + PyTypeObjectInfo { #[inline] fn type_object(py: Python) -> PyType { @@ -48,7 +76,7 @@ impl PyTypeObject for T where T: BaseObject + PythonObject + PyTypeObjectInfo pub fn initialize_type(py: Python, module_name: Option<&str>, type_name: &str, type_object: &mut ffi::PyTypeObject) -> PyResult - where T: BaseObject + PythonObject + where T: BaseObject + PyTypeObjectInfo { // type name let name = match module_name { @@ -63,23 +91,23 @@ pub fn initialize_type(py: Python, module_name: Option<&str>, type_name: &str // dealloc type_object.tp_dealloc = Some(tp_dealloc_callback::); - // GC support - ::update_type_object(type_object); - // type size type_object.tp_basicsize = ::size() as ffi::Py_ssize_t; + // GC support + // ::update_type_object(type_object); + // descriptor protocol - ::tp_as_descr(type_object); + // ::tp_as_descr(type_object); // iterator methods - ::tp_as_iter(type_object); + // ::tp_as_iter(type_object); // basic methods ::tp_as_object(type_object); // number methods - if let Some(meth) = ::tp_as_number() { + /*if let Some(meth) = ::tp_as_number() { static mut NB_METHODS: ffi::PyNumberMethods = ffi::PyNumberMethods_INIT; *(unsafe { &mut NB_METHODS }) = meth; type_object.tp_as_number = unsafe { &mut NB_METHODS }; @@ -156,7 +184,7 @@ pub fn initialize_type(py: Python, module_name: Option<&str>, type_name: &str // strange mem::forget(props); - } + }*/ // register type object unsafe { @@ -168,11 +196,12 @@ pub fn initialize_type(py: Python, module_name: Option<&str>, type_name: &str } } -unsafe extern "C" fn tp_dealloc_callback(obj: *mut ffi::PyObject) where T: BaseObject +unsafe extern "C" fn tp_dealloc_callback(obj: *mut ffi::PyObject) + where T: BaseObject + PyTypeObjectInfo { let guard = AbortOnDrop("Cannot unwind out of tp_dealloc"); let py = Python::assume_gil_acquired(); - let r = T::dealloc(py, obj); + let r = ::dealloc(py, obj); mem::forget(guard); r } diff --git a/src/conversion.rs b/src/conversion.rs index 4ea3bad6..870ddfbc 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -20,6 +20,8 @@ use ffi; use python::{Python, PythonObject}; use objects::{PyObject, PyTuple}; use err::PyResult; +use pyptr::{Py, PyPtr}; + /// Conversion trait that allows various objects to be converted into Python objects. pub trait ToPyObject { @@ -112,6 +114,12 @@ pub trait FromPyObject<'source> : Sized { fn extract(py: Python, obj: &'source PyObject) -> PyResult; } +pub trait FromPyObj<'source> : Sized { + /// Extracts `Self` from the source `PyObject`. + fn extr(py: &'source Py<'source, S>) -> PyResult + where S: ::class::typeob::PyTypeObjectInfo; +} + pub trait RefFromPyObject { fn with_extracted(py: Python, obj: &PyObject, f: F) -> PyResult where F: FnOnce(&Self) -> R; @@ -133,25 +141,6 @@ impl RefFromPyObject for T /// Identity conversion: allows using existing `PyObject` instances where /// `T: ToPyObject` is expected. -/*impl ToPyObject for T where T: PythonObject { - #[inline] - fn to_py_object(&self, py: Python) -> PyObject { - PyClone::clone_ref(self, py).into_object() - } - - #[inline] - 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 { diff --git a/src/lib.rs b/src/lib.rs index f94ee488..66b70feb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -90,8 +90,12 @@ pub use pyo3cls::*; pub mod ffi; pub use ffi::{Py_ssize_t, Py_hash_t}; -mod pyptr; + +pub mod pyptr; pub use pyptr::{Py, PyPtr}; +pub use python::PyWithCheckedDowncast; +pub use conversion::FromPyObj; + pub use err::{PyErr, PyResult}; pub use objects::*; pub use python::{Python, PythonObject, diff --git a/src/objects/mod.rs b/src/objects/mod.rs index 4e69aef8..31bd0ebd 100644 --- a/src/objects/mod.rs +++ b/src/objects/mod.rs @@ -108,6 +108,16 @@ macro_rules! pyobject_newtype( pyobject_newtype!($name, $checkfunction); impl $crate::class::typeob::PyTypeObjectInfo for $name { + #[inline] + fn size() -> usize { + Self::offset() + $crate::std::mem::size_of::<$name>() + } + + #[inline] + fn offset() -> usize { + 0 + } + #[inline] fn type_name() -> &'static str { stringify!($name) diff --git a/src/objects/object.rs b/src/objects/object.rs index 490597a1..8a60adcd 100644 --- a/src/objects/object.rs +++ b/src/objects/object.rs @@ -144,6 +144,7 @@ impl PyObject { /// Undefined behavior if the pointer is NULL or invalid. #[inline] pub unsafe fn from_borrowed_ptr(_py : Python, ptr : *mut ffi::PyObject) -> PyObject { + println!("from_borrowed_ptr: {} {:?} {}", ptr.is_null(), ptr, ffi::Py_REFCNT(ptr)); debug_assert!(!ptr.is_null() && ffi::Py_REFCNT(ptr) > 0); ffi::Py_INCREF(ptr); PyObject { ptr: make_shared(ptr) } diff --git a/src/pyptr.rs b/src/pyptr.rs index 7e4bbe6f..9d41919f 100644 --- a/src/pyptr.rs +++ b/src/pyptr.rs @@ -2,19 +2,22 @@ use std; use std::marker::PhantomData; -use std::os::raw::c_int; +use std::os::raw::{c_int, c_void}; use std::ops::Deref; use ffi; -use err::PyResult; +use err::{PyErr, PyResult}; use python::Python; use class::{BaseObject, PyTypeObject}; use objects::{PyObject, PyType}; +use ::ToPyObject; +use class::typeob::PyTypeObjectInfo; +#[derive(Debug)] pub struct PyPtr { - inner: *mut ffi::PyObject, + pub inner: *mut ffi::PyObject, _t: PhantomData, } @@ -28,57 +31,80 @@ impl PyPtr { } } +// PyObject is thread-safe, because all operations on it require a Python<'p> token. +unsafe impl Send for PyPtr {} +unsafe impl Sync for PyPtr {} + +/// Dropping a `PyPtr` decrements the reference count on the object by 1. +impl Drop for PyPtr { + fn drop(&mut self) { + unsafe { + println!("drop PyPtr: {:?} {}", self.inner, ffi::Py_REFCNT(self.inner)); + } + + let _gil_guard = Python::acquire_gil(); + unsafe { ffi::Py_DECREF(self.inner); } + } +} + pub struct Py<'p, T> { - inner: *mut ffi::PyObject, + pub inner: *mut ffi::PyObject, _t: PhantomData, _py: PhantomData>, } -impl<'p, T> Py<'p, T> where T: BaseObject + PyTypeObject { +/// Creates a Py instance for the given FFI pointer. +/// Calls Py_INCREF() on the ptr. +/// Undefined behavior if the pointer is NULL or invalid. +#[inline] +pub unsafe fn from_borrowed_ptr<'p, T>(_py: Python<'p>, ptr: *mut ffi::PyObject) -> Py<'p, T> { + debug_assert!(!ptr.is_null() && ffi::Py_REFCNT(ptr) > 0); + ffi::Py_INCREF(ptr); + Py {inner: ptr, _t: PhantomData, _py: PhantomData} +} - fn new(py: Python<'p>, value: T) -> PyResult> { - unsafe { - let obj = try!(Py::::alloc(py, value)); +pub fn new<'p, T>(py: Python<'p>, value: T) -> PyResult> where T: BaseObject +{ + unsafe { + let obj = try!(::alloc(py, value)); - Ok(Py{inner: obj, _t: PhantomData, _py: PhantomData}) - } + Ok(Py{inner: obj, _t: PhantomData, _py: PhantomData}) + } +} + + +impl<'p, T> Py<'p, T> where T: PyTypeObjectInfo +{ + /// Creates a Py instance for the given FFI pointer. + /// This moves ownership over the pointer into the Py. + /// Undefined behavior if the pointer is NULL or invalid. + #[inline] + pub unsafe fn from_owned_ptr(_py: Python<'p>, ptr: *mut ffi::PyObject) -> Py<'p, T> { + debug_assert!(!ptr.is_null() && ffi::Py_REFCNT(ptr) > 0); + Py {inner: ptr, _t: PhantomData, _py: PhantomData} } - unsafe fn alloc(py: Python, value: T) -> PyResult<*mut ffi::PyObject> - { - let ty = py.get_type::(); - let obj = try!(PyObject::alloc(py, &ty, ())); - - let align = std::mem::align_of::(); - let bs = ::size(); - - // round base_size up to next multiple of align - let offset = (bs + align - 1) / align * align; - - let ptr = (obj.as_ptr() as *mut u8).offset(offset as isize) as *mut T; - std::ptr::write(ptr, value); - - Ok(obj.as_ptr()) + /// Creates a Py instance for the given FFI pointer. + /// Calls Py_INCREF() on the ptr. + /// Undefined behavior if the pointer is NULL or invalid. + #[inline] + pub unsafe fn from_borrowed_ptr(_py: Python<'p>, ptr: *mut ffi::PyObject) -> Py<'p, T> { + debug_assert!(!ptr.is_null() && ffi::Py_REFCNT(ptr) > 0); + ffi::Py_INCREF(ptr); + Py {inner: ptr, _t: PhantomData, _py: PhantomData} } - unsafe fn dealloc(py: Python, obj: *mut ffi::PyObject) { - let align = std::mem::align_of::(); - let bs = ::size(); - - // round base_size up to next multiple of align - let offset = (bs + align - 1) / align * align; - - let ptr = (obj as *mut u8).offset(offset as isize) as *mut T; - std::ptr::drop_in_place(ptr); - - PyObject::dealloc(py, obj) + /// Gets the reference count of this Py object. + #[inline] + pub fn get_refcnt(&self, _py: Python) -> usize { + unsafe { ffi::Py_REFCNT(self.inner) as usize } } #[inline] - pub fn as_ref(&self) -> &'p T { + pub fn as_ref(&self) -> &T { let align = std::mem::align_of::(); - let bs = ::size(); + let bs = ::size(); // round base_size up to next multiple of align let offset = (bs + align - 1) / align * align; @@ -90,9 +116,9 @@ impl<'p, T> Py<'p, T> where T: BaseObject + PyTypeObject { } #[inline] - pub fn as_mut(&self) -> &'p mut T { + pub fn as_mut(&self) -> &mut T { let align = std::mem::align_of::(); - let bs = ::size(); + let bs = ::size(); // round base_size up to next multiple of align let offset = (bs + align - 1) / align * align; @@ -118,21 +144,165 @@ impl<'p, T> Py<'p, T> where T: BaseObject + PyTypeObject { pub fn into_ptr(self) -> PyPtr { PyPtr { inner: self.inner, _t: PhantomData } } -} -impl<'p, T> Deref for Py<'p, T> where T: BaseObject { - type Target = T; + /// Casts the PyObject to a concrete Python object type. + /// Fails with `PythonObjectDowncastError` if the object is not of the expected type. + /// This is a wrapper function around `PythonObjectWithCheckedDowncast::downcast_from()`. + #[inline] + pub fn cast(&self) -> Result, ::PythonObjectDowncastError<'p>> + where D: ::PyWithCheckedDowncast<'p> + { + ::PyWithCheckedDowncast::downcast_from(self.clone()) + } - fn deref(&self) -> &T { + /// Casts the PyObject to a concrete Python object type. + /// Fails with `PythonObjectDowncastError` if the object is not of the expected type. + /// This is a wrapper function around `PythonObjectWithCheckedDowncast::downcast_from()`. + #[inline] + pub fn cast_into(self) -> Result, ::PythonObjectDowncastError<'p>> + where D: ::PyWithCheckedDowncast<'p> + { + ::PyWithCheckedDowncast::downcast_from(self) + } + + /// Casts the PyObject to a concrete Python object type. + /// Fails with `PythonObjectDowncastError` if the object is not of the expected type. + /// This is a wrapper function around + /// `PythonObjectWithCheckedDowncast::downcast_borrow_from()`. + #[inline] + pub fn cast_as(&'p self) -> Result<&'p D, ::PythonObjectDowncastError<'p>> + where D: ::PyWithCheckedDowncast<'p> + { + ::PyWithCheckedDowncast::downcast_borrow_from(&self) + } + + /// Unchecked downcast from other Py<> to Self. + /// Undefined behavior if the input object does not have the expected type. + #[inline] + pub unsafe fn unchecked_downcast_from<'a, S>(py: Py<'a, S>) -> Py<'a, T> + { + let res = Py {inner: py.inner, _t: PhantomData, _py: PhantomData}; + std::mem::forget(py); + res + } + + /// Unchecked downcast from Py<'p, S> to &'p S. + /// Undefined behavior if the input object does not have the expected type. + #[inline] + pub unsafe fn unchecked_downcast_borrow_from<'a, S>(py: &'a Py<'a, S>) -> &'a T { let align = std::mem::align_of::(); - let bs = ::size(); + let bs = ::size(); // round base_size up to next multiple of align let offset = (bs + align - 1) / align * align; unsafe { - let ptr = (self.inner as *mut u8).offset(offset as isize) as *mut T; + let ptr = (py.inner as *mut u8).offset(offset as isize) as *mut T; ptr.as_ref().unwrap() } } + + /// Extracts some type from the Python object. + /// This is a wrapper function around `FromPyObject::from_py_object()`. + #[inline] + pub fn extr(&'p self) -> PyResult + where D: ::conversion::FromPyObj<'p> + { + ::conversion::FromPyObj::extr(&self) + } +} + +impl<'p, T> Clone for Py<'p, T> { + fn clone(&self) -> Self { + unsafe { + debug_assert!(!self.inner.is_null() && ffi::Py_REFCNT(self.inner) > 0); + ffi::Py_INCREF(self.inner); + Py {inner: self.inner, _t: PhantomData, _py: PhantomData} + } + } +} + +impl<'p, T> Deref for Py<'p, T> where T: PyTypeObjectInfo + PyTypeObject + ::PythonObject { + type Target = T; + + fn deref(&self) -> &T { + self.as_ref() + } +} + +impl<'p, T> ::PyWithCheckedDowncast<'p> for T where T: PyTypeObjectInfo +{ + #[inline] + default fn downcast_from(ob: Py<'p, S>) + -> Result, ::PythonObjectDowncastError<'p>> + { + let checked = unsafe { ffi::PyObject_TypeCheck(ob.inner, T::type_object()) != 0 }; + + if checked { + Ok( unsafe { Py::::unchecked_downcast_from(ob) }) + } else { + let py = unsafe {Python::assume_gil_acquired()}; + Err(::PythonObjectDowncastError(py, None)) + } + } + + #[inline] + default fn downcast_borrow_from<'source, S>( + ob: &'source Py<'p, S>) -> Result<&'source T, ::PythonObjectDowncastError<'p>> + where S: PyTypeObjectInfo + { + let checked = unsafe { ffi::PyObject_TypeCheck(ob.inner, T::type_object()) != 0 }; + + if checked { + Ok( unsafe { Py::::unchecked_downcast_borrow_from(ob) }) + } else { + let py = unsafe {Python::assume_gil_acquired()}; + Err(::PythonObjectDowncastError(py, None)) + } + } +} + +impl<'source, T> ::FromPyObj<'source> for &'source T + where T: PyTypeObjectInfo +{ + #[inline] + default fn extr(py: &'source Py<'source, S>) -> PyResult<&'source T> + where S: PyTypeObjectInfo + { + Ok(::PyWithCheckedDowncast::downcast_borrow_from(py)?) + //Ok(py.cast_as::()?) + } +} + +impl<'source, T> ::FromPyObj<'source> for Py<'source, T> + where T: PyTypeObjectInfo +{ + #[inline] + default fn extr(py: &'source Py<'source, S>) -> PyResult> + where S: PyTypeObjectInfo + { + Ok(::PyWithCheckedDowncast::downcast_from(py.clone())?) + } +} + +//impl<'p, T> Deref for Py<'p, T> where T: BaseObject { +//} + +impl<'p, T> ToPyObject for Py<'p, T> { + #[inline] + fn to_py_object(&self, py: Python) -> PyObject { + unsafe {PyObject::from_owned_ptr(py, self.inner)} + } + + #[inline] + fn into_py_object(self, py: Python) -> PyObject { + unsafe {PyObject::from_borrowed_ptr(py, self.inner)} + } + + #[inline] + fn with_borrowed_ptr(&self, _py: Python, f: F) -> R + where F: FnOnce(*mut ffi::PyObject) -> R + { + f(self.inner) + } } diff --git a/src/python.rs b/src/python.rs index 9369c268..25d0fc7c 100644 --- a/src/python.rs +++ b/src/python.rs @@ -41,6 +41,7 @@ use pythonrun::GILGuard; pub struct Python<'p>(PhantomData<&'p GILGuard>); /// Trait implemented by all Python object types. + pub trait PythonObject : Send + Sized + 'static { /// Casts the Python object to PyObject. fn as_object(&self) -> &PyObject; @@ -73,6 +74,18 @@ pub trait PythonObjectWithCheckedDowncast : PythonObject { fn downcast_borrow_from<'a, 'p>(Python<'p>, &'a PyObject) -> Result<&'a Self, PythonObjectDowncastError<'p>>; } +/// Trait implemented by Python object types that allow a checked downcast. +pub trait PyWithCheckedDowncast<'p> : Sized { + + /// Cast from PyObject to a concrete Python object type. + fn downcast_from(::Py<'p, S>) -> Result<::Py<'p, Self>, PythonObjectDowncastError<'p>>; + + /// Cast from PyObject to a concrete Python object type. + fn downcast_borrow_from<'source, S>(&'source ::Py<'p, S>) + -> Result<&'source Self, PythonObjectDowncastError<'p>> + where S: ::class::typeob::PyTypeObjectInfo; +} + impl PythonObjectWithCheckedDowncast for T where T: PyTypeObject + PythonObject { #[inline] default fn downcast_from<'p>(py: Python<'p>, obj: PyObject)