This commit is contained in:
chacha
2025-09-23 22:43:39 +02:00
parent 100d2cadcb
commit 7ffe05f514
4 changed files with 59 additions and 66 deletions

View File

@@ -33,8 +33,8 @@ class Appliance(IAppliance, metaclass=_MetaAppliance):
@classmethod
def _freeze_unknown_field_schema(cls, name: str):
if name == "features":
for feature in cls.__lam_schema__["features"].values():
feature.freeze_class()
for v in cls.__lam_schema__["features"].values():
v.freeze_class()
return
super()._freeze_unknown_field_schema(name)

View File

@@ -31,17 +31,17 @@ class BaseElement:
if self.__lam_class_mutable__:
self.validate_schema()
setSchemaKeys = set(self.__lam_schema__.keys())
setSchemaKeys = set(self.__lam_schema__)
setInstanceKeys = {_[0] for _ in self.__dict__.items() if is_data_attribute(_[0], _[1])}
for unknown_attr in setInstanceKeys - setSchemaKeys:
self._freeze_unknown_attr(unknown_attr)
for k_unknown in setInstanceKeys - setSchemaKeys:
self._freeze_unknown_attr(k_unknown)
for unknown_attr in setSchemaKeys - setInstanceKeys:
self._freeze_missing_attr(unknown_attr)
for k_missing in setSchemaKeys - setInstanceKeys:
self._freeze_missing_attr(k_missing)
for attrName in setSchemaKeys & setInstanceKeys:
object.__setattr__(self, attrName, LAMdeepfreeze(self.__dict__[attrName]))
for k in setSchemaKeys & setInstanceKeys:
object.__setattr__(self, k, LAMdeepfreeze(self.__dict__[k]))
self.__lam_object_mutable__ = False
@@ -52,17 +52,17 @@ class BaseElement:
raise SchemaViolation(f"Attribute <{name}> is missing from instance")
def validate_schema(self):
setSchemaKeys = set(self.__lam_schema__.keys())
setSchemaKeys = set(self.__lam_schema__)
setInstanceKeys = {_[0] for _ in self.__dict__.items() if is_data_attribute(_[0], _[1])}
for unknown_attr in setInstanceKeys - setSchemaKeys:
self._validate_schema_unknown_attr(unknown_attr)
for k_unknown in setInstanceKeys - setSchemaKeys:
self._validate_schema_unknown_attr(k_unknown)
for missing_attr in setSchemaKeys - setInstanceKeys:
self._validate_schema_missing_attr(missing_attr)
for k_missing in setSchemaKeys - setInstanceKeys:
self._validate_schema_missing_attr(k_missing)
for attrName in setSchemaKeys & setInstanceKeys:
self.__lam_schema__[attrName].validate(self.__dict__[attrName])
for k in setSchemaKeys & setInstanceKeys:
self.__lam_schema__[k_missing].validate(self.__dict__[k])
def _validate_schema_unknown_attr(self, name: str):
raise SchemaViolation(f"Attribute <{name}> is not in the schema")
@@ -75,10 +75,8 @@ class BaseElement:
if cls.__lam_class_mutable__ or force:
cls.validate_schema_class()
# class should not have any elements so they are all unknown
for unknown_attr in {
_[0] for _ in cls.__dict__.items() if is_data_attribute(_[0], _[1])
}:
cls._freeze_unknown_attr_class(unknown_attr)
for k_unknown in {_[0] for _ in cls.__dict__.items() if is_data_attribute(_[0], _[1])}:
cls._freeze_unknown_attr_class(k_unknown)
for k, v in cls.__lam_schema__.items():
if isinstance(v, LAMField):
@@ -99,8 +97,8 @@ class BaseElement:
@classmethod
def validate_schema_class(cls):
# class should not have any elements so they are all unknown
for unknown_attr in {_[0] for _ in cls.__dict__.items() if is_data_attribute(_[0], _[1])}:
cls._validate_unknown_attr_class(unknown_attr)
for k_unknown in {_[0] for _ in cls.__dict__.items() if is_data_attribute(_[0], _[1])}:
cls._validate_unknown_attr_class(k_unknown)
for k, v in cls.__lam_schema__.items():
if isinstance(v, LAMField):

View File

@@ -84,7 +84,7 @@ class _MetaAppliance(_MetaElement):
if _fname == "feature":
raise InvalidFieldName("'feature' is a reserved Field name")
if _fname in namespace["__lam_schema__"]["features"].keys():
if _fname in namespace["__lam_schema__"]["features"]:
if not issubclass(_fvalue, namespace["__lam_schema__"]["features"][_fname]):
raise InvalidFeatureInheritance(
f"Feature {_fname} is not an instance of {bases[0]}.{_fname}"
@@ -131,26 +131,24 @@ class _MetaAppliance(_MetaElement):
- Subclass replacements
- Dict overrides (class + patch dict)
"""
for fname, fdef in obj.__lam_schema__["features"].items():
for k, v in obj.__lam_schema__["features"].items():
# Case 1: plain class or subclass
if isinstance(fdef, type) and issubclass(fdef, Feature):
inst = fdef()
object.__setattr__(obj, fname, inst)
if isinstance(v, type) and issubclass(v, Feature):
inst = v()
object.__setattr__(obj, k, inst)
# Case 2: (class, dict) → dict overrides
elif isinstance(fdef, tuple) and len(fdef) == 2:
feat_cls, overrides = fdef
elif isinstance(v, tuple) and len(v) == 2:
feat_cls, overrides = v
print(overrides)
print(feat_cls)
inst = feat_cls(**overrides)
object.__setattr__(obj, fname, inst)
obj.__lam_schema__["features"][fname] = feat_cls
object.__setattr__(obj, k, inst)
obj.__lam_schema__["features"][k] = feat_cls
else:
raise InvalidFieldValue(
f"Invalid feature definition stored for '{fname}': {fdef!r}"
)
raise InvalidFieldValue(f"Invalid feature definition stored for '{k}': {fdef!r}")
def apply_overrides(cls, obj, stack_exts, *args, **kwargs):
"""

View File

@@ -259,8 +259,8 @@ class _MetaElement(type):
# force field without default value to be instantiated (with None)
if "__annotations__" in namespace:
for _funknown in [_ for _ in namespace["__annotations__"] if _ not in namespace.keys()]:
namespace[_funknown] = None
for k_unknown in [_ for _ in namespace["__annotations__"] if _ not in namespace]:
namespace[k_unknown] = None
namespace["__lam_initialized__"] = False
namespace["__lam_class_mutable__"] = ClassMutable in stack_exts["kwargs"]["options"]
@@ -306,34 +306,32 @@ class _MetaElement(type):
stack_exts["new_fields"] = {}
stack_exts["initializer"] = None
initializer_name: Optional[str] = None
for _fname, _fvalue in namespace.items():
if _fname == f"_{name}__initializer" or (
name.startswith("_") and _fname == "__initializer"
):
if not isinstance(_fvalue, classmethod):
for k, v in namespace.items():
if k == f"_{name}__initializer" or (name.startswith("_") and k == "__initializer"):
if not isinstance(v, classmethod):
raise InvalidInitializerType("__initializer should be a classmethod")
stack_exts["initializer"] = _fvalue.__func__
stack_exts["initializer"] = v.__func__
if name.startswith("_"):
initializer_name = "__initializer"
else:
initializer_name = f"_{name}__initializer"
elif _fname.startswith("_"):
elif k.startswith("_"):
pass
elif isinstance(_fvalue, classmethod):
elif isinstance(v, classmethod):
pass
elif isinstance(_fvalue, FunctionType):
elif isinstance(v, FunctionType):
pass
else:
print(f"Parsing Field: {_fname} / {_fvalue}")
if _fname in namespace["__lam_schema__"]: # Modified fields
mcs.process_modified_field(name, bases, namespace, _fname, _fvalue, stack_exts)
print(f"Parsing Field: {k} / {v}")
if k in namespace["__lam_schema__"]: # Modified fields
mcs.process_modified_field(name, bases, namespace, k, v, stack_exts)
else: # New fieds
mcs.process_new_field(name, bases, namespace, _fname, _fvalue, stack_exts)
mcs.process_new_field(name, bases, namespace, k, v, stack_exts)
# removing modified fields from class (will add them back later)
for _fname in stack_exts["new_fields"]:
del namespace[_fname]
for _fname in stack_exts["modified_fields"]:
del namespace[_fname]
for k in stack_exts["new_fields"]:
del namespace[k]
for k in stack_exts["modified_fields"]:
del namespace[k]
if stack_exts["initializer"] is not None and initializer_name is not None:
del namespace[initializer_name]
@@ -441,10 +439,10 @@ class _MetaElement(type):
_check_initializer_safety(stack_exts["initializer"])
init_fieldvalues = {}
init_fieldtypes = {}
for _fname, _fvalue in cls.__lam_schema__.items():
if isinstance(_fvalue, LAMField):
init_fieldvalues[_fname] = deepcopy(_fvalue.value)
init_fieldtypes[_fname] = _fvalue.annotations
for k, v in cls.__lam_schema__.items():
if isinstance(v, LAMField):
init_fieldvalues[k] = deepcopy(v.value)
init_fieldtypes[k] = v.annotations
fakecls = ModelSpecView(init_fieldvalues, init_fieldtypes, cls.__name__, cls.__module__)
# fakecls = cls
@@ -463,9 +461,8 @@ class _MetaElement(type):
)
safe_initializer(fakecls) # pylint: disable=not-callable
for _fname, _fvalue in fakecls.export().items():
field = cls.__lam_schema__[_fname]
field.update_value(_fvalue)
for k, v in fakecls.export().items():
cls.__lam_schema__[k].update_value(v)
def __new__(
mcs: type["_MetaElement"],
@@ -514,12 +511,12 @@ class _MetaElement(type):
- For modified fields: copy the parent's LAMField, update its value.
- For new fields: set the freshly built LAMField and record its source.
"""
for _fname, _fvalue in stack_exts["modified_fields"].items():
cls.__lam_schema__[_fname].update_value(_fvalue)
for k, v in stack_exts["modified_fields"].items():
cls.__lam_schema__[k].update_value(v)
for _fname, _fvalue in stack_exts["new_fields"].items():
_fvalue.add_source(cls)
cls.__lam_schema__[_fname] = _fvalue
for k, v in stack_exts["new_fields"].items():
v.add_source(cls)
cls.__lam_schema__[k] = v
def __call__(cls: Type, *args: Any, **kw: Any): # intentionally untyped
"""BaseElement new instance"""
@@ -568,7 +565,7 @@ class _MetaElement(type):
kwargs.pop(k)
if kwargs:
unknown = ", ".join(sorted(kwargs.keys()))
unknown = ", ".join(sorted(kwargs))
raise InvalidFieldValue(f"Unknown parameters: {unknown}")
def finalize_instance(cls: Type, obj: Any, stack_exts: dict[str, Any]):