added PySuper and __init__ support
This commit is contained in:
parent
dcaa3130d9
commit
665a1da016
|
@ -23,6 +23,7 @@ pub enum FnType {
|
|||
Setter(Option<String>),
|
||||
Fn,
|
||||
FnNew,
|
||||
FnInit,
|
||||
FnCall,
|
||||
FnClass,
|
||||
FnStatic,
|
||||
|
@ -288,6 +289,9 @@ fn parse_attributes(attrs: &mut Vec<syn::Attribute>) -> (FnType, Vec<Argument>)
|
|||
"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<syn::Attribute>) -> (FnType, Vec<Argument>)
|
|||
"new" => {
|
||||
res = Some(FnType::FnNew)
|
||||
},
|
||||
"init" => {
|
||||
res = Some(FnType::FnInit)
|
||||
},
|
||||
"call" => {
|
||||
res = Some(FnType::FnCall)
|
||||
},
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -20,6 +20,8 @@ pub fn gen_py_method<'a>(cls: &Box<syn::Ty>, 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<syn::Ty>, name: &syn::Ident, spec: &FnSpec) -> T
|
|||
}
|
||||
}
|
||||
|
||||
/// Generate function wrapper for ffi::initproc
|
||||
fn impl_wrap_init(cls: &Box<syn::Ty>, 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<syn::Ty>, name: &syn::Ident, spec: &FnSpec) -> Tokens {
|
||||
let names: Vec<syn::Ident> = 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! {
|
||||
|
|
|
@ -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::<ffi::newfunc, ffi::PyCFunction>(meth)
|
||||
},
|
||||
PyMethodType::PyInitFunc(meth) =>
|
||||
unsafe {
|
||||
std::mem::transmute::<ffi::initproc, ffi::PyCFunction>(meth)
|
||||
},
|
||||
};
|
||||
|
||||
ffi::PyMethodDef {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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<T> ObjectProtocol for T where T: PyObjectWithToken + ToPyPointer {
|
|||
|
||||
#[inline]
|
||||
fn call_method<A>(&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<T> 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,
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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<T>(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__<A>(&self, args: A, kwargs: Option<&PyDict>)
|
||||
-> PyResult<&PyObjectRef>
|
||||
where A: IntoPyTuple
|
||||
{
|
||||
self.call_method("__new__", args, kwargs)
|
||||
}
|
||||
|
||||
pub fn __init__<A>(&self, args: A, kwargs: Option<&PyDict>)
|
||||
-> PyResult<&PyObjectRef>
|
||||
where A: IntoPyTuple
|
||||
{
|
||||
self.call_method("__init__", args, kwargs)
|
||||
}
|
||||
}
|
|
@ -318,6 +318,16 @@ impl<'p> Python<'p> {
|
|||
<T as PyDowncastFrom>::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<T>(self, ptr: *mut ffi::PyObject) -> &'p T
|
||||
|
|
|
@ -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<T> PyObjectAlloc<T> 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::<T>()?;
|
||||
let (new, init, call, mut methods) = py_class_method_defs::<T>()?;
|
||||
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<T: PyTypeInfo>(type_object: &mut ffi::PyTypeObject) {
|
|||
}
|
||||
|
||||
fn py_class_method_defs<T>() -> PyResult<(Option<ffi::newfunc>,
|
||||
Option<ffi::initproc>,
|
||||
Option<ffi::PyCFunctionWithKeywords>,
|
||||
Vec<ffi::PyMethodDef>)>
|
||||
{
|
||||
let mut defs = Vec::new();
|
||||
let mut call = None;
|
||||
let mut new = None;
|
||||
let mut init = None;
|
||||
|
||||
for def in <T as class::methods::PyMethodsProtocolImpl>::py_methods() {
|
||||
match *def {
|
||||
|
@ -354,6 +361,13 @@ fn py_class_method_defs<T>() -> PyResult<(Option<ffi::newfunc>,
|
|||
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<T>() -> PyResult<(Option<ffi::newfunc>,
|
|||
|
||||
py_class_async_methods::<T>(&mut defs);
|
||||
|
||||
Ok((new, call, defs))
|
||||
Ok((new, init, call, defs))
|
||||
}
|
||||
|
||||
#[cfg(Py_3)]
|
||||
|
|
Loading…
Reference in New Issue