Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/objectstate/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@
'ObjectStateEditSession',
]

__version__ = "3.0.0"
__version__ = "3.0.1"
__author__ = 'OpenHCS Team'
__description__ = 'Generic configuration framework for lazy dataclass resolution'

Expand Down
25 changes: 16 additions & 9 deletions src/objectstate/lazy_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
9 changes: 8 additions & 1 deletion src/objectstate/ui_visibility.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading