From 3143b3baf8ef8b1023ed76f225af59e2e8a71e06 Mon Sep 17 00:00:00 2001 From: Itamar Oren Date: Thu, 13 Aug 2026 07:45:35 -0700 Subject: [PATCH] Read implicitron's own annotations via inspect.get_annotations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: `expand_args_fields` gated its whole member-processing pass on `"__annotations__" in some_class.__dict__`. Under PEP 649 that entry is not in the class dict until something materializes it, so on 3.14 the check was simply False, no member was processed, and the pass that moves defaultable members to the end of `__annotations__` never ran. The class then reached `dataclasses.dataclass(eq=False)` with a defaulted `_class_type` field ahead of the non-defaulted `` it replaces, which is a hard `TypeError` at class construction. `inspect.get_annotations` returns a class's own annotations — not a base's — which is exactly what the `__dict__` lookup was expressing, and it behaves identically on 3.12. The in-place `del` / re-add on `some_class.__annotations__` further down is unaffected: attribute access materializes and caches the dict on 3.14, so the mutations stick and `dataclasses` sees them (verified on both interpreters). 325 canary failures in V58. Reviewed By: bottler, ambv Differential Revision: D115343017 fbshipit-source-id: 33f38dfac9f10c46b1f6b0e57f7005c9d7750dfa --- pytorch3d/implicitron/tools/config.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/pytorch3d/implicitron/tools/config.py b/pytorch3d/implicitron/tools/config.py index 431be1a5..821155a2 100644 --- a/pytorch3d/implicitron/tools/config.py +++ b/pytorch3d/implicitron/tools/config.py @@ -903,13 +903,17 @@ def expand_args_fields( processed_members.update(base._processed_members) to_process: List[Tuple[str, Type, _ProcessType]] = [] - if "__annotations__" in some_class.__dict__: - for name, type_ in some_class.__annotations__.items(): - underlying_and_process_type = _get_type_to_process(type_) - if underlying_and_process_type is None: - continue - underlying_type, process_type = underlying_and_process_type - to_process.append((name, underlying_type, process_type)) + # Only this class's own annotations, never a base's. Reading + # some_class.__dict__["__annotations__"] used to express that, but under + # PEP 649 the entry is not in the class dict until something materializes + # it, so on 3.14 the lookup silently found nothing and no member was + # processed at all. + for name, type_ in inspect.get_annotations(some_class).items(): + underlying_and_process_type = _get_type_to_process(type_) + if underlying_and_process_type is None: + continue + underlying_type, process_type = underlying_and_process_type + to_process.append((name, underlying_type, process_type)) for name, underlying_type, process_type in to_process: processed_members[name] = some_class.__annotations__[name]