Compare commits
3 Commits
0.0.1.post
...
0.0.1.post
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d20712a72f | ||
|
|
2837b6439f | ||
|
|
b4d6ed6130 |
BIN
dabmodel.zip
BIN
dabmodel.zip
Binary file not shown.
0
src/dabmodel/LAMFields/__init__.py
Normal file
0
src/dabmodel/LAMFields/__init__.py
Normal file
@@ -36,3 +36,5 @@ from .exception import (
|
||||
InvalidFeatureInheritance,
|
||||
FeatureNotBound,
|
||||
)
|
||||
|
||||
__all__ = [name for name in globals() if not name.startswith("_")]
|
||||
|
||||
0
src/dabmodel/meta/__init__.py
Normal file
0
src/dabmodel/meta/__init__.py
Normal file
@@ -34,11 +34,11 @@ 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["__DABSchema__"]:
|
||||
namespace["__DABSchema__"]["features"] = {}
|
||||
if "features" not in namespace["__LAMSchema__"]:
|
||||
namespace["__LAMSchema__"]["features"] = {}
|
||||
else:
|
||||
namespace["__DABSchema__"]["features"] = copy(
|
||||
namespace["__DABSchema__"]["features"]
|
||||
namespace["__LAMSchema__"]["features"] = copy(
|
||||
namespace["__LAMSchema__"]["features"]
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@@ -78,8 +78,8 @@ class _MetaAppliance(_MetaElement):
|
||||
- Else, if `_fvalue` is a Feature *class*, stage into `new_features`.
|
||||
- Otherwise, it is a regular field: delegate to meta.process_new_field.
|
||||
"""
|
||||
if _fname in namespace["__DABSchema__"]["features"].keys():
|
||||
if not issubclass(_fvalue, namespace["__DABSchema__"]["features"][_fname]):
|
||||
if _fname in namespace["__LAMSchema__"]["features"].keys():
|
||||
if not issubclass(_fvalue, namespace["__LAMSchema__"]["features"][_fname]):
|
||||
raise InvalidFeatureInheritance(
|
||||
f"Feature {_fname} is not an instance of {bases[0]}.{_fname}"
|
||||
)
|
||||
@@ -103,16 +103,16 @@ class _MetaAppliance(_MetaElement):
|
||||
|
||||
For each new/modified feature:
|
||||
- bind it to `cls` (sets the feature's `_BoundAppliance`),
|
||||
- register it under `cls.__DABSchema__["features"]`.
|
||||
- register it under `cls.__LAMSchema__["features"]`.
|
||||
"""
|
||||
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
|
||||
cls.__DABSchema__["features"][_ftname] = _ftvalue
|
||||
cls.__LAMSchema__["features"][_ftname] = _ftvalue
|
||||
for _ftname, _ftvalue in extensions["new_features"].items():
|
||||
_ftvalue._BoundAppliance = cls # pylint: disable=protected-access
|
||||
cls.__DABSchema__["features"][_ftname] = _ftvalue
|
||||
cls.__LAMSchema__["features"][_ftname] = _ftvalue
|
||||
|
||||
def finalize_instance(cls: Type, obj, extensions: dict[str, Any]):
|
||||
"""
|
||||
@@ -122,7 +122,7 @@ class _MetaAppliance(_MetaElement):
|
||||
- Subclass replacements
|
||||
- Dict overrides (class + patch dict)
|
||||
"""
|
||||
for fname, fdef in obj.__DABSchema__.get("features", {}).items():
|
||||
for fname, fdef in obj.__LAMSchema__.get("features", {}).items():
|
||||
# Case 1: plain class or subclass
|
||||
if isinstance(fdef, type) and issubclass(fdef, Feature):
|
||||
inst = fdef()
|
||||
@@ -133,11 +133,11 @@ class _MetaAppliance(_MetaElement):
|
||||
feat_cls, overrides = fdef
|
||||
inst = feat_cls()
|
||||
for field_name, new_val in overrides.items():
|
||||
if field_name not in feat_cls.__DABSchema__:
|
||||
if field_name not in feat_cls.__LAMSchema__:
|
||||
raise InvalidFieldValue(
|
||||
f"Feature '{fname}' has no field '{field_name}'"
|
||||
)
|
||||
field = feat_cls.__DABSchema__[field_name]
|
||||
field = feat_cls.__LAMSchema__[field_name]
|
||||
try:
|
||||
check_type(
|
||||
new_val,
|
||||
@@ -150,7 +150,7 @@ class _MetaAppliance(_MetaElement):
|
||||
f"expected {field.annotations}, got {new_val!r}"
|
||||
) from exp
|
||||
object.__setattr__(inst, field_name, LAMdeepfreeze(new_val))
|
||||
inst.__DABSchema__[field_name] = FrozenLAMField(
|
||||
inst.__LAMSchema__[field_name] = FrozenLAMField(
|
||||
LAMField(field_name, new_val, field.annotations, field._info)
|
||||
)
|
||||
object.__setattr__(obj, fname, inst)
|
||||
@@ -173,8 +173,8 @@ class _MetaAppliance(_MetaElement):
|
||||
"""
|
||||
# --- field overrides (unchanged) ---
|
||||
for k, v in list(kwargs.items()):
|
||||
if k in cls.__DABSchema__: # regular field
|
||||
field = cls.__DABSchema__[k]
|
||||
if k in cls.__LAMSchema__: # regular field
|
||||
field = cls.__LAMSchema__[k]
|
||||
try:
|
||||
check_type(
|
||||
v,
|
||||
@@ -187,15 +187,15 @@ class _MetaAppliance(_MetaElement):
|
||||
) from exp
|
||||
|
||||
object.__setattr__(obj, k, LAMdeepfreeze(v))
|
||||
obj.__DABSchema__[k] = FrozenLAMField(
|
||||
obj.__LAMSchema__[k] = FrozenLAMField(
|
||||
LAMField(k, v, field.annotations, field._info)
|
||||
)
|
||||
kwargs.pop(k)
|
||||
|
||||
# --- feature overrides ---
|
||||
for k, v in list(kwargs.items()):
|
||||
if k in cls.__DABSchema__.get("features", {}):
|
||||
base_feat_cls = cls.__DABSchema__["features"][k]
|
||||
if k in cls.__LAMSchema__.get("features", {}):
|
||||
base_feat_cls = cls.__LAMSchema__["features"][k]
|
||||
|
||||
# Case 1: subclass replacement (inheritance)
|
||||
if isinstance(v, type) and issubclass(v, base_feat_cls):
|
||||
@@ -205,13 +205,13 @@ class _MetaAppliance(_MetaElement):
|
||||
f"Feature {v.__name__} is not bound to {cls.__name__}"
|
||||
)
|
||||
# record subclass into instance schema
|
||||
obj.__DABSchema__["features"][k] = v
|
||||
obj.__LAMSchema__["features"][k] = v
|
||||
kwargs.pop(k)
|
||||
|
||||
# Case 2: dict override
|
||||
elif isinstance(v, dict):
|
||||
# store (class, override_dict) for finalize_instance
|
||||
obj.__DABSchema__["features"][k] = (base_feat_cls, v)
|
||||
obj.__LAMSchema__["features"][k] = (base_feat_cls, v)
|
||||
kwargs.pop(k)
|
||||
|
||||
else:
|
||||
@@ -227,7 +227,7 @@ class _MetaAppliance(_MetaElement):
|
||||
raise FeatureNotBound(
|
||||
f"Feature {v.__name__} is not bound to {cls.__name__}"
|
||||
)
|
||||
obj.__DABSchema__["features"][k] = v
|
||||
obj.__LAMSchema__["features"][k] = v
|
||||
kwargs.pop(k)
|
||||
|
||||
if kwargs:
|
||||
|
||||
@@ -337,7 +337,7 @@ class _MetaElement(type):
|
||||
modified_fields: Dict[str, Any] = {}
|
||||
new_fields: Dict[str, LAMField[Any]] = {}
|
||||
initializer: Optional[Callable[..., Any]] = None
|
||||
__DABSchema__: dict[str, Any] = {}
|
||||
__LAMSchema__: dict[str, Any] = {}
|
||||
|
||||
@classmethod
|
||||
def check_class(
|
||||
@@ -371,7 +371,7 @@ class _MetaElement(type):
|
||||
"__DABSchema__ not found in base class, broken inheritance chain."
|
||||
)
|
||||
# copy inherited schema
|
||||
namespace["__DABSchema__"] = copy(bases[0].__DABSchema__)
|
||||
namespace["__DABSchema__"] = copy(bases[0].__LAMSchema__)
|
||||
|
||||
# force field without default value to be instantiated (with None)
|
||||
if "__annotations__" in namespace:
|
||||
@@ -569,7 +569,7 @@ class _MetaElement(type):
|
||||
_check_initializer_safety(mcs.initializer)
|
||||
init_fieldvalues = {}
|
||||
init_fieldtypes = {}
|
||||
for _fname, _fvalue in cls.__DABSchema__.items():
|
||||
for _fname, _fvalue in cls.__LAMSchema__.items():
|
||||
if isinstance(_fvalue, LAMField):
|
||||
init_fieldvalues[_fname] = deepcopy(_fvalue.raw_value)
|
||||
init_fieldtypes[_fname] = _fvalue.annotations
|
||||
@@ -594,14 +594,14 @@ class _MetaElement(type):
|
||||
try:
|
||||
check_type(
|
||||
_fvalue,
|
||||
cls.__DABSchema__[_fname].annotations,
|
||||
cls.__LAMSchema__[_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.__DABSchema__[_fname].update_value(_fvalue)
|
||||
cls.__LAMSchema__[_fname].update_value(_fvalue)
|
||||
|
||||
def __new__(
|
||||
mcs: type["_MetaElement"],
|
||||
@@ -638,12 +638,12 @@ class _MetaElement(type):
|
||||
- For new fields: set the freshly built LAMField and record its source.
|
||||
"""
|
||||
for _fname, _fvalue in mcs.modified_fields.items():
|
||||
cls.__DABSchema__[_fname] = deepcopy(bases[0].__DABSchema__[_fname])
|
||||
cls.__DABSchema__[_fname].update_value(_fvalue)
|
||||
cls.__LAMSchema__[_fname] = deepcopy(bases[0].__LAMSchema__[_fname])
|
||||
cls.__LAMSchema__[_fname].update_value(_fvalue)
|
||||
|
||||
for _fname, _fvalue in mcs.new_fields.items():
|
||||
_fvalue.add_source(cls)
|
||||
cls.__DABSchema__[_fname] = _fvalue
|
||||
cls.__LAMSchema__[_fname] = _fvalue
|
||||
|
||||
def __call__(cls: Type, *args: Any, **kw: Any): # intentionally untyped
|
||||
"""BaseElement new instance"""
|
||||
@@ -672,7 +672,7 @@ class _MetaElement(type):
|
||||
|
||||
Copies each LAMField.value to an instance attribute (deep-frozen view).
|
||||
"""
|
||||
for _fname, _fvalue in cls.__DABSchema__.items():
|
||||
for _fname, _fvalue in cls.__LAMSchema__.items():
|
||||
if isinstance(_fvalue, LAMField):
|
||||
object.__setattr__(obj, _fname, _fvalue.value)
|
||||
|
||||
@@ -684,8 +684,8 @@ class _MetaElement(type):
|
||||
|
||||
Creates a per-instance `__DABSchema__` dict where each field is read-only.
|
||||
"""
|
||||
inst_schema = copy(obj.__DABSchema__)
|
||||
for _fname, _fvalue in cls.__DABSchema__.items():
|
||||
inst_schema = copy(obj.__LAMSchema__)
|
||||
for _fname, _fvalue in cls.__LAMSchema__.items():
|
||||
if isinstance(_fvalue, LAMField):
|
||||
inst_schema[_fname] = FrozenLAMField(_fvalue)
|
||||
|
||||
@@ -735,7 +735,7 @@ class _MetaElement(type):
|
||||
if key.startswith("_"): # allow private and dunder attrs
|
||||
return orig_setattr(_self, key, value)
|
||||
# block writes after init if key is readonly
|
||||
if key in _self.__DABSchema__.keys():
|
||||
if key in _self.__LAMSchema__.keys():
|
||||
if key in _self.__dict__:
|
||||
raise ReadOnlyField(f"{key} is read-only")
|
||||
# elif key in _self.__DABSchema__["features"].keys():
|
||||
|
||||
@@ -53,7 +53,7 @@ class MainTests(unittest.TestCase):
|
||||
# field is in the object
|
||||
self.assertIn(name, dir(obj))
|
||||
# field is in the schema
|
||||
self.assertIn(name, obj.__DABSchema__.keys())
|
||||
self.assertIn(name, obj.__LAMSchema__.keys())
|
||||
# field is readable
|
||||
self.assertEqual(getattr(obj, name), default_value)
|
||||
# field is read only
|
||||
@@ -596,19 +596,19 @@ class MainTests(unittest.TestCase):
|
||||
expected_default_value: str,
|
||||
expected_type: type,
|
||||
):
|
||||
self.assertIn(field_name, appliance.__DABSchema__)
|
||||
self.assertIn("doc", dir(appliance.__DABSchema__[field_name]))
|
||||
self.assertEqual(appliance.__DABSchema__[field_name].doc, "")
|
||||
self.assertIn("annotations", dir(appliance.__DABSchema__[field_name]))
|
||||
self.assertEqual(appliance.__DABSchema__[field_name].annotations, expected_type)
|
||||
self.assertIn("value", dir(appliance.__DABSchema__[field_name]))
|
||||
self.assertEqual(appliance.__DABSchema__[field_name].value, expected_value)
|
||||
self.assertIn("default_value", dir(appliance.__DABSchema__[field_name]))
|
||||
self.assertIn(field_name, appliance.__LAMSchema__)
|
||||
self.assertIn("doc", dir(appliance.__LAMSchema__[field_name]))
|
||||
self.assertEqual(appliance.__LAMSchema__[field_name].doc, "")
|
||||
self.assertIn("annotations", dir(appliance.__LAMSchema__[field_name]))
|
||||
self.assertEqual(appliance.__LAMSchema__[field_name].annotations, expected_type)
|
||||
self.assertIn("value", dir(appliance.__LAMSchema__[field_name]))
|
||||
self.assertEqual(appliance.__LAMSchema__[field_name].value, expected_value)
|
||||
self.assertIn("default_value", dir(appliance.__LAMSchema__[field_name]))
|
||||
self.assertEqual(
|
||||
appliance.__DABSchema__[field_name].default_value, expected_default_value
|
||||
appliance.__LAMSchema__[field_name].default_value, expected_default_value
|
||||
)
|
||||
self.assertIn("constraints", dir(appliance.__DABSchema__[field_name]))
|
||||
self.assertEqual(appliance.__DABSchema__[field_name].constraints, ())
|
||||
self.assertIn("constraints", dir(appliance.__LAMSchema__[field_name]))
|
||||
self.assertEqual(appliance.__LAMSchema__[field_name].constraints, ())
|
||||
|
||||
def test_immutable_fields_schema(self):
|
||||
"""Testing first appliance level, and Field types (annotated)"""
|
||||
@@ -809,40 +809,40 @@ class MainTests(unittest.TestCase):
|
||||
app1 = Appliance1()
|
||||
|
||||
self.immutable_vars__test_field(app1, "StrVar", "default value", "test")
|
||||
self.assertEqual(app1.__DABSchema__["StrVar"].doc, "foo1")
|
||||
self.assertEqual(app1.__LAMSchema__["StrVar"].doc, "foo1")
|
||||
self.immutable_vars__test_field(app1, "StrVar2", "default value2", "test2")
|
||||
self.assertEqual(app1.__DABSchema__["StrVar2"].doc, "foo2")
|
||||
self.assertEqual(app1.__LAMSchema__["StrVar2"].doc, "foo2")
|
||||
self.immutable_vars__test_field(app1, "VarInt", 12, 13)
|
||||
self.assertEqual(app1.__DABSchema__["VarInt"].doc, "foo3")
|
||||
self.assertEqual(app1.__LAMSchema__["VarInt"].doc, "foo3")
|
||||
self.immutable_vars__test_field(app1, "VarInt2", 21, 22)
|
||||
self.assertEqual(app1.__DABSchema__["VarInt2"].doc, "foo4")
|
||||
self.assertEqual(app1.__LAMSchema__["VarInt2"].doc, "foo4")
|
||||
self.immutable_vars__test_field(app1, "VarFloat", 12.1, 32)
|
||||
self.assertEqual(app1.__DABSchema__["VarFloat"].doc, "foo5")
|
||||
self.assertEqual(app1.__LAMSchema__["VarFloat"].doc, "foo5")
|
||||
self.immutable_vars__test_field(app1, "VarFloat2", 21.2, 42)
|
||||
self.assertEqual(app1.__DABSchema__["VarFloat2"].doc, "foo6")
|
||||
self.assertEqual(app1.__LAMSchema__["VarFloat2"].doc, "foo6")
|
||||
self.immutable_vars__test_field(
|
||||
app1, "VarComplex", complex(3, 5), complex(1, 2)
|
||||
)
|
||||
self.assertEqual(app1.__DABSchema__["VarComplex"].doc, "foo7")
|
||||
self.assertEqual(app1.__LAMSchema__["VarComplex"].doc, "foo7")
|
||||
self.immutable_vars__test_field(
|
||||
app1, "VarComplex2", complex(8, 6), complex(3, 2)
|
||||
)
|
||||
self.assertEqual(app1.__DABSchema__["VarComplex2"].doc, "foo8")
|
||||
self.assertEqual(app1.__LAMSchema__["VarComplex2"].doc, "foo8")
|
||||
self.immutable_vars__test_field(app1, "VarBool", True, False)
|
||||
self.assertEqual(app1.__DABSchema__["VarBool"].doc, "foo9")
|
||||
self.assertEqual(app1.__LAMSchema__["VarBool"].doc, "foo9")
|
||||
self.immutable_vars__test_field(app1, "VarBool2", False, True)
|
||||
self.assertEqual(app1.__DABSchema__["VarBool2"].doc, "foo10")
|
||||
self.assertEqual(app1.__LAMSchema__["VarBool2"].doc, "foo10")
|
||||
self.immutable_vars__test_field(
|
||||
app1, "VarBytes", bytes.fromhex("2Ef0 F1f2 "), bytes.fromhex("11f0 F1f2 ")
|
||||
)
|
||||
self.assertEqual(app1.__DABSchema__["VarBytes"].doc, "foo11")
|
||||
self.assertEqual(app1.__LAMSchema__["VarBytes"].doc, "foo11")
|
||||
self.immutable_vars__test_field(
|
||||
app1,
|
||||
"VarBytes2",
|
||||
bytes.fromhex("2ff0 F7f2 "),
|
||||
bytes.fromhex("11f0 F1e2 "),
|
||||
)
|
||||
self.assertEqual(app1.__DABSchema__["VarBytes2"].doc, "foo12")
|
||||
self.assertEqual(app1.__LAMSchema__["VarBytes2"].doc, "foo12")
|
||||
|
||||
with self.assertRaises(dm.InvalidFieldAnnotation):
|
||||
|
||||
@@ -1008,10 +1008,10 @@ class MainTests(unittest.TestCase):
|
||||
class Appliance3(Appliance2):
|
||||
NewValue: str = "newval"
|
||||
|
||||
self.assertNotIn("NewValue", Appliance2.__DABSchema__)
|
||||
self.assertNotIn("NewValue", app2.__DABSchema__)
|
||||
self.assertNotIn("NewValue", Appliance1.__DABSchema__)
|
||||
self.assertNotIn("NewValue", app1.__DABSchema__)
|
||||
self.assertNotIn("NewValue", Appliance2.__LAMSchema__)
|
||||
self.assertNotIn("NewValue", app2.__LAMSchema__)
|
||||
self.assertNotIn("NewValue", Appliance1.__LAMSchema__)
|
||||
self.assertNotIn("NewValue", app1.__LAMSchema__)
|
||||
|
||||
app3 = Appliance3()
|
||||
self.immutable_vars__test_field(app3, "StrVar", "moded value", "test")
|
||||
@@ -1638,19 +1638,19 @@ class MainTests(unittest.TestCase):
|
||||
|
||||
app1 = Appliance1()
|
||||
|
||||
self.assertIsInstance(Appliance1.__DABSchema__["VarStrOuter"], dm.LAMField)
|
||||
self.assertIsInstance(app1.__DABSchema__["VarStrOuter"], dm.FrozenLAMField)
|
||||
self.assertIn("Feature1", app1.__DABSchema__["features"])
|
||||
self.assertIsInstance(Appliance1.__LAMSchema__["VarStrOuter"], dm.LAMField)
|
||||
self.assertIsInstance(app1.__LAMSchema__["VarStrOuter"], dm.FrozenLAMField)
|
||||
self.assertIn("Feature1", app1.__LAMSchema__["features"])
|
||||
self.assertIn(
|
||||
"VarStrInner", app1.__DABSchema__["features"]["Feature1"].__DABSchema__
|
||||
"VarStrInner", app1.__LAMSchema__["features"]["Feature1"].__LAMSchema__
|
||||
)
|
||||
self.assertIsInstance(
|
||||
app1.__DABSchema__["features"]["Feature1"].__DABSchema__["VarStrInner"],
|
||||
app1.__LAMSchema__["features"]["Feature1"].__LAMSchema__["VarStrInner"],
|
||||
dm.LAMField,
|
||||
)
|
||||
self.assertTrue(hasattr(app1, "Feature1"))
|
||||
self.assertIsInstance(
|
||||
app1.Feature1.__DABSchema__["VarStrInner"], dm.FrozenLAMField
|
||||
app1.Feature1.__LAMSchema__["VarStrInner"], dm.FrozenLAMField
|
||||
)
|
||||
self.assertTrue(hasattr(app1.Feature1, "VarStrInner"))
|
||||
|
||||
@@ -1690,19 +1690,19 @@ class MainTests(unittest.TestCase):
|
||||
app2 = Appliance2()
|
||||
app3 = Appliance3()
|
||||
|
||||
self.assertIsInstance(Appliance1.__DABSchema__["VarStrOuter"], dm.LAMField)
|
||||
self.assertIsInstance(app1.__DABSchema__["VarStrOuter"], dm.FrozenLAMField)
|
||||
self.assertIn("Feature1", app1.__DABSchema__["features"])
|
||||
self.assertIsInstance(Appliance1.__LAMSchema__["VarStrOuter"], dm.LAMField)
|
||||
self.assertIsInstance(app1.__LAMSchema__["VarStrOuter"], dm.FrozenLAMField)
|
||||
self.assertIn("Feature1", app1.__LAMSchema__["features"])
|
||||
self.assertIn(
|
||||
"VarStrInner", app1.__DABSchema__["features"]["Feature1"].__DABSchema__
|
||||
"VarStrInner", app1.__LAMSchema__["features"]["Feature1"].__LAMSchema__
|
||||
)
|
||||
self.assertIsInstance(
|
||||
app1.__DABSchema__["features"]["Feature1"].__DABSchema__["VarStrInner"],
|
||||
app1.__LAMSchema__["features"]["Feature1"].__LAMSchema__["VarStrInner"],
|
||||
dm.LAMField,
|
||||
)
|
||||
self.assertTrue(hasattr(app1, "Feature1"))
|
||||
self.assertIsInstance(
|
||||
app1.Feature1.__DABSchema__["VarStrInner"], dm.FrozenLAMField
|
||||
app1.Feature1.__LAMSchema__["VarStrInner"], dm.FrozenLAMField
|
||||
)
|
||||
self.assertTrue(hasattr(app1.Feature1, "VarStrInner"))
|
||||
self.assertEqual(app1.VarStrOuter, "testvalue APPLIANCE1")
|
||||
|
||||
Reference in New Issue
Block a user