diff --git a/pyo3cls/src/method.rs b/pyo3cls/src/method.rs index 36250caf..70648593 100644 --- a/pyo3cls/src/method.rs +++ b/pyo3cls/src/method.rs @@ -23,6 +23,7 @@ pub enum FnType { Setter(Option), Fn, FnNew, + FnInit, FnCall, FnClass, FnStatic, @@ -288,6 +289,9 @@ fn parse_attributes(attrs: &mut Vec) -> (FnType, Vec) "new" | "__new__" => { res = Some(FnType::FnNew) }, + "init" | "__init__" => { + res = Some(FnType::FnInit) + }, "call" | "__call__" => { res = Some(FnType::FnCall) }, @@ -321,6 +325,9 @@ fn parse_attributes(attrs: &mut Vec) -> (FnType, Vec) "new" => { res = Some(FnType::FnNew) }, + "init" => { + res = Some(FnType::FnInit) + }, "call" => { res = Some(FnType::FnCall) }, diff --git a/pyo3cls/src/py_class.rs b/pyo3cls/src/py_class.rs index 5ded6e13..3ae2f445 100644 --- a/pyo3cls/src/py_class.rs +++ b/pyo3cls/src/py_class.rs @@ -189,9 +189,11 @@ fn impl_class(cls: &syn::Ident, base: &syn::Ident, let py = gil.python(); let imp = py.import(#m) + .map_err(|e| e.print(py)) .expect(format!( "Can not import module: {}", #m).as_ref()); let cls = imp.get(#cls_name) + .map_err(|e| e.print(py)) .expect(format!( "Can not load exception class: {}.{}", #m, #cls_name).as_ref()); TYPE_OBJECT = cls.into_ptr() as *mut ffi::PyTypeObject; diff --git a/pyo3cls/src/py_method.rs b/pyo3cls/src/py_method.rs index 43d30358..ff9c985c 100644 --- a/pyo3cls/src/py_method.rs +++ b/pyo3cls/src/py_method.rs @@ -20,6 +20,8 @@ pub fn gen_py_method<'a>(cls: &Box, name: &syn::Ident, impl_py_method_def(name, doc, &spec, &impl_wrap(cls, name, &spec, true)), FnType::FnNew => impl_py_method_def_new(name, doc, &impl_wrap_type(cls, name, &spec)), + FnType::FnInit => + impl_py_method_def_init(name, doc, &impl_wrap_init(cls, name, &spec)), FnType::FnCall => impl_py_method_def_call(name, doc, &impl_wrap(cls, name, &spec, false)), FnType::FnClass => @@ -155,6 +157,39 @@ pub fn impl_wrap_type(cls: &Box, name: &syn::Ident, spec: &FnSpec) -> T } } +/// Generate function wrapper for ffi::initproc +fn impl_wrap_init(cls: &Box, name: &syn::Ident, spec: &FnSpec) -> Tokens { + let cb = impl_call(cls, name, &spec); + let body = impl_arg_params(&spec, cb); + + quote! { + #[allow(unused_mut)] + unsafe extern "C" fn __wrap( + _slf: *mut _pyo3::ffi::PyObject, + _args: *mut _pyo3::ffi::PyObject, + _kwargs: *mut _pyo3::ffi::PyObject) -> _pyo3::c_int + { + const _LOCATION: &'static str = concat!(stringify!(#cls),".",stringify!(#name),"()"); + let _pool = _pyo3::GILPool::new(); + let _py = _pyo3::Python::assume_gil_acquired(); + let _slf = _py.mut_cast_from_borrowed_ptr::<#cls>(_slf); + let _args = _py.cast_from_borrowed_ptr::<_pyo3::PyTuple>(_args); + let _kwargs = _pyo3::argparse::get_kwargs(_py, _kwargs); + + let _result: PyResult<()> = { + #body + }; + match _result { + Ok(_) => 0, + Err(e) => { + e.restore(_py); + -1 + } + } + } + } +} + /// Generate class method wrapper (PyCFunction, PyCFunctionWithKeywords) pub fn impl_wrap_class(cls: &Box, name: &syn::Ident, spec: &FnSpec) -> Tokens { let names: Vec = spec.args.iter().enumerate().map( @@ -578,6 +613,22 @@ pub fn impl_py_method_def_new(name: &syn::Ident, doc: syn::Lit, wrapper: &Tokens } } +pub fn impl_py_method_def_init(name: &syn::Ident, doc: syn::Lit, wrapper: &Tokens) -> Tokens +{ + quote! { + _pyo3::class::PyMethodDefType::Init({ + #wrapper + + _pyo3::class::PyMethodDef { + ml_name: stringify!(#name), + ml_meth: _pyo3::class::PyMethodType::PyInitFunc(__wrap), + ml_flags: _pyo3::ffi::METH_VARARGS | _pyo3::ffi::METH_KEYWORDS, + ml_doc: #doc, + } + }) + } +} + pub fn impl_py_method_def_class(name: &syn::Ident, doc: syn::Lit, wrapper: &Tokens) -> Tokens { quote! { diff --git a/src/class/methods.rs b/src/class/methods.rs index 6661a8f8..9ecf66e2 100644 --- a/src/class/methods.rs +++ b/src/class/methods.rs @@ -12,6 +12,8 @@ static NO_PY_METHODS: &'static [PyMethodDefType] = &[]; pub enum PyMethodDefType { /// Represents class `__new__` method New(PyMethodDef), + /// Represents class `__init__` method + Init(PyMethodDef), /// Represents class `__call__` method Call(PyMethodDef), /// Represents class method @@ -32,6 +34,7 @@ pub enum PyMethodType { PyCFunctionWithKeywords(ffi::PyCFunctionWithKeywords), PyNoArgsFunction(ffi::PyNoArgsFunction), PyNewFunc(ffi::newfunc), + PyInitFunc(ffi::initproc), } #[derive(Copy, Clone)] @@ -82,6 +85,10 @@ impl PyMethodDef { unsafe { std::mem::transmute::(meth) }, + PyMethodType::PyInitFunc(meth) => + unsafe { + std::mem::transmute::(meth) + }, }; ffi::PyMethodDef { diff --git a/src/ffi2/object.rs b/src/ffi2/object.rs index 8ec930b1..5379f445 100644 --- a/src/ffi2/object.rs +++ b/src/ffi2/object.rs @@ -830,6 +830,11 @@ pub fn PyObject_Check(_arg1: *mut PyObject) -> c_int { 1 } +#[inline] +pub fn PySuper_Check(_arg1: *mut PyObject) -> c_int { + 0 +} + #[cfg_attr(windows, link(name="pythonXY"))] extern "C" { fn _PyTrash_thread_deposit_object(o: *mut PyObject); fn _PyTrash_thread_destroy_chain(); diff --git a/src/ffi3/object.rs b/src/ffi3/object.rs index 5c7f5d99..880b0d56 100644 --- a/src/ffi3/object.rs +++ b/src/ffi3/object.rs @@ -28,7 +28,7 @@ pub const PyObject_HEAD_INIT: PyObject = PyObject { }; #[repr(C)] -#[derive(Copy, Clone)] +#[derive(Debug, Copy, Clone)] pub struct PyVarObject { pub ob_base: PyObject, pub ob_size: Py_ssize_t, @@ -387,7 +387,7 @@ mod typeobject { }; #[repr(C)] - #[derive(Copy, Clone)] + #[derive(Debug, Copy, Clone)] pub struct PyTypeObject { pub ob_base: object::PyVarObject, pub tp_name: *const c_char, @@ -821,3 +821,8 @@ pub const Py_GE : c_int = 5; pub fn PyObject_Check(_arg1: *mut PyObject) -> c_int { 1 } + +#[inline] +pub fn PySuper_Check(_arg1: *mut PyObject) -> c_int { + 0 +} diff --git a/src/objectprotocol.rs b/src/objectprotocol.rs index 50f3315f..f8e38915 100644 --- a/src/objectprotocol.rs +++ b/src/objectprotocol.rs @@ -8,7 +8,7 @@ use ffi; use err::{self, PyErr, PyResult}; use python::{Python, ToPyPointer, PyDowncastFrom}; use object::PyObject; -use objects::{PyObjectRef, PyDict, PyString, PyIterator, PyType}; +use objects::{PyObjectRef, PyDict, PyString, PyIterator, PyType, PySuper}; use conversion::{ToPyObject, ToBorrowedObject, IntoPyTuple, FromPyObject}; use instance::PyObjectWithToken; @@ -121,6 +121,10 @@ pub trait ObjectProtocol { /// Gets the Python type object for this object's type. fn get_type(&self) -> &PyType; + /// Gets the Python super object for this object. + /// This is equivalent to the Python expression: 'super()' + fn get_super(&self) -> &PySuper; + /// Casts the PyObject to a concrete Python object type. fn cast_as<'a, D>(&'a self) -> Option<&'a D> where D: PyDowncastFrom, @@ -259,7 +263,7 @@ impl ObjectProtocol for T where T: PyObjectWithToken + ToPyPointer { #[inline] fn call_method(&self, name: &str, args: A, kwargs: Option<&PyDict>) - -> PyResult<&PyObjectRef> + -> PyResult<&PyObjectRef> where A: IntoPyTuple { name.with_borrowed_ptr(self.py(), |name| unsafe { @@ -345,13 +349,16 @@ impl ObjectProtocol for T where T: PyObjectWithToken + ToPyPointer { } } - #[inline] fn get_type(&self) -> &PyType { unsafe { PyType::from_type_ptr(self.py(), (*self.as_ptr()).ob_type) } } + fn get_super(&self) -> &PySuper { + PySuper::new(self) + } + #[inline] fn cast_as<'a, D>(&'a self) -> Option<&'a D> where D: PyDowncastFrom, diff --git a/src/objects/mod.rs b/src/objects/mod.rs index 34ec94f0..e078c1ad 100644 --- a/src/objects/mod.rs +++ b/src/objects/mod.rs @@ -3,6 +3,7 @@ #[macro_use] mod exc_impl; pub use self::typeobject::PyType; +pub use self::superobj::PySuper; pub use self::module::PyModule; pub use self::iterator::PyIterator; pub use self::boolobject::PyBool; @@ -265,6 +266,7 @@ mod slice; mod stringdata; mod stringutils; mod set; +mod superobj; pub mod exc; #[cfg(Py_3)] diff --git a/src/objects/superobj.rs b/src/objects/superobj.rs new file mode 100644 index 00000000..515e7dea --- /dev/null +++ b/src/objects/superobj.rs @@ -0,0 +1,54 @@ +// Copyright (c) 2017-present PyO3 Project and Contributors + +use std; + +use ffi; +use object::PyObject; +use objects::{PyObjectRef, PyDict}; +use objectprotocol::ObjectProtocol; +use conversion::IntoPyTuple; +use python::{ToPyPointer, IntoPyPointer}; +use err::{self, PyResult}; +use instance::PyObjectWithToken; + +/// Represents a reference to a Python `super object`. +pub struct PySuper(PyObject); + +pyobject_convert!(PySuper); +pyobject_nativetype!(PySuper, PySuper_Type, PySuper_Check); + + +impl PySuper { + + pub fn new(object: &T) -> &PySuper where T: PyObjectWithToken + ToPyPointer { + // Create the arguments for super() + unsafe { + let o = object.py().from_borrowed_ptr(object.as_ptr()); + let args = (object.get_type(), o).into_tuple(object.py()).into_ptr(); + + // Creat the class super() + let ptr = ffi::PyType_GenericNew(&mut ffi::PySuper_Type, args, std::ptr::null_mut()); + let oref = object.py().cast_from_ptr(ptr); + + // call __init__ on super object + if (*ffi::Py_TYPE(ptr)).tp_init.unwrap()(ptr, args, std::ptr::null_mut()) == -1 { + err::panic_after_error() + } + oref + } + } + + pub fn __new__(&self, args: A, kwargs: Option<&PyDict>) + -> PyResult<&PyObjectRef> + where A: IntoPyTuple + { + self.call_method("__new__", args, kwargs) + } + + pub fn __init__(&self, args: A, kwargs: Option<&PyDict>) + -> PyResult<&PyObjectRef> + where A: IntoPyTuple + { + self.call_method("__init__", args, kwargs) + } +} diff --git a/src/python.rs b/src/python.rs index 45cb6183..8a47acd2 100644 --- a/src/python.rs +++ b/src/python.rs @@ -318,6 +318,16 @@ impl<'p> Python<'p> { ::unchecked_downcast_from(p) } + /// Register `ffi::PyObject` pointer in release pool + pub unsafe fn from_borrowed_ptr(self, ptr: *mut ffi::PyObject) -> &'p PyObjectRef + { + if ptr.is_null() { + ::err::panic_after_error(); + } else { + pythonrun::register_borrowed(self, ptr) + } + } + /// Register `ffi::PyObject` pointer in release pool, /// and do unchecked downcast to specific type. pub unsafe fn cast_from_ptr(self, ptr: *mut ffi::PyObject) -> &'p T diff --git a/src/typeob.rs b/src/typeob.rs index 9c01d65e..cd6fda2c 100644 --- a/src/typeob.rs +++ b/src/typeob.rs @@ -6,6 +6,7 @@ use std; use std::mem; use std::ffi::{CStr, CString}; use std::collections::HashMap; +use std::os::raw::c_void; use {ffi, class, pythonrun}; use err::{PyErr, PyResult}; @@ -122,17 +123,11 @@ impl PyObjectAlloc for T where T : PyTypeInfo { let ptr = (obj as *mut u8).offset(T::offset()) as *mut T; std::ptr::drop_in_place(ptr); - let ty = ffi::Py_TYPE(obj); - if ffi::PyType_IS_GC(ty) != 0 { - ffi::PyObject_GC_Del(obj as *mut ::c_void); - } else { - 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 ffi::PyType_HasFeature(ty, ffi::Py_TPFLAGS_HEAPTYPE) != 0 { - ffi::Py_DECREF(ty as *mut ffi::PyObject); + if ffi::PyObject_CallFinalizerFromDealloc(obj) < 0 { + return } + + (*ffi::Py_TYPE(obj)).tp_free.unwrap()(obj as *mut c_void); } } @@ -244,14 +239,24 @@ pub fn initialize_type<'p, T>(py: Python<'p>, module_name: Option<&str>) -> PyRe } // normal methods - let (new, call, mut methods) = py_class_method_defs::()?; + let (new, init, call, mut methods) = py_class_method_defs::()?; if !methods.is_empty() { methods.push(ffi::PyMethodDef_INIT); type_object.tp_methods = methods.as_mut_ptr(); mem::forget(methods); } + + match (new, init) { + (None, Some(_)) => { + panic!("{}.__new__ method is required if __init__ method defined", T::NAME); + } + _ => () + } + // __new__ method type_object.tp_new = new; + // __init__ method + type_object.tp_init = init; // __call__ method type_object.tp_call = call; @@ -333,12 +338,14 @@ fn py_class_flags(type_object: &mut ffi::PyTypeObject) { } fn py_class_method_defs() -> PyResult<(Option, + Option, Option, Vec)> { let mut defs = Vec::new(); let mut call = None; let mut new = None; + let mut init = None; for def in ::py_methods() { match *def { @@ -354,6 +361,13 @@ fn py_class_method_defs() -> PyResult<(Option, panic!("Method type is not supoorted by tp_call slot") } } + PyMethodDefType::Init(ref def) => { + if let class::methods::PyMethodType::PyInitFunc(meth) = def.ml_meth { + init = Some(meth) + } else { + panic!("Method type is not supoorted by tp_init slot") + } + } PyMethodDefType::Method(ref def) => { defs.push(def.as_method_def()); } @@ -385,7 +399,7 @@ fn py_class_method_defs() -> PyResult<(Option, py_class_async_methods::(&mut defs); - Ok((new, call, defs)) + Ok((new, init, call, defs)) } #[cfg(Py_3)]