2017-05-16 05:24:06 +00:00
|
|
|
// Copyright (c) 2017-present PyO3 Project and Contributors
|
|
|
|
|
2019-02-01 13:01:18 +00:00
|
|
|
use crate::method::{FnArg, FnSpec, FnType};
|
2019-02-18 19:07:41 +00:00
|
|
|
use crate::pymethod::{impl_py_getter_def, impl_py_setter_def, impl_wrap_getter, impl_wrap_setter};
|
2019-02-01 13:01:18 +00:00
|
|
|
use crate::utils;
|
2018-07-03 20:28:40 +00:00
|
|
|
use proc_macro2::{Span, TokenStream};
|
2019-02-01 13:01:18 +00:00
|
|
|
use quote::quote;
|
2019-12-17 22:14:28 +00:00
|
|
|
use syn::ext::IdentExt;
|
2019-02-18 19:07:41 +00:00
|
|
|
use syn::parse::{Parse, ParseStream};
|
|
|
|
use syn::punctuated::Punctuated;
|
|
|
|
use syn::{parse_quote, Expr, Token};
|
|
|
|
|
|
|
|
/// The parsed arguments of the pyclass macro
|
|
|
|
pub struct PyClassArgs {
|
|
|
|
pub freelist: Option<syn::Expr>,
|
|
|
|
pub name: Option<syn::Expr>,
|
|
|
|
pub flags: Vec<syn::Expr>,
|
|
|
|
pub base: syn::TypePath,
|
2019-12-21 14:21:41 +00:00
|
|
|
pub has_extends: bool,
|
2019-06-03 03:18:44 +00:00
|
|
|
pub module: Option<syn::LitStr>,
|
2019-02-18 19:07:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Parse for PyClassArgs {
|
|
|
|
fn parse(input: ParseStream) -> syn::parse::Result<Self> {
|
|
|
|
let mut slf = PyClassArgs::default();
|
|
|
|
|
|
|
|
let vars = Punctuated::<Expr, Token![,]>::parse_terminated(input)?;
|
|
|
|
for expr in vars {
|
|
|
|
slf.add_expr(&expr)?;
|
|
|
|
}
|
|
|
|
Ok(slf)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for PyClassArgs {
|
|
|
|
fn default() -> Self {
|
|
|
|
PyClassArgs {
|
|
|
|
freelist: None,
|
|
|
|
name: None,
|
2019-06-03 03:18:44 +00:00
|
|
|
module: None,
|
2019-02-18 19:07:41 +00:00
|
|
|
// We need the 0 as value for the constant we're later building using quote for when there
|
|
|
|
// are no other flags
|
2019-12-21 14:21:41 +00:00
|
|
|
flags: vec![parse_quote! { 0 }],
|
|
|
|
base: parse_quote! { pyo3::types::PyAny },
|
|
|
|
has_extends: false,
|
2019-02-18 19:07:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PyClassArgs {
|
|
|
|
/// Adda single expression from the comma separated list in the attribute, which is
|
|
|
|
/// either a single word or an assignment expression
|
|
|
|
fn add_expr(&mut self, expr: &Expr) -> syn::parse::Result<()> {
|
|
|
|
match expr {
|
|
|
|
syn::Expr::Path(ref exp) if exp.path.segments.len() == 1 => self.add_path(exp),
|
|
|
|
syn::Expr::Assign(ref assign) => self.add_assign(assign),
|
|
|
|
_ => Err(syn::Error::new_spanned(expr, "Could not parse arguments")),
|
|
|
|
}
|
|
|
|
}
|
2018-05-13 19:24:40 +00:00
|
|
|
|
2019-02-18 19:07:41 +00:00
|
|
|
/// Match a single flag
|
|
|
|
fn add_assign(&mut self, assign: &syn::ExprAssign) -> syn::Result<()> {
|
|
|
|
let key = match *assign.left {
|
|
|
|
syn::Expr::Path(ref exp) if exp.path.segments.len() == 1 => {
|
2019-08-11 12:16:02 +00:00
|
|
|
exp.path.segments.first().unwrap().ident.to_string()
|
2019-02-18 19:07:41 +00:00
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
return Err(syn::Error::new_spanned(assign, "could not parse argument"));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
match key.as_str() {
|
|
|
|
"freelist" => {
|
|
|
|
// We allow arbitrary expressions here so you can e.g. use `8*64`
|
|
|
|
self.freelist = Some(*assign.right.clone());
|
|
|
|
}
|
|
|
|
"name" => match *assign.right {
|
|
|
|
syn::Expr::Path(ref exp) if exp.path.segments.len() == 1 => {
|
|
|
|
self.name = Some(exp.clone().into());
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
return Err(syn::Error::new_spanned(
|
|
|
|
*assign.right.clone(),
|
|
|
|
"Wrong 'name' format",
|
|
|
|
));
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"extends" => match *assign.right {
|
|
|
|
syn::Expr::Path(ref exp) => {
|
|
|
|
self.base = syn::TypePath {
|
|
|
|
path: exp.path.clone(),
|
|
|
|
qself: None,
|
|
|
|
};
|
2019-12-21 14:21:41 +00:00
|
|
|
self.has_extends = true;
|
2019-02-18 19:07:41 +00:00
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
return Err(syn::Error::new_spanned(
|
|
|
|
*assign.right.clone(),
|
|
|
|
"Wrong format for extends",
|
|
|
|
));
|
|
|
|
}
|
|
|
|
},
|
2019-06-03 03:18:44 +00:00
|
|
|
"module" => match *assign.right {
|
|
|
|
syn::Expr::Lit(syn::ExprLit {
|
|
|
|
lit: syn::Lit::Str(ref lit),
|
|
|
|
..
|
|
|
|
}) => {
|
|
|
|
self.module = Some(lit.clone());
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
return Err(syn::Error::new_spanned(
|
|
|
|
*assign.right.clone(),
|
|
|
|
"Wrong format for module",
|
|
|
|
));
|
|
|
|
}
|
|
|
|
},
|
2019-02-18 19:07:41 +00:00
|
|
|
_ => {
|
|
|
|
return Err(syn::Error::new_spanned(
|
|
|
|
*assign.left.clone(),
|
|
|
|
"Unsupported parameter",
|
|
|
|
));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Match a key/value flag
|
|
|
|
fn add_path(&mut self, exp: &syn::ExprPath) -> syn::Result<()> {
|
2019-08-11 12:16:02 +00:00
|
|
|
let flag = exp.path.segments.first().unwrap().ident.to_string();
|
2019-02-18 19:07:41 +00:00
|
|
|
let path = match flag.as_str() {
|
|
|
|
"gc" => {
|
2019-12-08 08:18:25 +00:00
|
|
|
parse_quote! {pyo3::type_flags::GC}
|
2019-02-18 19:07:41 +00:00
|
|
|
}
|
|
|
|
"weakref" => {
|
2019-12-08 08:18:25 +00:00
|
|
|
parse_quote! {pyo3::type_flags::WEAKREF}
|
2019-02-18 19:07:41 +00:00
|
|
|
}
|
|
|
|
"subclass" => {
|
2019-12-08 08:18:25 +00:00
|
|
|
parse_quote! {pyo3::type_flags::BASETYPE}
|
2019-02-18 19:07:41 +00:00
|
|
|
}
|
|
|
|
"dict" => {
|
2019-12-08 08:18:25 +00:00
|
|
|
parse_quote! {pyo3::type_flags::DICT}
|
2019-02-18 19:07:41 +00:00
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
return Err(syn::Error::new_spanned(
|
|
|
|
exp.path.clone(),
|
|
|
|
"Unsupported parameter",
|
|
|
|
));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
self.flags.push(syn::Expr::Path(path));
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn build_py_class(class: &mut syn::ItemStruct, attr: &PyClassArgs) -> syn::Result<TokenStream> {
|
2019-11-24 14:00:21 +00:00
|
|
|
let text_signature = utils::parse_text_signature_attrs(
|
|
|
|
&mut class.attrs,
|
|
|
|
&get_class_python_name(&class.ident, attr),
|
|
|
|
)?;
|
|
|
|
let doc = utils::get_doc(&class.attrs, text_signature, true)?;
|
2017-08-05 02:53:23 +00:00
|
|
|
let mut descriptors = Vec::new();
|
2018-05-13 19:24:40 +00:00
|
|
|
|
2019-06-04 05:17:07 +00:00
|
|
|
check_generics(class)?;
|
2018-07-04 17:07:27 +00:00
|
|
|
if let syn::Fields::Named(ref mut fields) = class.fields {
|
|
|
|
for field in fields.named.iter_mut() {
|
2019-02-18 19:07:41 +00:00
|
|
|
let field_descs = parse_descriptors(field)?;
|
2018-11-12 13:15:11 +00:00
|
|
|
if !field_descs.is_empty() {
|
|
|
|
descriptors.push((field.clone(), field_descs));
|
2017-05-28 05:45:48 +00:00
|
|
|
}
|
2018-05-13 19:24:40 +00:00
|
|
|
}
|
|
|
|
} else {
|
2019-02-18 19:07:41 +00:00
|
|
|
return Err(syn::Error::new_spanned(
|
|
|
|
&class.fields,
|
|
|
|
"#[pyclass] can only be used with C-style structs",
|
|
|
|
));
|
2017-05-17 06:43:39 +00:00
|
|
|
}
|
|
|
|
|
2019-12-17 17:36:56 +00:00
|
|
|
impl_class(&class.ident, &attr, doc, descriptors)
|
2017-05-16 05:24:06 +00:00
|
|
|
}
|
|
|
|
|
2019-02-18 19:07:41 +00:00
|
|
|
/// Parses `#[pyo3(get, set)]`
|
|
|
|
fn parse_descriptors(item: &mut syn::Field) -> syn::Result<Vec<FnType>> {
|
2017-08-05 02:53:23 +00:00
|
|
|
let mut descs = Vec::new();
|
|
|
|
let mut new_attrs = Vec::new();
|
|
|
|
for attr in item.attrs.iter() {
|
2019-02-18 19:07:41 +00:00
|
|
|
if let Ok(syn::Meta::List(ref list)) = attr.parse_meta() {
|
2019-08-11 12:16:02 +00:00
|
|
|
if list.path.is_ident("pyo3") {
|
|
|
|
for meta in list.nested.iter() {
|
|
|
|
if let syn::NestedMeta::Meta(ref metaitem) = meta {
|
|
|
|
if metaitem.path().is_ident("get") {
|
2019-12-17 17:36:56 +00:00
|
|
|
descs.push(FnType::Getter);
|
2019-08-11 12:16:02 +00:00
|
|
|
} else if metaitem.path().is_ident("set") {
|
2019-12-17 17:36:56 +00:00
|
|
|
descs.push(FnType::Setter);
|
2019-08-11 12:16:02 +00:00
|
|
|
} else {
|
|
|
|
return Err(syn::Error::new_spanned(
|
|
|
|
metaitem,
|
|
|
|
"Only get and set are supported",
|
|
|
|
));
|
2017-08-05 02:53:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-08-11 12:16:02 +00:00
|
|
|
} else {
|
|
|
|
new_attrs.push(attr.clone())
|
2017-08-05 02:53:23 +00:00
|
|
|
}
|
2018-05-13 19:24:40 +00:00
|
|
|
} else {
|
|
|
|
new_attrs.push(attr.clone());
|
2017-08-05 02:53:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
item.attrs.clear();
|
|
|
|
item.attrs.extend(new_attrs);
|
2019-02-18 19:07:41 +00:00
|
|
|
Ok(descs)
|
2017-08-05 02:53:23 +00:00
|
|
|
}
|
|
|
|
|
2019-01-30 14:26:45 +00:00
|
|
|
/// The orphan rule disallows using a generic inventory struct, so we create the whole boilerplate
|
|
|
|
/// once per class
|
|
|
|
fn impl_inventory(cls: &syn::Ident) -> TokenStream {
|
|
|
|
// Try to build a unique type that gives a hint about it's function when
|
|
|
|
// it comes up in error messages
|
|
|
|
let name = cls.to_string() + "GeneratedPyo3Inventory";
|
|
|
|
let inventory_cls = syn::Ident::new(&name, Span::call_site());
|
|
|
|
|
|
|
|
quote! {
|
|
|
|
#[doc(hidden)]
|
|
|
|
pub struct #inventory_cls {
|
2019-03-16 10:40:56 +00:00
|
|
|
methods: &'static [pyo3::class::PyMethodDefType],
|
2019-01-30 14:26:45 +00:00
|
|
|
}
|
|
|
|
|
2019-03-16 10:40:56 +00:00
|
|
|
impl pyo3::class::methods::PyMethodsInventory for #inventory_cls {
|
|
|
|
fn new(methods: &'static [pyo3::class::PyMethodDefType]) -> Self {
|
2019-01-30 14:26:45 +00:00
|
|
|
Self {
|
|
|
|
methods
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-16 10:40:56 +00:00
|
|
|
fn get_methods(&self) -> &'static [pyo3::class::PyMethodDefType] {
|
2019-01-30 14:26:45 +00:00
|
|
|
self.methods
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-16 10:40:56 +00:00
|
|
|
impl pyo3::class::methods::PyMethodsInventoryDispatch for #cls {
|
2019-01-30 14:26:45 +00:00
|
|
|
type InventoryType = #inventory_cls;
|
|
|
|
}
|
|
|
|
|
2019-03-16 10:40:56 +00:00
|
|
|
pyo3::inventory::collect!(#inventory_cls);
|
2019-01-30 14:26:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-24 14:00:21 +00:00
|
|
|
fn get_class_python_name(cls: &syn::Ident, attr: &PyClassArgs) -> TokenStream {
|
|
|
|
match &attr.name {
|
|
|
|
Some(name) => quote! { #name },
|
|
|
|
None => quote! { #cls },
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-13 19:24:40 +00:00
|
|
|
fn impl_class(
|
|
|
|
cls: &syn::Ident,
|
2019-02-18 19:07:41 +00:00
|
|
|
attr: &PyClassArgs,
|
2019-12-17 17:36:56 +00:00
|
|
|
doc: syn::LitStr,
|
2018-07-03 20:28:40 +00:00
|
|
|
descriptors: Vec<(syn::Field, Vec<FnType>)>,
|
2019-12-17 17:36:56 +00:00
|
|
|
) -> syn::Result<TokenStream> {
|
2019-11-27 22:31:16 +00:00
|
|
|
let cls_name = get_class_python_name(cls, attr).to_string();
|
2017-05-18 23:57:39 +00:00
|
|
|
|
2017-06-09 21:27:37 +00:00
|
|
|
let extra = {
|
2019-02-18 19:07:41 +00:00
|
|
|
if let Some(freelist) = &attr.freelist {
|
2018-11-12 13:58:16 +00:00
|
|
|
quote! {
|
2019-12-08 08:18:25 +00:00
|
|
|
impl pyo3::freelist::PyClassWithFreeList for #cls {
|
2017-06-09 21:27:37 +00:00
|
|
|
#[inline]
|
2019-03-16 10:40:56 +00:00
|
|
|
fn get_free_list() -> &'static mut pyo3::freelist::FreeList<*mut pyo3::ffi::PyObject> {
|
|
|
|
static mut FREELIST: *mut pyo3::freelist::FreeList<*mut pyo3::ffi::PyObject> = 0 as *mut _;
|
2017-06-09 21:27:37 +00:00
|
|
|
unsafe {
|
|
|
|
if FREELIST.is_null() {
|
|
|
|
FREELIST = Box::into_raw(Box::new(
|
2019-03-16 10:40:56 +00:00
|
|
|
pyo3::freelist::FreeList::with_capacity(#freelist)));
|
2017-06-09 21:27:37 +00:00
|
|
|
}
|
2017-07-18 14:10:56 +00:00
|
|
|
&mut *FREELIST
|
2017-06-09 21:27:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-11-12 13:58:16 +00:00
|
|
|
}
|
2017-06-09 21:27:37 +00:00
|
|
|
} else {
|
2018-11-12 13:58:16 +00:00
|
|
|
quote! {
|
2019-12-08 08:18:25 +00:00
|
|
|
impl pyo3::pyclass::PyClassAlloc for #cls {}
|
2018-11-12 13:58:16 +00:00
|
|
|
}
|
2017-06-09 21:27:37 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-08-05 02:53:23 +00:00
|
|
|
let extra = if !descriptors.is_empty() {
|
2019-04-13 23:47:19 +00:00
|
|
|
let path = syn::Path::from(syn::PathSegment::from(cls.clone()));
|
|
|
|
let ty = syn::Type::from(syn::TypePath { path, qself: None });
|
2019-12-17 17:36:56 +00:00
|
|
|
let desc_impls = impl_descriptors(&ty, descriptors)?;
|
2018-11-12 13:58:16 +00:00
|
|
|
quote! {
|
2017-08-05 02:53:23 +00:00
|
|
|
#desc_impls
|
|
|
|
#extra
|
2018-11-12 13:58:16 +00:00
|
|
|
}
|
2017-08-05 02:53:23 +00:00
|
|
|
} else {
|
|
|
|
extra
|
|
|
|
};
|
|
|
|
|
2017-07-24 20:03:18 +00:00
|
|
|
// insert space for weak ref
|
|
|
|
let mut has_weakref = false;
|
2017-07-28 14:21:59 +00:00
|
|
|
let mut has_dict = false;
|
2019-07-14 14:35:06 +00:00
|
|
|
let mut has_gc = false;
|
2019-02-18 19:07:41 +00:00
|
|
|
for f in attr.flags.iter() {
|
2018-05-13 19:24:40 +00:00
|
|
|
if let syn::Expr::Path(ref epath) = f {
|
2019-12-08 08:18:25 +00:00
|
|
|
if epath.path == parse_quote! { pyo3::type_flags::WEAKREF } {
|
2018-05-13 19:24:40 +00:00
|
|
|
has_weakref = true;
|
2019-12-08 08:18:25 +00:00
|
|
|
} else if epath.path == parse_quote! { pyo3::type_flags::DICT } {
|
2018-05-13 19:24:40 +00:00
|
|
|
has_dict = true;
|
2019-12-08 08:18:25 +00:00
|
|
|
} else if epath.path == parse_quote! { pyo3::type_flags::GC } {
|
2019-07-14 14:35:06 +00:00
|
|
|
has_gc = true;
|
2018-05-13 19:24:40 +00:00
|
|
|
}
|
2017-07-24 20:03:18 +00:00
|
|
|
}
|
|
|
|
}
|
2019-12-21 14:21:41 +00:00
|
|
|
|
2017-07-24 20:03:18 +00:00
|
|
|
let weakref = if has_weakref {
|
2019-12-08 08:18:25 +00:00
|
|
|
quote! { type WeakRef = pyo3::pyclass_slots::PyClassWeakRefSlot; }
|
2017-07-24 20:03:18 +00:00
|
|
|
} else {
|
2019-12-08 08:18:25 +00:00
|
|
|
quote! { type WeakRef = pyo3::pyclass_slots::PyClassDummySlot; }
|
2017-07-24 20:03:18 +00:00
|
|
|
};
|
2017-07-28 14:21:59 +00:00
|
|
|
let dict = if has_dict {
|
2019-12-08 08:18:25 +00:00
|
|
|
quote! { type Dict = pyo3::pyclass_slots::PyClassDictSlot; }
|
2017-07-28 14:21:59 +00:00
|
|
|
} else {
|
2019-12-08 08:18:25 +00:00
|
|
|
quote! { type Dict = pyo3::pyclass_slots::PyClassDummySlot; }
|
2017-07-28 14:21:59 +00:00
|
|
|
};
|
2019-06-03 03:18:44 +00:00
|
|
|
let module = if let Some(m) = &attr.module {
|
|
|
|
quote! { Some(#m) }
|
|
|
|
} else {
|
|
|
|
quote! { None }
|
|
|
|
};
|
2017-07-24 20:03:18 +00:00
|
|
|
|
2019-07-14 14:35:06 +00:00
|
|
|
// Enforce at compile time that PyGCProtocol is implemented
|
|
|
|
let gc_impl = if has_gc {
|
|
|
|
let closure_name = format!("__assertion_closure_{}", cls.to_string());
|
|
|
|
let closure_token = syn::Ident::new(&closure_name, Span::call_site());
|
|
|
|
quote! {
|
|
|
|
fn #closure_token() {
|
|
|
|
use pyo3::class;
|
|
|
|
|
|
|
|
fn _assert_implements_protocol<'p, T: pyo3::class::PyGCProtocol<'p>>() {}
|
|
|
|
_assert_implements_protocol::<#cls>();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
quote! {}
|
|
|
|
};
|
|
|
|
|
2019-01-30 14:26:45 +00:00
|
|
|
let inventory_impl = impl_inventory(&cls);
|
|
|
|
|
2019-02-18 19:07:41 +00:00
|
|
|
let base = &attr.base;
|
|
|
|
let flags = &attr.flags;
|
2019-12-21 14:21:41 +00:00
|
|
|
let extended = if attr.has_extends {
|
|
|
|
quote! { pyo3::type_flags::EXTENDED }
|
|
|
|
} else {
|
|
|
|
quote! { 0 }
|
|
|
|
};
|
2019-02-18 19:07:41 +00:00
|
|
|
|
2020-01-05 07:01:05 +00:00
|
|
|
// If #cls is not extended type, we allow Self->PyObject conversion
|
|
|
|
let into_pyobject = if !attr.has_extends {
|
|
|
|
quote! {
|
|
|
|
impl pyo3::IntoPy<PyObject> for #cls {
|
|
|
|
fn into_py(self, py: pyo3::Python) -> pyo3::PyObject {
|
|
|
|
pyo3::IntoPy::into_py(pyo3::Py::new(py, self).unwrap(), py)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
quote! {}
|
|
|
|
};
|
|
|
|
|
2019-12-17 17:36:56 +00:00
|
|
|
Ok(quote! {
|
2020-01-30 13:13:42 +00:00
|
|
|
unsafe impl pyo3::type_object::PyTypeInfo for #cls {
|
2017-05-23 06:19:14 +00:00
|
|
|
type Type = #cls;
|
2017-07-28 02:47:01 +00:00
|
|
|
type BaseType = #base;
|
2019-12-08 08:18:25 +00:00
|
|
|
type ConcreteLayout = pyo3::pyclass::PyClassShell<Self>;
|
2020-01-05 07:01:05 +00:00
|
|
|
type Initializer = pyo3::pyclass_init::PyClassInitializer<Self>;
|
2017-07-28 02:47:01 +00:00
|
|
|
|
2017-07-13 23:45:50 +00:00
|
|
|
const NAME: &'static str = #cls_name;
|
2019-06-03 03:18:44 +00:00
|
|
|
const MODULE: Option<&'static str> = #module;
|
2017-07-13 23:45:50 +00:00
|
|
|
const DESCRIPTION: &'static str = #doc;
|
2019-12-21 14:21:41 +00:00
|
|
|
const FLAGS: usize = #(#flags)|* | #extended;
|
2017-05-23 06:19:14 +00:00
|
|
|
|
2017-05-17 06:43:39 +00:00
|
|
|
#[inline]
|
2020-01-30 13:13:42 +00:00
|
|
|
fn type_object() -> std::ptr::NonNull<pyo3::ffi::PyTypeObject> {
|
|
|
|
use pyo3::type_object::LazyTypeObject;
|
|
|
|
static TYPE_OBJECT: LazyTypeObject = LazyTypeObject::new();
|
2020-02-03 07:34:42 +00:00
|
|
|
TYPE_OBJECT.get_pyclass_type::<Self>()
|
2017-05-17 06:43:39 +00:00
|
|
|
}
|
|
|
|
}
|
2017-05-25 05:43:07 +00:00
|
|
|
|
2019-12-08 08:18:25 +00:00
|
|
|
impl pyo3::PyClass for #cls {
|
|
|
|
#dict
|
|
|
|
#weakref
|
|
|
|
}
|
|
|
|
|
2020-01-13 22:56:16 +00:00
|
|
|
impl pyo3::conversion::FromPyObjectImpl for #cls {
|
|
|
|
type Impl = pyo3::conversion::extract_impl::Cloned;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl pyo3::conversion::FromPyObjectImpl for &'_ #cls {
|
|
|
|
type Impl = pyo3::conversion::extract_impl::Reference;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl pyo3::conversion::FromPyObjectImpl for &'_ mut #cls {
|
|
|
|
type Impl = pyo3::conversion::extract_impl::MutReference;
|
|
|
|
}
|
|
|
|
|
2020-01-05 07:01:05 +00:00
|
|
|
#into_pyobject
|
2018-09-06 16:04:13 +00:00
|
|
|
|
2019-01-30 14:26:45 +00:00
|
|
|
#inventory_impl
|
|
|
|
|
2017-05-28 05:45:48 +00:00
|
|
|
#extra
|
2019-07-14 14:35:06 +00:00
|
|
|
|
|
|
|
#gc_impl
|
|
|
|
|
2019-12-17 17:36:56 +00:00
|
|
|
})
|
2017-05-16 05:24:06 +00:00
|
|
|
}
|
2017-06-01 16:45:00 +00:00
|
|
|
|
2019-12-17 17:36:56 +00:00
|
|
|
fn impl_descriptors(
|
|
|
|
cls: &syn::Type,
|
|
|
|
descriptors: Vec<(syn::Field, Vec<FnType>)>,
|
|
|
|
) -> syn::Result<TokenStream> {
|
2018-07-03 20:28:40 +00:00
|
|
|
let methods: Vec<TokenStream> = descriptors
|
|
|
|
.iter()
|
|
|
|
.flat_map(|&(ref field, ref fns)| {
|
|
|
|
fns.iter()
|
|
|
|
.map(|desc| {
|
2020-01-27 08:13:55 +00:00
|
|
|
let name = field.ident.as_ref().unwrap();
|
2018-07-03 20:28:40 +00:00
|
|
|
let field_ty = &field.ty;
|
|
|
|
match *desc {
|
2019-12-17 17:36:56 +00:00
|
|
|
FnType::Getter => {
|
2018-07-03 20:28:40 +00:00
|
|
|
quote! {
|
|
|
|
impl #cls {
|
2019-03-16 10:40:56 +00:00
|
|
|
fn #name(&self) -> pyo3::PyResult<#field_ty> {
|
2018-07-03 20:28:40 +00:00
|
|
|
Ok(self.#name.clone())
|
|
|
|
}
|
|
|
|
}
|
2017-08-05 02:53:23 +00:00
|
|
|
}
|
|
|
|
}
|
2019-12-17 17:36:56 +00:00
|
|
|
FnType::Setter => {
|
2018-07-03 20:28:40 +00:00
|
|
|
let setter_name =
|
2020-01-27 08:13:55 +00:00
|
|
|
syn::Ident::new(&format!("set_{}", name.unraw()), Span::call_site());
|
2018-07-03 20:28:40 +00:00
|
|
|
quote! {
|
|
|
|
impl #cls {
|
2019-03-16 10:40:56 +00:00
|
|
|
fn #setter_name(&mut self, value: #field_ty) -> pyo3::PyResult<()> {
|
2018-07-03 20:28:40 +00:00
|
|
|
self.#name = value;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
2017-08-05 02:53:23 +00:00
|
|
|
}
|
|
|
|
}
|
2018-07-03 20:28:40 +00:00
|
|
|
_ => unreachable!(),
|
2017-08-05 02:53:23 +00:00
|
|
|
}
|
2018-07-03 20:28:40 +00:00
|
|
|
})
|
|
|
|
.collect::<Vec<TokenStream>>()
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
let py_methods: Vec<TokenStream> = descriptors
|
|
|
|
.iter()
|
|
|
|
.flat_map(|&(ref field, ref fns)| {
|
|
|
|
fns.iter()
|
|
|
|
.map(|desc| {
|
2020-01-27 08:13:55 +00:00
|
|
|
let name = field.ident.as_ref().unwrap();
|
2018-07-03 20:28:40 +00:00
|
|
|
|
2020-02-03 08:01:30 +00:00
|
|
|
let doc = utils::get_doc(&field.attrs, None, true)
|
|
|
|
.unwrap_or_else(|_| syn::LitStr::new(&name.to_string(), name.span()));
|
2018-07-03 20:28:40 +00:00
|
|
|
|
|
|
|
let field_ty = &field.ty;
|
|
|
|
match *desc {
|
2019-12-17 17:36:56 +00:00
|
|
|
FnType::Getter => {
|
|
|
|
let spec = FnSpec {
|
|
|
|
tp: FnType::Getter,
|
|
|
|
name: &name,
|
2019-12-17 22:14:28 +00:00
|
|
|
python_name: name.unraw(),
|
2019-12-17 17:36:56 +00:00
|
|
|
attrs: Vec::new(),
|
|
|
|
args: Vec::new(),
|
|
|
|
output: parse_quote!(PyResult<#field_ty>),
|
|
|
|
doc,
|
|
|
|
};
|
|
|
|
Ok(impl_py_getter_def(&spec, &impl_wrap_getter(&cls, &spec)?))
|
|
|
|
}
|
|
|
|
FnType::Setter => {
|
2020-01-27 08:13:55 +00:00
|
|
|
let setter_name = syn::Ident::new(
|
|
|
|
&format!("set_{}", name.unraw()),
|
|
|
|
Span::call_site(),
|
|
|
|
);
|
2018-07-03 20:28:40 +00:00
|
|
|
let spec = FnSpec {
|
2019-12-17 17:36:56 +00:00
|
|
|
tp: FnType::Setter,
|
|
|
|
name: &setter_name,
|
2019-12-17 22:14:28 +00:00
|
|
|
python_name: name.unraw(),
|
2018-07-03 20:28:40 +00:00
|
|
|
attrs: Vec::new(),
|
|
|
|
args: vec![FnArg {
|
|
|
|
name: &name,
|
|
|
|
mutability: &None,
|
|
|
|
by_ref: &None,
|
|
|
|
ty: field_ty,
|
|
|
|
optional: None,
|
|
|
|
py: true,
|
|
|
|
reference: false,
|
|
|
|
}],
|
2019-02-18 19:07:41 +00:00
|
|
|
output: parse_quote!(PyResult<()>),
|
2018-07-03 20:28:40 +00:00
|
|
|
doc,
|
2019-12-17 17:36:56 +00:00
|
|
|
};
|
|
|
|
Ok(impl_py_setter_def(&spec, &impl_wrap_setter(&cls, &spec)?))
|
2018-07-03 20:28:40 +00:00
|
|
|
}
|
|
|
|
_ => unreachable!(),
|
|
|
|
}
|
2018-09-28 21:34:57 +00:00
|
|
|
})
|
2019-12-17 17:36:56 +00:00
|
|
|
.collect::<Vec<syn::Result<TokenStream>>>()
|
2018-09-28 21:34:57 +00:00
|
|
|
})
|
2019-12-17 17:36:56 +00:00
|
|
|
.collect::<syn::Result<_>>()?;
|
2017-08-05 02:53:23 +00:00
|
|
|
|
2019-12-17 17:36:56 +00:00
|
|
|
Ok(quote! {
|
2017-08-05 02:53:23 +00:00
|
|
|
#(#methods)*
|
|
|
|
|
2019-03-16 10:40:56 +00:00
|
|
|
pyo3::inventory::submit! {
|
2019-02-01 13:49:25 +00:00
|
|
|
#![crate = pyo3] {
|
2019-03-16 10:40:56 +00:00
|
|
|
type ClsInventory = <#cls as pyo3::class::methods::PyMethodsInventoryDispatch>::InventoryType;
|
|
|
|
<ClsInventory as pyo3::class::methods::PyMethodsInventory>::new(&[#(#py_methods),*])
|
2017-08-05 02:53:23 +00:00
|
|
|
}
|
|
|
|
}
|
2019-12-17 17:36:56 +00:00
|
|
|
})
|
2017-08-05 02:53:23 +00:00
|
|
|
}
|
2019-06-04 05:17:07 +00:00
|
|
|
|
|
|
|
fn check_generics(class: &mut syn::ItemStruct) -> syn::Result<()> {
|
|
|
|
if class.generics.params.is_empty() {
|
|
|
|
Ok(())
|
|
|
|
} else {
|
|
|
|
Err(syn::Error::new_spanned(
|
|
|
|
&class.generics,
|
|
|
|
"#[pyclass] cannot have generic parameters",
|
|
|
|
))
|
|
|
|
}
|
|
|
|
}
|