added properties support
This commit is contained in:
parent
6d40d651a1
commit
9b1c4c55f4
|
@ -13,8 +13,10 @@ use proc_macro::TokenStream;
|
|||
use quote::{Tokens, ToTokens};
|
||||
|
||||
mod py_class;
|
||||
mod py_impl;
|
||||
mod py_proto;
|
||||
mod py_method;
|
||||
mod utils;
|
||||
|
||||
|
||||
#[proc_macro_attribute]
|
||||
|
@ -23,7 +25,6 @@ pub fn proto(_: TokenStream, input: TokenStream) -> TokenStream {
|
|||
let source = input.to_string();
|
||||
|
||||
// Parse the string representation into a syntax tree
|
||||
//let ast: syn::Crate = source.parse().unwrap();
|
||||
let mut ast = syn::parse_item(&source).unwrap();
|
||||
|
||||
// Build the output
|
||||
|
@ -44,7 +45,6 @@ pub fn class(_: TokenStream, input: TokenStream) -> TokenStream {
|
|||
let source = input.to_string();
|
||||
|
||||
// Parse the string representation into a syntax tree
|
||||
//let ast: syn::Crate = source.parse().unwrap();
|
||||
let mut ast = syn::parse_derive_input(&source).unwrap();
|
||||
|
||||
// Build the output
|
||||
|
@ -57,3 +57,22 @@ pub fn class(_: TokenStream, input: TokenStream) -> TokenStream {
|
|||
|
||||
TokenStream::from_str(s.as_str()).unwrap()
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn methods(_: TokenStream, input: TokenStream) -> TokenStream {
|
||||
// Construct a string representation of the type definition
|
||||
let source = input.to_string();
|
||||
|
||||
// Parse the string representation into a syntax tree
|
||||
let mut ast = syn::parse_item(&source).unwrap();
|
||||
|
||||
// Build the output
|
||||
let expanded = py_impl::build_py_methods(&mut ast);
|
||||
|
||||
// Return the generated impl as a TokenStream
|
||||
let mut tokens = Tokens::new();
|
||||
ast.to_tokens(&mut tokens);
|
||||
let s = String::from(tokens.as_str()) + expanded.as_str();
|
||||
|
||||
TokenStream::from_str(s.as_str()).unwrap()
|
||||
}
|
||||
|
|
|
@ -133,7 +133,7 @@ fn impl_checked_downcast(cls: &syn::Ident) -> Tokens {
|
|||
|
||||
fn impl_class_init(cls: &syn::Ident) -> Tokens {
|
||||
quote! {
|
||||
impl pyo3::class::methods::PyClassInit for #cls {
|
||||
impl pyo3::class::typeob::PyClassInit for #cls {
|
||||
fn init() -> bool {
|
||||
true
|
||||
}
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||
|
||||
use syn;
|
||||
use quote::Tokens;
|
||||
|
||||
use py_method;
|
||||
|
||||
|
||||
pub fn build_py_methods(ast: &mut syn::Item) -> Tokens {
|
||||
match ast.node {
|
||||
syn::ItemKind::Impl(_, _, _, ref path, ref ty, ref mut impl_items) => {
|
||||
if let &Some(_) = path {
|
||||
panic!("#[methods] can not be used only with trait impl block");
|
||||
} else {
|
||||
impl_methods(ty, impl_items)
|
||||
}
|
||||
},
|
||||
_ => panic!("#[methods] can only be used with Impl blocks"),
|
||||
}
|
||||
}
|
||||
|
||||
fn impl_methods(ty: &Box<syn::Ty>, impls: &mut Vec<syn::ImplItem>) -> Tokens {
|
||||
|
||||
// get method names in impl block
|
||||
let mut methods = Vec::new();
|
||||
for iimpl in impls.iter_mut() {
|
||||
match iimpl.node {
|
||||
syn::ImplItemKind::Method(ref mut sig, ref mut block) => {
|
||||
methods.push(py_method::gen_py_method(
|
||||
ty, &iimpl.ident, sig, block, &mut iimpl.attrs));
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
let tokens = quote! {
|
||||
impl pyo3::class::methods::PyMethodsProtocolImpl for #ty {
|
||||
fn py_methods() -> &'static [pyo3::class::PyMethodDefType] {
|
||||
static METHODS: &'static [pyo3::class::PyMethodDefType] = &[
|
||||
#(#methods),*
|
||||
];
|
||||
METHODS
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let dummy_const = syn::Ident::new("_IMPL_PYO3_METHODS");
|
||||
quote! {
|
||||
#[feature(specialization)]
|
||||
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
|
||||
const #dummy_const: () = {
|
||||
extern crate pyo3;
|
||||
use pyo3::ffi;
|
||||
|
||||
#tokens
|
||||
};
|
||||
}
|
||||
}
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
use syn;
|
||||
use quote::Tokens;
|
||||
use utils::for_err_msg;
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Arg<'a> {
|
||||
|
@ -11,13 +13,22 @@ struct Arg<'a> {
|
|||
pub optional: Option<&'a syn::Ty>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
enum FnType {
|
||||
Getter(Option<String>),
|
||||
Setter(Option<String>),
|
||||
Fn,
|
||||
}
|
||||
|
||||
|
||||
pub fn gen_py_method<'a>(cls: &Box<syn::Ty>, name: &syn::Ident,
|
||||
sig: &mut syn::MethodSig, _block: &mut syn::Block,
|
||||
_attrs: &Vec<syn::Attribute>) -> Tokens
|
||||
meth_attrs: &mut Vec<syn::Attribute>) -> Tokens
|
||||
{
|
||||
check_generic(name, sig);
|
||||
|
||||
let fn_type = parse_attributes(meth_attrs);
|
||||
|
||||
//let mut has_self = false;
|
||||
let mut py = false;
|
||||
let mut arguments: Vec<Arg> = Vec::new();
|
||||
|
@ -50,10 +61,103 @@ pub fn gen_py_method<'a>(cls: &Box<syn::Ty>, name: &syn::Ident,
|
|||
}
|
||||
}
|
||||
|
||||
impl_py_method_def(name, &impl_wrap(cls, name, arguments))
|
||||
match fn_type {
|
||||
FnType::Fn =>
|
||||
impl_py_method_def(name, &impl_wrap(cls, name, arguments)),
|
||||
FnType::Getter(getter) =>
|
||||
impl_py_getter_def(name, getter, &impl_wrap_getter(cls, name, arguments)),
|
||||
FnType::Setter(setter) =>
|
||||
impl_py_setter_def(name, setter, &impl_wrap_setter(cls, name, arguments)),
|
||||
}
|
||||
}
|
||||
|
||||
fn check_generic(name: &syn::Ident, sig: &syn::MethodSig) {
|
||||
fn parse_attributes(attrs: &mut Vec<syn::Attribute>) -> FnType {
|
||||
let mut new_attrs = Vec::new();
|
||||
let mut res: Option<FnType> = None;
|
||||
|
||||
for attr in attrs.iter() {
|
||||
match attr.value {
|
||||
syn::MetaItem::Word(ref name) => {
|
||||
match name.as_ref() {
|
||||
"setter" | "getter" => {
|
||||
if attr.style == syn::AttrStyle::Inner {
|
||||
panic!("Inner style attribute is not
|
||||
supported for setter and getter");
|
||||
}
|
||||
if res != None {
|
||||
panic!("setter/getter attribute can not be used mutiple times");
|
||||
}
|
||||
if name.as_ref() == "setter" {
|
||||
res = Some(FnType::Setter(None))
|
||||
} else {
|
||||
res = Some(FnType::Getter(None))
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
new_attrs.push(attr.clone())
|
||||
}
|
||||
}
|
||||
},
|
||||
syn::MetaItem::List(ref name, ref meta) => {
|
||||
match name.as_ref() {
|
||||
"setter" | "getter" => {
|
||||
if attr.style == syn::AttrStyle::Inner {
|
||||
panic!("Inner style attribute is not
|
||||
supported for setter and getter");
|
||||
}
|
||||
if res != None {
|
||||
panic!("setter/getter attribute can not be used mutiple times");
|
||||
}
|
||||
if meta.len() != 1 {
|
||||
panic!("setter/getter requires one value");
|
||||
}
|
||||
match *meta.first().unwrap() {
|
||||
syn::NestedMetaItem::MetaItem(syn::MetaItem::Word(ref w)) => {
|
||||
if name.as_ref() == "setter" {
|
||||
res = Some(FnType::Setter(Some(w.to_string())))
|
||||
} else {
|
||||
res = Some(FnType::Getter(Some(w.to_string())))
|
||||
}
|
||||
},
|
||||
syn::NestedMetaItem::Literal(ref lit) => {
|
||||
match *lit {
|
||||
syn::Lit::Str(ref s, syn::StrStyle::Cooked) => {
|
||||
if name.as_ref() == "setter" {
|
||||
res = Some(FnType::Setter(Some(s.clone())))
|
||||
} else {
|
||||
res = Some(FnType::Getter(Some(s.clone())))
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
panic!("setter/getter attribute requires str value");
|
||||
},
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
println!("cannot parse {:?} attribute: {:?}", name, meta);
|
||||
},
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
new_attrs.push(attr.clone())
|
||||
}
|
||||
}
|
||||
},
|
||||
syn::MetaItem::NameValue(_, _) => {
|
||||
new_attrs.push(attr.clone())
|
||||
},
|
||||
}
|
||||
}
|
||||
attrs.clear();
|
||||
attrs.extend(new_attrs);
|
||||
|
||||
match res {
|
||||
Some(tp) => tp,
|
||||
None => FnType::Fn,
|
||||
}
|
||||
}
|
||||
|
||||
fn check_generic(name: &syn::Ident, sig: &syn::MethodSig) {
|
||||
if !sig.generics.ty_params.is_empty() {
|
||||
panic!("python method can not be generic: {:?}", name);
|
||||
}
|
||||
|
@ -73,14 +177,18 @@ fn check_arg_ty_and_optional<'a>(name: &'a syn::Ident, ty: &'a syn::Ty) -> Optio
|
|||
match segment.parameters {
|
||||
syn::PathParameters::AngleBracketed(ref params) => {
|
||||
if params.types.len() != 1 {
|
||||
panic!("argument type is not supported by python method: {:?} ({:?})",
|
||||
name, ty);
|
||||
panic!("argument type is not supported by python method: {:?} ({:?}) {:?}",
|
||||
for_err_msg(name),
|
||||
for_err_msg(ty),
|
||||
for_err_msg(path));
|
||||
}
|
||||
Some(¶ms.types[0])
|
||||
},
|
||||
_ => {
|
||||
panic!("argument type is not supported by python method: {:?} ({:?})",
|
||||
name, ty);
|
||||
panic!("argument type is not supported by python method: {:?} ({:?}) {:?}",
|
||||
for_err_msg(name),
|
||||
for_err_msg(ty),
|
||||
for_err_msg(path));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -91,8 +199,10 @@ fn check_arg_ty_and_optional<'a>(name: &'a syn::Ident, ty: &'a syn::Ty) -> Optio
|
|||
}
|
||||
},
|
||||
_ => {
|
||||
panic!("argument type is not supported by python method: {:?} ({:?})",
|
||||
name, ty);
|
||||
None
|
||||
//panic!("argument type is not supported by python method: {:?} ({:?})",
|
||||
//for_err_msg(name),
|
||||
//for_err_msg(ty));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -128,6 +238,54 @@ fn impl_wrap(cls: &Box<syn::Ty>, name: &syn::Ident, args: Vec<Arg>) -> Tokens {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/// Generate functiona wrapper (PyCFunction, PyCFunctionWithKeywords)
|
||||
fn impl_wrap_getter(cls: &Box<syn::Ty>, name: &syn::Ident, _args: Vec<Arg>) -> Tokens {
|
||||
quote! {
|
||||
unsafe extern "C" fn wrap (slf: *mut pyo3::ffi::PyObject,
|
||||
_: *mut pyo3::c_void)
|
||||
-> *mut pyo3::ffi::PyObject
|
||||
{
|
||||
const LOCATION: &'static str = concat!(
|
||||
stringify!(#cls), ".getter_", stringify!(#name), "()");
|
||||
pyo3::_detail::handle_callback(
|
||||
LOCATION, pyo3::_detail::PyObjectCallbackConverter, |py|
|
||||
{
|
||||
let slf = pyo3::PyObject::from_borrowed_ptr(
|
||||
py, slf).unchecked_cast_into::<#cls>();
|
||||
let ret = slf.#name(py);
|
||||
pyo3::PyDrop::release_ref(slf, py);
|
||||
ret
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate functiona wrapper (PyCFunction, PyCFunctionWithKeywords)
|
||||
fn impl_wrap_setter(cls: &Box<syn::Ty>, name: &syn::Ident, _args: Vec<Arg>) -> Tokens {
|
||||
quote! {
|
||||
unsafe extern "C" fn wrap(slf: *mut pyo3::ffi::PyObject,
|
||||
value: *mut pyo3::ffi::PyObject,
|
||||
_: *mut pyo3::c_void) -> pyo3::c_int
|
||||
{
|
||||
const LOCATION: &'static str = concat!(
|
||||
stringify!(#cls), ".setter", stringify!(#name), "()");
|
||||
pyo3::_detail::handle_callback(
|
||||
LOCATION, pyo3::py_class::slots::UnitCallbackConverter, |py|
|
||||
{
|
||||
let slf = pyo3::PyObject::from_borrowed_ptr(py, slf)
|
||||
.unchecked_cast_into::<#cls>();
|
||||
let value = pyo3::PyObject::from_borrowed_ptr(py, value);
|
||||
let ret = slf.#name(py, &value);
|
||||
pyo3::PyDrop::release_ref(slf, py);
|
||||
pyo3::PyDrop::release_ref(value, py);
|
||||
ret.map(|o| ())
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn impl_call(cls: &Box<syn::Ty>, fname: &syn::Ident, args: &Vec<Arg>) -> Tokens {
|
||||
let names: Vec<&syn::Ident> = args.iter().map(|item| item.name).collect();
|
||||
quote! {
|
||||
|
@ -223,14 +381,66 @@ fn impl_arg_param(arg: &Arg, body: &Tokens) -> Tokens {
|
|||
}
|
||||
|
||||
fn impl_py_method_def(name: &syn::Ident, wrapper: &Tokens) -> Tokens {
|
||||
quote! {{
|
||||
#wrapper
|
||||
quote! {
|
||||
pyo3::class::PyMethodDefType::Method({
|
||||
#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,
|
||||
ml_doc: "",
|
||||
}
|
||||
}}
|
||||
pyo3::class::PyMethodDef {
|
||||
ml_name: stringify!(#name),
|
||||
ml_meth: pyo3::class::PyMethodType::PyCFunctionWithKeywords(wrap),
|
||||
ml_flags: pyo3::ffi::METH_VARARGS | pyo3::ffi::METH_KEYWORDS,
|
||||
ml_doc: "",
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn impl_py_setter_def(name: &syn::Ident, setter: Option<String>, wrapper: &Tokens) -> Tokens {
|
||||
let n = if let Some(ref name) = setter {
|
||||
name.to_string()
|
||||
} else {
|
||||
let n = String::from(name.as_ref());
|
||||
if n.starts_with("set_") {
|
||||
n[4..].to_string()
|
||||
} else {
|
||||
n
|
||||
}
|
||||
};
|
||||
|
||||
quote! {
|
||||
pyo3::class::PyMethodDefType::Setter({
|
||||
#wrapper
|
||||
|
||||
pyo3::class::PySetterDef {
|
||||
name: #n,
|
||||
meth: wrap,
|
||||
doc: "",
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn impl_py_getter_def(name: &syn::Ident, getter: Option<String>, wrapper: &Tokens) -> Tokens {
|
||||
let n = if let Some(ref name) = getter {
|
||||
name.to_string()
|
||||
} else {
|
||||
let n = String::from(name.as_ref());
|
||||
if n.starts_with("get_") {
|
||||
n[4..].to_string()
|
||||
} else {
|
||||
n
|
||||
}
|
||||
};
|
||||
|
||||
quote! {
|
||||
pyo3::class::PyMethodDefType::Getter({
|
||||
#wrapper
|
||||
|
||||
pyo3::class::PyGetterDef {
|
||||
name: #n,
|
||||
meth: wrap,
|
||||
doc: "",
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -99,10 +99,10 @@ pub fn build_py_proto(ast: &mut syn::Item) -> Tokens {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
panic!("#[py_proto] can only be used with protocol trait implementations")
|
||||
panic!("#[proto] can only be used with protocol trait implementations")
|
||||
}
|
||||
},
|
||||
_ => panic!("#[py_proto] can only be used with Impl blocks"),
|
||||
_ => panic!("#[proto] can only be used with Impl blocks"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -135,7 +135,7 @@ fn impl_protocol(name: &'static str,
|
|||
syn::ImplItemKind::Method(ref mut sig, ref mut block) => {
|
||||
if methods.methods.contains(&iimpl.ident.as_ref()) {
|
||||
py_methods.push(py_method::gen_py_method(
|
||||
ty, &iimpl.ident, sig, block, &iimpl.attrs));
|
||||
ty, &iimpl.ident, sig, block, &mut iimpl.attrs));
|
||||
} else {
|
||||
meth.push(String::from(iimpl.ident.as_ref()));
|
||||
|
||||
|
@ -175,8 +175,8 @@ fn impl_protocol(name: &'static str,
|
|||
METHODS
|
||||
}
|
||||
|
||||
fn py_methods() -> &'static [pyo3::class::PyMethodDef] {
|
||||
static METHODS: &'static [pyo3::class::PyMethodDef] = &[
|
||||
fn py_methods() -> &'static [pyo3::class::PyMethodDefType] {
|
||||
static METHODS: &'static [pyo3::class::PyMethodDefType] = &[
|
||||
#(#py_methods),*
|
||||
];
|
||||
METHODS
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
use quote::{Tokens, ToTokens};
|
||||
|
||||
|
||||
pub fn for_err_msg(i: &ToTokens) -> String {
|
||||
let mut tokens = Tokens::new();
|
||||
|
||||
i.to_tokens(&mut tokens);
|
||||
tokens.as_str().to_string()
|
||||
}
|
|
@ -45,14 +45,14 @@ impl<P> PyContextProtocol for P {
|
|||
pub trait PyContextProtocolImpl {
|
||||
fn methods() -> &'static [&'static str];
|
||||
|
||||
fn py_methods() -> &'static [::class::PyMethodDef];
|
||||
fn py_methods() -> &'static [::methods::PyMethodDefType];
|
||||
}
|
||||
|
||||
impl<T> PyContextProtocolImpl for T {
|
||||
default fn methods() -> &'static [&'static str] {
|
||||
NO_METHODS
|
||||
}
|
||||
default fn py_methods() -> &'static [::class::PyMethodDef] {
|
||||
default fn py_methods() -> &'static [::methods::PyMethodDefType] {
|
||||
NO_PY_METHODS
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,13 @@ use std::ffi::CString;
|
|||
use ::{ffi, exc, class, py_class, PyErr, Python, PyResult, PythonObject};
|
||||
use objects::PyType;
|
||||
use function::AbortOnDrop;
|
||||
use class::NO_PY_METHODS;
|
||||
|
||||
pub enum PyMethodDefType {
|
||||
Method(PyMethodDef),
|
||||
Getter(PyGetterDef),
|
||||
Setter(PySetterDef),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum PyMethodType {
|
||||
|
@ -23,12 +29,31 @@ pub struct PyMethodDef {
|
|||
pub ml_doc: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct PyGetterDef {
|
||||
pub name: &'static str,
|
||||
pub meth: ffi::getter,
|
||||
pub doc: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct PySetterDef {
|
||||
pub name: &'static str,
|
||||
pub meth: ffi::setter,
|
||||
pub doc: &'static str,
|
||||
}
|
||||
|
||||
unsafe impl Sync for PyMethodDef {}
|
||||
unsafe impl Sync for ffi::PyMethodDef {}
|
||||
|
||||
unsafe impl Sync for PyGetterDef {}
|
||||
unsafe impl Sync for PySetterDef {}
|
||||
unsafe impl Sync for ffi::PyGetSetDef {}
|
||||
|
||||
|
||||
impl PyMethodDef {
|
||||
|
||||
fn as_method_def(&self) -> ffi::PyMethodDef {
|
||||
pub fn as_method_def(&self) -> ffi::PyMethodDef {
|
||||
let meth = match self.ml_meth {
|
||||
PyMethodType::PyCFunction(meth) => meth,
|
||||
PyMethodType::PyCFunctionWithKeywords(meth) =>
|
||||
|
@ -48,138 +73,33 @@ impl PyMethodDef {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
pub trait PyClassInit {
|
||||
|
||||
fn init() -> bool;
|
||||
|
||||
fn type_object() -> &'static mut ffi::PyTypeObject;
|
||||
|
||||
fn build_type(py: Python,
|
||||
module_name: Option<&str>,
|
||||
type_object: &mut ffi::PyTypeObject) -> PyResult<PyType>;
|
||||
|
||||
}
|
||||
|
||||
impl<T> PyClassInit for T where T: PythonObject + py_class::BaseObject {
|
||||
|
||||
default fn init() -> bool { false }
|
||||
|
||||
default fn type_object() -> &'static mut ffi::PyTypeObject {
|
||||
static mut TYPE_OBJECT: ffi::PyTypeObject = ffi::PyTypeObject_INIT;
|
||||
unsafe {
|
||||
&mut TYPE_OBJECT
|
||||
}
|
||||
}
|
||||
|
||||
default fn build_type(py: Python, module_name: Option<&str>,
|
||||
type_object: &mut ffi::PyTypeObject) -> PyResult<PyType> {
|
||||
// type name
|
||||
let name = match module_name {
|
||||
Some(module_name) => CString::new(
|
||||
format!("{}.{}", module_name, stringify!(type_name))),
|
||||
None => CString::new(stringify!(type_name))
|
||||
};
|
||||
let name = name.expect(
|
||||
"Module name/type name must not contain NUL byte").into_raw();
|
||||
|
||||
type_object.tp_name = name;
|
||||
|
||||
// dealloc
|
||||
type_object.tp_dealloc = Some(tp_dealloc_callback::<T>);
|
||||
|
||||
// GC support
|
||||
<T as class::gc::PyGCProtocolImpl>::update_type_object(type_object);
|
||||
|
||||
// type size
|
||||
type_object.tp_basicsize = <T as py_class::BaseObject>::size() as ffi::Py_ssize_t;
|
||||
|
||||
// number methods
|
||||
if let Some(meth) = ffi::PyNumberMethods::new::<T>() {
|
||||
static mut NB_METHODS: ffi::PyNumberMethods = ffi::PyNumberMethods_INIT;
|
||||
*(unsafe { &mut NB_METHODS }) = meth;
|
||||
type_object.tp_as_number = unsafe { &mut NB_METHODS };
|
||||
} else {
|
||||
type_object.tp_as_number = 0 as *mut ffi::PyNumberMethods;
|
||||
}
|
||||
|
||||
// mapping methods
|
||||
if let Some(meth) = ffi::PyMappingMethods::new::<T>() {
|
||||
static mut MP_METHODS: ffi::PyMappingMethods = ffi::PyMappingMethods_INIT;
|
||||
*(unsafe { &mut MP_METHODS }) = meth;
|
||||
type_object.tp_as_mapping = unsafe { &mut MP_METHODS };
|
||||
} else {
|
||||
type_object.tp_as_mapping = 0 as *mut ffi::PyMappingMethods;
|
||||
}
|
||||
|
||||
// sequence methods
|
||||
if let Some(meth) = ffi::PySequenceMethods::new::<T>() {
|
||||
static mut SQ_METHODS: ffi::PySequenceMethods = ffi::PySequenceMethods_INIT;
|
||||
*(unsafe { &mut SQ_METHODS }) = meth;
|
||||
type_object.tp_as_sequence = unsafe { &mut SQ_METHODS };
|
||||
} else {
|
||||
type_object.tp_as_sequence = 0 as *mut ffi::PySequenceMethods;
|
||||
}
|
||||
|
||||
// async methods
|
||||
if let Some(meth) = ffi::PyAsyncMethods::new::<T>() {
|
||||
static mut ASYNC_METHODS: ffi::PyAsyncMethods = ffi::PyAsyncMethods_INIT;
|
||||
*(unsafe { &mut ASYNC_METHODS }) = meth;
|
||||
type_object.tp_as_async = unsafe { &mut ASYNC_METHODS };
|
||||
} else {
|
||||
type_object.tp_as_async = 0 as *mut ffi::PyAsyncMethods;
|
||||
}
|
||||
|
||||
// buffer protocol
|
||||
if let Some(meth) = ffi::PyBufferProcs::new::<T>() {
|
||||
static mut BUFFER_PROCS: ffi::PyBufferProcs = ffi::PyBufferProcs_INIT;
|
||||
*(unsafe { &mut BUFFER_PROCS }) = meth;
|
||||
type_object.tp_as_buffer = unsafe { &mut BUFFER_PROCS };
|
||||
} else {
|
||||
type_object.tp_as_buffer = 0 as *mut ffi::PyBufferProcs;
|
||||
}
|
||||
|
||||
// normal methods
|
||||
let mut methods = class::methods::py_class_method_defs::<T>();
|
||||
if !methods.is_empty() {
|
||||
methods.push(ffi::PyMethodDef_INIT);
|
||||
type_object.tp_methods = methods.as_ptr() as *mut _;
|
||||
|
||||
static mut METHODS: *const ffi::PyMethodDef = 0 as *const _;
|
||||
*(unsafe { &mut METHODS }) = methods.as_ptr();
|
||||
}
|
||||
|
||||
// register type object
|
||||
unsafe {
|
||||
if ffi::PyType_Ready(type_object) == 0 {
|
||||
Ok(PyType::from_type_ptr(py, type_object))
|
||||
} else {
|
||||
Err(PyErr::fetch(py))
|
||||
}
|
||||
impl PyGetterDef {
|
||||
pub fn copy_to(&self, dst: &mut ffi::PyGetSetDef) {
|
||||
if dst.name.is_null() {
|
||||
dst.name = CString::new(self.name).expect(
|
||||
"Method name must not contain NULL byte").into_raw();
|
||||
}
|
||||
dst.get = Some(self.meth.clone());
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe extern "C" fn tp_dealloc_callback<T>(obj: *mut ffi::PyObject)
|
||||
where T: py_class::BaseObject
|
||||
{
|
||||
let guard = AbortOnDrop("Cannot unwind out of tp_dealloc");
|
||||
let py = Python::assume_gil_acquired();
|
||||
let r = T::dealloc(py, obj);
|
||||
mem::forget(guard);
|
||||
r
|
||||
impl PySetterDef {
|
||||
pub fn copy_to(&self, dst: &mut ffi::PyGetSetDef) {
|
||||
if dst.name.is_null() {
|
||||
dst.name = CString::new(self.name).expect(
|
||||
"Method name must not contain NULL byte").into_raw();
|
||||
}
|
||||
dst.set = Some(self.meth.clone());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn py_class_method_defs<T>() -> Vec<ffi::PyMethodDef> {
|
||||
let mut defs = Vec::new();
|
||||
|
||||
for def in <T as class::context::PyContextProtocolImpl>::py_methods() {
|
||||
defs.push(def.as_method_def())
|
||||
}
|
||||
|
||||
for def in <T as class::number::PyNumberProtocolImpl>::py_methods() {
|
||||
defs.push(def.as_method_def())
|
||||
}
|
||||
|
||||
defs
|
||||
#[doc(hidden)]
|
||||
pub trait PyMethodsProtocolImpl {
|
||||
fn py_methods() -> &'static [PyMethodDefType];
|
||||
}
|
||||
|
||||
impl<T> PyMethodsProtocolImpl for T {
|
||||
default fn py_methods() -> &'static [PyMethodDefType] {
|
||||
NO_PY_METHODS
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ pub mod methods;
|
|||
pub mod number;
|
||||
pub mod gc;
|
||||
pub mod sequence;
|
||||
pub mod typeob;
|
||||
|
||||
pub use self::async::*;
|
||||
pub use self::buffer::*;
|
||||
|
@ -19,9 +20,10 @@ pub use self::number::PyNumberProtocol;
|
|||
pub use self::mapping::PyMappingProtocol;
|
||||
pub use self::sequence::PySequenceProtocol;
|
||||
|
||||
pub use self::methods::{PyMethodDef, PyMethodType};
|
||||
pub use self::methods::{PyMethodDef, PyMethodDefType, PyMethodType,
|
||||
PyGetterDef, PySetterDef};
|
||||
|
||||
use self::gc::PyGCProtocolImpl;
|
||||
|
||||
pub static NO_METHODS: &'static [&'static str] = &[];
|
||||
pub static NO_PY_METHODS: &'static [PyMethodDef] = &[];
|
||||
pub static NO_PY_METHODS: &'static [PyMethodDefType] = &[];
|
||||
|
|
|
@ -232,14 +232,14 @@ impl<T> PyNumberProtocol for T where T: PythonObject {
|
|||
#[doc(hidden)]
|
||||
pub trait PyNumberProtocolImpl {
|
||||
fn methods() -> &'static [&'static str];
|
||||
fn py_methods() -> &'static [::class::PyMethodDef];
|
||||
fn py_methods() -> &'static [::class::PyMethodDefType];
|
||||
}
|
||||
|
||||
impl<T> PyNumberProtocolImpl for T {
|
||||
default fn methods() -> &'static [&'static str] {
|
||||
NO_METHODS
|
||||
}
|
||||
default fn py_methods() -> &'static [::class::PyMethodDef] {
|
||||
default fn py_methods() -> &'static [::class::PyMethodDefType] {
|
||||
NO_PY_METHODS
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,202 @@
|
|||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
use std::ffi::CString;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use ::{ffi, exc, class, py_class, PyErr, Python, PyResult, PythonObject};
|
||||
use objects::PyType;
|
||||
use function::AbortOnDrop;
|
||||
use class::PyMethodDefType;
|
||||
|
||||
|
||||
pub trait PyClassInit {
|
||||
|
||||
fn init() -> bool;
|
||||
|
||||
fn type_object() -> &'static mut ffi::PyTypeObject;
|
||||
|
||||
fn build_type(py: Python,
|
||||
module_name: Option<&str>,
|
||||
type_object: &mut ffi::PyTypeObject) -> PyResult<PyType>;
|
||||
|
||||
}
|
||||
|
||||
impl<T> PyClassInit for T where T: PythonObject + py_class::BaseObject {
|
||||
|
||||
default fn init() -> bool { false }
|
||||
|
||||
default fn type_object() -> &'static mut ffi::PyTypeObject {
|
||||
static mut TYPE_OBJECT: ffi::PyTypeObject = ffi::PyTypeObject_INIT;
|
||||
unsafe {
|
||||
&mut TYPE_OBJECT
|
||||
}
|
||||
}
|
||||
|
||||
default fn build_type(py: Python, module_name: Option<&str>,
|
||||
type_object: &mut ffi::PyTypeObject) -> PyResult<PyType> {
|
||||
// type name
|
||||
let name = match module_name {
|
||||
Some(module_name) => CString::new(
|
||||
format!("{}.{}", module_name, stringify!(type_name))),
|
||||
None => CString::new(stringify!(type_name))
|
||||
};
|
||||
let name = name.expect(
|
||||
"Module name/type name must not contain NUL byte").into_raw();
|
||||
|
||||
type_object.tp_name = name;
|
||||
|
||||
// dealloc
|
||||
type_object.tp_dealloc = Some(tp_dealloc_callback::<T>);
|
||||
|
||||
// GC support
|
||||
<T as class::gc::PyGCProtocolImpl>::update_type_object(type_object);
|
||||
|
||||
// type size
|
||||
type_object.tp_basicsize = <T as py_class::BaseObject>::size() as ffi::Py_ssize_t;
|
||||
|
||||
// number methods
|
||||
if let Some(meth) = ffi::PyNumberMethods::new::<T>() {
|
||||
static mut NB_METHODS: ffi::PyNumberMethods = ffi::PyNumberMethods_INIT;
|
||||
*(unsafe { &mut NB_METHODS }) = meth;
|
||||
type_object.tp_as_number = unsafe { &mut NB_METHODS };
|
||||
} else {
|
||||
type_object.tp_as_number = 0 as *mut ffi::PyNumberMethods;
|
||||
}
|
||||
|
||||
// mapping methods
|
||||
if let Some(meth) = ffi::PyMappingMethods::new::<T>() {
|
||||
static mut MP_METHODS: ffi::PyMappingMethods = ffi::PyMappingMethods_INIT;
|
||||
*(unsafe { &mut MP_METHODS }) = meth;
|
||||
type_object.tp_as_mapping = unsafe { &mut MP_METHODS };
|
||||
} else {
|
||||
type_object.tp_as_mapping = 0 as *mut ffi::PyMappingMethods;
|
||||
}
|
||||
|
||||
// sequence methods
|
||||
if let Some(meth) = ffi::PySequenceMethods::new::<T>() {
|
||||
static mut SQ_METHODS: ffi::PySequenceMethods = ffi::PySequenceMethods_INIT;
|
||||
*(unsafe { &mut SQ_METHODS }) = meth;
|
||||
type_object.tp_as_sequence = unsafe { &mut SQ_METHODS };
|
||||
} else {
|
||||
type_object.tp_as_sequence = 0 as *mut ffi::PySequenceMethods;
|
||||
}
|
||||
|
||||
// async methods
|
||||
if let Some(meth) = ffi::PyAsyncMethods::new::<T>() {
|
||||
static mut ASYNC_METHODS: ffi::PyAsyncMethods = ffi::PyAsyncMethods_INIT;
|
||||
*(unsafe { &mut ASYNC_METHODS }) = meth;
|
||||
type_object.tp_as_async = unsafe { &mut ASYNC_METHODS };
|
||||
} else {
|
||||
type_object.tp_as_async = 0 as *mut ffi::PyAsyncMethods;
|
||||
}
|
||||
|
||||
// buffer protocol
|
||||
if let Some(meth) = ffi::PyBufferProcs::new::<T>() {
|
||||
static mut BUFFER_PROCS: ffi::PyBufferProcs = ffi::PyBufferProcs_INIT;
|
||||
*(unsafe { &mut BUFFER_PROCS }) = meth;
|
||||
type_object.tp_as_buffer = unsafe { &mut BUFFER_PROCS };
|
||||
} else {
|
||||
type_object.tp_as_buffer = 0 as *mut ffi::PyBufferProcs;
|
||||
}
|
||||
|
||||
// normal methods
|
||||
let mut methods = py_class_method_defs::<T>();
|
||||
if !methods.is_empty() {
|
||||
methods.push(ffi::PyMethodDef_INIT);
|
||||
type_object.tp_methods = methods.as_ptr() as *mut _;
|
||||
|
||||
static mut METHODS: *const ffi::PyMethodDef = 0 as *const _;
|
||||
*(unsafe { &mut METHODS }) = methods.as_ptr();
|
||||
}
|
||||
|
||||
// properties
|
||||
let mut props = py_class_properties::<T>();
|
||||
if !props.is_empty() {
|
||||
props.push(ffi::PyGetSetDef_INIT);
|
||||
let props = props.into_boxed_slice();
|
||||
type_object.tp_getset = props.as_ptr() as *mut _;
|
||||
|
||||
static mut PROPS: *const ffi::PyGetSetDef = 0 as *const _;
|
||||
*(unsafe { &mut PROPS }) = props.as_ptr();
|
||||
|
||||
// strange
|
||||
mem::forget(props);
|
||||
}
|
||||
|
||||
// register type object
|
||||
unsafe {
|
||||
if ffi::PyType_Ready(type_object) == 0 {
|
||||
Ok(PyType::from_type_ptr(py, type_object))
|
||||
} else {
|
||||
Err(PyErr::fetch(py))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe extern "C" fn tp_dealloc_callback<T>(obj: *mut ffi::PyObject)
|
||||
where T: py_class::BaseObject
|
||||
{
|
||||
let guard = AbortOnDrop("Cannot unwind out of tp_dealloc");
|
||||
let py = Python::assume_gil_acquired();
|
||||
let r = T::dealloc(py, obj);
|
||||
mem::forget(guard);
|
||||
r
|
||||
}
|
||||
|
||||
fn py_class_method_defs<T>() -> Vec<ffi::PyMethodDef> {
|
||||
let mut defs = Vec::new();
|
||||
|
||||
for def in <T as class::context::PyContextProtocolImpl>::py_methods() {
|
||||
match def {
|
||||
&PyMethodDefType::Method(ref def) => defs.push(def.as_method_def()),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
for def in <T as class::number::PyNumberProtocolImpl>::py_methods() {
|
||||
match def {
|
||||
&PyMethodDefType::Method(ref def) => defs.push(def.as_method_def()),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
for def in <T as class::methods::PyMethodsProtocolImpl>::py_methods() {
|
||||
match def {
|
||||
&PyMethodDefType::Method(ref def) => defs.push(def.as_method_def()),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
defs
|
||||
}
|
||||
|
||||
|
||||
fn py_class_properties<T>() -> Vec<ffi::PyGetSetDef> {
|
||||
let mut defs = HashMap::new();
|
||||
|
||||
for def in <T as class::methods::PyMethodsProtocolImpl>::py_methods() {
|
||||
match def {
|
||||
&PyMethodDefType::Getter(ref getter) => {
|
||||
let name = getter.name.to_string();
|
||||
if !defs.contains_key(&name) {
|
||||
let _ = defs.insert(name.clone(), ffi::PyGetSetDef_INIT);
|
||||
}
|
||||
let def = defs.get_mut(&name).unwrap();
|
||||
getter.copy_to(def);
|
||||
},
|
||||
&PyMethodDefType::Setter(ref setter) => {
|
||||
let name = setter.name.to_string();
|
||||
if !defs.contains_key(&name) {
|
||||
let _ = defs.insert(name.clone(), ffi::PyGetSetDef_INIT);
|
||||
}
|
||||
let def = defs.get_mut(&name).unwrap();
|
||||
setter.copy_to(def);
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
defs.values().map(|i| i.clone()).collect()
|
||||
}
|
|
@ -1,20 +1,19 @@
|
|||
use std::ptr;
|
||||
use std::os::raw::{c_void, c_char, c_int};
|
||||
use ffi::object::{PyObject, PyTypeObject};
|
||||
use ffi::structmember::PyMemberDef;
|
||||
use ffi::methodobject::PyMethodDef;
|
||||
|
||||
pub type getter =
|
||||
unsafe extern "C" fn
|
||||
(slf: *mut PyObject, closure: *mut c_void)
|
||||
-> *mut PyObject;
|
||||
unsafe extern "C" fn(slf: *mut PyObject, closure: *mut c_void)
|
||||
-> *mut PyObject;
|
||||
|
||||
pub type setter =
|
||||
unsafe extern "C" fn
|
||||
(slf: *mut PyObject, value: *mut PyObject,
|
||||
closure: *mut c_void) -> c_int;
|
||||
unsafe extern "C" fn(slf: *mut PyObject, value: *mut PyObject,
|
||||
closure: *mut c_void) -> c_int;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy)]
|
||||
#[derive(Copy, Debug)]
|
||||
pub struct PyGetSetDef {
|
||||
pub name: *mut c_char,
|
||||
pub get: Option<getter>,
|
||||
|
@ -23,6 +22,14 @@ pub struct PyGetSetDef {
|
|||
pub closure: *mut c_void,
|
||||
}
|
||||
|
||||
pub const PyGetSetDef_INIT : PyGetSetDef = PyGetSetDef {
|
||||
name: ptr::null_mut(),
|
||||
get: None,
|
||||
set: None,
|
||||
doc: ptr::null_mut(),
|
||||
closure: ptr::null_mut(),
|
||||
};
|
||||
|
||||
impl Clone for PyGetSetDef {
|
||||
#[inline] fn clone(&self) -> PyGetSetDef { *self }
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ mod py_class;
|
|||
mod py_class_impl;
|
||||
#[doc(hidden)] pub mod slots;
|
||||
#[doc(hidden)] pub mod members;
|
||||
#[doc(hidden)] pub mod properties;
|
||||
|
||||
use std::os::raw::c_void;
|
||||
use std::{mem, ptr, cell};
|
||||
|
|
|
@ -1,141 +0,0 @@
|
|||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||
|
||||
use ffi;
|
||||
|
||||
pub fn type_error_to_unit(py: ::Python, e: ::PyErr) -> ::PyResult<()> {
|
||||
if e.matches(py, py.get_type::<::exc::TypeError>()) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! py_class_init_properties {
|
||||
($class:ident, $py:ident, $type_object: ident, { }) => {{}};
|
||||
($class:ident, $py:ident, $type_object: ident, { $( $prop:expr; )+ }) =>
|
||||
{ unsafe {
|
||||
let mut defs = Vec::new();
|
||||
$(defs.push($prop);)+
|
||||
defs.push(
|
||||
$crate::_detail::ffi::PyGetSetDef {
|
||||
name: 0 as *mut $crate::_detail::libc::c_char,
|
||||
get: None,
|
||||
set: None,
|
||||
doc: 0 as *mut $crate::_detail::libc::c_char,
|
||||
closure: 0 as *mut $crate::_detail::libc::c_void,
|
||||
});
|
||||
let props = defs.into_boxed_slice();
|
||||
|
||||
$type_object.tp_getset =
|
||||
props.as_ptr() as *mut $crate::_detail::ffi::PyGetSetDef;
|
||||
|
||||
use std::mem;
|
||||
mem::forget(props);
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! py_class_property_impl {
|
||||
|
||||
({} $class:ident $py:ident $name:ident { $( $descr_name:ident = $descr_expr:expr; )* } ) =>
|
||||
{{
|
||||
let mut getset_def: $crate::_detail::ffi::PyGetSetDef =
|
||||
$crate::_detail::ffi::PyGetSetDef {
|
||||
name: 0 as *mut $crate::_detail::libc::c_char,
|
||||
get: None,
|
||||
set: None,
|
||||
doc: 0 as *mut $crate::_detail::libc::c_char,
|
||||
closure: 0 as *mut $crate::_detail::libc::c_void,
|
||||
};
|
||||
getset_def.name = concat!(stringify!($name), "\0").as_ptr() as *mut _;
|
||||
|
||||
$( getset_def.$descr_name = Some($descr_expr); )*
|
||||
|
||||
getset_def
|
||||
}};
|
||||
|
||||
( { get (&$slf:ident) -> $res_type:ty { $($body:tt)* } $($tail:tt)* }
|
||||
$class:ident $py:ident $name:ident { $( $descr_name:ident = $descr_expr:expr; )* } ) =>
|
||||
{
|
||||
py_class_property_impl!{
|
||||
{ $($tail)* } $class $py $name
|
||||
/* methods: */ {
|
||||
$( $descr_name = $descr_expr; )*
|
||||
get = {
|
||||
unsafe extern "C" fn wrap_getter_method(
|
||||
slf: *mut $crate::_detail::ffi::PyObject,
|
||||
_: *mut $crate::_detail::libc::c_void)
|
||||
-> *mut $crate::_detail::ffi::PyObject
|
||||
{
|
||||
const LOCATION: &'static str = concat!(
|
||||
stringify!($class), ".getter_", stringify!($name), "()");
|
||||
|
||||
fn get($slf: &$class, $py: $crate::Python) -> $res_type {
|
||||
$($body)*
|
||||
};
|
||||
|
||||
$crate::_detail::handle_callback(
|
||||
LOCATION, $crate::_detail::PyObjectCallbackConverter,
|
||||
|py| {
|
||||
let slf = $crate::PyObject::from_borrowed_ptr(
|
||||
py, slf).unchecked_cast_into::<$class>();
|
||||
let ret = get(&slf, py);
|
||||
$crate::PyDrop::release_ref(slf, py);
|
||||
ret
|
||||
})
|
||||
}
|
||||
wrap_getter_method
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
( { set(&$slf:ident, $value:ident : $value_type:ty)
|
||||
-> $res_type:ty { $( $body:tt )* } $($tail:tt)* }
|
||||
$class:ident $py:ident $name:ident { $( $descr_name:ident = $descr_expr:expr; )* } ) =>
|
||||
{
|
||||
py_class_property_impl! {
|
||||
{ $($tail)* } $class $py $name
|
||||
/* methods: */ {
|
||||
$( $descr_name = $descr_expr; )*
|
||||
set = {
|
||||
unsafe extern "C" fn wrap_setter_method(
|
||||
slf: *mut $crate::_detail::ffi::PyObject,
|
||||
value: *mut $crate::_detail::ffi::PyObject,
|
||||
_: *mut $crate::_detail::libc::c_void)
|
||||
-> $crate::_detail::libc::c_int
|
||||
{
|
||||
const LOCATION: &'static str = concat!(
|
||||
stringify!($class), ".setter_", stringify!($name), "()");
|
||||
|
||||
fn set($slf: &$class,
|
||||
$py: $crate::Python, $value: $value_type) -> $res_type {
|
||||
$($body)*
|
||||
};
|
||||
|
||||
$crate::_detail::handle_callback(
|
||||
LOCATION, $crate::py_class::slots::UnitCallbackConverter, move |py| {
|
||||
let slf = $crate::PyObject::from_borrowed_ptr(py, slf)
|
||||
.unchecked_cast_into::<$class>();
|
||||
let value = $crate::PyObject::from_borrowed_ptr(py, value);
|
||||
|
||||
let ret = match <$value_type as $crate::FromPyObject>::extract(py, &value) {
|
||||
Ok(value) => set(&slf, py, value),
|
||||
Err(e) =>
|
||||
$crate::py_class::properties::type_error_to_unit(py, e)
|
||||
};
|
||||
$crate::PyDrop::release_ref(slf, py);
|
||||
$crate::PyDrop::release_ref(value, py);
|
||||
ret
|
||||
})
|
||||
}
|
||||
wrap_setter_method
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
|
@ -126,8 +126,7 @@ macro_rules! py_class_impl {
|
|||
|
||||
fn init($py: $crate::Python, module_name: Option<&str>) -> $crate::PyResult<$crate::PyType> {
|
||||
py_class_init_members!($class, $py, TYPE_OBJECT, $members);
|
||||
py_class_init_properties!($class, $py, TYPE_OBJECT, $properties);
|
||||
unsafe { <$class as $crate::class::methods::PyClassInit>
|
||||
unsafe { <$class as $crate::class::typeob::PyClassInit>
|
||||
::build_type($py, module_name, &mut TYPE_OBJECT) }
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue