add class and static methods #17
This commit is contained in:
parent
b3079f759c
commit
a90c763bc0
|
@ -20,6 +20,8 @@ pub enum FnType {
|
|||
Fn,
|
||||
FnNew,
|
||||
FnCall,
|
||||
FnClass,
|
||||
FnStatic,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -43,32 +45,47 @@ impl<'a> FnSpec<'a> {
|
|||
meth_attrs: &'a mut Vec<syn::Attribute>) -> FnSpec<'a> {
|
||||
let (fn_type, fn_attrs) = parse_attributes(meth_attrs);
|
||||
|
||||
//let mut has_self = false;
|
||||
let mut has_self = false;
|
||||
let mut py = false;
|
||||
let mut arguments = Vec::new();
|
||||
|
||||
for input in sig.decl.inputs[1..].iter() {
|
||||
for input in sig.decl.inputs.iter() {
|
||||
match input {
|
||||
&syn::FnArg::SelfRef(_, _) => {
|
||||
//has_self = true;
|
||||
has_self = true;
|
||||
},
|
||||
&syn::FnArg::SelfValue(_) => {
|
||||
//has_self = true;
|
||||
has_self = true;
|
||||
}
|
||||
&syn::FnArg::Captured(ref pat, ref ty) => {
|
||||
// skip first argument (cls)
|
||||
if (fn_type == FnType::FnClass || fn_type == FnType::FnNew) && !has_self {
|
||||
has_self = true;
|
||||
continue
|
||||
}
|
||||
|
||||
let (mode, ident) = match pat {
|
||||
&syn::Pat::Ident(ref mode, ref ident, _) =>
|
||||
(mode, ident),
|
||||
_ =>
|
||||
panic!("unsupported argument: {:?}", pat),
|
||||
};
|
||||
// TODO add check for first py: Python arg
|
||||
if py {
|
||||
let opt = check_arg_ty_and_optional(name, ty);
|
||||
arguments.push(FnArg{name: ident, mode: mode, ty: ty, optional: opt});
|
||||
} else {
|
||||
py = true;
|
||||
|
||||
if !py {
|
||||
match ty {
|
||||
&syn::Ty::Path(_, ref path) =>
|
||||
if let Some(segment) = path.segments.last() {
|
||||
if segment.ident.as_ref() == "Python" {
|
||||
py = true;
|
||||
continue;
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
let opt = check_arg_ty_and_optional(name, ty);
|
||||
arguments.push(FnArg{name: ident, mode: mode, ty: ty, optional: opt});
|
||||
}
|
||||
&syn::FnArg::Ignored(_) =>
|
||||
panic!("ignored argument: {:?}", name),
|
||||
|
@ -204,6 +221,12 @@ fn parse_attributes(attrs: &mut Vec<syn::Attribute>) -> (FnType, Vec<FnAttr>) {
|
|||
"call" | "__call__" => {
|
||||
res = Some(FnType::FnCall)
|
||||
},
|
||||
"classmethod" => {
|
||||
res = Some(FnType::FnClass)
|
||||
},
|
||||
"staticmethod" => {
|
||||
res = Some(FnType::FnStatic)
|
||||
},
|
||||
"setter" | "getter" => {
|
||||
if attr.style == syn::AttrStyle::Inner {
|
||||
panic!("Inner style attribute is not
|
||||
|
|
|
@ -16,9 +16,13 @@ pub fn gen_py_method<'a>(cls: &Box<syn::Ty>, name: &syn::Ident,
|
|||
FnType::Fn =>
|
||||
impl_py_method_def(name, &spec, &impl_wrap(cls, name, &spec, true)),
|
||||
FnType::FnNew =>
|
||||
impl_py_method_def_new(name, &impl_wrap_new(cls, name, &spec)),
|
||||
impl_py_method_def_new(name, &impl_wrap_type(cls, name, &spec)),
|
||||
FnType::FnCall =>
|
||||
impl_py_method_def_call(name, &impl_wrap(cls, name, &spec, false)),
|
||||
FnType::FnClass =>
|
||||
impl_py_method_def_class(name, &impl_wrap_class(cls, name, &spec)),
|
||||
FnType::FnStatic =>
|
||||
impl_py_method_def_static(name, &impl_wrap_static(cls, name, &spec)),
|
||||
FnType::Getter(ref getter) =>
|
||||
impl_py_getter_def(name, getter, &impl_wrap_getter(cls, name, &spec)),
|
||||
FnType::Setter(ref setter) =>
|
||||
|
@ -125,9 +129,13 @@ pub fn impl_proto_wrap(cls: &Box<syn::Ty>, name: &syn::Ident, spec: &FnSpec) ->
|
|||
}
|
||||
}
|
||||
|
||||
/// Generate function wrapper (PyCFunction, PyCFunctionWithKeywords)
|
||||
pub fn impl_wrap_new(cls: &Box<syn::Ty>, name: &syn::Ident, spec: &FnSpec) -> Tokens {
|
||||
let cb = impl_class_new(cls, name, spec);
|
||||
/// Generate class method wrapper (PyCFunction, PyCFunctionWithKeywords)
|
||||
pub fn impl_wrap_type(cls: &Box<syn::Ty>, name: &syn::Ident, spec: &FnSpec) -> Tokens {
|
||||
let names: Vec<&syn::Ident> = spec.args.iter().map(|item| item.name).collect();
|
||||
let cb = quote! {{
|
||||
#cls::#name(&cls, py, #(#names),*)
|
||||
}};
|
||||
|
||||
let body = impl_arg_params(spec, cb);
|
||||
let output = &spec.output;
|
||||
|
||||
|
@ -154,6 +162,69 @@ pub fn impl_wrap_new(cls: &Box<syn::Ty>, name: &syn::Ident, spec: &FnSpec) -> To
|
|||
}
|
||||
}
|
||||
|
||||
/// 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().map(|item| item.name).collect();
|
||||
let cb = quote! {{
|
||||
#cls::#name(&cls, py, #(#names),*)
|
||||
}};
|
||||
let body = impl_arg_params(spec, cb);
|
||||
let output = &spec.output;
|
||||
|
||||
quote! {
|
||||
#[allow(unused_mut)]
|
||||
unsafe extern "C" fn wrap(cls: *mut _pyo3::ffi::PyObject,
|
||||
args: *mut _pyo3::ffi::PyObject,
|
||||
kwargs: *mut _pyo3::ffi::PyObject) -> *mut _pyo3::ffi::PyObject
|
||||
{
|
||||
const LOCATION: &'static str = concat!(stringify!(#cls),".",stringify!(#name), "()");
|
||||
|
||||
_pyo3::callback::cb_meth(LOCATION, |py| {
|
||||
let cls = _pyo3::PyType::from_type_ptr(py, cls as *mut _pyo3::ffi::PyTypeObject);
|
||||
let args = _pyo3::PyTuple::from_borrowed_ptr(py, args);
|
||||
let kwargs = _pyo3::argparse::get_kwargs(py, kwargs);
|
||||
|
||||
let result: #output = {
|
||||
#body
|
||||
};
|
||||
_pyo3::callback::cb_convert(
|
||||
_pyo3::callback::PyObjectCallbackConverter, py, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate static method wrapper (PyCFunction, PyCFunctionWithKeywords)
|
||||
pub fn impl_wrap_static(cls: &Box<syn::Ty>, name: &syn::Ident, spec: &FnSpec) -> Tokens {
|
||||
let names: Vec<&syn::Ident> = spec.args.iter().map(|item| item.name).collect();
|
||||
let cb = quote! {{
|
||||
#cls::#name(py, #(#names),*)
|
||||
}};
|
||||
|
||||
let body = impl_arg_params(spec, cb);
|
||||
let output = &spec.output;
|
||||
|
||||
quote! {
|
||||
#[allow(unused_mut)]
|
||||
unsafe extern "C" fn wrap(_slf: *mut _pyo3::ffi::PyObject,
|
||||
args: *mut _pyo3::ffi::PyObject,
|
||||
kwargs: *mut _pyo3::ffi::PyObject) -> *mut _pyo3::ffi::PyObject
|
||||
{
|
||||
const LOCATION: &'static str = concat!(stringify!(#cls),".",stringify!(#name), "()");
|
||||
|
||||
_pyo3::callback::cb_meth(LOCATION, |py| {
|
||||
let args = _pyo3::PyTuple::from_borrowed_ptr(py, args);
|
||||
let kwargs = _pyo3::argparse::get_kwargs(py, kwargs);
|
||||
|
||||
let result: #output = {
|
||||
#body
|
||||
};
|
||||
_pyo3::callback::cb_convert(
|
||||
_pyo3::callback::PyObjectCallbackConverter, py, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate functiona wrapper (PyCFunction, PyCFunctionWithKeywords)
|
||||
fn impl_wrap_getter(cls: &Box<syn::Ty>, name: &syn::Ident, _spec: &FnSpec) -> Tokens {
|
||||
|
@ -216,13 +287,6 @@ fn impl_call(_cls: &Box<syn::Ty>, fname: &syn::Ident, spec: &FnSpec) -> Tokens {
|
|||
}}
|
||||
}
|
||||
|
||||
fn impl_class_new(cls: &Box<syn::Ty>, fname: &syn::Ident, spec: &FnSpec) -> Tokens {
|
||||
let names: Vec<&syn::Ident> = spec.args.iter().map(|item| item.name).collect();
|
||||
quote! {{
|
||||
#cls::#fname(&cls, py, #(#names),*)
|
||||
}}
|
||||
}
|
||||
|
||||
fn impl_arg_params(spec: &FnSpec, body: Tokens) -> Tokens {
|
||||
if spec.args.is_empty() {
|
||||
return body
|
||||
|
@ -428,6 +492,37 @@ pub fn impl_py_method_def_new(name: &syn::Ident, wrapper: &Tokens) -> Tokens {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn impl_py_method_def_class(name: &syn::Ident, wrapper: &Tokens) -> Tokens {
|
||||
quote! {
|
||||
_pyo3::class::PyMethodDefType::Class({
|
||||
#wrapper
|
||||
|
||||
_pyo3::class::PyMethodDef {
|
||||
ml_name: stringify!(#name),
|
||||
ml_meth: _pyo3::class::PyMethodType::PyCFunctionWithKeywords(wrap),
|
||||
ml_flags: _pyo3::ffi::METH_VARARGS | _pyo3::ffi::METH_KEYWORDS |
|
||||
_pyo3::ffi::METH_CLASS,
|
||||
ml_doc: "",
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn impl_py_method_def_static(name: &syn::Ident, wrapper: &Tokens) -> Tokens {
|
||||
quote! {
|
||||
_pyo3::class::PyMethodDefType::Static({
|
||||
#wrapper
|
||||
|
||||
_pyo3::class::PyMethodDef {
|
||||
ml_name: stringify!(#name),
|
||||
ml_meth: _pyo3::class::PyMethodType::PyCFunctionWithKeywords(wrap),
|
||||
ml_flags: _pyo3::ffi::METH_VARARGS | _pyo3::ffi::METH_KEYWORDS | _pyo3::ffi::METH_STATIC,
|
||||
ml_doc: "",
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn impl_py_method_def_call(name: &syn::Ident, wrapper: &Tokens) -> Tokens {
|
||||
quote! {
|
||||
_pyo3::class::PyMethodDefType::Call({
|
||||
|
|
|
@ -13,6 +13,8 @@ static NO_PY_METHODS: &'static [PyMethodDefType] = &[];
|
|||
pub enum PyMethodDefType {
|
||||
New(PyMethodDef),
|
||||
Call(PyMethodDef),
|
||||
Class(PyMethodDef),
|
||||
Static(PyMethodDef),
|
||||
Method(PyMethodDef),
|
||||
Getter(PyGetterDef),
|
||||
Setter(PySetterDef),
|
||||
|
@ -86,8 +88,21 @@ impl PyMethodDef {
|
|||
|
||||
pub fn as_method_descr(&self, py: Python, ty: *mut ffi::PyTypeObject) -> PyResult<PyObject> {
|
||||
unsafe {
|
||||
PyObject::from_owned_ptr_or_err(
|
||||
py, ffi::PyDescr_NewMethod(ty, Box::into_raw(Box::new(self.as_method_def()))))
|
||||
if self.ml_flags & ffi::METH_CLASS != 0 {
|
||||
PyObject::from_owned_ptr_or_err(
|
||||
py, ffi::PyDescr_NewClassMethod(
|
||||
ty, Box::into_raw(Box::new(self.as_method_def()))))
|
||||
}
|
||||
else if self.ml_flags & ffi::METH_STATIC != 0 {
|
||||
PyObject::from_owned_ptr_or_err(
|
||||
py, ffi::PyCFunction_New(
|
||||
Box::into_raw(Box::new(self.as_method_def())), std::ptr::null_mut()))
|
||||
}
|
||||
else {
|
||||
PyObject::from_owned_ptr_or_err(
|
||||
py, ffi::PyDescr_NewMethod(
|
||||
ty, Box::into_raw(Box::new(self.as_method_def()))))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,12 +16,14 @@ pub type PyCFunction =
|
|||
|
||||
#[cfg(all(Py_3_6, not(Py_LIMITED_API)))]
|
||||
pub type _PyCFunctionFast =
|
||||
unsafe extern "C" fn (slf: *mut PyObject, args: *mut *mut PyObject,
|
||||
nargs: ::ffi::pyport::Py_ssize_t, kwnames: *mut PyObject)
|
||||
-> *mut PyObject;
|
||||
unsafe extern "C" fn (slf: *mut PyObject,
|
||||
args: *mut *mut PyObject,
|
||||
nargs: ::ffi::pyport::Py_ssize_t,
|
||||
kwnames: *mut PyObject) -> *mut PyObject;
|
||||
|
||||
pub type PyCFunctionWithKeywords =
|
||||
unsafe extern "C" fn (slf: *mut PyObject, args: *mut PyObject,
|
||||
unsafe extern "C" fn (slf: *mut PyObject,
|
||||
args: *mut PyObject,
|
||||
kwds: *mut PyObject) -> *mut PyObject;
|
||||
|
||||
pub type PyNoArgsFunction =
|
||||
|
@ -31,7 +33,8 @@ pub type PyNoArgsFunction =
|
|||
pub fn PyCFunction_GetFunction(f: *mut PyObject) -> Option<PyCFunction>;
|
||||
pub fn PyCFunction_GetSelf(f: *mut PyObject) -> *mut PyObject;
|
||||
pub fn PyCFunction_GetFlags(f: *mut PyObject) -> c_int;
|
||||
pub fn PyCFunction_Call(f: *mut PyObject, args: *mut PyObject,
|
||||
pub fn PyCFunction_Call(f: *mut PyObject,
|
||||
args: *mut PyObject,
|
||||
kwds: *mut PyObject) -> *mut PyObject;
|
||||
}
|
||||
|
||||
|
|
|
@ -136,8 +136,11 @@ macro_rules! pyobject_nativetype(
|
|||
impl $crate::python::IntoPyPointer for $name {
|
||||
/// Gets the underlying FFI pointer, returns a owned pointer.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
fn into_ptr(self) -> *mut $crate::ffi::PyObject {
|
||||
unsafe{$crate::std::mem::transmute(self)}
|
||||
let ptr = self.0.as_ptr();
|
||||
$crate::std::mem::forget(self);
|
||||
ptr
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -131,6 +131,7 @@ impl<T> PyTypeObject for T where T: PyObjectAlloc + PyTypeInfo {
|
|||
#[inline]
|
||||
fn type_object(py: Python) -> PyType {
|
||||
let mut ty = <T as PyTypeInfo>::type_object();
|
||||
//return unsafe { PyType::from_type_ptr(py, ty) };
|
||||
|
||||
if (ty.tp_flags & ffi::Py_TPFLAGS_READY) != 0 {
|
||||
unsafe { PyType::from_type_ptr(py, ty) }
|
||||
|
@ -281,6 +282,12 @@ fn py_class_method_defs<T>(py: Python, type_object: *mut ffi::PyTypeObject)
|
|||
&PyMethodDefType::Method(ref def) => {
|
||||
defs.set_item(py, def.ml_name, def.as_method_descr(py, type_object)?)?;
|
||||
}
|
||||
&PyMethodDefType::Class(ref def) => {
|
||||
defs.set_item(py, def.ml_name, def.as_method_descr(py, type_object)?)?;
|
||||
}
|
||||
&PyMethodDefType::Static(ref def) => {
|
||||
defs.set_item(py, def.ml_name, def.as_method_descr(py, type_object)?)?;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -241,23 +241,27 @@ fn instance_method_with_args() {
|
|||
py.run("assert obj.method(multiplier=6) == 42", None, Some(&d)).unwrap();
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
#[py::class]
|
||||
struct ClassMethod {}
|
||||
struct ClassMethod {token: PyToken}
|
||||
|
||||
#[py::ptr(ClassMethod)]
|
||||
struct ClassMethodPtr(PyPtr);
|
||||
|
||||
#[py::methods]
|
||||
impl ClassMethod {
|
||||
#[new]
|
||||
fn __new__(cls: &PyType, py: Python) -> PyResult<ClassMethod> {
|
||||
ClassMethod::create_instance(py)
|
||||
fn __new__(cls: &PyType, py: Python) -> PyResult<ClassMethodPtr> {
|
||||
py.init(|t| ClassMethod{token: t})
|
||||
}
|
||||
|
||||
//#[classmethod]
|
||||
//def method(cls) -> PyResult<String> {
|
||||
// Ok(format!("{}.method()!", cls.name(py)))
|
||||
//}
|
||||
#[classmethod]
|
||||
fn method(cls: &PyType, py: Python) -> PyResult<String> {
|
||||
Ok(format!("{}.method()!", cls.name(py)))
|
||||
}
|
||||
}
|
||||
|
||||
//#[test]
|
||||
#[test]
|
||||
fn class_method() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
|
@ -266,24 +270,32 @@ fn class_method() {
|
|||
d.set_item(py, "C", py.get_type::<ClassMethod>()).unwrap();
|
||||
py.run("assert C.method() == 'ClassMethod.method()!'", None, Some(&d)).unwrap();
|
||||
py.run("assert C().method() == 'ClassMethod.method()!'", None, Some(&d)).unwrap();
|
||||
}*/
|
||||
}
|
||||
|
||||
//py_class!(class ClassMethodWithArgs |py| {
|
||||
// @classmethod
|
||||
// def method(cls, input: &str) -> PyResult<String> {
|
||||
// Ok(format!("{}.method({})", cls.name(py), input))
|
||||
// }
|
||||
//});
|
||||
|
||||
//#[test]
|
||||
/*fn class_method_with_args() {
|
||||
#[py::class]
|
||||
struct ClassMethodWithArgs{token: PyToken}
|
||||
|
||||
#[py::ptr(ClassMethodWithArgs)]
|
||||
struct ClassMethodWithArgsPtr(PyPtr);
|
||||
|
||||
#[py::methods]
|
||||
impl ClassMethodWithArgs {
|
||||
#[classmethod]
|
||||
fn method(cls: &PyType, py: Python, input: PyString) -> PyResult<String> {
|
||||
Ok(format!("{}.method({})", cls.name(py), input))
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn class_method_with_args() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
|
||||
let d = PyDict::new(py);
|
||||
d.set_item(py, "C", py.get_type::<ClassMethodWithArgs>()).unwrap();
|
||||
py.run("assert C.method('abc') == 'ClassMethodWithArgs.method(abc)'", None, Some(&d)).unwrap();
|
||||
}*/
|
||||
}
|
||||
|
||||
#[py::class]
|
||||
struct StaticMethod {
|
||||
|
@ -300,14 +312,14 @@ impl StaticMethod {
|
|||
py.init(|t| StaticMethod{token: t})
|
||||
}
|
||||
|
||||
//#[staticmethod]
|
||||
//fn method(py: Python) -> PyResult<&'static str> {
|
||||
// Ok("StaticMethod.method()!")
|
||||
//}
|
||||
#[staticmethod]
|
||||
fn method(py: Python) -> PyResult<&'static str> {
|
||||
Ok("StaticMethod.method()!")
|
||||
}
|
||||
}
|
||||
|
||||
//#[test]
|
||||
/*fn static_method() {
|
||||
#[test]
|
||||
fn static_method() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
|
||||
|
@ -316,26 +328,34 @@ impl StaticMethod {
|
|||
d.set_item(py, "C", py.get_type::<StaticMethod>()).unwrap();
|
||||
py.run("assert C.method() == 'StaticMethod.method()!'", None, Some(&d)).unwrap();
|
||||
py.run("assert C().method() == 'StaticMethod.method()!'", None, Some(&d)).unwrap();
|
||||
}*/
|
||||
}
|
||||
|
||||
//py_class!(class StaticMethodWithArgs |py| {
|
||||
// @staticmethod
|
||||
// def method(input: i32) -> PyResult<String> {
|
||||
// Ok(format!("0x{:x}", input))
|
||||
// }
|
||||
//});
|
||||
#[py::class]
|
||||
struct StaticMethodWithArgs{token: PyToken}
|
||||
|
||||
//#[test]
|
||||
//fn static_method_with_args() {
|
||||
// let gil = Python::acquire_gil();
|
||||
// let py = gil.python();
|
||||
#[py::ptr(StaticMethodWithArgs)]
|
||||
struct StaticMethodWithArgsPtr(PyPtr);
|
||||
|
||||
// assert_eq!(StaticMethodWithArgs::method(py, 1234).unwrap(), "0x4d2");
|
||||
// let d = PyDict::new(py);
|
||||
// d.set_item(py, "C", py.get_type::<StaticMethodWithArgs>()).unwrap();
|
||||
// py.run("assert C.method(1337) == '0x539'", None, Some(&d)).unwrap();
|
||||
//}
|
||||
#[py::methods]
|
||||
impl StaticMethodWithArgs {
|
||||
|
||||
#[staticmethod]
|
||||
fn method(py: Python, input: i32) -> PyResult<String> {
|
||||
Ok(format!("0x{:x}", input))
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn static_method_with_args() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
|
||||
assert_eq!(StaticMethodWithArgs::method(py, 1234).unwrap(), "0x4d2");
|
||||
|
||||
let d = PyDict::new(py);
|
||||
d.set_item(py, "C", py.get_type::<StaticMethodWithArgs>()).unwrap();
|
||||
py.run("assert C.method(1337) == '0x539'", None, Some(&d)).unwrap();
|
||||
}
|
||||
|
||||
#[py::class]
|
||||
struct GCIntegration {
|
||||
|
|
Loading…
Reference in New Issue