diff --git a/src/dabmodel/__init__.py b/src/dabmodel/__init__.py index 80ec0df..cd289cd 100644 --- a/src/dabmodel/__init__.py +++ b/src/dabmodel/__init__.py @@ -13,6 +13,7 @@ Main module __init__ file. from .__metadata__ import __version__, __Summuary__, __Name__ from .model import ( DABFieldInfo, + DABField, BaseAppliance, BaseFeature, DABModelException, diff --git a/src/dabmodel/model.py b/src/dabmodel/model.py index 6874815..5765c4e 100644 --- a/src/dabmodel/model.py +++ b/src/dabmodel/model.py @@ -476,7 +476,7 @@ class BaseMeta(type): mcs: type["BaseMeta"], name: str, bases: tuple[type[Any], ...], namespace: dict[str, Any] # pylint: disable=unused-argument ) -> None: """early BaseElement checks""" - # print("__NEW__ Defining:", name, "with keys:", list(namespace)) + print("__NEW__ Defining:", name, "with keys:", list(namespace)) if len(bases) > 1: raise MultipleInheritanceForbidden("Multiple inheritance is not supported by dabmodel") @@ -532,9 +532,9 @@ class BaseMeta(type): mcs: type["BaseMeta"], name: str, bases: tuple[type[Any], ...], namespace: dict[str, Any], _fname: str, _fvalue: Any ): # pylint: disable=unused-argument """preprocessing BaseElement modified Fields""" - # print(f"Modified field: {_fname}") + print(f"Modified field: {_fname}") if "__annotations__" in namespace and _fname in namespace["__annotations__"]: - raise ReadOnlyFieldAnnotation("annotations cannot be modified on derived classes") + raise ReadOnlyFieldAnnotation(f"annotations cannot be modified on derived classes {_fname}") try: check_type( _fvalue, @@ -552,7 +552,7 @@ class BaseMeta(type): mcs: type["BaseMeta"], name: str, bases: tuple[type[Any], ...], namespace: dict[str, Any], _fname: str, _fvalue: Any ): # pylint: disable=unused-argument """preprocessing BaseElement new Fields""" - # print(f"New field: {_fname}") + print(f"New field: {_fname}") # print(f"type is: {type(_fvalue)}") # print(f"value is: {_fvalue}") @@ -694,27 +694,54 @@ class BaseFeature(BaseElement,metaclass=BaseMetaFeature): Base class for Appliance's Features. Features are optional traits of an appliance. """ + Enabled:bool=False class BaseMetaAppliance(BaseMeta): - + + @classmethod + def pre_check( + mcs: type["BaseMeta"], name: str, bases: tuple[type[Any], ...], namespace: dict[str, Any] # pylint: disable=unused-argument + ) -> None: + """early BaseElement checks""" + print("__NEW__ Defining:", name, "with keys:", list(namespace)) + super().pre_check(name,bases,namespace) + print(bases) + if len(bases)>0 and ("__DABFeatures__" not in dir(bases[0])): + print("add missing fields") + namespace["__DABFeatures__"] = {} + else: + print(f"bases[0]:{bases[0].__name__}") + print(dir(bases[0])) + # check class tree origin + if "__DABFeatures__" not in dir(bases[0]): + raise BrokenInheritance("__DABFeatures__ not found in base class, broken inheritance chain.") + # copy inherited schema + print("COPY") + namespace["__DABFeatures__"] = copy(bases[0].__DABFeatures__) + + @classmethod def pre_processing(mcs: type["BaseMeta"], name: str, bases: tuple[type[Any], ...], namespace: dict[str, Any]): mcs.features: dict[str,type[BaseFeature]] = {} super().pre_processing(name,bases,namespace) - for _ftname in mcs.features.keys(): - del namespace[_ftname] + #for _ftname in mcs.features.keys(): + # del namespace[_ftname] @classmethod def pre_processing_new_fields( mcs: type["BaseMeta"], name: str, bases: tuple[type[Any], ...], namespace: dict[str, Any], _fname: str, _fvalue: Any ): # pylint: disable=unused-argument """preprocessing BaseElement new Fields""" + print('pre_processing_new_fields') if _fname == "features": raise ReservedFieldName("feature is a reserved field name") - elif _fname =="get_features": - pass elif isinstance(_fvalue,BaseMetaFeature): + print("find Feature") + print(namespace["__DABFeatures__"]) + if _fname in namespace["__DABFeatures__"].keys(): + print("existing Feature !") mcs.features[_fname]=_fvalue + #namespace["__DABFeatures__"][_fname] = _fvalue else: super().pre_processing_new_fields(name,bases,namespace,_fname,_fvalue) @@ -723,13 +750,14 @@ class BaseMetaAppliance(BaseMeta): mcs: type["BaseMeta"], cls, name: str, bases: tuple[type[Any], ...], namespace: dict[str, Any] # pylint: disable=unused-argument ): super().save_values(cls,name,bases,namespace) + print(dir(mcs)) cls.__DABFeatures__ = mcs.features def modify_object(self, obj): # intentionally untyped - for _ftname,_ftvalue in self.features.items(): + for _ftname,_ftvalue in self.__DABFeatures__.items(): instft = _ftvalue() setattr(self, _ftname,instft ) - self.__DABFeatures__[_ftname] = instft + #self.__DABFeatures__[_ftname] = instft class BaseAppliance(BaseElement,metaclass=BaseMetaAppliance): """BaseFeature class diff --git a/test/test_model.py b/test/test_model.py index 53a4934..78ce5e5 100644 --- a/test/test_model.py +++ b/test/test_model.py @@ -1176,7 +1176,7 @@ class TestConfigWithoutEnabledFlag(unittest.TestCase): test_initializer_safe_testfc() def test_feature(self): - """Testing first appliance level, and Field types (simple)""" + """Testing first appliance feature, and Field types (simple)""" @@ -1193,6 +1193,48 @@ class TestConfigWithoutEnabledFlag(unittest.TestCase): self.assertIn("Feature1",app1.__DABFeatures__) self.assertIn("VarStrInner",app1.__DABFeatures__["Feature1"].__DABSchema__) self.assertIsInstance(app1.__DABFeatures__["Feature1"].__DABSchema__["VarStrInner"],dm.FrozenDABField) + + def test_feature_inheritance(self): + """Testing first appliance feature, and Field types (simple)""" + + + + # class can be created + class Appliance1(dm.BaseAppliance): + VarStrOuter: str = "testvalue APPLIANCE1" + class Feature1(dm.BaseFeature): + VarStrInner: str = "testvalue FEATURE1" + VarInt:int=42 + + print(dir(Appliance1)) + + class Appliance2(Appliance1): + VarStrOuter = "testvalue APPLIANCE2" + class Feature2(dm.BaseFeature): + VarStrInner: str = "testvalue FEATURE2" + + print(dir(Appliance2)) + + class Appliance3(Appliance2): + VarStrOuter = "testvalue APPLIANCE3" + class Feature1(Appliance1.Feature1): + VarStrInner = "testvalue FEATURE1 modded" + class Feature3(dm.BaseFeature): + VarStrInner: str = "testvalue FEATURE3" + + print(dir(Appliance3)) + + app1 = Appliance1() + app2 = Appliance2() + app3 = Appliance3() + + self.assertIsInstance(app1.__DABSchema__["VarStrOuter"],dm.FrozenDABField) + self.assertTrue(hasattr(app1, "Feature1")) + self.assertTrue(hasattr(app1.Feature1, "VarStrInner")) + self.assertIn("Feature1",app1.__DABFeatures__) + self.assertIn("VarStrInner",app1.__DABFeatures__["Feature1"].__DABSchema__) + self.assertIsInstance(app1.Feature1.__DABSchema__["VarStrInner"],dm.FrozenDABField) + self.assertIsInstance(app1.__DABFeatures__["Feature1"].__DABSchema__["VarStrInner"],dm.DABField) # ---------- main ---------- if __name__ == "__main__":