1use std::borrow::Cow;
2use std::fmt::Debug;
3
4use proc_macro2::{Ident, Span, TokenStream};
5use quote::{format_ident, quote, quote_spanned, ToTokens};
6use syn::ext::IdentExt;
7use syn::parse::{Parse, ParseStream};
8use syn::punctuated::Punctuated;
9use syn::{parse_quote, parse_quote_spanned, spanned::Spanned, ImplItemFn, Result, Token};
10
11use crate::attributes::kw::frozen;
12use crate::attributes::{
13 self, kw, take_pyo3_options, CrateAttribute, ExtendsAttribute, FreelistAttribute,
14 ModuleAttribute, NameAttribute, NameLitStr, RenameAllAttribute, StrFormatterAttribute,
15};
16use crate::combine_errors::CombineErrors;
17#[cfg(feature = "experimental-inspect")]
18use crate::introspection::{
19 class_introspection_code, function_introspection_code, introspection_id_const,
20 unique_element_id,
21};
22use crate::konst::{ConstAttributes, ConstSpec};
23use crate::method::{FnArg, FnSpec, PyArg, RegularArg};
24use crate::pyfunction::ConstructorAttribute;
25#[cfg(feature = "experimental-inspect")]
26use crate::pyfunction::FunctionSignature;
27use crate::pyimpl::{gen_py_const, get_cfg_attributes, PyClassMethodsType};
28use crate::pymethod::{
29 impl_py_class_attribute, impl_py_getter_def, impl_py_setter_def, MethodAndMethodDef,
30 MethodAndSlotDef, PropertyType, SlotDef, __GETITEM__, __HASH__, __INT__, __LEN__, __REPR__,
31 __RICHCMP__, __STR__,
32};
33use crate::pyversions::{is_abi3_before, is_py_before};
34use crate::utils::{self, apply_renaming_rule, Ctx, LitCStr, PythonDoc};
35use crate::PyFunctionOptions;
36
37#[derive(Copy, Clone, Debug, PartialEq, Eq)]
39pub enum PyClassKind {
40 Struct,
41 Enum,
42}
43
44#[derive(Clone)]
46pub struct PyClassArgs {
47 pub class_kind: PyClassKind,
48 pub options: PyClassPyO3Options,
49}
50
51impl PyClassArgs {
52 fn parse(input: ParseStream<'_>, kind: PyClassKind) -> Result<Self> {
53 Ok(PyClassArgs {
54 class_kind: kind,
55 options: PyClassPyO3Options::parse(input)?,
56 })
57 }
58
59 pub fn parse_struct_args(input: ParseStream<'_>) -> syn::Result<Self> {
60 Self::parse(input, PyClassKind::Struct)
61 }
62
63 pub fn parse_enum_args(input: ParseStream<'_>) -> syn::Result<Self> {
64 Self::parse(input, PyClassKind::Enum)
65 }
66}
67
68#[derive(Clone, Default)]
69pub struct PyClassPyO3Options {
70 pub krate: Option<CrateAttribute>,
71 pub dict: Option<kw::dict>,
72 pub eq: Option<kw::eq>,
73 pub eq_int: Option<kw::eq_int>,
74 pub extends: Option<ExtendsAttribute>,
75 pub get_all: Option<kw::get_all>,
76 pub freelist: Option<FreelistAttribute>,
77 pub frozen: Option<kw::frozen>,
78 pub hash: Option<kw::hash>,
79 pub immutable_type: Option<kw::immutable_type>,
80 pub mapping: Option<kw::mapping>,
81 pub module: Option<ModuleAttribute>,
82 pub name: Option<NameAttribute>,
83 pub ord: Option<kw::ord>,
84 pub rename_all: Option<RenameAllAttribute>,
85 pub sequence: Option<kw::sequence>,
86 pub set_all: Option<kw::set_all>,
87 pub str: Option<StrFormatterAttribute>,
88 pub subclass: Option<kw::subclass>,
89 pub unsendable: Option<kw::unsendable>,
90 pub weakref: Option<kw::weakref>,
91 pub generic: Option<kw::generic>,
92}
93
94pub enum PyClassPyO3Option {
95 Crate(CrateAttribute),
96 Dict(kw::dict),
97 Eq(kw::eq),
98 EqInt(kw::eq_int),
99 Extends(ExtendsAttribute),
100 Freelist(FreelistAttribute),
101 Frozen(kw::frozen),
102 GetAll(kw::get_all),
103 Hash(kw::hash),
104 ImmutableType(kw::immutable_type),
105 Mapping(kw::mapping),
106 Module(ModuleAttribute),
107 Name(NameAttribute),
108 Ord(kw::ord),
109 RenameAll(RenameAllAttribute),
110 Sequence(kw::sequence),
111 SetAll(kw::set_all),
112 Str(StrFormatterAttribute),
113 Subclass(kw::subclass),
114 Unsendable(kw::unsendable),
115 Weakref(kw::weakref),
116 Generic(kw::generic),
117}
118
119impl Parse for PyClassPyO3Option {
120 fn parse(input: ParseStream<'_>) -> Result<Self> {
121 let lookahead = input.lookahead1();
122 if lookahead.peek(Token![crate]) {
123 input.parse().map(PyClassPyO3Option::Crate)
124 } else if lookahead.peek(kw::dict) {
125 input.parse().map(PyClassPyO3Option::Dict)
126 } else if lookahead.peek(kw::eq) {
127 input.parse().map(PyClassPyO3Option::Eq)
128 } else if lookahead.peek(kw::eq_int) {
129 input.parse().map(PyClassPyO3Option::EqInt)
130 } else if lookahead.peek(kw::extends) {
131 input.parse().map(PyClassPyO3Option::Extends)
132 } else if lookahead.peek(attributes::kw::freelist) {
133 input.parse().map(PyClassPyO3Option::Freelist)
134 } else if lookahead.peek(attributes::kw::frozen) {
135 input.parse().map(PyClassPyO3Option::Frozen)
136 } else if lookahead.peek(attributes::kw::get_all) {
137 input.parse().map(PyClassPyO3Option::GetAll)
138 } else if lookahead.peek(attributes::kw::hash) {
139 input.parse().map(PyClassPyO3Option::Hash)
140 } else if lookahead.peek(attributes::kw::immutable_type) {
141 input.parse().map(PyClassPyO3Option::ImmutableType)
142 } else if lookahead.peek(attributes::kw::mapping) {
143 input.parse().map(PyClassPyO3Option::Mapping)
144 } else if lookahead.peek(attributes::kw::module) {
145 input.parse().map(PyClassPyO3Option::Module)
146 } else if lookahead.peek(kw::name) {
147 input.parse().map(PyClassPyO3Option::Name)
148 } else if lookahead.peek(attributes::kw::ord) {
149 input.parse().map(PyClassPyO3Option::Ord)
150 } else if lookahead.peek(kw::rename_all) {
151 input.parse().map(PyClassPyO3Option::RenameAll)
152 } else if lookahead.peek(attributes::kw::sequence) {
153 input.parse().map(PyClassPyO3Option::Sequence)
154 } else if lookahead.peek(attributes::kw::set_all) {
155 input.parse().map(PyClassPyO3Option::SetAll)
156 } else if lookahead.peek(attributes::kw::str) {
157 input.parse().map(PyClassPyO3Option::Str)
158 } else if lookahead.peek(attributes::kw::subclass) {
159 input.parse().map(PyClassPyO3Option::Subclass)
160 } else if lookahead.peek(attributes::kw::unsendable) {
161 input.parse().map(PyClassPyO3Option::Unsendable)
162 } else if lookahead.peek(attributes::kw::weakref) {
163 input.parse().map(PyClassPyO3Option::Weakref)
164 } else if lookahead.peek(attributes::kw::generic) {
165 input.parse().map(PyClassPyO3Option::Generic)
166 } else {
167 Err(lookahead.error())
168 }
169 }
170}
171
172impl Parse for PyClassPyO3Options {
173 fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
174 let mut options: PyClassPyO3Options = Default::default();
175
176 for option in Punctuated::<PyClassPyO3Option, syn::Token![,]>::parse_terminated(input)? {
177 options.set_option(option)?;
178 }
179
180 Ok(options)
181 }
182}
183
184impl PyClassPyO3Options {
185 pub fn take_pyo3_options(&mut self, attrs: &mut Vec<syn::Attribute>) -> syn::Result<()> {
186 take_pyo3_options(attrs)?
187 .into_iter()
188 .try_for_each(|option| self.set_option(option))
189 }
190
191 fn set_option(&mut self, option: PyClassPyO3Option) -> syn::Result<()> {
192 macro_rules! set_option {
193 ($key:ident) => {
194 {
195 ensure_spanned!(
196 self.$key.is_none(),
197 $key.span() => concat!("`", stringify!($key), "` may only be specified once")
198 );
199 self.$key = Some($key);
200 }
201 };
202 }
203
204 match option {
205 PyClassPyO3Option::Crate(krate) => set_option!(krate),
206 PyClassPyO3Option::Dict(dict) => {
207 ensure_spanned!(
208 !is_abi3_before(3, 9),
209 dict.span() => "`dict` requires Python >= 3.9 when using the `abi3` feature"
210 );
211 set_option!(dict);
212 }
213 PyClassPyO3Option::Eq(eq) => set_option!(eq),
214 PyClassPyO3Option::EqInt(eq_int) => set_option!(eq_int),
215 PyClassPyO3Option::Extends(extends) => set_option!(extends),
216 PyClassPyO3Option::Freelist(freelist) => set_option!(freelist),
217 PyClassPyO3Option::Frozen(frozen) => set_option!(frozen),
218 PyClassPyO3Option::GetAll(get_all) => set_option!(get_all),
219 PyClassPyO3Option::ImmutableType(immutable_type) => {
220 ensure_spanned!(
221 !(is_py_before(3, 10) || is_abi3_before(3, 14)),
222 immutable_type.span() => "`immutable_type` requires Python >= 3.10 or >= 3.14 (ABI3)"
223 );
224 set_option!(immutable_type)
225 }
226 PyClassPyO3Option::Hash(hash) => set_option!(hash),
227 PyClassPyO3Option::Mapping(mapping) => set_option!(mapping),
228 PyClassPyO3Option::Module(module) => set_option!(module),
229 PyClassPyO3Option::Name(name) => set_option!(name),
230 PyClassPyO3Option::Ord(ord) => set_option!(ord),
231 PyClassPyO3Option::RenameAll(rename_all) => set_option!(rename_all),
232 PyClassPyO3Option::Sequence(sequence) => set_option!(sequence),
233 PyClassPyO3Option::SetAll(set_all) => set_option!(set_all),
234 PyClassPyO3Option::Str(str) => set_option!(str),
235 PyClassPyO3Option::Subclass(subclass) => set_option!(subclass),
236 PyClassPyO3Option::Unsendable(unsendable) => set_option!(unsendable),
237 PyClassPyO3Option::Weakref(weakref) => {
238 ensure_spanned!(
239 !is_abi3_before(3, 9),
240 weakref.span() => "`weakref` requires Python >= 3.9 when using the `abi3` feature"
241 );
242 set_option!(weakref);
243 }
244 PyClassPyO3Option::Generic(generic) => set_option!(generic),
245 }
246 Ok(())
247 }
248}
249
250pub fn build_py_class(
251 class: &mut syn::ItemStruct,
252 mut args: PyClassArgs,
253 methods_type: PyClassMethodsType,
254) -> syn::Result<TokenStream> {
255 args.options.take_pyo3_options(&mut class.attrs)?;
256
257 let ctx = &Ctx::new(&args.options.krate, None);
258 let doc = utils::get_doc(&class.attrs, None, ctx)?;
259
260 if let Some(lt) = class.generics.lifetimes().next() {
261 bail_spanned!(
262 lt.span() => concat!(
263 "#[pyclass] cannot have lifetime parameters. For an explanation, see \
264 https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/class.html#no-lifetime-parameters"
265 )
266 );
267 }
268
269 ensure_spanned!(
270 class.generics.params.is_empty(),
271 class.generics.span() => concat!(
272 "#[pyclass] cannot have generic parameters. For an explanation, see \
273 https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/class.html#no-generic-parameters"
274 )
275 );
276
277 let mut field_options: Vec<(&syn::Field, FieldPyO3Options)> = match &mut class.fields {
278 syn::Fields::Named(fields) => fields
279 .named
280 .iter_mut()
281 .map(
282 |field| match FieldPyO3Options::take_pyo3_options(&mut field.attrs) {
283 Ok(options) => Ok((&*field, options)),
284 Err(e) => Err(e),
285 },
286 )
287 .collect::<Vec<_>>(),
288 syn::Fields::Unnamed(fields) => fields
289 .unnamed
290 .iter_mut()
291 .map(
292 |field| match FieldPyO3Options::take_pyo3_options(&mut field.attrs) {
293 Ok(options) => Ok((&*field, options)),
294 Err(e) => Err(e),
295 },
296 )
297 .collect::<Vec<_>>(),
298 syn::Fields::Unit => {
299 let mut results = Vec::new();
300
301 if let Some(attr) = args.options.set_all {
302 results.push(Err(syn::Error::new_spanned(attr, UNIT_SET)));
303 };
304 if let Some(attr) = args.options.get_all {
305 results.push(Err(syn::Error::new_spanned(attr, UNIT_GET)));
306 };
307
308 results
309 }
310 }
311 .into_iter()
312 .try_combine_syn_errors()?;
313
314 if let Some(attr) = args.options.get_all {
315 for (_, FieldPyO3Options { get, .. }) in &mut field_options {
316 if let Some(old_get) = get.replace(Annotated::Struct(attr)) {
317 return Err(syn::Error::new(old_get.span(), DUPE_GET));
318 }
319 }
320 }
321
322 if let Some(attr) = args.options.set_all {
323 for (_, FieldPyO3Options { set, .. }) in &mut field_options {
324 if let Some(old_set) = set.replace(Annotated::Struct(attr)) {
325 return Err(syn::Error::new(old_set.span(), DUPE_SET));
326 }
327 }
328 }
329
330 impl_class(&class.ident, &args, doc, field_options, methods_type, ctx)
331}
332
333enum Annotated<X, Y> {
334 Field(X),
335 Struct(Y),
336}
337
338impl<X: Spanned, Y: Spanned> Annotated<X, Y> {
339 fn span(&self) -> Span {
340 match self {
341 Self::Field(x) => x.span(),
342 Self::Struct(y) => y.span(),
343 }
344 }
345}
346
347struct FieldPyO3Options {
349 get: Option<Annotated<kw::get, kw::get_all>>,
350 set: Option<Annotated<kw::set, kw::set_all>>,
351 name: Option<NameAttribute>,
352}
353
354enum FieldPyO3Option {
355 Get(attributes::kw::get),
356 Set(attributes::kw::set),
357 Name(NameAttribute),
358}
359
360impl Parse for FieldPyO3Option {
361 fn parse(input: ParseStream<'_>) -> Result<Self> {
362 let lookahead = input.lookahead1();
363 if lookahead.peek(attributes::kw::get) {
364 input.parse().map(FieldPyO3Option::Get)
365 } else if lookahead.peek(attributes::kw::set) {
366 input.parse().map(FieldPyO3Option::Set)
367 } else if lookahead.peek(attributes::kw::name) {
368 input.parse().map(FieldPyO3Option::Name)
369 } else {
370 Err(lookahead.error())
371 }
372 }
373}
374
375impl FieldPyO3Options {
376 fn take_pyo3_options(attrs: &mut Vec<syn::Attribute>) -> Result<Self> {
377 let mut options = FieldPyO3Options {
378 get: None,
379 set: None,
380 name: None,
381 };
382
383 for option in take_pyo3_options(attrs)? {
384 match option {
385 FieldPyO3Option::Get(kw) => {
386 if options.get.replace(Annotated::Field(kw)).is_some() {
387 return Err(syn::Error::new(kw.span(), UNIQUE_GET));
388 }
389 }
390 FieldPyO3Option::Set(kw) => {
391 if options.set.replace(Annotated::Field(kw)).is_some() {
392 return Err(syn::Error::new(kw.span(), UNIQUE_SET));
393 }
394 }
395 FieldPyO3Option::Name(name) => {
396 if options.name.replace(name).is_some() {
397 return Err(syn::Error::new(options.name.span(), UNIQUE_NAME));
398 }
399 }
400 }
401 }
402
403 Ok(options)
404 }
405}
406
407fn get_class_python_name<'a>(cls: &'a syn::Ident, args: &'a PyClassArgs) -> Cow<'a, syn::Ident> {
408 args.options
409 .name
410 .as_ref()
411 .map(|name_attr| Cow::Borrowed(&name_attr.value.0))
412 .unwrap_or_else(|| Cow::Owned(cls.unraw()))
413}
414
415fn get_class_python_module_and_name<'a>(cls: &'a Ident, args: &'a PyClassArgs) -> String {
416 let name = get_class_python_name(cls, args);
417 if let Some(module) = &args.options.module {
418 let value = module.value.value();
419 format!("{value}.{name}")
420 } else {
421 name.to_string()
422 }
423}
424
425fn impl_class(
426 cls: &syn::Ident,
427 args: &PyClassArgs,
428 doc: PythonDoc,
429 field_options: Vec<(&syn::Field, FieldPyO3Options)>,
430 methods_type: PyClassMethodsType,
431 ctx: &Ctx,
432) -> syn::Result<TokenStream> {
433 let Ctx { pyo3_path, .. } = ctx;
434 let pytypeinfo_impl = impl_pytypeinfo(cls, args, ctx);
435
436 if let Some(str) = &args.options.str {
437 if str.value.is_some() {
438 let no_naming_conflict = field_options.iter().all(|x| x.1.name.is_none())
440 & args.options.name.is_none()
441 & args.options.rename_all.is_none();
442 ensure_spanned!(no_naming_conflict, str.value.span() => "The format string syntax is incompatible with any renaming via `name` or `rename_all`");
443 }
444 }
445
446 let mut default_methods = descriptors_to_items(
447 cls,
448 args.options.rename_all.as_ref(),
449 args.options.frozen,
450 field_options,
451 ctx,
452 )?;
453
454 let (default_class_geitem, default_class_geitem_method) =
455 pyclass_class_geitem(&args.options, &syn::parse_quote!(#cls), ctx)?;
456
457 if let Some(default_class_geitem_method) = default_class_geitem_method {
458 default_methods.push(default_class_geitem_method);
459 }
460
461 let (default_str, default_str_slot) =
462 implement_pyclass_str(&args.options, &syn::parse_quote!(#cls), ctx);
463
464 let (default_richcmp, default_richcmp_slot) =
465 pyclass_richcmp(&args.options, &syn::parse_quote!(#cls), ctx)?;
466
467 let (default_hash, default_hash_slot) =
468 pyclass_hash(&args.options, &syn::parse_quote!(#cls), ctx)?;
469
470 let mut slots = Vec::new();
471 slots.extend(default_richcmp_slot);
472 slots.extend(default_hash_slot);
473 slots.extend(default_str_slot);
474
475 let py_class_impl = PyClassImplsBuilder::new(cls, args, methods_type, default_methods, slots)
476 .doc(doc)
477 .impl_all(ctx)?;
478
479 Ok(quote! {
480 impl #pyo3_path::types::DerefToPyAny for #cls {}
481
482 #pytypeinfo_impl
483
484 #py_class_impl
485
486 #[doc(hidden)]
487 #[allow(non_snake_case)]
488 impl #cls {
489 #default_richcmp
490 #default_hash
491 #default_str
492 #default_class_geitem
493 }
494 })
495}
496
497enum PyClassEnum<'a> {
498 Simple(PyClassSimpleEnum<'a>),
499 Complex(PyClassComplexEnum<'a>),
500}
501
502impl<'a> PyClassEnum<'a> {
503 fn new(enum_: &'a mut syn::ItemEnum) -> syn::Result<Self> {
504 let has_only_unit_variants = enum_
505 .variants
506 .iter()
507 .all(|variant| matches!(variant.fields, syn::Fields::Unit));
508
509 Ok(if has_only_unit_variants {
510 let simple_enum = PyClassSimpleEnum::new(enum_)?;
511 Self::Simple(simple_enum)
512 } else {
513 let complex_enum = PyClassComplexEnum::new(enum_)?;
514 Self::Complex(complex_enum)
515 })
516 }
517}
518
519pub fn build_py_enum(
520 enum_: &mut syn::ItemEnum,
521 mut args: PyClassArgs,
522 method_type: PyClassMethodsType,
523) -> syn::Result<TokenStream> {
524 args.options.take_pyo3_options(&mut enum_.attrs)?;
525
526 let ctx = &Ctx::new(&args.options.krate, None);
527 if let Some(extends) = &args.options.extends {
528 bail_spanned!(extends.span() => "enums can't extend from other classes");
529 } else if let Some(subclass) = &args.options.subclass {
530 bail_spanned!(subclass.span() => "enums can't be inherited by other classes");
531 } else if enum_.variants.is_empty() {
532 bail_spanned!(enum_.brace_token.span.join() => "#[pyclass] can't be used on enums without any variants");
533 }
534
535 if let Some(generic) = &args.options.generic {
536 bail_spanned!(generic.span() => "enums do not support #[pyclass(generic)]");
537 }
538
539 let doc = utils::get_doc(&enum_.attrs, None, ctx)?;
540 let enum_ = PyClassEnum::new(enum_)?;
541 impl_enum(enum_, &args, doc, method_type, ctx)
542}
543
544struct PyClassSimpleEnum<'a> {
545 ident: &'a syn::Ident,
546 repr_type: syn::Ident,
549 variants: Vec<PyClassEnumUnitVariant<'a>>,
550}
551
552impl<'a> PyClassSimpleEnum<'a> {
553 fn new(enum_: &'a mut syn::ItemEnum) -> syn::Result<Self> {
554 fn is_numeric_type(t: &syn::Ident) -> bool {
555 [
556 "u8", "i8", "u16", "i16", "u32", "i32", "u64", "i64", "u128", "i128", "usize",
557 "isize",
558 ]
559 .iter()
560 .any(|&s| t == s)
561 }
562
563 fn extract_unit_variant_data(
564 variant: &mut syn::Variant,
565 ) -> syn::Result<PyClassEnumUnitVariant<'_>> {
566 use syn::Fields;
567 let ident = match &variant.fields {
568 Fields::Unit => &variant.ident,
569 _ => bail_spanned!(variant.span() => "Must be a unit variant."),
570 };
571 let options = EnumVariantPyO3Options::take_pyo3_options(&mut variant.attrs)?;
572 let cfg_attrs = get_cfg_attributes(&variant.attrs);
573 Ok(PyClassEnumUnitVariant {
574 ident,
575 options,
576 cfg_attrs,
577 })
578 }
579
580 let ident = &enum_.ident;
581
582 let mut repr_type = syn::Ident::new("isize", proc_macro2::Span::call_site());
586 if let Some(attr) = enum_.attrs.iter().find(|attr| attr.path().is_ident("repr")) {
587 let args =
588 attr.parse_args_with(Punctuated::<TokenStream, Token![!]>::parse_terminated)?;
589 if let Some(ident) = args
590 .into_iter()
591 .filter_map(|ts| syn::parse2::<syn::Ident>(ts).ok())
592 .find(is_numeric_type)
593 {
594 repr_type = ident;
595 }
596 }
597
598 let variants: Vec<_> = enum_
599 .variants
600 .iter_mut()
601 .map(extract_unit_variant_data)
602 .collect::<syn::Result<_>>()?;
603 Ok(Self {
604 ident,
605 repr_type,
606 variants,
607 })
608 }
609}
610
611struct PyClassComplexEnum<'a> {
612 ident: &'a syn::Ident,
613 variants: Vec<PyClassEnumVariant<'a>>,
614}
615
616impl<'a> PyClassComplexEnum<'a> {
617 fn new(enum_: &'a mut syn::ItemEnum) -> syn::Result<Self> {
618 let witness = enum_
619 .variants
620 .iter()
621 .find(|variant| !matches!(variant.fields, syn::Fields::Unit))
622 .expect("complex enum has a non-unit variant")
623 .ident
624 .to_owned();
625
626 let extract_variant_data =
627 |variant: &'a mut syn::Variant| -> syn::Result<PyClassEnumVariant<'a>> {
628 use syn::Fields;
629 let ident = &variant.ident;
630 let options = EnumVariantPyO3Options::take_pyo3_options(&mut variant.attrs)?;
631
632 let variant = match &variant.fields {
633 Fields::Unit => {
634 bail_spanned!(variant.span() => format!(
635 "Unit variant `{ident}` is not yet supported in a complex enum\n\
636 = help: change to an empty tuple variant instead: `{ident}()`\n\
637 = note: the enum is complex because of non-unit variant `{witness}`",
638 ident=ident, witness=witness))
639 }
640 Fields::Named(fields) => {
641 let fields = fields
642 .named
643 .iter()
644 .map(|field| PyClassEnumVariantNamedField {
645 ident: field.ident.as_ref().expect("named field has an identifier"),
646 ty: &field.ty,
647 span: field.span(),
648 })
649 .collect();
650
651 PyClassEnumVariant::Struct(PyClassEnumStructVariant {
652 ident,
653 fields,
654 options,
655 })
656 }
657 Fields::Unnamed(types) => {
658 let fields = types
659 .unnamed
660 .iter()
661 .map(|field| PyClassEnumVariantUnnamedField {
662 ty: &field.ty,
663 span: field.span(),
664 })
665 .collect();
666
667 PyClassEnumVariant::Tuple(PyClassEnumTupleVariant {
668 ident,
669 fields,
670 options,
671 })
672 }
673 };
674
675 Ok(variant)
676 };
677
678 let ident = &enum_.ident;
679
680 let variants: Vec<_> = enum_
681 .variants
682 .iter_mut()
683 .map(extract_variant_data)
684 .collect::<syn::Result<_>>()?;
685
686 Ok(Self { ident, variants })
687 }
688}
689
690enum PyClassEnumVariant<'a> {
691 Struct(PyClassEnumStructVariant<'a>),
693 Tuple(PyClassEnumTupleVariant<'a>),
694}
695
696trait EnumVariant {
697 fn get_ident(&self) -> &syn::Ident;
698 fn get_options(&self) -> &EnumVariantPyO3Options;
699
700 fn get_python_name(&self, args: &PyClassArgs) -> Cow<'_, syn::Ident> {
701 self.get_options()
702 .name
703 .as_ref()
704 .map(|name_attr| Cow::Borrowed(&name_attr.value.0))
705 .unwrap_or_else(|| {
706 let name = self.get_ident().unraw();
707 if let Some(attr) = &args.options.rename_all {
708 let new_name = apply_renaming_rule(attr.value.rule, &name.to_string());
709 Cow::Owned(Ident::new(&new_name, Span::call_site()))
710 } else {
711 Cow::Owned(name)
712 }
713 })
714 }
715}
716
717impl EnumVariant for PyClassEnumVariant<'_> {
718 fn get_ident(&self) -> &syn::Ident {
719 match self {
720 PyClassEnumVariant::Struct(struct_variant) => struct_variant.ident,
721 PyClassEnumVariant::Tuple(tuple_variant) => tuple_variant.ident,
722 }
723 }
724
725 fn get_options(&self) -> &EnumVariantPyO3Options {
726 match self {
727 PyClassEnumVariant::Struct(struct_variant) => &struct_variant.options,
728 PyClassEnumVariant::Tuple(tuple_variant) => &tuple_variant.options,
729 }
730 }
731}
732
733struct PyClassEnumUnitVariant<'a> {
735 ident: &'a syn::Ident,
736 options: EnumVariantPyO3Options,
737 cfg_attrs: Vec<&'a syn::Attribute>,
738}
739
740impl EnumVariant for PyClassEnumUnitVariant<'_> {
741 fn get_ident(&self) -> &syn::Ident {
742 self.ident
743 }
744
745 fn get_options(&self) -> &EnumVariantPyO3Options {
746 &self.options
747 }
748}
749
750struct PyClassEnumStructVariant<'a> {
752 ident: &'a syn::Ident,
753 fields: Vec<PyClassEnumVariantNamedField<'a>>,
754 options: EnumVariantPyO3Options,
755}
756
757struct PyClassEnumTupleVariant<'a> {
758 ident: &'a syn::Ident,
759 fields: Vec<PyClassEnumVariantUnnamedField<'a>>,
760 options: EnumVariantPyO3Options,
761}
762
763struct PyClassEnumVariantNamedField<'a> {
764 ident: &'a syn::Ident,
765 ty: &'a syn::Type,
766 span: Span,
767}
768
769struct PyClassEnumVariantUnnamedField<'a> {
770 ty: &'a syn::Type,
771 span: Span,
772}
773
774#[derive(Clone, Default)]
776struct EnumVariantPyO3Options {
777 name: Option<NameAttribute>,
778 constructor: Option<ConstructorAttribute>,
779}
780
781enum EnumVariantPyO3Option {
782 Name(NameAttribute),
783 Constructor(ConstructorAttribute),
784}
785
786impl Parse for EnumVariantPyO3Option {
787 fn parse(input: ParseStream<'_>) -> Result<Self> {
788 let lookahead = input.lookahead1();
789 if lookahead.peek(attributes::kw::name) {
790 input.parse().map(EnumVariantPyO3Option::Name)
791 } else if lookahead.peek(attributes::kw::constructor) {
792 input.parse().map(EnumVariantPyO3Option::Constructor)
793 } else {
794 Err(lookahead.error())
795 }
796 }
797}
798
799impl EnumVariantPyO3Options {
800 fn take_pyo3_options(attrs: &mut Vec<syn::Attribute>) -> Result<Self> {
801 let mut options = EnumVariantPyO3Options::default();
802
803 take_pyo3_options(attrs)?
804 .into_iter()
805 .try_for_each(|option| options.set_option(option))?;
806
807 Ok(options)
808 }
809
810 fn set_option(&mut self, option: EnumVariantPyO3Option) -> syn::Result<()> {
811 macro_rules! set_option {
812 ($key:ident) => {
813 {
814 ensure_spanned!(
815 self.$key.is_none(),
816 $key.span() => concat!("`", stringify!($key), "` may only be specified once")
817 );
818 self.$key = Some($key);
819 }
820 };
821 }
822
823 match option {
824 EnumVariantPyO3Option::Constructor(constructor) => set_option!(constructor),
825 EnumVariantPyO3Option::Name(name) => set_option!(name),
826 }
827 Ok(())
828 }
829}
830
831#[allow(dead_code)]
833pub enum PyFmtName {
834 Str,
835 Repr,
836}
837
838fn implement_py_formatting(
839 ty: &syn::Type,
840 ctx: &Ctx,
841 option: &StrFormatterAttribute,
842) -> (ImplItemFn, MethodAndSlotDef) {
843 let mut fmt_impl = match &option.value {
844 Some(opt) => {
845 let fmt = &opt.fmt;
846 let args = &opt
847 .args
848 .iter()
849 .map(|member| quote! {self.#member})
850 .collect::<Vec<TokenStream>>();
851 let fmt_impl: ImplItemFn = syn::parse_quote! {
852 fn __pyo3__generated____str__(&self) -> ::std::string::String {
853 ::std::format!(#fmt, #(#args, )*)
854 }
855 };
856 fmt_impl
857 }
858 None => {
859 let fmt_impl: syn::ImplItemFn = syn::parse_quote! {
860 fn __pyo3__generated____str__(&self) -> ::std::string::String {
861 ::std::format!("{}", &self)
862 }
863 };
864 fmt_impl
865 }
866 };
867 let fmt_slot = generate_protocol_slot(
868 ty,
869 &mut fmt_impl,
870 &__STR__,
871 "__str__",
872 #[cfg(feature = "experimental-inspect")]
873 FunctionIntrospectionData {
874 names: &["__str__"],
875 arguments: Vec::new(),
876 returns: parse_quote! { ::std::string::String },
877 },
878 ctx,
879 )
880 .unwrap();
881 (fmt_impl, fmt_slot)
882}
883
884fn implement_pyclass_str(
885 options: &PyClassPyO3Options,
886 ty: &syn::Type,
887 ctx: &Ctx,
888) -> (Option<ImplItemFn>, Option<MethodAndSlotDef>) {
889 match &options.str {
890 Some(option) => {
891 let (default_str, default_str_slot) = implement_py_formatting(ty, ctx, option);
892 (Some(default_str), Some(default_str_slot))
893 }
894 _ => (None, None),
895 }
896}
897
898fn impl_enum(
899 enum_: PyClassEnum<'_>,
900 args: &PyClassArgs,
901 doc: PythonDoc,
902 methods_type: PyClassMethodsType,
903 ctx: &Ctx,
904) -> Result<TokenStream> {
905 if let Some(str_fmt) = &args.options.str {
906 ensure_spanned!(str_fmt.value.is_none(), str_fmt.value.span() => "The format string syntax cannot be used with enums")
907 }
908
909 match enum_ {
910 PyClassEnum::Simple(simple_enum) => {
911 impl_simple_enum(simple_enum, args, doc, methods_type, ctx)
912 }
913 PyClassEnum::Complex(complex_enum) => {
914 impl_complex_enum(complex_enum, args, doc, methods_type, ctx)
915 }
916 }
917}
918
919fn impl_simple_enum(
920 simple_enum: PyClassSimpleEnum<'_>,
921 args: &PyClassArgs,
922 doc: PythonDoc,
923 methods_type: PyClassMethodsType,
924 ctx: &Ctx,
925) -> Result<TokenStream> {
926 let cls = simple_enum.ident;
927 let ty: syn::Type = syn::parse_quote!(#cls);
928 let variants = simple_enum.variants;
929 let pytypeinfo = impl_pytypeinfo(cls, args, ctx);
930
931 for variant in &variants {
932 ensure_spanned!(variant.options.constructor.is_none(), variant.options.constructor.span() => "`constructor` can't be used on a simple enum variant");
933 }
934
935 let variant_cfg_check = generate_cfg_check(&variants, cls);
936
937 let (default_repr, default_repr_slot) = {
938 let variants_repr = variants.iter().map(|variant| {
939 let variant_name = variant.ident;
940 let cfg_attrs = &variant.cfg_attrs;
941 let repr = format!(
943 "{}.{}",
944 get_class_python_name(cls, args),
945 variant.get_python_name(args),
946 );
947 quote! { #(#cfg_attrs)* #cls::#variant_name => #repr, }
948 });
949 let mut repr_impl: syn::ImplItemFn = syn::parse_quote! {
950 fn __pyo3__repr__(&self) -> &'static str {
951 match *self {
952 #(#variants_repr)*
953 }
954 }
955 };
956 let repr_slot = generate_default_protocol_slot(&ty, &mut repr_impl, &__REPR__, ctx)?;
957 (repr_impl, repr_slot)
958 };
959
960 let (default_str, default_str_slot) = implement_pyclass_str(&args.options, &ty, ctx);
961
962 let repr_type = &simple_enum.repr_type;
963
964 let (default_int, default_int_slot) = {
965 let variants_to_int = variants.iter().map(|variant| {
967 let variant_name = variant.ident;
968 let cfg_attrs = &variant.cfg_attrs;
969 quote! { #(#cfg_attrs)* #cls::#variant_name => #cls::#variant_name as #repr_type, }
970 });
971 let mut int_impl: syn::ImplItemFn = syn::parse_quote! {
972 fn __pyo3__int__(&self) -> #repr_type {
973 match *self {
974 #(#variants_to_int)*
975 }
976 }
977 };
978 let int_slot = generate_default_protocol_slot(&ty, &mut int_impl, &__INT__, ctx)?;
979 (int_impl, int_slot)
980 };
981
982 let (default_richcmp, default_richcmp_slot) = pyclass_richcmp_simple_enum(
983 &args.options,
984 &ty,
985 repr_type,
986 #[cfg(feature = "experimental-inspect")]
987 &get_class_python_name(cls, args).to_string(),
988 ctx,
989 )?;
990 let (default_hash, default_hash_slot) = pyclass_hash(&args.options, &ty, ctx)?;
991
992 let mut default_slots = vec![default_repr_slot, default_int_slot];
993 default_slots.extend(default_richcmp_slot);
994 default_slots.extend(default_hash_slot);
995 default_slots.extend(default_str_slot);
996
997 let pyclass_impls = PyClassImplsBuilder::new(
998 cls,
999 args,
1000 methods_type,
1001 simple_enum_default_methods(
1002 cls,
1003 variants
1004 .iter()
1005 .map(|v| (v.ident, v.get_python_name(args), &v.cfg_attrs)),
1006 ctx,
1007 ),
1008 default_slots,
1009 )
1010 .doc(doc)
1011 .impl_all(ctx)?;
1012
1013 Ok(quote! {
1014 #variant_cfg_check
1015
1016 #pytypeinfo
1017
1018 #pyclass_impls
1019
1020 #[doc(hidden)]
1021 #[allow(non_snake_case)]
1022 impl #cls {
1023 #default_repr
1024 #default_int
1025 #default_richcmp
1026 #default_hash
1027 #default_str
1028 }
1029 })
1030}
1031
1032fn impl_complex_enum(
1033 complex_enum: PyClassComplexEnum<'_>,
1034 args: &PyClassArgs,
1035 doc: PythonDoc,
1036 methods_type: PyClassMethodsType,
1037 ctx: &Ctx,
1038) -> Result<TokenStream> {
1039 let Ctx { pyo3_path, .. } = ctx;
1040 let cls = complex_enum.ident;
1041 let ty: syn::Type = syn::parse_quote!(#cls);
1042
1043 let args = {
1045 let mut rigged_args = args.clone();
1046 rigged_args.options.frozen = parse_quote!(frozen);
1048 rigged_args.options.subclass = parse_quote!(subclass);
1050 rigged_args
1051 };
1052
1053 let ctx = &Ctx::new(&args.options.krate, None);
1054 let cls = complex_enum.ident;
1055 let variants = complex_enum.variants;
1056 let pytypeinfo = impl_pytypeinfo(cls, &args, ctx);
1057
1058 let (default_richcmp, default_richcmp_slot) = pyclass_richcmp(&args.options, &ty, ctx)?;
1059 let (default_hash, default_hash_slot) = pyclass_hash(&args.options, &ty, ctx)?;
1060
1061 let (default_str, default_str_slot) = implement_pyclass_str(&args.options, &ty, ctx);
1062
1063 let mut default_slots = vec![];
1064 default_slots.extend(default_richcmp_slot);
1065 default_slots.extend(default_hash_slot);
1066 default_slots.extend(default_str_slot);
1067
1068 let impl_builder = PyClassImplsBuilder::new(
1069 cls,
1070 &args,
1071 methods_type,
1072 complex_enum_default_methods(
1073 cls,
1074 variants
1075 .iter()
1076 .map(|v| (v.get_ident(), v.get_python_name(&args))),
1077 ctx,
1078 ),
1079 default_slots,
1080 )
1081 .doc(doc);
1082
1083 let enum_into_pyobject_impl = {
1084 let match_arms = variants
1085 .iter()
1086 .map(|variant| {
1087 let variant_ident = variant.get_ident();
1088 let variant_cls = gen_complex_enum_variant_class_ident(cls, variant.get_ident());
1089 quote! {
1090 #cls::#variant_ident { .. } => {
1091 let pyclass_init = <#pyo3_path::PyClassInitializer<Self> as ::std::convert::From<Self>>::from(self).add_subclass(#variant_cls);
1092 unsafe { #pyo3_path::Bound::new(py, pyclass_init).map(|b| b.cast_into_unchecked()) }
1093 }
1094 }
1095 });
1096 let output_type = if cfg!(feature = "experimental-inspect") {
1097 let full_name = get_class_python_module_and_name(cls, &args);
1098 quote! { const OUTPUT_TYPE: &'static str = #full_name; }
1099 } else {
1100 quote! {}
1101 };
1102 quote! {
1103 impl<'py> #pyo3_path::conversion::IntoPyObject<'py> for #cls {
1104 type Target = Self;
1105 type Output = #pyo3_path::Bound<'py, <Self as #pyo3_path::conversion::IntoPyObject<'py>>::Target>;
1106 type Error = #pyo3_path::PyErr;
1107 #output_type
1108
1109 fn into_pyobject(self, py: #pyo3_path::Python<'py>) -> ::std::result::Result<
1110 <Self as #pyo3_path::conversion::IntoPyObject>::Output,
1111 <Self as #pyo3_path::conversion::IntoPyObject>::Error,
1112 > {
1113 match self {
1114 #(#match_arms)*
1115 }
1116 }
1117 }
1118 }
1119 };
1120
1121 let pyclass_impls: TokenStream = [
1122 impl_builder.impl_pyclass(ctx),
1123 impl_builder.impl_extractext(ctx),
1124 enum_into_pyobject_impl,
1125 impl_builder.impl_pyclassimpl(ctx)?,
1126 impl_builder.impl_add_to_module(ctx),
1127 impl_builder.impl_freelist(ctx),
1128 impl_builder.impl_introspection(ctx),
1129 ]
1130 .into_iter()
1131 .collect();
1132
1133 let mut variant_cls_zsts = vec![];
1134 let mut variant_cls_pytypeinfos = vec![];
1135 let mut variant_cls_pyclass_impls = vec![];
1136 let mut variant_cls_impls = vec![];
1137 for variant in variants {
1138 let variant_cls = gen_complex_enum_variant_class_ident(cls, variant.get_ident());
1139
1140 let variant_cls_zst = quote! {
1141 #[doc(hidden)]
1142 #[allow(non_camel_case_types)]
1143 struct #variant_cls;
1144 };
1145 variant_cls_zsts.push(variant_cls_zst);
1146
1147 let variant_args = PyClassArgs {
1148 class_kind: PyClassKind::Struct,
1149 options: {
1151 let mut rigged_options: PyClassPyO3Options = parse_quote!(extends = #cls, frozen);
1152 rigged_options.module.clone_from(&args.options.module);
1154 rigged_options
1155 },
1156 };
1157
1158 let variant_cls_pytypeinfo = impl_pytypeinfo(&variant_cls, &variant_args, ctx);
1159 variant_cls_pytypeinfos.push(variant_cls_pytypeinfo);
1160
1161 let (variant_cls_impl, field_getters, mut slots) =
1162 impl_complex_enum_variant_cls(cls, &variant, ctx)?;
1163 variant_cls_impls.push(variant_cls_impl);
1164
1165 let variant_new = complex_enum_variant_new(cls, variant, ctx)?;
1166 slots.push(variant_new);
1167
1168 let pyclass_impl = PyClassImplsBuilder::new(
1169 &variant_cls,
1170 &variant_args,
1171 methods_type,
1172 field_getters,
1173 slots,
1174 )
1175 .impl_all(ctx)?;
1176
1177 variant_cls_pyclass_impls.push(pyclass_impl);
1178 }
1179
1180 Ok(quote! {
1181 #pytypeinfo
1182
1183 #pyclass_impls
1184
1185 #[doc(hidden)]
1186 #[allow(non_snake_case)]
1187 impl #cls {
1188 #default_richcmp
1189 #default_hash
1190 #default_str
1191 }
1192
1193 #(#variant_cls_zsts)*
1194
1195 #(#variant_cls_pytypeinfos)*
1196
1197 #(#variant_cls_pyclass_impls)*
1198
1199 #(#variant_cls_impls)*
1200 })
1201}
1202
1203fn impl_complex_enum_variant_cls(
1204 enum_name: &syn::Ident,
1205 variant: &PyClassEnumVariant<'_>,
1206 ctx: &Ctx,
1207) -> Result<(TokenStream, Vec<MethodAndMethodDef>, Vec<MethodAndSlotDef>)> {
1208 match variant {
1209 PyClassEnumVariant::Struct(struct_variant) => {
1210 impl_complex_enum_struct_variant_cls(enum_name, struct_variant, ctx)
1211 }
1212 PyClassEnumVariant::Tuple(tuple_variant) => {
1213 impl_complex_enum_tuple_variant_cls(enum_name, tuple_variant, ctx)
1214 }
1215 }
1216}
1217
1218fn impl_complex_enum_variant_match_args(
1219 ctx @ Ctx { pyo3_path, .. }: &Ctx,
1220 variant_cls_type: &syn::Type,
1221 field_names: &[Ident],
1222) -> syn::Result<(MethodAndMethodDef, syn::ImplItemFn)> {
1223 let ident = format_ident!("__match_args__");
1224 let field_names_unraw = field_names.iter().map(|name| name.unraw());
1225 let mut match_args_impl: syn::ImplItemFn = {
1226 parse_quote! {
1227 #[classattr]
1228 fn #ident(py: #pyo3_path::Python<'_>) -> #pyo3_path::PyResult<#pyo3_path::Bound<'_, #pyo3_path::types::PyTuple>> {
1229 #pyo3_path::types::PyTuple::new::<&str, _>(py, [
1230 #(stringify!(#field_names_unraw),)*
1231 ])
1232 }
1233 }
1234 };
1235
1236 let spec = FnSpec::parse(
1237 &mut match_args_impl.sig,
1238 &mut match_args_impl.attrs,
1239 Default::default(),
1240 )?;
1241 let variant_match_args = impl_py_class_attribute(variant_cls_type, &spec, ctx)?;
1242
1243 Ok((variant_match_args, match_args_impl))
1244}
1245
1246fn impl_complex_enum_struct_variant_cls(
1247 enum_name: &syn::Ident,
1248 variant: &PyClassEnumStructVariant<'_>,
1249 ctx: &Ctx,
1250) -> Result<(TokenStream, Vec<MethodAndMethodDef>, Vec<MethodAndSlotDef>)> {
1251 let Ctx { pyo3_path, .. } = ctx;
1252 let variant_ident = &variant.ident;
1253 let variant_cls = gen_complex_enum_variant_class_ident(enum_name, variant.ident);
1254 let variant_cls_type = parse_quote!(#variant_cls);
1255
1256 let mut field_names: Vec<Ident> = vec![];
1257 let mut fields_with_types: Vec<TokenStream> = vec![];
1258 let mut field_getters = vec![];
1259 let mut field_getter_impls: Vec<TokenStream> = vec![];
1260 for field in &variant.fields {
1261 let field_name = field.ident;
1262 let field_type = field.ty;
1263 let field_with_type = quote! { #field_name: #field_type };
1264
1265 let field_getter =
1266 complex_enum_variant_field_getter(&variant_cls_type, field_name, field.span, ctx)?;
1267
1268 let field_getter_impl = quote! {
1269 fn #field_name(slf: #pyo3_path::PyClassGuard<'_, Self>, py: #pyo3_path::Python<'_>) -> #pyo3_path::PyResult<#pyo3_path::Py<#pyo3_path::PyAny>> {
1270 #[allow(unused_imports)]
1271 use #pyo3_path::impl_::pyclass::Probe as _;
1272 match &*slf.into_super() {
1273 #enum_name::#variant_ident { #field_name, .. } =>
1274 #pyo3_path::impl_::pyclass::ConvertField::<
1275 { #pyo3_path::impl_::pyclass::IsIntoPyObjectRef::<#field_type>::VALUE },
1276 { #pyo3_path::impl_::pyclass::IsIntoPyObject::<#field_type>::VALUE },
1277 >::convert_field::<#field_type>(#field_name, py),
1278 _ => ::core::unreachable!("Wrong complex enum variant found in variant wrapper PyClass"),
1279 }
1280 }
1281 };
1282
1283 field_names.push(field_name.clone());
1284 fields_with_types.push(field_with_type);
1285 field_getters.push(field_getter);
1286 field_getter_impls.push(field_getter_impl);
1287 }
1288
1289 let (variant_match_args, match_args_const_impl) =
1290 impl_complex_enum_variant_match_args(ctx, &variant_cls_type, &field_names)?;
1291
1292 field_getters.push(variant_match_args);
1293
1294 let cls_impl = quote! {
1295 #[doc(hidden)]
1296 #[allow(non_snake_case)]
1297 impl #variant_cls {
1298 #[allow(clippy::too_many_arguments)]
1299 fn __pymethod_constructor__(py: #pyo3_path::Python<'_>, #(#fields_with_types,)*) -> #pyo3_path::PyClassInitializer<#variant_cls> {
1300 let base_value = #enum_name::#variant_ident { #(#field_names,)* };
1301 <#pyo3_path::PyClassInitializer<#enum_name> as ::std::convert::From<#enum_name>>::from(base_value).add_subclass(#variant_cls)
1302 }
1303
1304 #match_args_const_impl
1305
1306 #(#field_getter_impls)*
1307 }
1308 };
1309
1310 Ok((cls_impl, field_getters, Vec::new()))
1311}
1312
1313fn impl_complex_enum_tuple_variant_field_getters(
1314 ctx: &Ctx,
1315 variant: &PyClassEnumTupleVariant<'_>,
1316 enum_name: &syn::Ident,
1317 variant_cls_type: &syn::Type,
1318 variant_ident: &&Ident,
1319 field_names: &mut Vec<Ident>,
1320 fields_types: &mut Vec<syn::Type>,
1321) -> Result<(Vec<MethodAndMethodDef>, Vec<syn::ImplItemFn>)> {
1322 let Ctx { pyo3_path, .. } = ctx;
1323
1324 let mut field_getters = vec![];
1325 let mut field_getter_impls = vec![];
1326
1327 for (index, field) in variant.fields.iter().enumerate() {
1328 let field_name = format_ident!("_{}", index);
1329 let field_type = field.ty;
1330
1331 let field_getter =
1332 complex_enum_variant_field_getter(variant_cls_type, &field_name, field.span, ctx)?;
1333
1334 let field_access_tokens: Vec<_> = (0..variant.fields.len())
1336 .map(|i| {
1337 if i == index {
1338 quote! { val }
1339 } else {
1340 quote! { _ }
1341 }
1342 })
1343 .collect();
1344 let field_getter_impl: syn::ImplItemFn = parse_quote! {
1345 fn #field_name(slf: #pyo3_path::PyClassGuard<'_, Self>, py: #pyo3_path::Python<'_>) -> #pyo3_path::PyResult<#pyo3_path::Py<#pyo3_path::PyAny>> {
1346 #[allow(unused_imports)]
1347 use #pyo3_path::impl_::pyclass::Probe as _;
1348 match &*slf.into_super() {
1349 #enum_name::#variant_ident ( #(#field_access_tokens), *) =>
1350 #pyo3_path::impl_::pyclass::ConvertField::<
1351 { #pyo3_path::impl_::pyclass::IsIntoPyObjectRef::<#field_type>::VALUE },
1352 { #pyo3_path::impl_::pyclass::IsIntoPyObject::<#field_type>::VALUE },
1353 >::convert_field::<#field_type>(val, py),
1354 _ => ::core::unreachable!("Wrong complex enum variant found in variant wrapper PyClass"),
1355 }
1356 }
1357 };
1358
1359 field_names.push(field_name);
1360 fields_types.push(field_type.clone());
1361 field_getters.push(field_getter);
1362 field_getter_impls.push(field_getter_impl);
1363 }
1364
1365 Ok((field_getters, field_getter_impls))
1366}
1367
1368fn impl_complex_enum_tuple_variant_len(
1369 ctx: &Ctx,
1370
1371 variant_cls_type: &syn::Type,
1372 num_fields: usize,
1373) -> Result<(MethodAndSlotDef, syn::ImplItemFn)> {
1374 let Ctx { pyo3_path, .. } = ctx;
1375
1376 let mut len_method_impl: syn::ImplItemFn = parse_quote! {
1377 fn __len__(slf: #pyo3_path::PyClassGuard<'_, Self>) -> #pyo3_path::PyResult<usize> {
1378 ::std::result::Result::Ok(#num_fields)
1379 }
1380 };
1381
1382 let variant_len =
1383 generate_default_protocol_slot(variant_cls_type, &mut len_method_impl, &__LEN__, ctx)?;
1384
1385 Ok((variant_len, len_method_impl))
1386}
1387
1388fn impl_complex_enum_tuple_variant_getitem(
1389 ctx: &Ctx,
1390 variant_cls: &syn::Ident,
1391 variant_cls_type: &syn::Type,
1392 num_fields: usize,
1393) -> Result<(MethodAndSlotDef, syn::ImplItemFn)> {
1394 let Ctx { pyo3_path, .. } = ctx;
1395
1396 let match_arms: Vec<_> = (0..num_fields)
1397 .map(|i| {
1398 let field_access = format_ident!("_{}", i);
1399 quote! { #i =>
1400 #pyo3_path::IntoPyObjectExt::into_py_any(#variant_cls::#field_access(slf, py)?, py)
1401 }
1402 })
1403 .collect();
1404
1405 let mut get_item_method_impl: syn::ImplItemFn = parse_quote! {
1406 fn __getitem__(slf: #pyo3_path::PyClassGuard<'_, Self>, py: #pyo3_path::Python<'_>, idx: usize) -> #pyo3_path::PyResult< #pyo3_path::Py<#pyo3_path::PyAny>> {
1407 match idx {
1408 #( #match_arms, )*
1409 _ => ::std::result::Result::Err(#pyo3_path::exceptions::PyIndexError::new_err("tuple index out of range")),
1410 }
1411 }
1412 };
1413
1414 let variant_getitem = generate_default_protocol_slot(
1415 variant_cls_type,
1416 &mut get_item_method_impl,
1417 &__GETITEM__,
1418 ctx,
1419 )?;
1420
1421 Ok((variant_getitem, get_item_method_impl))
1422}
1423
1424fn impl_complex_enum_tuple_variant_cls(
1425 enum_name: &syn::Ident,
1426 variant: &PyClassEnumTupleVariant<'_>,
1427 ctx: &Ctx,
1428) -> Result<(TokenStream, Vec<MethodAndMethodDef>, Vec<MethodAndSlotDef>)> {
1429 let Ctx { pyo3_path, .. } = ctx;
1430 let variant_ident = &variant.ident;
1431 let variant_cls = gen_complex_enum_variant_class_ident(enum_name, variant.ident);
1432 let variant_cls_type = parse_quote!(#variant_cls);
1433
1434 let mut slots = vec![];
1435
1436 let mut field_names: Vec<Ident> = vec![];
1438 let mut field_types: Vec<syn::Type> = vec![];
1439
1440 let (mut field_getters, field_getter_impls) = impl_complex_enum_tuple_variant_field_getters(
1441 ctx,
1442 variant,
1443 enum_name,
1444 &variant_cls_type,
1445 variant_ident,
1446 &mut field_names,
1447 &mut field_types,
1448 )?;
1449
1450 let num_fields = variant.fields.len();
1451
1452 let (variant_len, len_method_impl) =
1453 impl_complex_enum_tuple_variant_len(ctx, &variant_cls_type, num_fields)?;
1454
1455 slots.push(variant_len);
1456
1457 let (variant_getitem, getitem_method_impl) =
1458 impl_complex_enum_tuple_variant_getitem(ctx, &variant_cls, &variant_cls_type, num_fields)?;
1459
1460 slots.push(variant_getitem);
1461
1462 let (variant_match_args, match_args_method_impl) =
1463 impl_complex_enum_variant_match_args(ctx, &variant_cls_type, &field_names)?;
1464
1465 field_getters.push(variant_match_args);
1466
1467 let cls_impl = quote! {
1468 #[doc(hidden)]
1469 #[allow(non_snake_case)]
1470 impl #variant_cls {
1471 #[allow(clippy::too_many_arguments)]
1472 fn __pymethod_constructor__(py: #pyo3_path::Python<'_>, #(#field_names : #field_types,)*) -> #pyo3_path::PyClassInitializer<#variant_cls> {
1473 let base_value = #enum_name::#variant_ident ( #(#field_names,)* );
1474 <#pyo3_path::PyClassInitializer<#enum_name> as ::std::convert::From<#enum_name>>::from(base_value).add_subclass(#variant_cls)
1475 }
1476
1477 #len_method_impl
1478
1479 #getitem_method_impl
1480
1481 #match_args_method_impl
1482
1483 #(#field_getter_impls)*
1484 }
1485 };
1486
1487 Ok((cls_impl, field_getters, slots))
1488}
1489
1490fn gen_complex_enum_variant_class_ident(enum_: &syn::Ident, variant: &syn::Ident) -> syn::Ident {
1491 format_ident!("{}_{}", enum_, variant)
1492}
1493
1494#[cfg(feature = "experimental-inspect")]
1495struct FunctionIntrospectionData<'a> {
1496 names: &'a [&'a str],
1497 arguments: Vec<FnArg<'a>>,
1498 returns: syn::Type,
1499}
1500
1501fn generate_protocol_slot(
1502 cls: &syn::Type,
1503 method: &mut syn::ImplItemFn,
1504 slot: &SlotDef,
1505 name: &str,
1506 #[cfg(feature = "experimental-inspect")] introspection_data: FunctionIntrospectionData<'_>,
1507 ctx: &Ctx,
1508) -> syn::Result<MethodAndSlotDef> {
1509 let spec = FnSpec::parse(
1510 &mut method.sig,
1511 &mut Vec::new(),
1512 PyFunctionOptions::default(),
1513 )?;
1514 #[cfg_attr(not(feature = "experimental-inspect"), allow(unused_mut))]
1515 let mut def = slot.generate_type_slot(&syn::parse_quote!(#cls), &spec, name, ctx)?;
1516 #[cfg(feature = "experimental-inspect")]
1517 {
1518 let associated_method = def.associated_method;
1520 let signature = FunctionSignature::from_arguments(introspection_data.arguments);
1521 let returns = introspection_data.returns;
1522 let introspection = introspection_data
1523 .names
1524 .iter()
1525 .map(|name| {
1526 function_introspection_code(
1527 &ctx.pyo3_path,
1528 None,
1529 name,
1530 &signature,
1531 Some("self"),
1532 parse_quote!(-> #returns),
1533 [],
1534 Some(cls),
1535 )
1536 })
1537 .collect::<Vec<_>>();
1538 let const_name = format_ident!("_{}", unique_element_id()); def.associated_method = quote! {
1540 #associated_method
1541 const #const_name: () = {
1542 #(#introspection)*
1543 };
1544 };
1545 }
1546 Ok(def)
1547}
1548
1549fn generate_default_protocol_slot(
1550 cls: &syn::Type,
1551 method: &mut syn::ImplItemFn,
1552 slot: &SlotDef,
1553 ctx: &Ctx,
1554) -> syn::Result<MethodAndSlotDef> {
1555 let spec = FnSpec::parse(
1556 &mut method.sig,
1557 &mut Vec::new(),
1558 PyFunctionOptions::default(),
1559 )?;
1560 let name = spec.name.to_string();
1561 slot.generate_type_slot(
1562 &syn::parse_quote!(#cls),
1563 &spec,
1564 &format!("__default_{name}__"),
1565 ctx,
1566 )
1567}
1568
1569fn simple_enum_default_methods<'a>(
1570 cls: &'a syn::Ident,
1571 unit_variant_names: impl IntoIterator<
1572 Item = (
1573 &'a syn::Ident,
1574 Cow<'a, syn::Ident>,
1575 &'a Vec<&'a syn::Attribute>,
1576 ),
1577 >,
1578 ctx: &Ctx,
1579) -> Vec<MethodAndMethodDef> {
1580 let cls_type = syn::parse_quote!(#cls);
1581 let variant_to_attribute = |var_ident: &syn::Ident, py_ident: &syn::Ident| ConstSpec {
1582 rust_ident: var_ident.clone(),
1583 attributes: ConstAttributes {
1584 is_class_attr: true,
1585 name: Some(NameAttribute {
1586 kw: syn::parse_quote! { name },
1587 value: NameLitStr(py_ident.clone()),
1588 }),
1589 },
1590 };
1591 unit_variant_names
1592 .into_iter()
1593 .map(|(var, py_name, attrs)| {
1594 let method = gen_py_const(&cls_type, &variant_to_attribute(var, &py_name), ctx);
1595 let associated_method_tokens = method.associated_method;
1596 let method_def_tokens = method.method_def;
1597
1598 let associated_method = quote! {
1599 #(#attrs)*
1600 #associated_method_tokens
1601 };
1602 let method_def = quote! {
1603 #(#attrs)*
1604 #method_def_tokens
1605 };
1606
1607 MethodAndMethodDef {
1608 associated_method,
1609 method_def,
1610 }
1611 })
1612 .collect()
1613}
1614
1615fn complex_enum_default_methods<'a>(
1616 cls: &'a syn::Ident,
1617 variant_names: impl IntoIterator<Item = (&'a syn::Ident, Cow<'a, syn::Ident>)>,
1618 ctx: &Ctx,
1619) -> Vec<MethodAndMethodDef> {
1620 let cls_type = syn::parse_quote!(#cls);
1621 let variant_to_attribute = |var_ident: &syn::Ident, py_ident: &syn::Ident| ConstSpec {
1622 rust_ident: var_ident.clone(),
1623 attributes: ConstAttributes {
1624 is_class_attr: true,
1625 name: Some(NameAttribute {
1626 kw: syn::parse_quote! { name },
1627 value: NameLitStr(py_ident.clone()),
1628 }),
1629 },
1630 };
1631 variant_names
1632 .into_iter()
1633 .map(|(var, py_name)| {
1634 gen_complex_enum_variant_attr(cls, &cls_type, &variant_to_attribute(var, &py_name), ctx)
1635 })
1636 .collect()
1637}
1638
1639pub fn gen_complex_enum_variant_attr(
1640 cls: &syn::Ident,
1641 cls_type: &syn::Type,
1642 spec: &ConstSpec,
1643 ctx: &Ctx,
1644) -> MethodAndMethodDef {
1645 let Ctx { pyo3_path, .. } = ctx;
1646 let member = &spec.rust_ident;
1647 let wrapper_ident = format_ident!("__pymethod_variant_cls_{}__", member);
1648 let python_name = spec.null_terminated_python_name(ctx);
1649
1650 let variant_cls = format_ident!("{}_{}", cls, member);
1651 let associated_method = quote! {
1652 fn #wrapper_ident(py: #pyo3_path::Python<'_>) -> #pyo3_path::PyResult<#pyo3_path::Py<#pyo3_path::PyAny>> {
1653 ::std::result::Result::Ok(py.get_type::<#variant_cls>().into_any().unbind())
1654 }
1655 };
1656
1657 let method_def = quote! {
1658 #pyo3_path::impl_::pyclass::MaybeRuntimePyMethodDef::Static(
1659 #pyo3_path::impl_::pymethods::PyMethodDefType::ClassAttribute({
1660 #pyo3_path::impl_::pymethods::PyClassAttributeDef::new(
1661 #python_name,
1662 #cls_type::#wrapper_ident
1663 )
1664 })
1665 )
1666 };
1667
1668 MethodAndMethodDef {
1669 associated_method,
1670 method_def,
1671 }
1672}
1673
1674fn complex_enum_variant_new<'a>(
1675 cls: &'a syn::Ident,
1676 variant: PyClassEnumVariant<'a>,
1677 ctx: &Ctx,
1678) -> Result<MethodAndSlotDef> {
1679 match variant {
1680 PyClassEnumVariant::Struct(struct_variant) => {
1681 complex_enum_struct_variant_new(cls, struct_variant, ctx)
1682 }
1683 PyClassEnumVariant::Tuple(tuple_variant) => {
1684 complex_enum_tuple_variant_new(cls, tuple_variant, ctx)
1685 }
1686 }
1687}
1688
1689fn complex_enum_struct_variant_new<'a>(
1690 cls: &'a syn::Ident,
1691 variant: PyClassEnumStructVariant<'a>,
1692 ctx: &Ctx,
1693) -> Result<MethodAndSlotDef> {
1694 let Ctx { pyo3_path, .. } = ctx;
1695 let variant_cls = format_ident!("{}_{}", cls, variant.ident);
1696 let variant_cls_type: syn::Type = parse_quote!(#variant_cls);
1697
1698 let arg_py_ident: syn::Ident = parse_quote!(py);
1699 let arg_py_type: syn::Type = parse_quote!(#pyo3_path::Python<'_>);
1700
1701 let args = {
1702 let mut args = vec![
1703 FnArg::Py(PyArg {
1705 name: &arg_py_ident,
1706 ty: &arg_py_type,
1707 }),
1708 ];
1709
1710 for field in &variant.fields {
1711 args.push(FnArg::Regular(RegularArg {
1712 name: Cow::Borrowed(field.ident),
1713 ty: field.ty,
1714 from_py_with: None,
1715 default_value: None,
1716 option_wrapped_type: None,
1717 #[cfg(feature = "experimental-inspect")]
1718 annotation: None,
1719 }));
1720 }
1721 args
1722 };
1723
1724 let signature = if let Some(constructor) = variant.options.constructor {
1725 crate::pyfunction::FunctionSignature::from_arguments_and_attribute(
1726 args,
1727 constructor.into_signature(),
1728 )?
1729 } else {
1730 crate::pyfunction::FunctionSignature::from_arguments(args)
1731 };
1732
1733 let spec = FnSpec {
1734 tp: crate::method::FnType::FnNew,
1735 name: &format_ident!("__pymethod_constructor__"),
1736 python_name: format_ident!("__new__"),
1737 signature,
1738 convention: crate::method::CallingConvention::TpNew,
1739 text_signature: None,
1740 asyncness: None,
1741 unsafety: None,
1742 warnings: vec![],
1743 #[cfg(feature = "experimental-inspect")]
1744 output: syn::ReturnType::Default,
1745 };
1746
1747 crate::pymethod::impl_py_method_def_new(&variant_cls_type, &spec, ctx)
1748}
1749
1750fn complex_enum_tuple_variant_new<'a>(
1751 cls: &'a syn::Ident,
1752 variant: PyClassEnumTupleVariant<'a>,
1753 ctx: &Ctx,
1754) -> Result<MethodAndSlotDef> {
1755 let Ctx { pyo3_path, .. } = ctx;
1756
1757 let variant_cls: Ident = format_ident!("{}_{}", cls, variant.ident);
1758 let variant_cls_type: syn::Type = parse_quote!(#variant_cls);
1759
1760 let arg_py_ident: syn::Ident = parse_quote!(py);
1761 let arg_py_type: syn::Type = parse_quote!(#pyo3_path::Python<'_>);
1762
1763 let args = {
1764 let mut args = vec![FnArg::Py(PyArg {
1765 name: &arg_py_ident,
1766 ty: &arg_py_type,
1767 })];
1768
1769 for (i, field) in variant.fields.iter().enumerate() {
1770 args.push(FnArg::Regular(RegularArg {
1771 name: std::borrow::Cow::Owned(format_ident!("_{}", i)),
1772 ty: field.ty,
1773 from_py_with: None,
1774 default_value: None,
1775 option_wrapped_type: None,
1776 #[cfg(feature = "experimental-inspect")]
1777 annotation: None,
1778 }));
1779 }
1780 args
1781 };
1782
1783 let signature = if let Some(constructor) = variant.options.constructor {
1784 crate::pyfunction::FunctionSignature::from_arguments_and_attribute(
1785 args,
1786 constructor.into_signature(),
1787 )?
1788 } else {
1789 crate::pyfunction::FunctionSignature::from_arguments(args)
1790 };
1791
1792 let spec = FnSpec {
1793 tp: crate::method::FnType::FnNew,
1794 name: &format_ident!("__pymethod_constructor__"),
1795 python_name: format_ident!("__new__"),
1796 signature,
1797 convention: crate::method::CallingConvention::TpNew,
1798 text_signature: None,
1799 asyncness: None,
1800 unsafety: None,
1801 warnings: vec![],
1802 #[cfg(feature = "experimental-inspect")]
1803 output: syn::ReturnType::Default,
1804 };
1805
1806 crate::pymethod::impl_py_method_def_new(&variant_cls_type, &spec, ctx)
1807}
1808
1809fn complex_enum_variant_field_getter<'a>(
1810 variant_cls_type: &'a syn::Type,
1811 field_name: &'a syn::Ident,
1812 field_span: Span,
1813 ctx: &Ctx,
1814) -> Result<MethodAndMethodDef> {
1815 let mut arg = parse_quote!(py: Python<'_>);
1816 let py = FnArg::parse(&mut arg)?;
1817 let signature = crate::pyfunction::FunctionSignature::from_arguments(vec![py]);
1818
1819 let self_type = crate::method::SelfType::TryFromBoundRef(field_span);
1820
1821 let spec = FnSpec {
1822 tp: crate::method::FnType::Getter(self_type.clone()),
1823 name: field_name,
1824 python_name: field_name.unraw(),
1825 signature,
1826 convention: crate::method::CallingConvention::Noargs,
1827 text_signature: None,
1828 asyncness: None,
1829 unsafety: None,
1830 warnings: vec![],
1831 #[cfg(feature = "experimental-inspect")]
1832 output: syn::ReturnType::Type(Token, Box::new(variant_cls_type.clone())),
1833 };
1834
1835 let property_type = crate::pymethod::PropertyType::Function {
1836 self_type: &self_type,
1837 spec: &spec,
1838 doc: crate::get_doc(&[], None, ctx)?,
1839 };
1840
1841 let getter = crate::pymethod::impl_py_getter_def(variant_cls_type, property_type, ctx)?;
1842 Ok(getter)
1843}
1844
1845fn descriptors_to_items(
1846 cls: &syn::Ident,
1847 rename_all: Option<&RenameAllAttribute>,
1848 frozen: Option<frozen>,
1849 field_options: Vec<(&syn::Field, FieldPyO3Options)>,
1850 ctx: &Ctx,
1851) -> syn::Result<Vec<MethodAndMethodDef>> {
1852 let ty = syn::parse_quote!(#cls);
1853 let mut items = Vec::new();
1854 for (field_index, (field, options)) in field_options.into_iter().enumerate() {
1855 if let FieldPyO3Options {
1856 name: Some(name),
1857 get: None,
1858 set: None,
1859 } = options
1860 {
1861 return Err(syn::Error::new_spanned(name, USELESS_NAME));
1862 }
1863
1864 if options.get.is_some() {
1865 let getter = impl_py_getter_def(
1866 &ty,
1867 PropertyType::Descriptor {
1868 field_index,
1869 field,
1870 python_name: options.name.as_ref(),
1871 renaming_rule: rename_all.map(|rename_all| rename_all.value.rule),
1872 },
1873 ctx,
1874 )?;
1875 items.push(getter);
1876 }
1877
1878 if let Some(set) = options.set {
1879 ensure_spanned!(frozen.is_none(), set.span() => "cannot use `#[pyo3(set)]` on a `frozen` class");
1880 let setter = impl_py_setter_def(
1881 &ty,
1882 PropertyType::Descriptor {
1883 field_index,
1884 field,
1885 python_name: options.name.as_ref(),
1886 renaming_rule: rename_all.map(|rename_all| rename_all.value.rule),
1887 },
1888 ctx,
1889 )?;
1890 items.push(setter);
1891 };
1892 }
1893 Ok(items)
1894}
1895
1896fn impl_pytypeinfo(cls: &syn::Ident, attr: &PyClassArgs, ctx: &Ctx) -> TokenStream {
1897 let Ctx { pyo3_path, .. } = ctx;
1898 let cls_name = get_class_python_name(cls, attr).to_string();
1899
1900 let module = if let Some(ModuleAttribute { value, .. }) = &attr.options.module {
1901 quote! { ::core::option::Option::Some(#value) }
1902 } else {
1903 quote! { ::core::option::Option::None }
1904 };
1905
1906 quote! {
1907 unsafe impl #pyo3_path::type_object::PyTypeInfo for #cls {
1908 const NAME: &'static str = #cls_name;
1909 const MODULE: ::std::option::Option<&'static str> = #module;
1910
1911 #[inline]
1912 fn type_object_raw(py: #pyo3_path::Python<'_>) -> *mut #pyo3_path::ffi::PyTypeObject {
1913 use #pyo3_path::prelude::PyTypeMethods;
1914 <#cls as #pyo3_path::impl_::pyclass::PyClassImpl>::lazy_type_object()
1915 .get_or_try_init(py)
1916 .unwrap_or_else(|e| #pyo3_path::impl_::pyclass::type_object_init_failed(
1917 py,
1918 e,
1919 <Self as #pyo3_path::type_object::PyTypeInfo>::NAME
1920 ))
1921 .as_type_ptr()
1922 }
1923 }
1924 }
1925}
1926
1927fn pyclass_richcmp_arms(
1928 options: &PyClassPyO3Options,
1929 ctx: &Ctx,
1930) -> std::result::Result<TokenStream, syn::Error> {
1931 let Ctx { pyo3_path, .. } = ctx;
1932
1933 let eq_arms = options
1934 .eq
1935 .map(|eq| eq.span)
1936 .or(options.eq_int.map(|eq_int| eq_int.span))
1937 .map(|span| {
1938 quote_spanned! { span =>
1939 #pyo3_path::pyclass::CompareOp::Eq => {
1940 #pyo3_path::IntoPyObjectExt::into_py_any(self_val == other, py)
1941 },
1942 #pyo3_path::pyclass::CompareOp::Ne => {
1943 #pyo3_path::IntoPyObjectExt::into_py_any(self_val != other, py)
1944 },
1945 }
1946 })
1947 .unwrap_or_default();
1948
1949 if let Some(ord) = options.ord {
1950 ensure_spanned!(options.eq.is_some(), ord.span() => "The `ord` option requires the `eq` option.");
1951 }
1952
1953 let ord_arms = options
1954 .ord
1955 .map(|ord| {
1956 quote_spanned! { ord.span() =>
1957 #pyo3_path::pyclass::CompareOp::Gt => {
1958 #pyo3_path::IntoPyObjectExt::into_py_any(self_val > other, py)
1959 },
1960 #pyo3_path::pyclass::CompareOp::Lt => {
1961 #pyo3_path::IntoPyObjectExt::into_py_any(self_val < other, py)
1962 },
1963 #pyo3_path::pyclass::CompareOp::Le => {
1964 #pyo3_path::IntoPyObjectExt::into_py_any(self_val <= other, py)
1965 },
1966 #pyo3_path::pyclass::CompareOp::Ge => {
1967 #pyo3_path::IntoPyObjectExt::into_py_any(self_val >= other, py)
1968 },
1969 }
1970 })
1971 .unwrap_or_else(|| quote! { _ => ::std::result::Result::Ok(py.NotImplemented()) });
1972
1973 Ok(quote! {
1974 #eq_arms
1975 #ord_arms
1976 })
1977}
1978
1979fn pyclass_richcmp_simple_enum(
1980 options: &PyClassPyO3Options,
1981 cls: &syn::Type,
1982 repr_type: &syn::Ident,
1983 #[cfg(feature = "experimental-inspect")] class_name: &str,
1984 ctx: &Ctx,
1985) -> Result<(Option<syn::ImplItemFn>, Option<MethodAndSlotDef>)> {
1986 let Ctx { pyo3_path, .. } = ctx;
1987 if let Some(eq_int) = options.eq_int {
1988 ensure_spanned!(options.eq.is_some(), eq_int.span() => "The `eq_int` option requires the `eq` option.");
1989 }
1990
1991 if options.eq.is_none() && options.eq_int.is_none() {
1992 return Ok((None, None));
1993 }
1994
1995 let arms = pyclass_richcmp_arms(options, ctx)?;
1996
1997 let eq = options.eq.map(|eq| {
1998 quote_spanned! { eq.span() =>
1999 let self_val = self;
2000 if let ::std::result::Result::Ok(other) = other.cast::<Self>() {
2001 let other = &*other.borrow();
2002 return match op {
2003 #arms
2004 }
2005 }
2006 }
2007 });
2008
2009 let eq_int = options.eq_int.map(|eq_int| {
2010 quote_spanned! { eq_int.span() =>
2011 let self_val = self.__pyo3__int__();
2012 if let ::std::result::Result::Ok(other) = #pyo3_path::types::PyAnyMethods::extract::<#repr_type>(other).or_else(|_| {
2013 other.cast::<Self>().map(|o| o.borrow().__pyo3__int__())
2014 }) {
2015 return match op {
2016 #arms
2017 }
2018 }
2019 }
2020 });
2021
2022 let mut richcmp_impl = parse_quote! {
2023 fn __pyo3__generated____richcmp__(
2024 &self,
2025 py: #pyo3_path::Python,
2026 other: &#pyo3_path::Bound<'_, #pyo3_path::PyAny>,
2027 op: #pyo3_path::pyclass::CompareOp
2028 ) -> #pyo3_path::PyResult<#pyo3_path::Py<#pyo3_path::PyAny>> {
2029 #eq
2030
2031 #eq_int
2032
2033 ::std::result::Result::Ok(py.NotImplemented())
2034 }
2035 };
2036 let richcmp_slot = if options.eq.is_some() {
2037 generate_protocol_slot(
2038 cls,
2039 &mut richcmp_impl,
2040 &__RICHCMP__,
2041 "__richcmp__",
2042 #[cfg(feature = "experimental-inspect")]
2043 FunctionIntrospectionData {
2044 names: &["__eq__", "__ne__"],
2045 arguments: vec![FnArg::Regular(RegularArg {
2046 name: Cow::Owned(format_ident!("other")),
2047 ty: &parse_quote!(!),
2049 from_py_with: None,
2050 default_value: None,
2051 option_wrapped_type: None,
2052 annotation: Some(match (options.eq.is_some(), options.eq_int.is_some()) {
2053 (true, true) => {
2054 format!("{class_name} | int")
2055 }
2056 (true, false) => class_name.into(),
2057 (false, true) => "int".into(),
2058 (false, false) => unreachable!(),
2059 }),
2060 })],
2061 returns: parse_quote! { ::std::primitive::bool },
2062 },
2063 ctx,
2064 )?
2065 } else {
2066 generate_default_protocol_slot(cls, &mut richcmp_impl, &__RICHCMP__, ctx)?
2067 };
2068 Ok((Some(richcmp_impl), Some(richcmp_slot)))
2069}
2070
2071fn pyclass_richcmp(
2072 options: &PyClassPyO3Options,
2073 cls: &syn::Type,
2074 ctx: &Ctx,
2075) -> Result<(Option<syn::ImplItemFn>, Option<MethodAndSlotDef>)> {
2076 let Ctx { pyo3_path, .. } = ctx;
2077 if let Some(eq_int) = options.eq_int {
2078 bail_spanned!(eq_int.span() => "`eq_int` can only be used on simple enums.")
2079 }
2080
2081 let arms = pyclass_richcmp_arms(options, ctx)?;
2082 if options.eq.is_some() {
2083 let mut richcmp_impl = parse_quote! {
2084 fn __pyo3__generated____richcmp__(
2085 &self,
2086 py: #pyo3_path::Python,
2087 other: &#pyo3_path::Bound<'_, #pyo3_path::PyAny>,
2088 op: #pyo3_path::pyclass::CompareOp
2089 ) -> #pyo3_path::PyResult<#pyo3_path::Py<#pyo3_path::PyAny>> {
2090 let self_val = self;
2091 if let ::std::result::Result::Ok(other) = other.cast::<Self>() {
2092 let other = &*other.borrow();
2093 match op {
2094 #arms
2095 }
2096 } else {
2097 ::std::result::Result::Ok(py.NotImplemented())
2098 }
2099 }
2100 };
2101 let richcmp_slot = generate_protocol_slot(
2102 cls,
2103 &mut richcmp_impl,
2104 &__RICHCMP__,
2105 "__richcmp__",
2106 #[cfg(feature = "experimental-inspect")]
2107 FunctionIntrospectionData {
2108 names: if options.ord.is_some() {
2109 &["__eq__", "__ne__", "__lt__", "__le__", "__gt__", "__ge__"]
2110 } else {
2111 &["__eq__", "__ne__"]
2112 },
2113 arguments: vec![FnArg::Regular(RegularArg {
2114 name: Cow::Owned(format_ident!("other")),
2115 ty: &parse_quote!(&#cls),
2116 from_py_with: None,
2117 default_value: None,
2118 option_wrapped_type: None,
2119 annotation: None,
2120 })],
2121 returns: parse_quote! { ::std::primitive::bool },
2122 },
2123 ctx,
2124 )?;
2125 Ok((Some(richcmp_impl), Some(richcmp_slot)))
2126 } else {
2127 Ok((None, None))
2128 }
2129}
2130
2131fn pyclass_hash(
2132 options: &PyClassPyO3Options,
2133 cls: &syn::Type,
2134 ctx: &Ctx,
2135) -> Result<(Option<syn::ImplItemFn>, Option<MethodAndSlotDef>)> {
2136 if options.hash.is_some() {
2137 ensure_spanned!(
2138 options.frozen.is_some(), options.hash.span() => "The `hash` option requires the `frozen` option.";
2139 options.eq.is_some(), options.hash.span() => "The `hash` option requires the `eq` option.";
2140 );
2141 }
2142 match options.hash {
2143 Some(opt) => {
2144 let mut hash_impl = parse_quote_spanned! { opt.span() =>
2145 fn __pyo3__generated____hash__(&self) -> u64 {
2146 let mut s = ::std::collections::hash_map::DefaultHasher::new();
2147 ::std::hash::Hash::hash(self, &mut s);
2148 ::std::hash::Hasher::finish(&s)
2149 }
2150 };
2151 let hash_slot = generate_protocol_slot(
2152 cls,
2153 &mut hash_impl,
2154 &__HASH__,
2155 "__hash__",
2156 #[cfg(feature = "experimental-inspect")]
2157 FunctionIntrospectionData {
2158 names: &["__hash__"],
2159 arguments: Vec::new(),
2160 returns: parse_quote! { ::std::primitive::u64 },
2161 },
2162 ctx,
2163 )?;
2164 Ok((Some(hash_impl), Some(hash_slot)))
2165 }
2166 None => Ok((None, None)),
2167 }
2168}
2169
2170fn pyclass_class_geitem(
2171 options: &PyClassPyO3Options,
2172 cls: &syn::Type,
2173 ctx: &Ctx,
2174) -> Result<(Option<syn::ImplItemFn>, Option<MethodAndMethodDef>)> {
2175 let Ctx { pyo3_path, .. } = ctx;
2176 match options.generic {
2177 Some(_) => {
2178 let ident = format_ident!("__class_getitem__");
2179 let mut class_geitem_impl: syn::ImplItemFn = {
2180 parse_quote! {
2181 #[classmethod]
2182 fn #ident<'py>(
2183 cls: &#pyo3_path::Bound<'py, #pyo3_path::types::PyType>,
2184 key: &#pyo3_path::Bound<'py, #pyo3_path::types::PyAny>
2185 ) -> #pyo3_path::PyResult<#pyo3_path::Bound<'py, #pyo3_path::types::PyGenericAlias>> {
2186 #pyo3_path::types::PyGenericAlias::new(cls.py(), cls.as_any(), key)
2187 }
2188 }
2189 };
2190
2191 let spec = FnSpec::parse(
2192 &mut class_geitem_impl.sig,
2193 &mut class_geitem_impl.attrs,
2194 Default::default(),
2195 )?;
2196
2197 let class_geitem_method = crate::pymethod::impl_py_method_def(
2198 cls,
2199 &spec,
2200 &spec.get_doc(&class_geitem_impl.attrs, ctx)?,
2201 Some(quote!(#pyo3_path::ffi::METH_CLASS)),
2202 ctx,
2203 )?;
2204 Ok((Some(class_geitem_impl), Some(class_geitem_method)))
2205 }
2206 None => Ok((None, None)),
2207 }
2208}
2209
2210struct PyClassImplsBuilder<'a> {
2216 cls: &'a syn::Ident,
2217 attr: &'a PyClassArgs,
2218 methods_type: PyClassMethodsType,
2219 default_methods: Vec<MethodAndMethodDef>,
2220 default_slots: Vec<MethodAndSlotDef>,
2221 doc: Option<PythonDoc>,
2222}
2223
2224impl<'a> PyClassImplsBuilder<'a> {
2225 fn new(
2226 cls: &'a syn::Ident,
2227 attr: &'a PyClassArgs,
2228 methods_type: PyClassMethodsType,
2229 default_methods: Vec<MethodAndMethodDef>,
2230 default_slots: Vec<MethodAndSlotDef>,
2231 ) -> Self {
2232 Self {
2233 cls,
2234 attr,
2235 methods_type,
2236 default_methods,
2237 default_slots,
2238 doc: None,
2239 }
2240 }
2241
2242 fn doc(self, doc: PythonDoc) -> Self {
2243 Self {
2244 doc: Some(doc),
2245 ..self
2246 }
2247 }
2248
2249 fn impl_all(&self, ctx: &Ctx) -> Result<TokenStream> {
2250 Ok([
2251 self.impl_pyclass(ctx),
2252 self.impl_extractext(ctx),
2253 self.impl_into_py(ctx),
2254 self.impl_pyclassimpl(ctx)?,
2255 self.impl_add_to_module(ctx),
2256 self.impl_freelist(ctx),
2257 self.impl_introspection(ctx),
2258 ]
2259 .into_iter()
2260 .collect())
2261 }
2262
2263 fn impl_pyclass(&self, ctx: &Ctx) -> TokenStream {
2264 let Ctx { pyo3_path, .. } = ctx;
2265 let cls = self.cls;
2266
2267 let frozen = if self.attr.options.frozen.is_some() {
2268 quote! { #pyo3_path::pyclass::boolean_struct::True }
2269 } else {
2270 quote! { #pyo3_path::pyclass::boolean_struct::False }
2271 };
2272
2273 quote! {
2274 impl #pyo3_path::PyClass for #cls {
2275 type Frozen = #frozen;
2276 }
2277 }
2278 }
2279 fn impl_extractext(&self, ctx: &Ctx) -> TokenStream {
2280 let Ctx { pyo3_path, .. } = ctx;
2281 let cls = self.cls;
2282
2283 let input_type = if cfg!(feature = "experimental-inspect") {
2284 let full_name = get_class_python_module_and_name(cls, self.attr);
2285 quote! { const INPUT_TYPE: &'static str = #full_name; }
2286 } else {
2287 quote! {}
2288 };
2289 if self.attr.options.frozen.is_some() {
2290 quote! {
2291 impl<'a, 'holder, 'py> #pyo3_path::impl_::extract_argument::PyFunctionArgument<'a, 'holder, 'py, false> for &'holder #cls
2292 {
2293 type Holder = ::std::option::Option<#pyo3_path::PyClassGuard<'a, #cls>>;
2294
2295 #input_type
2296
2297 #[inline]
2298 fn extract(obj: &'a #pyo3_path::Bound<'py, #pyo3_path::PyAny>, holder: &'holder mut Self::Holder) -> #pyo3_path::PyResult<Self> {
2299 #pyo3_path::impl_::extract_argument::extract_pyclass_ref(obj, holder)
2300 }
2301 }
2302 }
2303 } else {
2304 quote! {
2305 impl<'a, 'holder, 'py> #pyo3_path::impl_::extract_argument::PyFunctionArgument<'a, 'holder, 'py, false> for &'holder #cls
2306 {
2307 type Holder = ::std::option::Option<#pyo3_path::PyClassGuard<'a, #cls>>;
2308
2309 #input_type
2310
2311 #[inline]
2312 fn extract(obj: &'a #pyo3_path::Bound<'py, #pyo3_path::PyAny>, holder: &'holder mut Self::Holder) -> #pyo3_path::PyResult<Self> {
2313 #pyo3_path::impl_::extract_argument::extract_pyclass_ref(obj, holder)
2314 }
2315 }
2316
2317 impl<'a, 'holder, 'py> #pyo3_path::impl_::extract_argument::PyFunctionArgument<'a, 'holder, 'py, false> for &'holder mut #cls
2318 {
2319 type Holder = ::std::option::Option<#pyo3_path::PyClassGuardMut<'a, #cls>>;
2320
2321 #input_type
2322
2323 #[inline]
2324 fn extract(obj: &'a #pyo3_path::Bound<'py, #pyo3_path::PyAny>, holder: &'holder mut Self::Holder) -> #pyo3_path::PyResult<Self> {
2325 #pyo3_path::impl_::extract_argument::extract_pyclass_ref_mut(obj, holder)
2326 }
2327 }
2328 }
2329 }
2330 }
2331
2332 fn impl_into_py(&self, ctx: &Ctx) -> TokenStream {
2333 let Ctx { pyo3_path, .. } = ctx;
2334 let cls = self.cls;
2335 let attr = self.attr;
2336 if attr.options.extends.is_none() {
2338 let output_type = if cfg!(feature = "experimental-inspect") {
2339 let full_name = get_class_python_module_and_name(cls, self.attr);
2340 quote! { const OUTPUT_TYPE: &'static str = #full_name; }
2341 } else {
2342 quote! {}
2343 };
2344 quote! {
2345 impl<'py> #pyo3_path::conversion::IntoPyObject<'py> for #cls {
2346 type Target = Self;
2347 type Output = #pyo3_path::Bound<'py, <Self as #pyo3_path::conversion::IntoPyObject<'py>>::Target>;
2348 type Error = #pyo3_path::PyErr;
2349 #output_type
2350
2351 fn into_pyobject(self, py: #pyo3_path::Python<'py>) -> ::std::result::Result<
2352 <Self as #pyo3_path::conversion::IntoPyObject>::Output,
2353 <Self as #pyo3_path::conversion::IntoPyObject>::Error,
2354 > {
2355 #pyo3_path::Bound::new(py, self)
2356 }
2357 }
2358 }
2359 } else {
2360 quote! {}
2361 }
2362 }
2363 fn impl_pyclassimpl(&self, ctx: &Ctx) -> Result<TokenStream> {
2364 let Ctx { pyo3_path, .. } = ctx;
2365 let cls = self.cls;
2366 let doc = self.doc.as_ref().map_or(
2367 LitCStr::empty(ctx).to_token_stream(),
2368 PythonDoc::to_token_stream,
2369 );
2370 let is_basetype = self.attr.options.subclass.is_some();
2371 let base = match &self.attr.options.extends {
2372 Some(extends_attr) => extends_attr.value.clone(),
2373 None => parse_quote! { #pyo3_path::PyAny },
2374 };
2375 let is_subclass = self.attr.options.extends.is_some();
2376 let is_mapping: bool = self.attr.options.mapping.is_some();
2377 let is_sequence: bool = self.attr.options.sequence.is_some();
2378 let is_immutable_type = self.attr.options.immutable_type.is_some();
2379
2380 ensure_spanned!(
2381 !(is_mapping && is_sequence),
2382 self.cls.span() => "a `#[pyclass]` cannot be both a `mapping` and a `sequence`"
2383 );
2384
2385 let dict_offset = if self.attr.options.dict.is_some() {
2386 quote! {
2387 fn dict_offset() -> ::std::option::Option<#pyo3_path::ffi::Py_ssize_t> {
2388 ::std::option::Option::Some(#pyo3_path::impl_::pyclass::dict_offset::<Self>())
2389 }
2390 }
2391 } else {
2392 TokenStream::new()
2393 };
2394
2395 let weaklist_offset = if self.attr.options.weakref.is_some() {
2397 quote! {
2398 fn weaklist_offset() -> ::std::option::Option<#pyo3_path::ffi::Py_ssize_t> {
2399 ::std::option::Option::Some(#pyo3_path::impl_::pyclass::weaklist_offset::<Self>())
2400 }
2401 }
2402 } else {
2403 TokenStream::new()
2404 };
2405
2406 let thread_checker = if self.attr.options.unsendable.is_some() {
2407 quote! { #pyo3_path::impl_::pyclass::ThreadCheckerImpl }
2408 } else {
2409 quote! { #pyo3_path::impl_::pyclass::SendablePyClass<#cls> }
2410 };
2411
2412 let (pymethods_items, inventory, inventory_class) = match self.methods_type {
2413 PyClassMethodsType::Specialization => (quote! { collector.py_methods() }, None, None),
2414 PyClassMethodsType::Inventory => {
2415 let inventory_class_name = syn::Ident::new(
2417 &format!("Pyo3MethodsInventoryFor{}", cls.unraw()),
2418 Span::call_site(),
2419 );
2420 (
2421 quote! {
2422 ::std::boxed::Box::new(
2423 ::std::iter::Iterator::map(
2424 #pyo3_path::inventory::iter::<<Self as #pyo3_path::impl_::pyclass::PyClassImpl>::Inventory>(),
2425 #pyo3_path::impl_::pyclass::PyClassInventory::items
2426 )
2427 )
2428 },
2429 Some(quote! { type Inventory = #inventory_class_name; }),
2430 Some(define_inventory_class(&inventory_class_name, ctx)),
2431 )
2432 }
2433 };
2434
2435 let default_methods = self
2436 .default_methods
2437 .iter()
2438 .map(|meth| &meth.associated_method)
2439 .chain(
2440 self.default_slots
2441 .iter()
2442 .map(|meth| &meth.associated_method),
2443 );
2444
2445 let default_method_defs = self.default_methods.iter().map(|meth| &meth.method_def);
2446 let default_slot_defs = self.default_slots.iter().map(|slot| &slot.slot_def);
2447 let freelist_slots = self.freelist_slots(ctx);
2448
2449 let class_mutability = if self.attr.options.frozen.is_some() {
2450 quote! {
2451 ImmutableChild
2452 }
2453 } else {
2454 quote! {
2455 MutableChild
2456 }
2457 };
2458
2459 let cls = self.cls;
2460 let attr = self.attr;
2461 let dict = if attr.options.dict.is_some() {
2462 quote! { #pyo3_path::impl_::pyclass::PyClassDictSlot }
2463 } else {
2464 quote! { #pyo3_path::impl_::pyclass::PyClassDummySlot }
2465 };
2466
2467 let weakref = if attr.options.weakref.is_some() {
2469 quote! { #pyo3_path::impl_::pyclass::PyClassWeakRefSlot }
2470 } else {
2471 quote! { #pyo3_path::impl_::pyclass::PyClassDummySlot }
2472 };
2473
2474 let base_nativetype = if attr.options.extends.is_some() {
2475 quote! { <Self::BaseType as #pyo3_path::impl_::pyclass::PyClassBaseType>::BaseNativeType }
2476 } else {
2477 quote! { #pyo3_path::PyAny }
2478 };
2479
2480 let pyclass_base_type_impl = attr.options.subclass.map(|subclass| {
2481 quote_spanned! { subclass.span() =>
2482 impl #pyo3_path::impl_::pyclass::PyClassBaseType for #cls {
2483 type LayoutAsBase = #pyo3_path::impl_::pycell::PyClassObject<Self>;
2484 type BaseNativeType = <Self as #pyo3_path::impl_::pyclass::PyClassImpl>::BaseNativeType;
2485 type Initializer = #pyo3_path::pyclass_init::PyClassInitializer<Self>;
2486 type PyClassMutability = <Self as #pyo3_path::impl_::pyclass::PyClassImpl>::PyClassMutability;
2487 }
2488 }
2489 });
2490
2491 let assertions = if attr.options.unsendable.is_some() {
2492 TokenStream::new()
2493 } else {
2494 let assert = quote_spanned! { cls.span() => #pyo3_path::impl_::pyclass::assert_pyclass_sync::<#cls>(); };
2495 quote! {
2496 const _: () = {
2497 #assert
2498 };
2499 }
2500 };
2501
2502 let type_name = if cfg!(feature = "experimental-inspect") {
2503 let full_name = get_class_python_module_and_name(cls, self.attr);
2504 quote! { const TYPE_NAME: &'static str = #full_name; }
2505 } else {
2506 quote! {}
2507 };
2508
2509 Ok(quote! {
2510 #assertions
2511
2512 #pyclass_base_type_impl
2513
2514 impl #pyo3_path::impl_::pyclass::PyClassImpl for #cls {
2515 const IS_BASETYPE: bool = #is_basetype;
2516 const IS_SUBCLASS: bool = #is_subclass;
2517 const IS_MAPPING: bool = #is_mapping;
2518 const IS_SEQUENCE: bool = #is_sequence;
2519 const IS_IMMUTABLE_TYPE: bool = #is_immutable_type;
2520
2521 type BaseType = #base;
2522 type ThreadChecker = #thread_checker;
2523 #inventory
2524 type PyClassMutability = <<#base as #pyo3_path::impl_::pyclass::PyClassBaseType>::PyClassMutability as #pyo3_path::impl_::pycell::PyClassMutability>::#class_mutability;
2525 type Dict = #dict;
2526 type WeakRef = #weakref;
2527 type BaseNativeType = #base_nativetype;
2528
2529 #type_name
2530
2531 fn items_iter() -> #pyo3_path::impl_::pyclass::PyClassItemsIter {
2532 use #pyo3_path::impl_::pyclass::*;
2533 let collector = PyClassImplCollector::<Self>::new();
2534 static INTRINSIC_ITEMS: PyClassItems = PyClassItems {
2535 methods: &[#(#default_method_defs),*],
2536 slots: &[#(#default_slot_defs),* #(#freelist_slots),*],
2537 };
2538 PyClassItemsIter::new(&INTRINSIC_ITEMS, #pymethods_items)
2539 }
2540
2541 const RAW_DOC: &'static ::std::ffi::CStr = #doc;
2542
2543 const DOC: &'static ::std::ffi::CStr = {
2544 use #pyo3_path::impl_ as impl_;
2545 use impl_::pyclass::Probe as _;
2546 const DOC_PIECES: &'static [&'static [u8]] = impl_::pyclass::doc::PyClassDocGenerator::<
2547 #cls,
2548 { impl_::pyclass::HasNewTextSignature::<#cls>::VALUE }
2549 >::DOC_PIECES;
2550 const LEN: usize = impl_::concat::combined_len(DOC_PIECES);
2551 const DOC: &'static [u8] = &impl_::concat::combine_to_array::<LEN>(DOC_PIECES);
2552 impl_::pyclass::doc::doc_bytes_as_cstr(DOC)
2553 };
2554
2555 #dict_offset
2556
2557 #weaklist_offset
2558
2559 fn lazy_type_object() -> &'static #pyo3_path::impl_::pyclass::LazyTypeObject<Self> {
2560 use #pyo3_path::impl_::pyclass::LazyTypeObject;
2561 static TYPE_OBJECT: LazyTypeObject<#cls> = LazyTypeObject::new();
2562 &TYPE_OBJECT
2563 }
2564 }
2565
2566 #[doc(hidden)]
2567 #[allow(non_snake_case)]
2568 impl #cls {
2569 #(#default_methods)*
2570 }
2571
2572 #inventory_class
2573 })
2574 }
2575
2576 fn impl_add_to_module(&self, ctx: &Ctx) -> TokenStream {
2577 let Ctx { pyo3_path, .. } = ctx;
2578 let cls = self.cls;
2579 quote! {
2580 impl #cls {
2581 #[doc(hidden)]
2582 pub const _PYO3_DEF: #pyo3_path::impl_::pymodule::AddClassToModule<Self> = #pyo3_path::impl_::pymodule::AddClassToModule::new();
2583 }
2584 }
2585 }
2586
2587 fn impl_freelist(&self, ctx: &Ctx) -> TokenStream {
2588 let cls = self.cls;
2589 let Ctx { pyo3_path, .. } = ctx;
2590
2591 self.attr.options.freelist.as_ref().map_or(quote! {}, |freelist| {
2592 let freelist = &freelist.value;
2593 quote! {
2594 impl #pyo3_path::impl_::pyclass::PyClassWithFreeList for #cls {
2595 #[inline]
2596 fn get_free_list(py: #pyo3_path::Python<'_>) -> &'static ::std::sync::Mutex<#pyo3_path::impl_::freelist::PyObjectFreeList> {
2597 static FREELIST: #pyo3_path::sync::PyOnceLock<::std::sync::Mutex<#pyo3_path::impl_::freelist::PyObjectFreeList>> = #pyo3_path::sync::PyOnceLock::new();
2598 &FREELIST.get_or_init(py, || ::std::sync::Mutex::new(#pyo3_path::impl_::freelist::PyObjectFreeList::with_capacity(#freelist)))
2599 }
2600 }
2601 }
2602 })
2603 }
2604
2605 fn freelist_slots(&self, ctx: &Ctx) -> Vec<TokenStream> {
2606 let Ctx { pyo3_path, .. } = ctx;
2607 let cls = self.cls;
2608
2609 if self.attr.options.freelist.is_some() {
2610 vec![
2611 quote! {
2612 #pyo3_path::ffi::PyType_Slot {
2613 slot: #pyo3_path::ffi::Py_tp_alloc,
2614 pfunc: #pyo3_path::impl_::pyclass::alloc_with_freelist::<#cls> as *mut _,
2615 }
2616 },
2617 quote! {
2618 #pyo3_path::ffi::PyType_Slot {
2619 slot: #pyo3_path::ffi::Py_tp_free,
2620 pfunc: #pyo3_path::impl_::pyclass::free_with_freelist::<#cls> as *mut _,
2621 }
2622 },
2623 ]
2624 } else {
2625 Vec::new()
2626 }
2627 }
2628
2629 #[cfg(feature = "experimental-inspect")]
2630 fn impl_introspection(&self, ctx: &Ctx) -> TokenStream {
2631 let Ctx { pyo3_path, .. } = ctx;
2632 let name = get_class_python_name(self.cls, self.attr).to_string();
2633 let ident = self.cls;
2634 let static_introspection = class_introspection_code(pyo3_path, ident, &name);
2635 let introspection_id = introspection_id_const();
2636 quote! {
2637 #static_introspection
2638 impl #ident {
2639 #introspection_id
2640 }
2641 }
2642 }
2643
2644 #[cfg(not(feature = "experimental-inspect"))]
2645 fn impl_introspection(&self, _ctx: &Ctx) -> TokenStream {
2646 quote! {}
2647 }
2648}
2649
2650fn define_inventory_class(inventory_class_name: &syn::Ident, ctx: &Ctx) -> TokenStream {
2651 let Ctx { pyo3_path, .. } = ctx;
2652 quote! {
2653 #[doc(hidden)]
2654 pub struct #inventory_class_name {
2655 items: #pyo3_path::impl_::pyclass::PyClassItems,
2656 }
2657 impl #inventory_class_name {
2658 pub const fn new(items: #pyo3_path::impl_::pyclass::PyClassItems) -> Self {
2659 Self { items }
2660 }
2661 }
2662
2663 impl #pyo3_path::impl_::pyclass::PyClassInventory for #inventory_class_name {
2664 fn items(&self) -> &#pyo3_path::impl_::pyclass::PyClassItems {
2665 &self.items
2666 }
2667 }
2668
2669 #pyo3_path::inventory::collect!(#inventory_class_name);
2670 }
2671}
2672
2673fn generate_cfg_check(variants: &[PyClassEnumUnitVariant<'_>], cls: &syn::Ident) -> TokenStream {
2674 if variants.is_empty() {
2675 return quote! {};
2676 }
2677
2678 let mut conditions = Vec::new();
2679
2680 for variant in variants {
2681 let cfg_attrs = &variant.cfg_attrs;
2682
2683 if cfg_attrs.is_empty() {
2684 return quote! {};
2687 }
2688
2689 for attr in cfg_attrs {
2690 if let syn::Meta::List(meta) = &attr.meta {
2691 let cfg_tokens = &meta.tokens;
2692 conditions.push(quote! { not(#cfg_tokens) });
2693 }
2694 }
2695 }
2696
2697 quote_spanned! {
2698 cls.span() =>
2699 #[cfg(all(#(#conditions),*))]
2700 ::core::compile_error!(concat!("#[pyclass] can't be used on enums without any variants - all variants of enum `", stringify!(#cls), "` have been configured out by cfg attributes"));
2701 }
2702}
2703
2704const UNIQUE_GET: &str = "`get` may only be specified once";
2705const UNIQUE_SET: &str = "`set` may only be specified once";
2706const UNIQUE_NAME: &str = "`name` may only be specified once";
2707
2708const DUPE_SET: &str = "useless `set` - the struct is already annotated with `set_all`";
2709const DUPE_GET: &str = "useless `get` - the struct is already annotated with `get_all`";
2710const UNIT_GET: &str =
2711 "`get_all` on an unit struct does nothing, because unit structs have no fields";
2712const UNIT_SET: &str =
2713 "`set_all` on an unit struct does nothing, because unit structs have no fields";
2714
2715const USELESS_NAME: &str = "`name` is useless without `get` or `set`";