1use crate::error::Diagnostic;
2use crate::pystructseq::PyStructSequenceMeta;
3use crate::util::{
4 ALL_ALLOWED_NAMES, AttrItemMeta, AttributeExt, ClassItemMeta, ContentItem, ContentItemInner,
5 ErrorVec, ItemMeta, ItemNursery, ModuleItemMeta, SimpleItemMeta, format_doc,
6 infer_native_call_flags, iter_use_idents, pyclass_ident_and_attrs, text_signature,
7};
8use core::str::FromStr;
9use proc_macro2::{Delimiter, Group, TokenStream, TokenTree};
10use quote::{ToTokens, format_ident, quote, quote_spanned};
11use rustpython_doc::DB;
12use std::collections::HashSet;
13use syn::{Attribute, Ident, Item, Result, parse_quote, spanned::Spanned};
14use syn_ext::ext::*;
15use syn_ext::types::NestedMeta;
16
17pub struct WithItem {
19 pub cfg_attrs: Vec<Attribute>,
20 pub path: syn::Path,
21}
22
23impl syn::parse::Parse for WithItem {
24 fn parse(input: syn::parse::ParseStream<'_>) -> Result<Self> {
25 let cfg_attrs = Attribute::parse_outer(input)?;
26 for attr in &cfg_attrs {
27 if !attr.path().is_ident("cfg") {
28 return Err(syn::Error::new_spanned(
29 attr,
30 "only #[cfg(...)] is supported in with()",
31 ));
32 }
33 }
34 let path = input.parse()?;
35 Ok(WithItem { cfg_attrs, path })
36 }
37}
38
39pub struct PyModuleArgs {
41 pub metas: Vec<NestedMeta>,
42 pub with_items: Vec<WithItem>,
43}
44
45impl syn::parse::Parse for PyModuleArgs {
46 fn parse(input: syn::parse::ParseStream<'_>) -> Result<Self> {
47 let mut metas = Vec::new();
48 let mut with_items = Vec::new();
49
50 while !input.is_empty() {
51 if input.peek(Ident) && input.peek2(syn::token::Paren) {
53 let fork = input.fork();
54 let ident: Ident = fork.parse()?;
55 if ident == "with" {
56 let _: Ident = input.parse()?;
58 let content;
59 syn::parenthesized!(content in input);
60 let items =
61 syn::punctuated::Punctuated::<WithItem, syn::Token![,]>::parse_terminated(
62 &content,
63 )?;
64 with_items.extend(items);
65 if !input.is_empty() {
66 input.parse::<syn::Token![,]>()?;
67 }
68 continue;
69 }
70 }
71 metas.push(input.parse::<NestedMeta>()?);
72 if input.is_empty() {
73 break;
74 }
75 input.parse::<syn::Token![,]>()?;
76 }
77
78 Ok(PyModuleArgs { metas, with_items })
79 }
80}
81
82fn negate_cfg_attrs(cfg_attrs: &[Attribute]) -> Vec<Attribute> {
84 if cfg_attrs.is_empty() {
85 return vec![];
86 }
87 let predicates: Vec<_> = cfg_attrs
88 .iter()
89 .map(|attr| match &attr.meta {
90 syn::Meta::List(list) => list.tokens.clone(),
91 _ => unreachable!("only #[cfg(...)] should be here"),
92 })
93 .collect();
94 if predicates.len() == 1 {
95 let predicate = &predicates[0];
96 vec![parse_quote!(#[cfg(not(#predicate))])]
97 } else {
98 vec![parse_quote!(#[cfg(not(all(#(#predicates),*)))])]
99 }
100}
101
102#[derive(Clone, Copy, Eq, PartialEq)]
103enum AttrName {
104 Function,
105 Attr,
106 Class,
107 Exception,
108 StructSequence,
109}
110
111impl core::fmt::Display for AttrName {
112 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
113 let s = match self {
114 Self::Function => "pyfunction",
115 Self::Attr => "pyattr",
116 Self::Class => "pyclass",
117 Self::Exception => "pyexception",
118 Self::StructSequence => "pystruct_sequence",
119 };
120 s.fmt(f)
121 }
122}
123
124impl FromStr for AttrName {
125 type Err = String;
126
127 fn from_str(s: &str) -> core::result::Result<Self, Self::Err> {
128 Ok(match s {
129 "pyfunction" => Self::Function,
130 "pyattr" => Self::Attr,
131 "pyclass" => Self::Class,
132 "pyexception" => Self::Exception,
133 "pystruct_sequence" => Self::StructSequence,
134 s => {
135 return Err(s.to_owned());
136 }
137 })
138 }
139}
140
141#[derive(Default)]
142struct ModuleContext {
143 name: String,
144 function_items: FunctionNursery,
145 attribute_items: ItemNursery,
146 has_module_exec: bool,
147 errors: Vec<syn::Error>,
148}
149
150pub fn impl_pymodule(args: PyModuleArgs, module_item: Item) -> Result<TokenStream> {
151 let PyModuleArgs { metas, with_items } = args;
152 let (doc, mut module_item) = match module_item {
153 Item::Mod(m) => (m.attrs.doc(), m),
154 other => bail_span!(other, "#[pymodule] can only be on a full module"),
155 };
156 let fake_ident = Ident::new("pymodule", module_item.span());
157 let module_meta =
158 ModuleItemMeta::from_nested(module_item.ident.clone(), fake_ident, metas.into_iter())?;
159
160 let mut context = ModuleContext {
162 name: module_meta.simple_name()?,
163 ..Default::default()
164 };
165 let items = module_item.items_mut().ok_or_else(|| {
166 module_meta.new_meta_error("requires actual module, not a module declaration")
167 })?;
168
169 for item in items.iter_mut() {
171 if let Item::Fn(func) = item
173 && func.sig.ident == "module_exec"
174 {
175 context.has_module_exec = true;
176 }
177 if matches!(item, Item::Impl(_) | Item::Trait(_)) {
178 continue;
180 }
181 let r = item.try_split_attr_mut(|attrs, item| {
182 let (py_items, cfgs) = attrs_to_module_items(attrs, module_item_new)?;
183 for py_item in py_items.iter().rev() {
184 let r = py_item.gen_module_item(ModuleItemArgs {
185 item,
186 attrs,
187 context: &mut context,
188 cfgs: cfgs.as_slice(),
189 });
190 context.errors.ok_or_push(r);
191 }
192 Ok(())
193 });
194 context.errors.ok_or_push(r);
195 }
196
197 let mut submodule_inits: Vec<TokenStream> = Vec::new();
199 for item in items.iter() {
200 if let Item::Mod(item_mod) = item {
201 let r = (|| -> Result<()> {
202 let attr = match item_mod
203 .attrs
204 .iter()
205 .find(|a| a.path().is_ident("pymodule"))
206 {
207 Some(attr) => attr,
208 None => return Ok(()),
209 };
210 let args_tokens = match &attr.meta {
211 syn::Meta::Path(_) => TokenStream::new(),
212 syn::Meta::List(list) => list.tokens.clone(),
213 _ => return Ok(()),
214 };
215 let mod_args: PyModuleArgs = syn::parse2(args_tokens)?;
216 let fake_ident = Ident::new("pymodule", attr.span());
217 let mod_meta = ModuleItemMeta::from_nested(
218 item_mod.ident.clone(),
219 fake_ident,
220 mod_args.metas.into_iter(),
221 )?;
222 if mod_meta.sub()? {
223 return Ok(());
224 }
225 let py_name = mod_meta.simple_name()?;
226 let mod_ident = &item_mod.ident;
227 let cfgs: Vec<_> = item_mod
228 .attrs
229 .iter()
230 .filter(|a| a.path().is_ident("cfg"))
231 .cloned()
232 .collect();
233 submodule_inits.push(quote! {
234 #(#cfgs)*
235 {
236 let child_def = #mod_ident::module_def(ctx);
237 let child = child_def.create_module(vm).expect("submodule create_module failed");
238 child.__init_methods(vm).expect("submodule __init_methods failed");
239 #mod_ident::module_exec(vm, &child).expect("submodule module_exec failed");
240 let child: ::rustpython_vm::PyObjectRef = child.into();
241 vm.__module_set_attr(module, ctx.intern_str(#py_name), child).expect("module set_attr submodule failed");
242 }
243 });
244 Ok(())
245 })();
246 context.errors.ok_or_push(r);
247 }
248 }
249
250 let module_name = context.name.as_str();
252 let function_items = context.function_items.validate()?;
253 let attribute_items = context.attribute_items.validate()?;
254 let doc = doc.or_else(|| DB.get(module_name).copied().map(str::to_owned));
255 let doc = if let Some(doc) = doc {
256 quote!(Some(#doc))
257 } else {
258 quote!(None)
259 };
260 let is_submodule = module_meta.sub()?;
261 if !is_submodule {
262 items.extend([
263 parse_quote! {
264 pub(crate) const MODULE_NAME: &'static str = #module_name;
265 },
266 parse_quote! {
267 pub(crate) const DOC: Option<&'static str> = #doc;
268 },
269 parse_quote! {
270 pub(crate) fn module_def(
271 ctx: &::rustpython_vm::Context,
272 ) -> &'static ::rustpython_vm::builtins::PyModuleDef {
273 DEF.get_or_init(|| {
274 let mut def = ::rustpython_vm::builtins::PyModuleDef {
275 name: ctx.intern_str(MODULE_NAME),
276 doc: DOC.map(|doc| ctx.intern_str(doc)),
277 methods: METHOD_DEFS,
278 slots: Default::default(),
279 };
280 def.slots.exec = Some(module_exec);
281 def
282 })
283 }
284 },
285 ]);
286 }
287 if !is_submodule && !context.has_module_exec {
288 items.push(parse_quote! {
289 pub(crate) fn module_exec(vm: &::rustpython_vm::VirtualMachine, module: &::rustpython_vm::Py<::rustpython_vm::builtins::PyModule>) -> ::rustpython_vm::PyResult<()> {
290 __module_exec(vm, module);
291 Ok(())
292 }
293 });
294 }
295 let (uncond_withs, cond_withs): (Vec<_>, Vec<_>) =
297 with_items.iter().partition(|w| w.cfg_attrs.is_empty());
298 let uncond_paths: Vec<_> = uncond_withs.iter().map(|w| &w.path).collect();
299
300 let method_defs = if with_items.is_empty() {
301 quote!(#function_items)
302 } else {
303 let cond_const_names: Vec<_> = cond_withs
306 .iter()
307 .enumerate()
308 .map(|(i, _)| format_ident!("__WITH_METHODS_{}", i))
309 .collect();
310 let cond_const_decls: Vec<_> = cond_withs
311 .iter()
312 .zip(&cond_const_names)
313 .map(|(w, name)| {
314 let cfg_attrs = &w.cfg_attrs;
315 let neg_attrs = negate_cfg_attrs(&w.cfg_attrs);
316 let path = &w.path;
317 quote! {
318 #(#cfg_attrs)*
319 const #name: &'static [::rustpython_vm::function::PyMethodDef] = super::#path::METHOD_DEFS;
320 #(#neg_attrs)*
321 const #name: &'static [::rustpython_vm::function::PyMethodDef] = &[];
322 }
323 })
324 .collect();
325
326 quote!({
327 const OWN_METHODS: &'static [::rustpython_vm::function::PyMethodDef] = &#function_items;
328 #(#cond_const_decls)*
329 rustpython_vm::function::PyMethodDef::__const_concat_arrays::<
330 { OWN_METHODS.len()
331 #(+ super::#uncond_paths::METHOD_DEFS.len())*
332 #(+ #cond_const_names.len())*
333 },
334 >(&[
335 #(super::#uncond_paths::METHOD_DEFS,)*
336 #(#cond_const_names,)*
337 OWN_METHODS
338 ])
339 })
340 };
341
342 let init_with_calls: Vec<_> = with_items
344 .iter()
345 .map(|w| {
346 let cfg_attrs = &w.cfg_attrs;
347 let path = &w.path;
348 quote! {
349 #(#cfg_attrs)*
350 super::#path::__init_attributes(vm, module);
351 }
352 })
353 .collect();
354
355 items.extend([
356 parse_quote! {
357 ::rustpython_vm::common::static_cell! {
358 pub(crate) static DEF: ::rustpython_vm::builtins::PyModuleDef;
359 }
360 },
361 parse_quote! {
362 pub(crate) const METHOD_DEFS: &'static [::rustpython_vm::function::PyMethodDef] = &#method_defs;
363 },
364 parse_quote! {
365 pub(crate) fn __init_attributes(
366 vm: &::rustpython_vm::VirtualMachine,
367 module: &::rustpython_vm::Py<::rustpython_vm::builtins::PyModule>,
368 ) {
369 #(#init_with_calls)*
370 let ctx = &vm.ctx;
371 #attribute_items
372 #(#submodule_inits)*
373 }
374 },
375 parse_quote! {
376 pub(crate) fn __module_exec(
377 vm: &::rustpython_vm::VirtualMachine,
378 module: &::rustpython_vm::Py<::rustpython_vm::builtins::PyModule>,
379 ) {
380 __init_attributes(vm, module);
381 }
382 },
383 parse_quote! {
384 pub(crate) fn __init_dict(
385 vm: &::rustpython_vm::VirtualMachine,
386 module: &::rustpython_vm::Py<::rustpython_vm::builtins::PyModule>,
387 ) {
388 ::rustpython_vm::builtins::PyModule::__init_dict_from_def(vm, module);
389 }
390 },
391 ]);
392
393 Ok(if let Some(error) = context.errors.into_error() {
394 let error = Diagnostic::from(error);
395 quote! {
396 #module_item
397 #error
398 }
399 } else {
400 module_item.into_token_stream()
401 })
402}
403
404fn module_item_new(
405 index: usize,
406 attr_name: AttrName,
407 py_attrs: Vec<usize>,
408) -> Box<dyn ModuleItem<AttrName = AttrName>> {
409 match attr_name {
410 AttrName::Function => Box::new(FunctionItem {
411 inner: ContentItemInner { index, attr_name },
412 py_attrs,
413 }),
414 AttrName::Attr => Box::new(AttributeItem {
415 inner: ContentItemInner { index, attr_name },
416 py_attrs,
417 }),
418 AttrName::Class | AttrName::Exception => Box::new(ClassItem {
420 inner: ContentItemInner { index, attr_name },
421 py_attrs,
422 }),
423 AttrName::StructSequence => Box::new(StructSequenceItem {
424 inner: ContentItemInner { index, attr_name },
425 py_attrs,
426 }),
427 }
428}
429
430fn attrs_to_module_items<F, R>(attrs: &[Attribute], item_new: F) -> Result<(Vec<R>, Vec<Attribute>)>
431where
432 F: Fn(usize, AttrName, Vec<usize>) -> R,
433{
434 let mut cfgs: Vec<Attribute> = Vec::new();
435 let mut result = Vec::new();
436
437 let mut iter = attrs.iter().enumerate().peekable();
438 while let Some((_, attr)) = iter.peek() {
439 let attr = *attr;
441 if let Some(ident) = attr.get_ident() {
442 let attr_name = ident.to_string();
443 if attr_name == "cfg" {
444 cfgs.push(attr.clone());
445 } else if ALL_ALLOWED_NAMES.contains(&attr_name.as_str()) {
446 break;
447 }
448 }
449 iter.next();
450 }
451
452 let mut closed = false;
453 let mut py_attrs = Vec::new();
454 for (i, attr) in iter {
455 let attr_name = if let Some(ident) = attr.get_ident() {
457 ident.to_string()
458 } else {
459 continue;
460 };
461 if attr_name == "cfg" {
462 bail_span!(attr, "#[py*] items must be placed under `cfgs`")
463 }
464
465 let attr_name = match AttrName::from_str(attr_name.as_str()) {
466 Ok(name) => name,
467 Err(wrong_name) => {
468 if !ALL_ALLOWED_NAMES.contains(&wrong_name.as_str()) {
469 continue;
470 } else if closed {
471 bail_span!(attr, "Only one #[pyattr] annotated #[py*] item can exist")
472 } else {
473 bail_span!(attr, "#[pymodule] doesn't accept #[{}]", wrong_name)
474 }
475 }
476 };
477
478 if attr_name == AttrName::Attr {
479 if !result.is_empty() {
480 bail_span!(
481 attr,
482 "#[pyattr] must be placed on top of other #[py*] items",
483 )
484 }
485 py_attrs.push(i);
486 continue;
487 }
488
489 if py_attrs.is_empty() {
490 result.push(item_new(i, attr_name, Vec::new()));
491 } else {
492 match attr_name {
493 AttrName::Class
494 | AttrName::Function
495 | AttrName::Exception
496 | AttrName::StructSequence => {
497 result.push(item_new(i, attr_name, py_attrs.clone()));
498 }
499 _ => {
500 bail_span!(
501 attr,
502 "#[pyclass], #[pyfunction], #[pyexception], or #[pystruct_sequence] can follow #[pyattr]",
503 )
504 }
505 }
506 py_attrs.clear();
507 closed = true;
508 }
509 }
510
511 if let Some(last) = py_attrs.pop() {
512 assert!(!closed);
513 result.push(item_new(last, AttrName::Attr, py_attrs));
514 }
515 Ok((result, cfgs))
516}
517
518#[derive(Default)]
519struct FunctionNursery {
520 items: Vec<FunctionNurseryItem>,
521}
522
523struct FunctionNurseryItem {
524 py_names: Vec<String>,
525 cfgs: Vec<Attribute>,
526 ident: Ident,
527 doc: String,
528 call_flags: TokenStream,
529}
530
531impl FunctionNursery {
532 fn add_item(&mut self, item: FunctionNurseryItem) {
533 self.items.push(item);
534 }
535
536 fn validate(self) -> Result<ValidatedFunctionNursery> {
537 let mut name_set = HashSet::new();
538 for item in &self.items {
539 for py_name in &item.py_names {
540 if !name_set.insert((py_name.to_owned(), &item.cfgs)) {
541 bail_span!(item.ident, "duplicate method name `{}`", py_name);
542 }
543 }
544 }
545 Ok(ValidatedFunctionNursery(self))
546 }
547}
548
549struct ValidatedFunctionNursery(FunctionNursery);
550
551impl ToTokens for ValidatedFunctionNursery {
552 fn to_tokens(&self, tokens: &mut TokenStream) {
553 let mut inner_tokens = TokenStream::new();
554 for item in &self.0.items {
555 let ident = &item.ident;
556 let cfgs = &item.cfgs;
557 let cfgs = quote!(#(#cfgs)*);
558 let py_names = &item.py_names;
559 let doc = &item.doc;
560 let doc = quote!(Some(#doc));
561 let flags = &item.call_flags;
562
563 inner_tokens.extend(quote![
564 #(
565 #cfgs
566 rustpython_vm::function::PyMethodDef::new_const(
567 #py_names,
568 #ident,
569 #flags,
570 #doc,
571 ),
572 )*
573 ]);
574 }
575 let array: TokenTree = Group::new(Delimiter::Bracket, inner_tokens).into();
576 tokens.extend([array]);
577 }
578}
579
580struct FunctionItem {
582 inner: ContentItemInner<AttrName>,
583 py_attrs: Vec<usize>,
584}
585
586struct ClassItem {
588 inner: ContentItemInner<AttrName>,
589 py_attrs: Vec<usize>,
590}
591
592struct AttributeItem {
594 inner: ContentItemInner<AttrName>,
595 py_attrs: Vec<usize>,
596}
597
598struct StructSequenceItem {
600 inner: ContentItemInner<AttrName>,
601 py_attrs: Vec<usize>,
602}
603
604impl ContentItem for FunctionItem {
605 type AttrName = AttrName;
606
607 fn inner(&self) -> &ContentItemInner<AttrName> {
608 &self.inner
609 }
610}
611
612impl ContentItem for ClassItem {
613 type AttrName = AttrName;
614
615 fn inner(&self) -> &ContentItemInner<AttrName> {
616 &self.inner
617 }
618}
619
620impl ContentItem for AttributeItem {
621 type AttrName = AttrName;
622
623 fn inner(&self) -> &ContentItemInner<AttrName> {
624 &self.inner
625 }
626}
627
628impl ContentItem for StructSequenceItem {
629 type AttrName = AttrName;
630
631 fn inner(&self) -> &ContentItemInner<AttrName> {
632 &self.inner
633 }
634}
635
636struct ModuleItemArgs<'a> {
637 item: &'a mut Item,
638 attrs: &'a mut Vec<Attribute>,
639 context: &'a mut ModuleContext,
640 cfgs: &'a [Attribute],
641}
642
643impl<'a> ModuleItemArgs<'a> {
644 fn module_name(&'a self) -> &'a str {
645 self.context.name.as_str()
646 }
647}
648
649trait ModuleItem: ContentItem {
650 fn gen_module_item(&self, args: ModuleItemArgs<'_>) -> Result<()>;
651}
652
653impl ModuleItem for FunctionItem {
654 fn gen_module_item(&self, args: ModuleItemArgs<'_>) -> Result<()> {
655 let func = args
656 .item
657 .function_or_method()
658 .map_err(|_| self.new_syn_error(args.item.span(), "can only be on a function"))?;
659 let ident = &func.sig().ident;
660
661 let item_attr = args.attrs.remove(self.index());
662 let item_meta = SimpleItemMeta::from_attr(ident.clone(), &item_attr)?;
663
664 let py_name = item_meta.simple_name()?;
665 let sig_doc = text_signature(func.sig(), &py_name);
666
667 let module = args.module_name();
668 let doc = args.attrs.doc().or_else(|| {
670 DB.get(&format!("{module}.{py_name}"))
671 .copied()
672 .map(str::to_owned)
673 });
674 let doc = if let Some(doc) = doc {
675 format_doc(&sig_doc, &doc)
676 } else {
677 sig_doc
678 };
679
680 let py_names = {
681 if self.py_attrs.is_empty() {
682 vec![py_name]
683 } else {
684 let mut py_names = HashSet::new();
685 py_names.insert(py_name);
686 for attr_index in self.py_attrs.iter().rev() {
687 let mut loop_unit = || {
688 let attr_attr = args.attrs.remove(*attr_index);
689 let item_meta = SimpleItemMeta::from_attr(ident.clone(), &attr_attr)?;
690
691 let py_name = item_meta.simple_name()?;
692 let inserted = py_names.insert(py_name.clone());
693 if !inserted {
694 return Err(self.new_syn_error(
695 ident.span(),
696 &format!(
697 "`{py_name}` is duplicated name for multiple py* attribute"
698 ),
699 ));
700 }
701 Ok(())
702 };
703 let r = loop_unit();
704 args.context.errors.ok_or_push(r);
705 }
706 let py_names: Vec<_> = py_names.into_iter().collect();
707 py_names
708 }
709 };
710 let call_flags = infer_native_call_flags(func.sig(), 0);
711
712 args.context.function_items.add_item(FunctionNurseryItem {
713 ident: ident.to_owned(),
714 py_names,
715 cfgs: args.cfgs.to_vec(),
716 doc,
717 call_flags,
718 });
719 Ok(())
720 }
721}
722
723impl ModuleItem for ClassItem {
724 fn gen_module_item(&self, args: ModuleItemArgs<'_>) -> Result<()> {
725 let (ident, _) = pyclass_ident_and_attrs(args.item)?;
726 let (class_name, class_new) = {
727 let class_attr = &mut args.attrs[self.inner.index];
728 let no_attr = class_attr.try_remove_name("no_attr")?;
729 if self.py_attrs.is_empty() {
730 if no_attr.is_none() {
732 bail_span!(
733 ident,
734 "#[{name}] requires #[pyattr] to be a module attribute. \
735 To keep it free type, try #[{name}(no_attr)]",
736 name = self.attr_name()
737 )
738 }
739 }
740 let no_attr = no_attr.is_some();
741 let is_use = matches!(&args.item, syn::Item::Use(_));
742
743 let class_meta = ClassItemMeta::from_attr(ident.clone(), class_attr)?;
744 let module_name = args.context.name.clone();
745 let module_name = if let Some(class_module_name) = class_meta.module().ok().flatten() {
746 class_module_name
747 } else {
748 class_attr.fill_nested_meta("module", || {
749 parse_quote! {module = #module_name}
750 })?;
751 module_name
752 };
753 let class_name = if no_attr && is_use {
754 "<NO ATTR>".to_owned()
755 } else {
756 class_meta.class_name()?
757 };
758 let class_new = quote_spanned!(ident.span() =>
759 let new_class = <#ident as ::rustpython_vm::class::PyClassImpl>::make_static_type();
760 {
764 let module_key = rustpython_vm::identifier!(ctx, __module__);
765 let has_module_getset = new_class.attributes.read()
766 .get(module_key)
767 .is_some_and(|v| v.downcastable::<rustpython_vm::builtins::PyGetSet>());
768 if !has_module_getset {
769 new_class.set_attr(module_key, vm.new_pyobj(#module_name));
770 }
771 }
772 );
773 (class_name, class_new)
774 };
775
776 let mut py_names = Vec::new();
777 for attr_index in self.py_attrs.iter().rev() {
778 let mut loop_unit = || {
779 let attr_attr = args.attrs.remove(*attr_index);
780 let item_meta = SimpleItemMeta::from_attr(ident.clone(), &attr_attr)?;
781
782 let py_name = item_meta
783 .optional_name()
784 .unwrap_or_else(|| class_name.clone());
785 py_names.push(py_name);
786
787 Ok(())
788 };
789 let r = loop_unit();
790 args.context.errors.ok_or_push(r);
791 }
792
793 let set_attr = match py_names.len() {
794 0 => quote! {
795 let _ = new_class; let _ = vm.ctx.intern_str(#class_name);
797 },
798 1 => {
799 let py_name = &py_names[0];
800 quote! {
801 vm.__module_set_attr(&module, vm.ctx.intern_str(#py_name), new_class).unwrap();
802 }
803 }
804 _ => quote! {
805 for name in [#(#py_names,)*] {
806 vm.__module_set_attr(&module, vm.ctx.intern_str(name), new_class.clone()).unwrap();
807 }
808 },
809 };
810
811 args.context.attribute_items.add_item(
812 ident.clone(),
813 py_names,
814 args.cfgs.to_vec(),
815 quote_spanned! { ident.span() =>
816 #class_new
817 #set_attr
818 },
819 0,
820 )?;
821 Ok(())
822 }
823}
824
825impl ModuleItem for StructSequenceItem {
826 fn gen_module_item(&self, args: ModuleItemArgs<'_>) -> Result<()> {
827 let pytype_ident = match args.item {
829 Item::Struct(s) => s.ident.clone(),
830 other => bail_span!(other, "#[pystruct_sequence] can only be on a struct"),
831 };
832
833 let structseq_attr = &args.attrs[self.inner.index];
835 let meta = PyStructSequenceMeta::from_attr(pytype_ident.clone(), structseq_attr)?;
836
837 let class_name = meta.class_name()?.ok_or_else(|| {
838 syn::Error::new_spanned(
839 structseq_attr,
840 "#[pystruct_sequence] requires name parameter",
841 )
842 })?;
843 let module_opt = meta.module()?;
844 let has_module = module_opt.is_some();
845 let module_name = module_opt.unwrap_or_else(|| args.context.name.clone());
846 if !has_module {
847 let structseq_attr = &mut args.attrs[self.inner.index];
848 structseq_attr.fill_nested_meta("module", || {
849 parse_quote! {module = #module_name}
850 })?;
851 }
852 let no_attr = meta.no_attr()?;
853
854 let class_new = quote_spanned!(pytype_ident.span() =>
856 let new_class = <#pytype_ident as ::rustpython_vm::class::PyClassImpl>::make_static_type();
857 {
858 let module_key = rustpython_vm::identifier!(ctx, __module__);
859 let has_module_getset = new_class.attributes.read()
860 .get(module_key)
861 .is_some_and(|v| v.downcastable::<rustpython_vm::builtins::PyGetSet>());
862 if !has_module_getset {
863 new_class.set_attr(module_key, vm.new_pyobj(#module_name));
864 }
865 }
866 );
867
868 let mut py_names = Vec::new();
870 for attr_index in self.py_attrs.iter().rev() {
871 let attr_attr = args.attrs.remove(*attr_index);
872 let item_meta = SimpleItemMeta::from_attr(pytype_ident.clone(), &attr_attr)?;
873 let py_name = item_meta
874 .optional_name()
875 .unwrap_or_else(|| class_name.clone());
876 py_names.push(py_name);
877 }
878
879 if self.py_attrs.is_empty() && !no_attr {
881 bail_span!(
882 pytype_ident,
883 "#[pystruct_sequence] requires #[pyattr] to be a module attribute. \
884 To keep it free type, try #[pystruct_sequence(..., no_attr)]"
885 )
886 }
887
888 let set_attr = match py_names.len() {
889 0 => quote! {
890 let _ = new_class; },
892 1 => {
893 let py_name = &py_names[0];
894 quote! {
895 vm.__module_set_attr(&module, vm.ctx.intern_str(#py_name), new_class).unwrap();
896 }
897 }
898 _ => quote! {
899 for name in [#(#py_names,)*] {
900 vm.__module_set_attr(&module, vm.ctx.intern_str(name), new_class.clone()).unwrap();
901 }
902 },
903 };
904
905 args.context.attribute_items.add_item(
906 pytype_ident.clone(),
907 py_names,
908 args.cfgs.to_vec(),
909 quote_spanned! { pytype_ident.span() =>
910 #class_new
911 #set_attr
912 },
913 0,
914 )?;
915 Ok(())
916 }
917}
918
919impl ModuleItem for AttributeItem {
920 fn gen_module_item(&self, args: ModuleItemArgs<'_>) -> Result<()> {
921 let cfgs = args.cfgs.to_vec();
922 let attr = args.attrs.remove(self.index());
923 let (ident, py_name, let_obj) = match args.item {
924 Item::Fn(syn::ItemFn { sig, block, .. }) => {
925 let ident = &sig.ident;
926 let attr_meta = AttrItemMeta::from_attr(ident.clone(), &attr)?;
929 if attr_meta.inner()._bool("once")? {
930 let stmts = &block.stmts;
931 let return_type = match &sig.output {
932 syn::ReturnType::Default => {
933 unreachable!("#[pyattr] attached function must have return type.")
934 }
935 syn::ReturnType::Type(_, ty) => ty,
936 };
937 let stmt: syn::Stmt = parse_quote! {
938 {
939 rustpython_common::static_cell! {
940 static ERROR: #return_type;
941 }
942 ERROR
943 .get_or_init(|| {
944 #(#stmts)*
945 })
946 .clone()
947 }
948 };
949 block.stmts = vec![stmt];
950 }
951
952 let py_name = attr_meta.simple_name()?;
953 (
954 ident.clone(),
955 py_name,
956 quote_spanned! { ident.span() =>
957 let obj = vm.new_pyobj(#ident(vm));
958 },
959 )
960 }
961 Item::Const(syn::ItemConst { ident, .. }) => {
962 let item_meta = SimpleItemMeta::from_attr(ident.clone(), &attr)?;
963 let py_name = item_meta.simple_name()?;
964 (
965 ident.clone(),
966 py_name,
967 quote_spanned! { ident.span() =>
968 let obj = vm.new_pyobj(#ident);
969 },
970 )
971 }
972 Item::Use(item) => {
973 if !self.py_attrs.is_empty() {
974 return Err(self
975 .new_syn_error(item.span(), "Only single #[pyattr] is allowed for `use`"));
976 }
977 let _ = iter_use_idents(item, |ident, is_unique| {
978 let item_meta = SimpleItemMeta::from_attr(ident.clone(), &attr)?;
979 let py_name = if is_unique {
980 item_meta.simple_name()?
981 } else if item_meta.optional_name().is_some() {
982 return Err(self.new_syn_error(
984 ident.span(),
985 "`name` attribute is not allowed for multiple use items",
986 ));
987 } else {
988 ident.to_string()
989 };
990 let tokens = quote_spanned! { ident.span() =>
991 vm.__module_set_attr(module, vm.ctx.intern_str(#py_name), vm.new_pyobj(#ident)).unwrap();
992 };
993 args.context.attribute_items.add_item(
994 ident.clone(),
995 vec![py_name],
996 cfgs.clone(),
997 tokens,
998 1,
999 )?;
1000 Ok(())
1001 })?;
1002 return Ok(());
1003 }
1004 other => {
1005 return Err(
1006 self.new_syn_error(other.span(), "can only be on a function, const and use")
1007 );
1008 }
1009 };
1010
1011 let (tokens, py_names) = if self.py_attrs.is_empty() {
1012 (
1013 quote_spanned! { ident.span() => {
1014 #let_obj
1015 vm.__module_set_attr(module, vm.ctx.intern_str(#py_name), obj).unwrap();
1016 }},
1017 vec![py_name],
1018 )
1019 } else {
1020 let mut names = vec![py_name];
1021 for attr_index in self.py_attrs.iter().rev() {
1022 let mut loop_unit = || {
1023 let attr_attr = args.attrs.remove(*attr_index);
1024 let item_meta = AttrItemMeta::from_attr(ident.clone(), &attr_attr)?;
1025 if item_meta.inner()._bool("once")? {
1026 return Err(self.new_syn_error(
1027 ident.span(),
1028 "#[pyattr(once)] is only allowed for the bottom-most item",
1029 ));
1030 }
1031
1032 let py_name = item_meta.optional_name().ok_or_else(|| {
1033 self.new_syn_error(
1034 ident.span(),
1035 "#[pyattr(name = ...)] is mandatory except for the bottom-most item",
1036 )
1037 })?;
1038 names.push(py_name);
1039 Ok(())
1040 };
1041 let r = loop_unit();
1042 args.context.errors.ok_or_push(r);
1043 }
1044 (
1045 quote_spanned! { ident.span() => {
1046 #let_obj
1047 for name in [#(#names),*] {
1048 vm.__module_set_attr(module, vm.ctx.intern_str(name), obj.clone()).unwrap();
1049 }
1050 }},
1051 names,
1052 )
1053 };
1054
1055 args.context
1056 .attribute_items
1057 .add_item(ident, py_names, cfgs, tokens, 1)?;
1058
1059 Ok(())
1060 }
1061}