added PySuper and __init__ support

This commit is contained in:
Nikolay Kim 2017-07-27 16:14:54 -07:00
parent dcaa3130d9
commit 665a1da016
11 changed files with 181 additions and 17 deletions

View File

@ -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)
},

View File

@ -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;

View File

@ -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! {

View File

@ -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 {

View File

@ -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();

View File

@ -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
}

View File

@ -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,
@ -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,

View File

@ -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)]

54
src/objects/superobj.rs Normal file
View File

@ -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)
}
}

View File

@ -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

View File

@ -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)]