diff --git a/derive/src/pyclass.rs b/derive/src/pyclass.rs index ffa793029..1d6474048 100644 --- a/derive/src/pyclass.rs +++ b/derive/src/pyclass.rs @@ -1,11 +1,11 @@ use super::Diagnostic; -use crate::util::{def_to_name, path_eq, strip_prefix, ItemMeta}; +use crate::util::{def_to_name, path_eq, strip_prefix, ItemIdent, ItemMeta}; use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::{quote, quote_spanned, ToTokens}; use std::collections::{HashMap, HashSet}; use syn::{ parse_quote, spanned::Spanned, Attribute, AttributeArgs, Ident, Index, Item, Lit, Meta, - NestedMeta, Signature, + NestedMeta, }; fn meta_to_vec(meta: Meta) -> Result, Meta> { @@ -54,7 +54,7 @@ impl Class { } } - fn extract_method(sig: &Signature, meta: Meta) -> Result { + fn extract_method(ident: &Ident, meta: Meta) -> Result { let nesteds = meta_to_vec(meta).map_err(|meta| { err_span!( meta, @@ -64,14 +64,14 @@ impl Class { })?; let item_meta = - ItemMeta::from_nested_meta("pymethod", sig, &nesteds, ItemMeta::ATTRIBUTE_NAMES)?; + ItemMeta::from_nested_meta("pymethod", &ident, &nesteds, ItemMeta::ATTRIBUTE_NAMES)?; Ok(ClassItem::Method { - item_ident: sig.ident.clone(), + item_ident: ident.clone(), py_name: item_meta.method_name()?, }) } - fn extract_classmethod(sig: &Signature, meta: Meta) -> Result { + fn extract_classmethod(ident: &Ident, meta: Meta) -> Result { let nesteds = meta_to_vec(meta).map_err(|meta| { err_span!( meta, @@ -79,15 +79,19 @@ impl Class { #[pyclassmethod(name = \"...\")]", ) })?; - let item_meta = - ItemMeta::from_nested_meta("pyclassmethod", sig, &nesteds, ItemMeta::ATTRIBUTE_NAMES)?; + let item_meta = ItemMeta::from_nested_meta( + "pyclassmethod", + &ident, + &nesteds, + ItemMeta::ATTRIBUTE_NAMES, + )?; Ok(ClassItem::ClassMethod { - item_ident: sig.ident.clone(), + item_ident: ident.clone(), py_name: item_meta.method_name()?, }) } - fn extract_property(sig: &Signature, meta: Meta) -> Result { + fn extract_property(ident: &Ident, meta: Meta) -> Result { let nesteds = meta_to_vec(meta).map_err(|meta| { err_span!( meta, @@ -96,26 +100,26 @@ impl Class { ) })?; let item_meta = - ItemMeta::from_nested_meta("pyproperty", sig, &nesteds, ItemMeta::PROPERTY_NAMES)?; + ItemMeta::from_nested_meta("pyproperty", &ident, &nesteds, ItemMeta::PROPERTY_NAMES)?; Ok(ClassItem::Property { py_name: item_meta.property_name()?, - item_ident: sig.ident.clone(), + item_ident: ident.clone(), setter: item_meta.setter()?, }) } - fn extract_slot(sig: &Signature, meta: Meta) -> Result { + fn extract_slot(ident: &Ident, meta: Meta) -> Result { let pyslot_err = "#[pyslot] must be of the form #[pyslot] or #[pyslot(slotname)]"; let nesteds = meta_to_vec(meta).map_err(|meta| err_span!(meta, "{}", pyslot_err))?; if nesteds.len() > 1 { return Err(Diagnostic::spanned_error("e!(#(#nesteds)*), pyslot_err)); } let slot_ident = if nesteds.is_empty() { - let ident_str = sig.ident.to_string(); + let ident_str = ident.to_string(); if let Some(stripped) = strip_prefix(&ident_str, "tp_") { - proc_macro2::Ident::new(stripped, sig.ident.span()) + proc_macro2::Ident::new(stripped, ident.span()) } else { - sig.ident.clone() + ident.clone() } } else { match nesteds.into_iter().next().unwrap() { @@ -128,14 +132,14 @@ impl Class { }; Ok(ClassItem::Slot { slot_ident, - item_ident: sig.ident.clone(), + item_ident: ident.clone(), }) } fn extract_item_from_syn( &mut self, attrs: &mut Vec, - sig: &Signature, + ident: &Ident, ) -> Result<(), Diagnostic> { let mut attr_idxs = Vec::new(); for (i, meta) in attrs @@ -149,10 +153,10 @@ impl Class { None => continue, }; let item = match name.to_string().as_str() { - "pymethod" => Self::extract_method(sig, meta)?, - "pyclassmethod" => Self::extract_classmethod(sig, meta)?, - "pyproperty" => Self::extract_property(sig, meta)?, - "pyslot" => Self::extract_slot(sig, meta)?, + "pymethod" => Self::extract_method(ident, meta)?, + "pyclassmethod" => Self::extract_classmethod(ident, meta)?, + "pyproperty" => Self::extract_property(ident, meta)?, + "pyslot" => Self::extract_slot(ident, meta)?, _ => { continue; } @@ -177,12 +181,7 @@ impl Class { } } -struct ItemSig<'a> { - attrs: &'a mut Vec, - sig: &'a Signature, -} - -fn extract_impl_items(mut items: Vec) -> Result { +fn extract_impl_items(mut items: Vec) -> Result { let mut diagnostics: Vec = Vec::new(); let mut class = Class::default(); @@ -190,7 +189,7 @@ fn extract_impl_items(mut items: Vec) -> Result Result { - Some(ItemSig { attrs, sig }) + Some(ItemIdent { + attrs, + ident: &sig.ident, + }) } _ => None, }) @@ -386,7 +388,10 @@ pub fn impl_pyimpl(attr: AttributeArgs, item: Item) -> Result { - Some(ItemSig { attrs, sig }) + Some(ItemIdent { + attrs, + ident: &sig.ident, + }) } _ => None, }) diff --git a/derive/src/pymodule.rs b/derive/src/pymodule.rs index a55d42cd6..94cb0e951 100644 --- a/derive/src/pymodule.rs +++ b/derive/src/pymodule.rs @@ -1,12 +1,9 @@ use super::Diagnostic; -use crate::util::{def_to_name, ItemMeta}; +use crate::util::{def_to_name, ItemIdent, ItemMeta}; use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::{quote, quote_spanned}; use std::collections::HashSet; -use syn::{ - parse_quote, spanned::Spanned, Attribute, AttributeArgs, Ident, Item, Meta, NestedMeta, - Signature, -}; +use syn::{parse_quote, spanned::Spanned, Attribute, AttributeArgs, Ident, Item, Meta, NestedMeta}; fn meta_to_vec(meta: Meta) -> Result, Meta> { match meta { @@ -24,6 +21,7 @@ struct Module { #[derive(PartialEq, Eq, Hash)] enum ModuleItem { Function { item_ident: Ident, py_name: String }, + Class { item_ident: Ident, py_name: String }, } impl Module { @@ -38,7 +36,7 @@ impl Module { } } - fn extract_function(sig: &Signature, meta: Meta) -> Result { + fn extract_function(ident: &Ident, meta: Meta) -> Result { let nesteds = meta_to_vec(meta).map_err(|meta| { err_span!( meta, @@ -48,9 +46,26 @@ impl Module { })?; let item_meta = - ItemMeta::from_nested_meta("pyfunction", sig, &nesteds, ItemMeta::SIMPLE_NAMES)?; + ItemMeta::from_nested_meta("pyfunction", &ident, &nesteds, ItemMeta::SIMPLE_NAMES)?; Ok(ModuleItem::Function { - item_ident: sig.ident.clone(), + item_ident: ident.clone(), + py_name: item_meta.simple_name()?, + }) + } + + fn extract_class(ident: &Ident, meta: Meta) -> Result { + let nesteds = meta_to_vec(meta).map_err(|meta| { + err_span!( + meta, + "#[pyclass = \"...\"] cannot be a name/value, you probably meant \ + #[pyclass(name = \"...\")]", + ) + })?; + + let item_meta = + ItemMeta::from_nested_meta("pyclass", &ident, &nesteds, ItemMeta::SIMPLE_NAMES)?; + Ok(ModuleItem::Class { + item_ident: ident.clone(), py_name: item_meta.simple_name()?, }) } @@ -58,7 +73,7 @@ impl Module { fn extract_item_from_syn( &mut self, attrs: &mut Vec, - sig: &Signature, + ident: &Ident, ) -> Result<(), Diagnostic> { let mut attr_idxs = Vec::new(); for (i, meta) in attrs @@ -71,12 +86,17 @@ impl Module { Some(name) => name, None => continue, }; - if name == "pyfunction" { - self.add_item(Self::extract_function(sig, meta)?, meta_span)?; - } else { - continue; - } - attr_idxs.push(i); + let item = match name.to_string().as_str() { + "pyfunction" => { + attr_idxs.push(i); + Self::extract_function(ident, meta)? + } + "pyclass" => Self::extract_class(ident, meta)?, + _ => { + continue; + } + }; + self.add_item(item, meta_span)?; } let mut i = 0; let mut attr_idxs = &*attr_idxs; @@ -95,33 +115,37 @@ impl Module { } } -struct ItemSig<'a> { - attrs: &'a mut Vec, - sig: &'a Signature, -} - -fn extract_module_items(mut items: Vec) -> Result { +fn extract_module_items(mut items: Vec) -> Result { let mut diagnostics: Vec = Vec::new(); - let mut class = Module::default(); + let mut module = Module::default(); for item in items.iter_mut() { push_diag_result!( diagnostics, - class.extract_item_from_syn(&mut item.attrs, item.sig), + module.extract_item_from_syn(&mut item.attrs, item.ident), ); } - let functions = class.items.into_iter().map(|item| match item { + let functions = module.items.into_iter().map(|item| match item { ModuleItem::Function { item_ident, py_name, } => { - let new_func = quote_spanned!(item_ident.span()=> .new_function(#item_ident)); + let new_func = quote_spanned!(item_ident.span() => .new_function(#item_ident)); quote! { vm.__module_set_attr(&module, #py_name, vm.ctx#new_func).unwrap(); } } + ModuleItem::Class { + item_ident, + py_name, + } => { + let new_class = quote_spanned!(item_ident.span() => #item_ident::make_class(&vm.ctx)); + quote! { + vm.__module_set_attr(&module, #py_name, #new_class).unwrap(); + } + } }); Diagnostic::from_vec(diagnostics)?; @@ -140,12 +164,16 @@ pub fn impl_pymodule(attr: AttributeArgs, item: Item) -> Result Some(ItemIdent { attrs, - vis: _vis, - sig, - .. - }) => Some(ItemSig { attrs, sig }), + ident: &sig.ident, + }), + Item::Struct(syn::ItemStruct { attrs, ident, .. }) => { + Some(ItemIdent { attrs, ident }) + } + Item::Enum(syn::ItemEnum { attrs, ident, .. }) => { + Some(ItemIdent { attrs, ident }) + } _ => None, }) .collect(); diff --git a/derive/src/util.rs b/derive/src/util.rs index a5cd61c86..deb2ef046 100644 --- a/derive/src/util.rs +++ b/derive/src/util.rs @@ -1,6 +1,6 @@ use super::Diagnostic; use std::collections::HashMap; -use syn::{AttributeArgs, Ident, Lit, Meta, NestedMeta, Path, Signature}; +use syn::{Attribute, AttributeArgs, Ident, Lit, Meta, NestedMeta, Path}; pub fn path_eq(path: &Path, s: &str) -> bool { path.get_ident().map_or(false, |id| id == s) @@ -40,8 +40,13 @@ pub fn strip_prefix<'a>(s: &'a str, prefix: &str) -> Option<&'a str> { } } +pub struct ItemIdent<'a> { + pub attrs: &'a mut Vec, + pub ident: &'a Ident, +} + pub struct ItemMeta<'a> { - sig: &'a Signature, + ident: &'a Ident, parent_type: &'static str, meta: HashMap>, } @@ -53,12 +58,12 @@ impl<'a> ItemMeta<'a> { pub fn from_nested_meta( parent_type: &'static str, - sig: &'a Signature, + ident: &'a Ident, nested_meta: &[NestedMeta], names: &[&'static str], ) -> Result { let mut extracted = Self { - sig, + ident, parent_type, meta: HashMap::new(), }; @@ -66,18 +71,13 @@ impl<'a> ItemMeta<'a> { let validate_name = |name: &str, extracted: &Self| -> Result<(), Diagnostic> { if names.contains(&name) { if extracted.meta.contains_key(name) { - bail_span!( - &sig.ident, - "#[{}] must have only one '{}'", - parent_type, - name - ); + bail_span!(ident, "#[{}] must have only one '{}'", parent_type, name); } else { Ok(()) } } else { bail_span!( - &sig.ident, + ident, "#[{}({})] is not one of allowed attributes {}", parent_type, name, @@ -123,7 +123,7 @@ impl<'a> ItemMeta<'a> { Some(s.value()) } else { bail_span!( - &self.sig.ident, + &self.ident, "#[{}({} = ...)] must be a string", self.parent_type, key @@ -132,7 +132,7 @@ impl<'a> ItemMeta<'a> { } Some(None) => { bail_span!( - &self.sig.ident, + &self.ident, "#[{}({} = ...)] is expected", self.parent_type, key, @@ -145,12 +145,7 @@ impl<'a> ItemMeta<'a> { fn _bool(&self, key: &str) -> Result { Ok(match self.meta.get(key) { Some(Some(_)) => { - bail_span!( - &self.sig.ident, - "#[{}({})] is expected", - self.parent_type, - key, - ); + bail_span!(&self.ident, "#[{}({})] is expected", self.parent_type, key,); } Some(None) => true, None => false, @@ -158,9 +153,7 @@ impl<'a> ItemMeta<'a> { } pub fn simple_name(&self) -> Result { - Ok(self - ._str("name")? - .unwrap_or_else(|| self.sig.ident.to_string())) + Ok(self._str("name")?.unwrap_or_else(|| self.ident.to_string())) } pub fn method_name(&self) -> Result { @@ -169,7 +162,7 @@ impl<'a> ItemMeta<'a> { Ok(if let Some(name) = name { name } else { - let name = self.sig.ident.to_string(); + let name = self.ident.to_string(); if magic { format!("__{}__", name) } else { @@ -186,12 +179,12 @@ impl<'a> ItemMeta<'a> { Ok(if let Some(name) = name { name } else { - let sig_name = self.sig.ident.to_string(); + let sig_name = self.ident.to_string(); let name = if setter { if let Some(name) = strip_prefix(&sig_name, "set_") { if name.is_empty() { bail_span!( - &self.sig.ident, + &self.ident, "A #[{}(setter)] fn with a set_* name must \ have something after \"set_\"", self.parent_type @@ -200,7 +193,7 @@ impl<'a> ItemMeta<'a> { name.to_string() } else { bail_span!( - &self.sig.ident, + &self.ident, "A #[{}(setter)] fn must either have a `name` \ parameter or a fn name along the lines of \"set_*\"", self.parent_type diff --git a/vm/src/stdlib/pystruct.rs b/vm/src/stdlib/pystruct.rs index c0487053d..320d9875a 100644 --- a/vm/src/stdlib/pystruct.rs +++ b/vm/src/stdlib/pystruct.rs @@ -9,7 +9,7 @@ * https://docs.rs/byteorder/1.2.6/byteorder/ */ -use crate::pyobject::{PyClassImpl, PyObjectRef}; +use crate::pyobject::PyObjectRef; use crate::VirtualMachine; #[pymodule] @@ -26,7 +26,9 @@ mod _struct { objbool::IntoPyBool, objbytes::PyBytesRef, objstr::PyString, objstr::PyStringRef, objtuple::PyTuple, objtype::PyClassRef, }; - use crate::pyobject::{Either, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject}; + use crate::pyobject::{ + Either, PyClassImpl, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject, + }; use crate::VirtualMachine; #[derive(Debug)] @@ -663,7 +665,7 @@ mod _struct { #[pyclass(name = "Struct")] #[derive(Debug)] - pub(crate) struct PyStruct { + struct PyStruct { spec: FormatSpec, fmt_str: PyStringRef, } @@ -695,6 +697,7 @@ mod _struct { fn format(&self) -> PyStringRef { self.fmt_str.clone() } + #[pyproperty] fn size(&self) -> usize { self.spec.size() @@ -730,7 +733,6 @@ pub(crate) fn make_module(vm: &VirtualMachine) -> PyObjectRef { let module = _struct::make_module(vm); extend_module!(vm, module, { "error" => struct_error, - "Struct" => _struct::PyStruct::make_class(ctx), }); module }