From 31795ce642b07f48acae420761ab19b8ece9f1cf Mon Sep 17 00:00:00 2001 From: Tristan Simas Date: Mon, 12 Oct 2026 00:34:52 -0400 Subject: [PATCH 1/2] WIP: rebuilt dataclasses carry their declaration's module at creation --- src/objectstate/lazy_factory.py | 25 ++++++++++++++++--------- src/objectstate/ui_visibility.py | 9 ++++++++- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/objectstate/lazy_factory.py b/src/objectstate/lazy_factory.py index e45675e..d7ae347 100644 --- a/src/objectstate/lazy_factory.py +++ b/src/objectstate/lazy_factory.py @@ -46,9 +46,17 @@ def _recreated_dataclass_namespace( declared_type: type, recreated_name: str, ) -> dict[str, object]: - """Return declaration-owned metadata for a recreated dataclass.""" + """Return declaration-owned metadata for a recreated dataclass. - namespace: dict[str, object] = {"__doc__": declared_type.__doc__} + The module and qualified name are in the namespace so the class has them + while it is being created: a metaclass that registers classes then sees the + declaration the class was rebuilt from, not ``abc`` or this module. + """ + + namespace: dict[str, object] = { + "__doc__": declared_type.__doc__, + "__module__": declared_type.__module__, + } if recreated_name == declared_type.__name__: namespace["__qualname__"] = declared_type.__qualname__ return namespace @@ -203,16 +211,15 @@ def rebuild_with_none_defaults( frozen=is_frozen, ) - # Preserve original metaclass if it's not just type - # This is critical for AutoRegisterMeta and other custom metaclasses - if orig_metaclass is not type: - # Re-create the class with the original metaclass - # We need to do this because make_dataclass doesn't accept metaclass parameter - # But we must NOT re-apply @dataclass since make_dataclass already did that + # make_dataclass takes no metaclass parameter: it derives one from the + # bases. When the declared class names a metaclass its bases do not carry, + # create the class once more with that metaclass. @dataclass is NOT + # re-applied, since make_dataclass already did that. + if type(new_cls) is not orig_metaclass: new_cls = orig_metaclass( new_cls.__name__, new_cls.__bases__, - dict(new_cls.__dict__), + {**new_cls.__dict__, **_recreated_dataclass_namespace(cls, recreated_name)}, ) return _restore_recreated_dataclass_metadata(new_cls, cls) diff --git a/src/objectstate/ui_visibility.py b/src/objectstate/ui_visibility.py index ab0a9ff..f54e81b 100644 --- a/src/objectstate/ui_visibility.py +++ b/src/objectstate/ui_visibility.py @@ -40,7 +40,14 @@ class UISpecialFieldRegistry: def register(cls, owner_type: T, field_names: tuple[str, ...]) -> T: if not field_names: raise ValueError("UI special field registration requires at least one field") - cls._fields_by_owner[owner_type] = frozenset(field_names) + declared = frozenset(field_names) + registered = cls._fields_by_owner.get(owner_type) + if registered is not None and registered != declared: + raise ValueError( + f"{owner_type.__qualname__} already declares UI special fields " + f"{sorted(registered)!r}; it cannot also declare {sorted(declared)!r}." + ) + cls._fields_by_owner[owner_type] = declared return owner_type @classmethod From b9a155dd13d02f1491cdf2374fadecd74d513447 Mon Sep 17 00:00:00 2001 From: Tristan Simas Date: Mon, 12 Oct 2026 08:25:53 -0400 Subject: [PATCH 2/2] 3.0.1: rebuilt lazy dataclasses carry their declaration's module; one UI special-field declaration per key Co-Authored-By: Claude Opus 5.5 --- src/objectstate/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/objectstate/__init__.py b/src/objectstate/__init__.py index a99ec78..931a7f5 100644 --- a/src/objectstate/__init__.py +++ b/src/objectstate/__init__.py @@ -210,7 +210,7 @@ 'ObjectStateEditSession', ] -__version__ = "3.0.0" +__version__ = "3.0.1" __author__ = 'OpenHCS Team' __description__ = 'Generic configuration framework for lazy dataclass resolution'