(attr: PunctuatedNestedMeta, item: Item)
| 546 | } |
| 547 | |
| 548 | pub(crate) fn impl_pyclass(attr: PunctuatedNestedMeta, item: Item) -> Result<TokenStream> { |
| 549 | if matches!(item, syn::Item::Use(_)) { |
| 550 | return Ok(quote!(#item)); |
| 551 | } |
| 552 | let (ident, attrs) = pyclass_ident_and_attrs(&item)?; |
| 553 | let fake_ident = Ident::new("pyclass", item.span()); |
| 554 | let class_meta = ClassItemMeta::from_nested(ident.clone(), fake_ident, attr.into_iter())?; |
| 555 | let class_name = class_meta.class_name()?; |
| 556 | let module_name = class_meta.module()?; |
| 557 | let base = class_meta.base()?; |
| 558 | let metaclass = class_meta.metaclass()?; |
| 559 | let unhashable = class_meta.unhashable()?; |
| 560 | |
| 561 | // Validate that if base is specified, the first field must be of the base type |
| 562 | if let Some(ref base_path) = base { |
| 563 | validate_base_field(&item, base_path)?; |
| 564 | } |
| 565 | |
| 566 | let class_def = generate_class_def( |
| 567 | ident, |
| 568 | &class_name, |
| 569 | module_name.as_deref(), |
| 570 | base.clone(), |
| 571 | metaclass, |
| 572 | unhashable, |
| 573 | attrs, |
| 574 | )?; |
| 575 | |
| 576 | const ALLOWED_TRAVERSE_OPTS: &[&str] = &["manual"]; |
| 577 | // Generate MaybeTraverse impl with both traverse and clear support |
| 578 | // |
| 579 | // For traverse: |
| 580 | // 1. no `traverse` at all: HAS_TRAVERSE = false, try_traverse does nothing |
| 581 | // 2. `traverse = "manual"`: HAS_TRAVERSE = true, but no #[derive(Traverse)] |
| 582 | // 3. `traverse`: HAS_TRAVERSE = true, and #[derive(Traverse)] |
| 583 | // |
| 584 | // For clear (tp_clear): |
| 585 | // 1. no `clear`: HAS_CLEAR = HAS_TRAVERSE (default: same as traverse) |
| 586 | // 2. `clear` or `clear = true`: HAS_CLEAR = true, try_clear calls Traverse::clear |
| 587 | // 3. `clear = false`: HAS_CLEAR = false (rare: traverse without clear) |
| 588 | let has_traverse = class_meta.inner()._has_key("traverse")?; |
| 589 | let has_clear = if class_meta.inner()._has_key("clear")? { |
| 590 | // If clear attribute is present, use its value |
| 591 | class_meta.inner()._bool("clear")? |
| 592 | } else { |
| 593 | // If clear attribute is absent, default to same as traverse |
| 594 | has_traverse |
| 595 | }; |
| 596 | |
| 597 | let derive_trace = if has_traverse { |
| 598 | // _optional_str returns Err when key exists without value (e.g., `traverse` vs `traverse = "manual"`) |
| 599 | // We want to derive Traverse in that case, so we handle Err as Ok(None) |
| 600 | let value = class_meta.inner()._optional_str("traverse").ok().flatten(); |
| 601 | if let Some(s) = value { |
| 602 | if !ALLOWED_TRAVERSE_OPTS.contains(&s.as_str()) { |
| 603 | bail_span!( |
| 604 | item, |
| 605 | "traverse attribute only accept {ALLOWED_TRAVERSE_OPTS:?} as value or no value at all", |
no test coverage detected