This commit is contained in:
chacha
2025-09-22 21:43:03 +02:00
parent ff55ef18d1
commit 09237ff8cd
5 changed files with 53 additions and 111 deletions

View File

@@ -1,20 +1,22 @@
from typing import Generic, TypeVar, Any
from typing import Generic, TypeVar, Any, Optional
from .LAMField import LAMField
from .Constraint import Constraint
from ..tools import LAMdeepfreeze
T_Field = TypeVar("T_Field")
from .LAMField import TV_LABField
class FrozenLAMField(Generic[T_Field]):
class FrozenLAMField(Generic[TV_LABField]):
"""FrozenLAMField class
a read-only proxy of a Field
"""
def __init__(self, inner_field: LAMField[T_Field]):
def __init__(self, inner_field: LAMField[TV_LABField]):
self._inner_field = inner_field
def validate(self, v: Optional[TV_LABField]):
self._inner_field.validate(v)
@property
def doc(self) -> str:
"""Returns Field's documentation (frozen)"""

View File

@@ -1,8 +1,9 @@
from typing import Generic, TypeVar, Optional, Any
from typeguard import check_type, CollectionCheckStrategy, TypeCheckError
from .LAMFieldInfo import LAMFieldInfo
from .Constraint import Constraint
from ..tools import LAMdeepfreeze
from ..exception import InvalidFieldValue
TV_LABField = TypeVar("TV_LABField")
@@ -13,10 +14,11 @@ class LAMField(Generic[TV_LABField]):
def __init__(self, name: str, v: Optional[TV_LABField], a: Any, i: LAMFieldInfo):
self._name: str = name
self._source: Optional[type] = None
self._info: LAMFieldInfo = i
self._annotations: Any = a
self.validate(v)
self._default_value: Optional[TV_LABField] = v
self._value: Optional[TV_LABField] = v
self._annotations: Any = a
self._info: LAMFieldInfo = i
self._constraints: list[Constraint[Any]] = i.constraints
def add_source(self, s: type) -> None:
@@ -37,6 +39,19 @@ class LAMField(Generic[TV_LABField]):
"""Returns Field's constraint"""
return self._info.constraints
def validate(self, v: Optional[TV_LABField]):
try:
check_type(
v,
self.annotations,
collection_check_strategy=CollectionCheckStrategy.ALL_ITEMS,
)
except TypeCheckError as exp:
raise InvalidFieldValue(
f"Value of Field <{self._name}> is not of expected type {self.annotations}."
) from exp
@property
def default_value(self) -> Any:
"""Returns Field's default value (frozen)"""
@@ -44,6 +59,7 @@ class LAMField(Generic[TV_LABField]):
def update_value(self, v: Optional[TV_LABField] = None) -> None:
"""Updates Field's value"""
self.validate(v)
self._value = v
@property
@@ -60,3 +76,8 @@ class LAMField(Generic[TV_LABField]):
def annotations(self) -> Any:
"""Returns Field's annotation"""
return self._annotations
@property
def info(self) -> LAMFieldInfo:
"""Returns Field's info"""
return self._info

View File

@@ -82,17 +82,7 @@ class BaseElement:
print(
f"Validating {attrName}: {self.__dict__[attrName]} / {self.__lam_schema__[attrName].annotations}"
)
try:
check_type(
self.__dict__[attrName],
self.__lam_schema__[attrName].annotations,
collection_check_strategy=CollectionCheckStrategy.ALL_ITEMS,
)
except TypeCheckError as exp:
raise InvalidFieldValue(
f"Value of Field <{attrName}> is not of expected type: <{self.__lam_schema__[attrName].annotations}>."
) from exp
self.__lam_schema__[attrName].validate(self.__dict__[attrName])
print("validate_schema done")
def _validate_schema_unknown_attr(self, name: str):

View File

@@ -29,6 +29,7 @@ class _MetaAppliance(_MetaElement):
Copies the parent's `features` mapping when inheriting to keep it per-class.
"""
super().check_class(name, bases, namespace, extensions) # type: ignore[misc]
if "features" not in namespace["__lam_schema__"]:
@@ -43,7 +44,6 @@ class _MetaAppliance(_MetaElement):
extensions: dict[str, Any],
):
super().inherit_schema(name, base, namespace, extensions)
# print(base.__lam_schema__["features"])
if "features" in base.__lam_schema__:
namespace["__lam_schema__"]["features"] = copy(base.__lam_schema__["features"])
@@ -120,11 +120,8 @@ class _MetaAppliance(_MetaElement):
super().commit_fields(cls, name, bases, namespace, extensions) # type: ignore[misc]
for _ftname, _ftvalue in extensions["modified_features"].items():
# _ftvalue._BoundAppliance = cls # pylint: disable=protected-access
# _ftvalue.bind_appliance(cls)
cls.__lam_schema__["features"][_ftname] = _ftvalue
for _ftname, _ftvalue in extensions["new_features"].items():
# _ftvalue.__lam_bound_appliance__ = cls # pylint: disable=protected-access
_ftvalue.bind_appliance(cls)
cls.__lam_schema__["features"][_ftname] = _ftvalue
@@ -150,19 +147,8 @@ class _MetaAppliance(_MetaElement):
if field_name not in feat_cls.__lam_schema__:
raise InvalidFieldValue(f"Feature '{fname}' has no field '{field_name}'")
field = feat_cls.__lam_schema__[field_name]
try:
check_type(
new_val,
field.annotations,
collection_check_strategy=CollectionCheckStrategy.ALL_ITEMS,
)
except TypeCheckError as exp:
raise InvalidFieldValue(
f"Invalid value for {fname}.{field_name}: "
f"expected {field.annotations}, got {new_val!r}"
) from exp
field.validate(new_val)
object.__setattr__(inst, field_name, LAMdeepfreeze(new_val))
# object.__setattr__(inst, field_name, new_val)
inst.__lam_schema__[field_name] = FrozenLAMField(
LAMField(field_name, new_val, field.annotations, field._info)
)

View File

@@ -260,11 +260,6 @@ class _MetaElement(type):
if not issubclass(bases[0], BaseElement):
raise BrokenInheritance("wrong base class")
# if "__lam_schema__" not in dir(bases[0]):
# namespace["__lam_schema__"] = {}
# copy inherited schema
# if "__lam_schema__" in dir(bases[0]):
mcs.inherit_schema(name, bases[0], namespace, extensions)
# force field without default value to be instantiated (with None)
@@ -363,16 +358,7 @@ class _MetaElement(type):
raise ReadOnlyFieldAnnotation(
f"annotations cannot be modified on derived classes {_fname}"
)
try:
check_type(
_fvalue,
namespace["__lam_schema__"][_fname].annotations,
collection_check_strategy=CollectionCheckStrategy.ALL_ITEMS,
)
except TypeCheckError as exp:
raise InvalidFieldValue(
f"Field <{_fname}> New Field value is not of expected type {bases[0].__annotations__[_fname]}."
) from exp
namespace["__lam_schema__"][_fname].validate(_fvalue)
mcs.modified_fields[_fname] = _fvalue
@classmethod
@@ -430,18 +416,6 @@ class _MetaElement(type):
_finfo = args[1]
# check if value is valid
try:
check_type(
_fvalue,
namespace["__annotations__"][_fname],
collection_check_strategy=CollectionCheckStrategy.ALL_ITEMS,
)
except TypeCheckError as exp:
raise InvalidFieldValue(
f"Value of Field <{_fname}> is not of expected type {namespace['__annotations__'][_fname]}."
) from exp
# print(f"!!VAL: {_fvalue}")
mcs.new_fields[_fname] = LAMField(
_fname, _fvalue, namespace["__annotations__"][_fname], _finfo
)
@@ -463,6 +437,7 @@ class _MetaElement(type):
On success, the computed values are validated and written back into the
class schema's DABFields.
"""
if mcs.initializer is not None:
_check_initializer_safety(mcs.initializer)
init_fieldvalues = {}
@@ -471,7 +446,9 @@ class _MetaElement(type):
if isinstance(_fvalue, LAMField):
init_fieldvalues[_fname] = deepcopy(_fvalue.raw_value)
init_fieldtypes[_fname] = _fvalue.annotations
fakecls = ModelSpecView(init_fieldvalues, init_fieldtypes, cls.__name__, cls.__module__)
# fakecls = cls
safe_globals = {
"__builtins__": {"__import__": _blocked_import},
**ALLOWED_HELPERS_DEFAULT,
@@ -486,19 +463,13 @@ class _MetaElement(type):
closure=mcs.initializer.__closure__,
)
safe_initializer(fakecls) # pylint: disable=not-callable
for _fname, _fvalue in fakecls.export().items():
try:
check_type(
_fvalue,
cls.__lam_schema__[_fname].annotations,
collection_check_strategy=CollectionCheckStrategy.ALL_ITEMS,
)
except TypeCheckError as exp:
raise InvalidFieldValue(
f"Value of Field <{_fname}> is not of expected type {namespace['__annotations__'][_fname]}."
) from exp
cls.__lam_schema__[_fname] = deepcopy(cls.__lam_schema__[_fname])
cls.__lam_schema__[_fname].update_value(_fvalue)
field = cls.__lam_schema__[_fname]
field.validate(_fvalue)
cls.__lam_schema__[_fname] = LAMField(
_fname, _fvalue, field.annotations, field.info
)
def __new__(
mcs: type["_MetaElement"],
@@ -508,14 +479,14 @@ class _MetaElement(type):
**kwargs,
) -> Type:
"""BaseElement new class"""
print(f"!!!!! {bases}")
print(kwargs)
extensions: dict[str, Any] = {}
extensions["kwargs"] = kwargs
if "options" not in extensions["kwargs"]:
extensions["kwargs"]["options"] = ()
elif extensions["kwargs"]["options"] is not tuple:
extensions["kwargs"]["options"] = (extensions["kwargs"]["options"],)
mcs.check_class(name, bases, namespace, extensions)
mcs.process_class_fields(name, bases, namespace, extensions)
@@ -524,9 +495,11 @@ class _MetaElement(type):
mcs.commit_fields(_cls, name, bases, namespace, extensions)
mcs.apply_initializer(_cls, name, bases, namespace, extensions)
mcs.finalize_class(_cls, name, bases, namespace, extensions)
if not _cls.__lam_class_mutable__:
_cls.freeze_class(True)
_cls.__lam_initialized__ = True
return _cls
@classmethod
@@ -612,21 +585,10 @@ class _MetaElement(type):
for k, v in list(kwargs.items()):
if k in cls.__lam_schema__: # regular field
field = cls.__lam_schema__[k]
try:
check_type(
v,
field.annotations,
collection_check_strategy=CollectionCheckStrategy.ALL_ITEMS,
)
except TypeCheckError as exp:
raise InvalidFieldValue(
f"Invalid value for field '{k}': expected {field.annotations}, got {v!r}"
) from exp
# object.__setattr__(obj, k, LAMdeepfreeze(v))
field.validate(v)
object.__setattr__(obj, k, v)
obj.__lam_schema__[k] = FrozenLAMField(
LAMField(k, v, field.annotations, field._info)
LAMField(k, v, field.annotations, field.info)
)
kwargs.pop(k)
@@ -644,12 +606,10 @@ class _MetaElement(type):
"""
def __setattr__(cls, name: str, value: Any):
print(f"Guarded class assignment: {cls.__name__}.{name} = {value!r}")
if not hasattr(cls, "__lam_initialized__") or not getattr(cls, "__lam_initialized__"):
return super().__setattr__(name, value)
if name.startswith("_"):
# raise AttributeError("Can't change private class attributes")
return super().__setattr__(name, value)
if name not in cls.__lam_schema__.keys():
@@ -658,40 +618,23 @@ class _MetaElement(type):
if not cls.__lam_class_mutable__:
raise ReadOnlyField(f"Class Field <{name}> is read only.")
try:
check_type(
value,
cls.__lam_schema__[name].annotations,
collection_check_strategy=CollectionCheckStrategy.ALL_ITEMS,
)
except TypeCheckError as exp:
raise InvalidFieldValue(
f"Value of Field <{name}> is not of expected type {cls.__lam_schema__[name].annotations}."
) from exp
cls.__lam_schema__[name] = deepcopy(cls.__lam_schema__[name])
cls.__lam_schema__[name].update_value(value)
field = cls.__lam_schema__[name]
field.validate(value)
cls.__lam_schema__[name] = LAMField(name, value, field.annotations, field.info)
def __getattr__(cls, name) -> Any:
print(f"Guarded class get: {cls.__name__}.{name}")
if hasattr(cls, "__lam_initialized__") and getattr(cls, "__lam_initialized__"):
if name.startswith("_"):
# raise AttributeError("Can't get private class attributes")
print("__getattr__ FORWARD1")
return super().__getattr__(name)
# return cls.__dict__[name]
if name in cls.__lam_schema__.keys():
if cls.__lam_class_mutable__:
print("HEy hey WHYYYYYY")
return cls.__lam_schema__[name].raw_value
else:
return cls.__lam_schema__[name].value
else:
raise NonExistingField("Non existing class attribute")
# return super().__getattr__(name)
else:
print("__getattr__ FORWARD2")
return super().__getattr__(name)
# return cls.__dict__[name]
@classmethod
def finalize_class(