1#[cfg(feature = "experimental-inspect")]
4use crate::introspection::{
5 attribute_introspection_code, introspection_id_const, module_introspection_code,
6};
7#[cfg(feature = "experimental-inspect")]
8use crate::py_expr::PyExpr;
9use crate::{
10 attributes::{
11 self, kw, take_attributes, take_pyo3_options, CrateAttribute, GILUsedAttribute,
12 ModuleAttribute, NameAttribute, SubmoduleAttribute,
13 },
14 combine_errors::CombineErrors,
15 get_doc,
16 pyclass::PyClassPyO3Option,
17 pyfunction::{impl_wrap_pyfunction, PyFunctionOptions},
18 utils::{has_attribute, has_attribute_with_namespace, Ctx, IdentOrStr, PythonDoc},
19};
20use proc_macro2::{Span, TokenStream};
21use quote::quote;
22use std::ffi::CString;
23use syn::LitCStr;
24use syn::{
25 ext::IdentExt,
26 parse::{Parse, ParseStream},
27 parse_quote, parse_quote_spanned,
28 punctuated::Punctuated,
29 spanned::Spanned,
30 token::Comma,
31 Item, Meta, Path, Result,
32};
33
34#[derive(Default)]
35pub struct PyModuleOptions {
36 krate: Option<CrateAttribute>,
37 name: Option<NameAttribute>,
38 module: Option<ModuleAttribute>,
39 submodule: Option<kw::submodule>,
40 gil_used: Option<GILUsedAttribute>,
41}
42
43impl Parse for PyModuleOptions {
44 fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
45 let mut options: PyModuleOptions = Default::default();
46
47 options.add_attributes(
48 Punctuated::<PyModulePyO3Option, syn::Token![,]>::parse_terminated(input)?,
49 )?;
50
51 Ok(options)
52 }
53}
54
55impl PyModuleOptions {
56 fn take_pyo3_options(&mut self, attrs: &mut Vec<syn::Attribute>) -> Result<()> {
57 self.add_attributes(take_pyo3_options(attrs)?)
58 }
59
60 fn add_attributes(
61 &mut self,
62 attrs: impl IntoIterator<Item = PyModulePyO3Option>,
63 ) -> Result<()> {
64 macro_rules! set_option {
65 ($key:ident $(, $extra:literal)?) => {
66 {
67 ensure_spanned!(
68 self.$key.is_none(),
69 $key.span() => concat!("`", stringify!($key), "` may only be specified once" $(, $extra)?)
70 );
71 self.$key = Some($key);
72 }
73 };
74 }
75 attrs
76 .into_iter()
77 .map(|attr| {
78 match attr {
79 PyModulePyO3Option::Crate(krate) => set_option!(krate),
80 PyModulePyO3Option::Name(name) => set_option!(name),
81 PyModulePyO3Option::Module(module) => set_option!(module),
82 PyModulePyO3Option::Submodule(submodule) => set_option!(
83 submodule,
84 " (it is implicitly always specified for nested modules)"
85 ),
86 PyModulePyO3Option::GILUsed(gil_used) => {
87 set_option!(gil_used)
88 }
89 }
90
91 Ok(())
92 })
93 .try_combine_syn_errors()?;
94 Ok(())
95 }
96}
97
98pub fn pymodule_module_impl(
99 module: &mut syn::ItemMod,
100 mut options: PyModuleOptions,
101) -> Result<TokenStream> {
102 let syn::ItemMod {
103 attrs,
104 vis,
105 unsafety: _,
106 ident,
107 mod_token,
108 content,
109 semi: _,
110 } = module;
111 let items = if let Some((_, items)) = content {
112 items
113 } else {
114 bail_spanned!(mod_token.span() => "`#[pymodule]` can only be used on inline modules")
115 };
116 options.take_pyo3_options(attrs)?;
117 let ctx = &Ctx::new(&options.krate, None);
118 let Ctx { pyo3_path, .. } = ctx;
119 let doc = get_doc(attrs, None, ctx)?;
120 let name = options
121 .name
122 .map_or_else(|| ident.unraw(), |name| name.value.0);
123 let full_name = if let Some(module) = &options.module {
124 format!("{}.{}", module.value.value(), name)
125 } else {
126 name.to_string()
127 };
128
129 let mut module_items = Vec::new();
130 let mut module_items_cfg_attrs = Vec::new();
131 #[cfg(feature = "experimental-inspect")]
132 let mut introspection_chunks = Vec::new();
133 #[cfg(not(feature = "experimental-inspect"))]
134 let introspection_chunks = Vec::<TokenStream>::new();
135
136 fn extract_use_items(
137 source: &syn::UseTree,
138 cfg_attrs: &[syn::Attribute],
139 target_items: &mut Vec<syn::Ident>,
140 target_cfg_attrs: &mut Vec<Vec<syn::Attribute>>,
141 ) -> Result<()> {
142 match source {
143 syn::UseTree::Name(name) => {
144 target_items.push(name.ident.clone());
145 target_cfg_attrs.push(cfg_attrs.to_vec());
146 }
147 syn::UseTree::Path(path) => {
148 extract_use_items(&path.tree, cfg_attrs, target_items, target_cfg_attrs)?
149 }
150 syn::UseTree::Group(group) => {
151 for tree in &group.items {
152 extract_use_items(tree, cfg_attrs, target_items, target_cfg_attrs)?
153 }
154 }
155 syn::UseTree::Glob(glob) => {
156 bail_spanned!(glob.span() => "#[pymodule] cannot import glob statements")
157 }
158 syn::UseTree::Rename(rename) => {
159 target_items.push(rename.rename.clone());
160 target_cfg_attrs.push(cfg_attrs.to_vec());
161 }
162 }
163 Ok(())
164 }
165
166 let mut pymodule_init = None;
167 let mut module_consts = Vec::new();
168 let mut module_consts_cfg_attrs = Vec::new();
169
170 let _: Vec<()> = (*items).iter_mut().map(|item|{
171 match item {
172 Item::Use(item_use) => {
173 let is_pymodule_export =
174 find_and_remove_attribute(&mut item_use.attrs, "pymodule_export");
175 if is_pymodule_export {
176 let cfg_attrs = get_cfg_attributes(&item_use.attrs);
177 extract_use_items(
178 &item_use.tree,
179 &cfg_attrs,
180 &mut module_items,
181 &mut module_items_cfg_attrs,
182 )?;
183 }
184 }
185 Item::Fn(item_fn) => {
186 ensure_spanned!(
187 !has_attribute(&item_fn.attrs, "pymodule_export"),
188 item.span() => "`#[pymodule_export]` may only be used on `use` or `const` statements"
189 );
190 let is_pymodule_init =
191 find_and_remove_attribute(&mut item_fn.attrs, "pymodule_init");
192 let ident = &item_fn.sig.ident;
193 if is_pymodule_init {
194 ensure_spanned!(
195 !has_attribute(&item_fn.attrs, "pyfunction"),
196 item_fn.span() => "`#[pyfunction]` cannot be used alongside `#[pymodule_init]`"
197 );
198 ensure_spanned!(pymodule_init.is_none(), item_fn.span() => "only one `#[pymodule_init]` may be specified");
199 pymodule_init = Some(quote! { #ident(module)?; });
200 } else if has_attribute(&item_fn.attrs, "pyfunction")
201 || has_attribute_with_namespace(
202 &item_fn.attrs,
203 Some(pyo3_path),
204 &["pyfunction"],
205 )
206 || has_attribute_with_namespace(
207 &item_fn.attrs,
208 Some(pyo3_path),
209 &["prelude", "pyfunction"],
210 )
211 {
212 module_items.push(ident.clone());
213 module_items_cfg_attrs.push(get_cfg_attributes(&item_fn.attrs));
214 }
215 }
216 Item::Struct(item_struct) => {
217 ensure_spanned!(
218 !has_attribute(&item_struct.attrs, "pymodule_export"),
219 item.span() => "`#[pymodule_export]` may only be used on `use` or `const` statements"
220 );
221 if has_attribute(&item_struct.attrs, "pyclass")
222 || has_attribute_with_namespace(
223 &item_struct.attrs,
224 Some(pyo3_path),
225 &["pyclass"],
226 )
227 || has_attribute_with_namespace(
228 &item_struct.attrs,
229 Some(pyo3_path),
230 &["prelude", "pyclass"],
231 )
232 {
233 module_items.push(item_struct.ident.clone());
234 module_items_cfg_attrs.push(get_cfg_attributes(&item_struct.attrs));
235 if !has_pyo3_module_declared::<PyClassPyO3Option>(
236 &item_struct.attrs,
237 "pyclass",
238 |option| matches!(option, PyClassPyO3Option::Module(_)),
239 )? {
240 set_module_attribute(&mut item_struct.attrs, &full_name);
241 }
242 }
243 }
244 Item::Enum(item_enum) => {
245 ensure_spanned!(
246 !has_attribute(&item_enum.attrs, "pymodule_export"),
247 item.span() => "`#[pymodule_export]` may only be used on `use` or `const` statements"
248 );
249 if has_attribute(&item_enum.attrs, "pyclass")
250 || has_attribute_with_namespace(&item_enum.attrs, Some(pyo3_path), &["pyclass"])
251 || has_attribute_with_namespace(
252 &item_enum.attrs,
253 Some(pyo3_path),
254 &["prelude", "pyclass"],
255 )
256 {
257 module_items.push(item_enum.ident.clone());
258 module_items_cfg_attrs.push(get_cfg_attributes(&item_enum.attrs));
259 if !has_pyo3_module_declared::<PyClassPyO3Option>(
260 &item_enum.attrs,
261 "pyclass",
262 |option| matches!(option, PyClassPyO3Option::Module(_)),
263 )? {
264 set_module_attribute(&mut item_enum.attrs, &full_name);
265 }
266 }
267 }
268 Item::Mod(item_mod) => {
269 ensure_spanned!(
270 !has_attribute(&item_mod.attrs, "pymodule_export"),
271 item.span() => "`#[pymodule_export]` may only be used on `use` or `const` statements"
272 );
273 if has_attribute(&item_mod.attrs, "pymodule")
274 || has_attribute_with_namespace(&item_mod.attrs, Some(pyo3_path), &["pymodule"])
275 || has_attribute_with_namespace(
276 &item_mod.attrs,
277 Some(pyo3_path),
278 &["prelude", "pymodule"],
279 )
280 {
281 module_items.push(item_mod.ident.clone());
282 module_items_cfg_attrs.push(get_cfg_attributes(&item_mod.attrs));
283 if !has_pyo3_module_declared::<PyModulePyO3Option>(
284 &item_mod.attrs,
285 "pymodule",
286 |option| matches!(option, PyModulePyO3Option::Module(_)),
287 )? {
288 set_module_attribute(&mut item_mod.attrs, &full_name);
289 }
290 item_mod
291 .attrs
292 .push(parse_quote_spanned!(item_mod.mod_token.span()=> #[pyo3(submodule)]));
293 }
294 }
295 Item::ForeignMod(item) => {
296 ensure_spanned!(
297 !has_attribute(&item.attrs, "pymodule_export"),
298 item.span() => "`#[pymodule_export]` may only be used on `use` or `const` statements"
299 );
300 }
301 Item::Trait(item) => {
302 ensure_spanned!(
303 !has_attribute(&item.attrs, "pymodule_export"),
304 item.span() => "`#[pymodule_export]` may only be used on `use` or `const` statements"
305 );
306 }
307 Item::Const(item) => {
308 if !find_and_remove_attribute(&mut item.attrs, "pymodule_export") {
309 return Ok(());
310 }
311 module_consts.push(item.ident.clone());
312 module_consts_cfg_attrs.push(get_cfg_attributes(&item.attrs));
313 #[cfg(feature = "experimental-inspect")]
314 {
315 let cfg_attrs = get_cfg_attributes(&item.attrs);
316 let chunk = attribute_introspection_code(
317 pyo3_path,
318 None,
319 item.ident.unraw().to_string(),
320 PyExpr::constant_from_expression(&item.expr),
321 (*item.ty).clone(),
322 true,
323 );
324 introspection_chunks.push(quote! {
325 #(#cfg_attrs)*
326 #chunk
327 });
328 }
329 }
330 Item::Static(item) => {
331 ensure_spanned!(
332 !has_attribute(&item.attrs, "pymodule_export"),
333 item.span() => "`#[pymodule_export]` may only be used on `use` or `const` statements"
334 );
335 }
336 Item::Macro(item) => {
337 ensure_spanned!(
338 !has_attribute(&item.attrs, "pymodule_export"),
339 item.span() => "`#[pymodule_export]` may only be used on `use` or `const` statements"
340 );
341 }
342 Item::ExternCrate(item) => {
343 ensure_spanned!(
344 !has_attribute(&item.attrs, "pymodule_export"),
345 item.span() => "`#[pymodule_export]` may only be used on `use` or `const` statements"
346 );
347 }
348 Item::Impl(item) => {
349 ensure_spanned!(
350 !has_attribute(&item.attrs, "pymodule_export"),
351 item.span() => "`#[pymodule_export]` may only be used on `use` or `const` statements"
352 );
353 }
354 Item::TraitAlias(item) => {
355 ensure_spanned!(
356 !has_attribute(&item.attrs, "pymodule_export"),
357 item.span() => "`#[pymodule_export]` may only be used on `use` or `const` statements"
358 );
359 }
360 Item::Type(item) => {
361 ensure_spanned!(
362 !has_attribute(&item.attrs, "pymodule_export"),
363 item.span() => "`#[pymodule_export]` may only be used on `use` or `const` statements"
364 );
365 }
366 Item::Union(item) => {
367 ensure_spanned!(
368 !has_attribute(&item.attrs, "pymodule_export"),
369 item.span() => "`#[pymodule_export]` may only be used on `use` or `const` statements"
370 );
371 }
372 _ => (),
373 }
374 Ok(())
375 }).try_combine_syn_errors()?;
376
377 #[cfg(feature = "experimental-inspect")]
378 let introspection = module_introspection_code(
379 pyo3_path,
380 &name.to_string(),
381 &module_items,
382 &module_items_cfg_attrs,
383 pymodule_init.is_some(),
384 );
385 #[cfg(not(feature = "experimental-inspect"))]
386 let introspection = quote! {};
387 #[cfg(feature = "experimental-inspect")]
388 let introspection_id = introspection_id_const();
389 #[cfg(not(feature = "experimental-inspect"))]
390 let introspection_id = quote! {};
391
392 let gil_used = options.gil_used.is_some_and(|op| op.value.value);
393
394 let initialization = module_initialization(
395 &full_name,
396 &name,
397 ctx,
398 quote! { __pyo3_pymodule },
399 options.submodule.is_some(),
400 gil_used,
401 doc,
402 );
403
404 let module_consts_names = module_consts.iter().map(|i| i.unraw().to_string());
405
406 Ok(quote!(
407 #(#attrs)*
408 #vis #mod_token #ident {
409 #(#items)*
410
411 #initialization
412 #introspection
413 #introspection_id
414 #(#introspection_chunks)*
415
416 fn __pyo3_pymodule(module: &#pyo3_path::Bound<'_, #pyo3_path::types::PyModule>) -> #pyo3_path::PyResult<()> {
417 use #pyo3_path::impl_::pymodule::PyAddToModule;
418 #(
419 #(#module_items_cfg_attrs)*
420 #module_items::_PYO3_DEF.add_to_module(module)?;
421 )*
422
423 #(
424 #(#module_consts_cfg_attrs)*
425 #pyo3_path::types::PyModuleMethods::add(module, #module_consts_names, #module_consts)?;
426 )*
427
428 #pymodule_init
429 ::std::result::Result::Ok(())
430 }
431 }
432 ))
433}
434
435pub fn pymodule_function_impl(
438 function: &mut syn::ItemFn,
439 mut options: PyModuleOptions,
440) -> Result<TokenStream> {
441 options.take_pyo3_options(&mut function.attrs)?;
442 process_functions_in_module(&options, function)?;
443 let ctx = &Ctx::new(&options.krate, None);
444 let Ctx { pyo3_path, .. } = ctx;
445 let ident = &function.sig.ident;
446 let name = options
447 .name
448 .map_or_else(|| ident.unraw(), |name| name.value.0);
449 let vis = &function.vis;
450 let doc = get_doc(&function.attrs, None, ctx)?;
451
452 let gil_used = options.gil_used.is_some_and(|op| op.value.value);
453
454 let initialization = module_initialization(
455 &name.to_string(),
456 &name,
457 ctx,
458 quote! { ModuleExec::__pyo3_module_exec },
459 false,
460 gil_used,
461 doc,
462 );
463
464 #[cfg(feature = "experimental-inspect")]
465 let introspection =
466 module_introspection_code(pyo3_path, &name.unraw().to_string(), &[], &[], true);
467 #[cfg(not(feature = "experimental-inspect"))]
468 let introspection = quote! {};
469 #[cfg(feature = "experimental-inspect")]
470 let introspection_id = introspection_id_const();
471 #[cfg(not(feature = "experimental-inspect"))]
472 let introspection_id = quote! {};
473
474 let mut module_args = Vec::new();
476 if function.sig.inputs.len() == 2 {
477 module_args.push(quote!(module.py()));
478 }
479 module_args
480 .push(quote!(::std::convert::Into::into(#pyo3_path::impl_::pymethods::BoundRef(module))));
481
482 Ok(quote! {
483 #[doc(hidden)]
484 #vis mod #ident {
485 #initialization
486 #introspection
487 #introspection_id
488 }
489
490 #[allow(unknown_lints, non_local_definitions)]
495 impl #ident::ModuleExec {
496 fn __pyo3_module_exec(module: &#pyo3_path::Bound<'_, #pyo3_path::types::PyModule>) -> #pyo3_path::PyResult<()> {
497 #ident(#(#module_args),*)
498 }
499 }
500 })
501}
502
503fn module_initialization(
504 full_name: &str,
505 name: &syn::Ident,
506 ctx: &Ctx,
507 module_exec: TokenStream,
508 is_submodule: bool,
509 gil_used: bool,
510 doc: PythonDoc,
511) -> TokenStream {
512 let Ctx { pyo3_path, .. } = ctx;
513 let pyinit_symbol = format!("PyInit_{name}");
514 let pyo3_name = LitCStr::new(&CString::new(full_name).unwrap(), Span::call_site());
515
516 let mut result = quote! {
517 #[doc(hidden)]
518 pub const __PYO3_NAME: &'static ::std::ffi::CStr = #pyo3_name;
519
520 #[doc(hidden)]
524 pub(super) struct ModuleExec;
525
526 #[doc(hidden)]
527 pub static _PYO3_DEF: #pyo3_path::impl_::pymodule::ModuleDef = {
528 use #pyo3_path::impl_::pymodule as impl_;
529
530 unsafe extern "C" fn __pyo3_module_exec(module: *mut #pyo3_path::ffi::PyObject) -> ::std::os::raw::c_int {
531 #pyo3_path::impl_::trampoline::module_exec(module, #module_exec)
532 }
533
534 static SLOTS: impl_::PyModuleSlots<4> = impl_::PyModuleSlotsBuilder::new()
535 .with_mod_exec(__pyo3_module_exec)
536 .with_gil_used(#gil_used)
537 .build();
538
539 impl_::ModuleDef::new(__PYO3_NAME, #doc, &SLOTS)
540 };
541 };
542 if !is_submodule {
543 result.extend(quote! {
544 #[doc(hidden)]
547 #[export_name = #pyinit_symbol]
548 pub unsafe extern "C" fn __pyo3_init() -> *mut #pyo3_path::ffi::PyObject {
549 _PYO3_DEF.init_multi_phase()
550 }
551 });
552 }
553 result
554}
555
556fn process_functions_in_module(options: &PyModuleOptions, func: &mut syn::ItemFn) -> Result<()> {
558 let ctx = &Ctx::new(&options.krate, None);
559 let Ctx { pyo3_path, .. } = ctx;
560 let mut stmts: Vec<syn::Stmt> = Vec::new();
561
562 for mut stmt in func.block.stmts.drain(..) {
563 if let syn::Stmt::Item(Item::Fn(func)) = &mut stmt {
564 if let Some((pyfn_span, pyfn_args)) = get_pyfn_attr(&mut func.attrs)? {
565 let module_name = pyfn_args.modname;
566 let wrapped_function = impl_wrap_pyfunction(func, pyfn_args.options)?;
567 let name = &func.sig.ident;
568 let statements: Vec<syn::Stmt> = syn::parse_quote_spanned! {
569 pyfn_span =>
570 #wrapped_function
571 {
572 use #pyo3_path::types::PyModuleMethods;
573 #module_name.add_function(#pyo3_path::wrap_pyfunction!(#name, #module_name.as_borrowed())?)?;
574 #[deprecated(note = "`pyfn` will be removed in a future PyO3 version, use declarative `#[pymodule]` with `mod` instead")]
575 #[allow(dead_code)]
576 const PYFN_ATTRIBUTE: () = ();
577 const _: () = PYFN_ATTRIBUTE;
578 }
579 };
580 stmts.extend(statements);
581 }
582 };
583 stmts.push(stmt);
584 }
585
586 func.block.stmts = stmts;
587 Ok(())
588}
589
590pub struct PyFnArgs {
591 modname: Path,
592 options: PyFunctionOptions,
593}
594
595impl Parse for PyFnArgs {
596 fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
597 let modname = input.parse().map_err(
598 |e| err_spanned!(e.span() => "expected module as first argument to #[pyfn()]"),
599 )?;
600
601 if input.is_empty() {
602 return Ok(Self {
603 modname,
604 options: Default::default(),
605 });
606 }
607
608 let _: Comma = input.parse()?;
609
610 Ok(Self {
611 modname,
612 options: input.parse()?,
613 })
614 }
615}
616
617fn get_pyfn_attr(attrs: &mut Vec<syn::Attribute>) -> syn::Result<Option<(Span, PyFnArgs)>> {
619 let mut pyfn_args: Option<(Span, PyFnArgs)> = None;
620
621 take_attributes(attrs, |attr| {
622 if attr.path().is_ident("pyfn") {
623 ensure_spanned!(
624 pyfn_args.is_none(),
625 attr.span() => "`#[pyfn] may only be specified once"
626 );
627 pyfn_args = Some((attr.path().span(), attr.parse_args()?));
628 Ok(true)
629 } else {
630 Ok(false)
631 }
632 })?;
633
634 if let Some((_, pyfn_args)) = &mut pyfn_args {
635 pyfn_args
636 .options
637 .add_attributes(take_pyo3_options(attrs)?)?;
638 }
639
640 Ok(pyfn_args)
641}
642
643fn get_cfg_attributes(attrs: &[syn::Attribute]) -> Vec<syn::Attribute> {
644 attrs
645 .iter()
646 .filter(|attr| attr.path().is_ident("cfg"))
647 .cloned()
648 .collect()
649}
650
651fn find_and_remove_attribute(attrs: &mut Vec<syn::Attribute>, ident: &str) -> bool {
652 let mut found = false;
653 attrs.retain(|attr| {
654 if attr.path().is_ident(ident) {
655 found = true;
656 false
657 } else {
658 true
659 }
660 });
661 found
662}
663
664impl PartialEq<syn::Ident> for IdentOrStr<'_> {
665 fn eq(&self, other: &syn::Ident) -> bool {
666 match self {
667 IdentOrStr::Str(s) => other == s,
668 IdentOrStr::Ident(i) => other == i,
669 }
670 }
671}
672
673fn set_module_attribute(attrs: &mut Vec<syn::Attribute>, module_name: &str) {
674 attrs.push(parse_quote!(#[pyo3(module = #module_name)]));
675}
676
677fn has_pyo3_module_declared<T: Parse>(
678 attrs: &[syn::Attribute],
679 root_attribute_name: &str,
680 is_module_option: impl Fn(&T) -> bool + Copy,
681) -> Result<bool> {
682 for attr in attrs {
683 if (attr.path().is_ident("pyo3") || attr.path().is_ident(root_attribute_name))
684 && matches!(attr.meta, Meta::List(_))
685 {
686 for option in &attr.parse_args_with(Punctuated::<T, Comma>::parse_terminated)? {
687 if is_module_option(option) {
688 return Ok(true);
689 }
690 }
691 }
692 }
693 Ok(false)
694}
695
696enum PyModulePyO3Option {
697 Submodule(SubmoduleAttribute),
698 Crate(CrateAttribute),
699 Name(NameAttribute),
700 Module(ModuleAttribute),
701 GILUsed(GILUsedAttribute),
702}
703
704impl Parse for PyModulePyO3Option {
705 fn parse(input: ParseStream<'_>) -> Result<Self> {
706 let lookahead = input.lookahead1();
707 if lookahead.peek(attributes::kw::name) {
708 input.parse().map(PyModulePyO3Option::Name)
709 } else if lookahead.peek(syn::Token![crate]) {
710 input.parse().map(PyModulePyO3Option::Crate)
711 } else if lookahead.peek(attributes::kw::module) {
712 input.parse().map(PyModulePyO3Option::Module)
713 } else if lookahead.peek(attributes::kw::submodule) {
714 input.parse().map(PyModulePyO3Option::Submodule)
715 } else if lookahead.peek(attributes::kw::gil_used) {
716 input.parse().map(PyModulePyO3Option::GILUsed)
717 } else {
718 Err(lookahead.error())
719 }
720 }
721}