work
This commit is contained in:
@@ -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)"""
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user