Compare commits
8 Commits
0.0.1.post
...
0.0.1.post
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f6e581381d | ||
|
|
981c5201a9 | ||
|
|
ab11052c8f | ||
|
|
4f5dade949 | ||
|
|
cce260bc5e | ||
|
|
915a4332ee | ||
|
|
4dca3eb9d1 | ||
|
|
e11c541139 |
@@ -13,6 +13,7 @@ Main module __init__ file.
|
||||
from .__metadata__ import __version__, __Summuary__, __Name__
|
||||
from .model import (
|
||||
DABFieldInfo,
|
||||
DABField,
|
||||
BaseAppliance,
|
||||
BaseFeature,
|
||||
DABModelException,
|
||||
@@ -27,4 +28,6 @@ from .model import (
|
||||
IncompletelyAnnotatedField,
|
||||
ImportForbidden,
|
||||
FunctionForbidden,
|
||||
FrozenDABField,
|
||||
InvalidFeatureInheritance,
|
||||
)
|
||||
|
||||
@@ -55,7 +55,6 @@ ALLOWED_ANNOTATIONS = {
|
||||
}
|
||||
|
||||
ALLOWED_MODEL_FIELDS_TYPES = (str, int, float, complex, bool, bytes)
|
||||
ALLOWED_MODEL_FIELDS_CONTAINERS = (dict, list, set, frozenset, tuple)
|
||||
|
||||
|
||||
# TV_ALLOWED_MODEL_FIELDS_TYPES = TypeVar("TV_ALLOWED_MODEL_FIELDS_TYPES", *ALLOWED_MODEL_FIELDS_TYPES, *ALLOWED_MODEL_FIELDS_CONTAINERS)
|
||||
@@ -66,7 +65,11 @@ class DABModelException(Exception):
|
||||
Base Exception for DABModelException class
|
||||
"""
|
||||
|
||||
|
||||
class ReservedFieldName(Exception):
|
||||
"""DABModelException Exception class
|
||||
Base Exception for DABModelException class
|
||||
"""
|
||||
|
||||
class MultipleInheritanceForbidden(DABModelException):
|
||||
"""MultipleInheritanceForbidden Exception class
|
||||
Multiple inheritance is forbidden when using dabmodel
|
||||
@@ -138,6 +141,10 @@ class ImportForbidden(DABModelException):
|
||||
Imports are forbidden
|
||||
"""
|
||||
|
||||
class InvalidFeatureInheritance(DABModelException):
|
||||
"""InvalidFeatureInheritance Exception class
|
||||
Features of same name in child appliance need to be from same type
|
||||
"""
|
||||
|
||||
class FunctionForbidden(DABModelException):
|
||||
"""FunctionForbidden Exception class
|
||||
@@ -298,7 +305,7 @@ class DABFieldInfo:
|
||||
self._constraints = constraints
|
||||
|
||||
@property
|
||||
def doc(self):
|
||||
def doc(self) -> str:
|
||||
"""Returns Field's documentation"""
|
||||
return self._doc
|
||||
|
||||
@@ -325,7 +332,7 @@ class DABField(Generic[T_Field]):
|
||||
self._source = s
|
||||
|
||||
@property
|
||||
def doc(self):
|
||||
def doc(self) -> str:
|
||||
"""Returns Field's documentation"""
|
||||
return self._info.doc
|
||||
|
||||
@@ -403,10 +410,14 @@ class ModelSpecView:
|
||||
|
||||
__slots__ = ("_vals", "_types", "_touched", "_name", "_module")
|
||||
|
||||
def __init__(self, values: dict, types_map: dict[str, type], name, module):
|
||||
def __init__(self, values: dict[str, Any], types_map: dict[str, type], name: str, module: str):
|
||||
self._name: str
|
||||
self._vals: dict[str, Any]
|
||||
self._types: dict[str, type]
|
||||
self._touched: set
|
||||
self._module: str
|
||||
object.__setattr__(self, "_vals", dict(values))
|
||||
object.__setattr__(self, "_types", types_map)
|
||||
object.__setattr__(self, "_touched", set())
|
||||
object.__setattr__(self, "_name", name)
|
||||
object.__setattr__(self, "_module", module)
|
||||
|
||||
@@ -421,16 +432,16 @@ class ModelSpecView:
|
||||
return self._module
|
||||
|
||||
@__module__.setter
|
||||
def __module__(self, value):
|
||||
def __module__(self, value: str):
|
||||
pass
|
||||
|
||||
def __getattr__(self, name):
|
||||
def __getattr__(self, name: str) -> Any:
|
||||
"""internal proxy getattr"""
|
||||
if name not in self._types:
|
||||
raise AttributeError(f"Unknown field {name}")
|
||||
return self._vals[name]
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
def __setattr__(self, name: str, value: Any):
|
||||
"""internal proxy setattr"""
|
||||
if name not in self._types:
|
||||
raise NonExistingField(f"Cannot set unknown field {name}")
|
||||
@@ -446,7 +457,6 @@ class ModelSpecView:
|
||||
raise InvalidFieldValue(f"Field <{name}> value is not of expected type {T}.") from exp
|
||||
|
||||
self._vals[name] = value
|
||||
self._touched.add(name)
|
||||
|
||||
def export(self) -> dict:
|
||||
"""exports all proxified values"""
|
||||
@@ -467,7 +477,8 @@ class BaseMeta(type):
|
||||
|
||||
@classmethod
|
||||
def pre_check(
|
||||
mcs: type["BaseMeta"], name: str, bases: tuple[type[Any], ...], namespace: dict[str, Any] # pylint: disable=unused-argument
|
||||
mcs: type["BaseMeta"], name: str, bases: tuple[type[Any], ...], namespace: dict[str, Any], # pylint: disable=unused-argument
|
||||
extensions : dict[str,Any]
|
||||
) -> None:
|
||||
"""early BaseElement checks"""
|
||||
# print("__NEW__ Defining:", name, "with keys:", list(namespace))
|
||||
@@ -489,13 +500,46 @@ class BaseMeta(type):
|
||||
namespace[_funknown] = None
|
||||
|
||||
@classmethod
|
||||
def pre_processing_modified(
|
||||
mcs: type["BaseMeta"], name: str, bases: tuple[type[Any], ...], namespace: dict[str, Any], _fname: str, _fvalue: Any
|
||||
def pre_processing(mcs: type["BaseMeta"], name: str, bases: tuple[type[Any], ...], namespace: dict[str, Any],extensions : dict[str,Any]):
|
||||
"""preprocessing BaseElement"""
|
||||
# iterating new and modified fields
|
||||
mcs.modified_field = {}
|
||||
mcs.new_fields = {}
|
||||
mcs.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):
|
||||
raise InvalidInitializerType()
|
||||
mcs.initializer = _fvalue.__func__
|
||||
if name.startswith("_"):
|
||||
initializer_name = "__initializer"
|
||||
else:
|
||||
initializer_name = f"_{name}__initializer"
|
||||
elif _fname.startswith("__"):
|
||||
pass
|
||||
else:
|
||||
# print(f"Parsing Field: {_fname} / {_fvalue}")
|
||||
if len(bases) == 1 and _fname in namespace["__DABSchema__"].keys(): # Modified fields
|
||||
mcs.pre_processing_modified_fields(name, bases, namespace, _fname, _fvalue, extensions)
|
||||
else: # New fieds
|
||||
mcs.pre_processing_new_fields(name, bases, namespace, _fname, _fvalue, extensions)
|
||||
# removing modified fields from class (will add them back later)
|
||||
for _fname in mcs.new_fields:
|
||||
del namespace[_fname]
|
||||
for _fname in mcs.modified_field:
|
||||
del namespace[_fname]
|
||||
if mcs.initializer is not None and initializer_name is not None:
|
||||
del namespace[initializer_name]
|
||||
|
||||
@classmethod
|
||||
def pre_processing_modified_fields(
|
||||
mcs: type["BaseMeta"], name: str, bases: tuple[type[Any], ...], namespace: dict[str, Any], _fname: str, _fvalue: Any,
|
||||
extensions : dict[str,Any]
|
||||
): # pylint: disable=unused-argument
|
||||
"""preprocessing BaseElement modified Fields"""
|
||||
# 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,
|
||||
@@ -509,13 +553,12 @@ class BaseMeta(type):
|
||||
mcs.modified_field[_fname] = _fvalue
|
||||
|
||||
@classmethod
|
||||
def pre_processing_new(
|
||||
mcs: type["BaseMeta"], name: str, bases: tuple[type[Any], ...], namespace: dict[str, Any], _fname: str, _fvalue: Any
|
||||
def pre_processing_new_fields(
|
||||
mcs: type["BaseMeta"], name: str, bases: tuple[type[Any], ...], namespace: dict[str, Any], _fname: str, _fvalue: Any,
|
||||
extensions : dict[str,Any]
|
||||
): # pylint: disable=unused-argument
|
||||
"""preprocessing BaseElement new Fields"""
|
||||
# print(f"New field: {_fname}")
|
||||
# print(f"type is: {type(_fvalue)}")
|
||||
# print(f"value is: {_fvalue}")
|
||||
#print(f"New field: {_fname}")
|
||||
|
||||
# check if field is annotated
|
||||
if "__annotations__" not in namespace or _fname not in namespace["__annotations__"]:
|
||||
@@ -540,8 +583,7 @@ class BaseMeta(type):
|
||||
raise InvalidFieldAnnotation("Only DABFieldInfo object is allowed as Annotated data.")
|
||||
|
||||
_finfo = args[1]
|
||||
|
||||
# print(f"annotation is: {namespace['__annotations__'][_fname]}")
|
||||
|
||||
# check if value is valid
|
||||
try:
|
||||
check_type(_fvalue, namespace["__annotations__"][_fname], collection_check_strategy=CollectionCheckStrategy.ALL_ITEMS)
|
||||
@@ -549,50 +591,19 @@ class BaseMeta(type):
|
||||
raise InvalidFieldValue(f"Value of Field <{_fname}> is not of expected type {namespace['__annotations__'][_fname]}.") from exp
|
||||
mcs.new_fields[_fname] = DABField(_fname, _fvalue, namespace["__annotations__"][_fname], _finfo)
|
||||
|
||||
@classmethod
|
||||
def pre_processing(mcs: type["BaseMeta"], name: str, bases: tuple[type[Any], ...], namespace: dict[str, Any]):
|
||||
"""preprocessing BaseElement"""
|
||||
# iterating new and modified fields
|
||||
mcs.modified_field = {}
|
||||
mcs.new_fields = {}
|
||||
mcs.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):
|
||||
raise InvalidInitializerType()
|
||||
mcs.initializer = _fvalue.__func__
|
||||
if name.startswith("_"):
|
||||
initializer_name = "__initializer"
|
||||
else:
|
||||
initializer_name = f"_{name}__initializer"
|
||||
elif _fname.startswith("__"):
|
||||
pass
|
||||
else:
|
||||
# print(f"Parsing Field: {_fname} / {_fvalue}")
|
||||
if len(bases) == 1 and _fname in namespace["__DABSchema__"].keys(): # Modified fields
|
||||
mcs.pre_processing_modified(name, bases, namespace, _fname, _fvalue)
|
||||
else: # New fieds
|
||||
mcs.pre_processing_new(name, bases, namespace, _fname, _fvalue)
|
||||
# removing modified fields from class (will add them back later)
|
||||
for _fname in mcs.new_fields:
|
||||
del namespace[_fname]
|
||||
for _fname in mcs.modified_field:
|
||||
del namespace[_fname]
|
||||
if mcs.initializer is not None and initializer_name is not None:
|
||||
del namespace[initializer_name]
|
||||
|
||||
@classmethod
|
||||
def call_initializer(
|
||||
mcs: type["BaseMeta"], cls, name: str, bases: tuple[type[Any], ...], namespace: dict[str, Any]
|
||||
): # pylint: disable=unused-argument
|
||||
mcs: type["BaseMeta"], cls, name: str, bases: tuple[type[Any], ...], namespace: dict[str, Any], # pylint: disable=unused-argument
|
||||
extensions : dict[str,Any]
|
||||
):
|
||||
"""BaseElement initializer processing"""
|
||||
if mcs.initializer is not None:
|
||||
init_fieldvalues = {}
|
||||
init_fieldtypes = {}
|
||||
for _fname, _fvalue in cls.__DABSchema__.items():
|
||||
init_fieldvalues[_fname] = deepcopy(_fvalue.raw_value)
|
||||
init_fieldtypes[_fname] = _fvalue.annotations
|
||||
if isinstance(_fvalue,DABField):
|
||||
init_fieldvalues[_fname] = deepcopy(_fvalue.raw_value)
|
||||
init_fieldtypes[_fname] = _fvalue.annotations
|
||||
fakecls = ModelSpecView(init_fieldvalues, init_fieldtypes, cls.__name__, cls.__module__)
|
||||
safe_globals = {"__builtins__": {"__import__": _blocked_import}, **ALLOWED_HELPERS_DEFAULT}
|
||||
if mcs.initializer.__code__.co_freevars:
|
||||
@@ -613,69 +624,154 @@ class BaseMeta(type):
|
||||
f"Value of Field <{_fname}> is not of expected type {namespace['__annotations__'][_fname]}."
|
||||
) from exp
|
||||
cls.__DABSchema__[_fname].update_value(_fvalue)
|
||||
|
||||
|
||||
def __new__(mcs: type["BaseMeta"], name: str, bases: tuple[type[Any], ...], namespace: dict[str, Any]) -> Type:
|
||||
"""BaseElement new class"""
|
||||
mcs.pre_check(name, bases, namespace)
|
||||
mcs.pre_processing(name, bases, namespace)
|
||||
extensions : dict[str,Any] = dict()
|
||||
mcs.pre_check(name, bases, namespace, extensions)
|
||||
mcs.pre_processing(name, bases, namespace, extensions)
|
||||
|
||||
_cls = super().__new__(mcs, name, bases, namespace)
|
||||
|
||||
orig_setattr = namespace.get("__setattr__", object.__setattr__)
|
||||
mcs.save_values(_cls, name, bases, namespace, extensions)
|
||||
mcs.call_initializer(_cls, name, bases, namespace, extensions)
|
||||
_cls.install_guard(extensions)
|
||||
|
||||
return _cls
|
||||
|
||||
@classmethod
|
||||
def save_values(
|
||||
mcs: type["BaseMeta"], cls, name: str, bases: tuple[type[Any], ...], namespace: dict[str, Any], # pylint: disable=unused-argument
|
||||
extensions : dict[str,Any]
|
||||
):
|
||||
for _fname, _fvalue in mcs.modified_field.items():
|
||||
cls.__DABSchema__[_fname] = deepcopy(bases[0].__DABSchema__[_fname])
|
||||
cls.__DABSchema__[_fname].update_value(_fvalue)
|
||||
|
||||
def guarded_setattr(self, key, value):
|
||||
for _fname, _fvalue in mcs.new_fields.items():
|
||||
_fvalue.add_source(mcs)
|
||||
cls.__DABSchema__[_fname] = _fvalue
|
||||
|
||||
def __call__(cls: Type, *args: Any, **kw: Any): # intentionally untyped
|
||||
"""BaseElement new instance"""
|
||||
obj = super().__call__(*args, **kw)
|
||||
|
||||
extensions = dict()
|
||||
|
||||
for _fname, _fvalue in cls.__DABSchema__.items():
|
||||
if isinstance(_fvalue,DABField):
|
||||
object.__setattr__(obj, _fname, _fvalue.value)
|
||||
|
||||
inst_schema = copy(obj.__DABSchema__)
|
||||
for _fname, _fvalue in cls.__DABSchema__.items():
|
||||
if isinstance(_fvalue,DABField):
|
||||
inst_schema[_fname] = FrozenDABField(_fvalue)
|
||||
|
||||
object.__setattr__(obj, "__DABSchema__", inst_schema)
|
||||
cls.modify_object(obj,extensions)
|
||||
|
||||
|
||||
return obj
|
||||
|
||||
def modify_object(cls:Type,obj,extensions : dict[str,Any]):
|
||||
pass
|
||||
|
||||
|
||||
def install_guard(cls:Type,extensions : dict[str,Any]):
|
||||
orig_setattr = getattr(cls,"__setattr__")
|
||||
|
||||
# cls.orig_setattr = orig_setattr
|
||||
|
||||
def guarded_setattr(_self, key: str, value: Any):
|
||||
if key.startswith("_"): # allow private and dunder attrs
|
||||
return orig_setattr(self, key, value)
|
||||
return orig_setattr(_self, key, value)
|
||||
# block writes after init if key is readonly
|
||||
if key in self.__DABSchema__.keys():
|
||||
if hasattr(self, key):
|
||||
if key in _self.__DABSchema__.keys():
|
||||
if key in _self.__dict__:
|
||||
raise ReadOnlyField(f"{key} is read-only")
|
||||
elif key in _self.__DABSchema__["features"].keys():
|
||||
if key in _self.__dict__:
|
||||
raise ReadOnlyField(f"{key} is read-only")
|
||||
else:
|
||||
raise NewFieldForbidden("creating new fields is not allowed")
|
||||
|
||||
return orig_setattr(self, key, value)
|
||||
|
||||
namespace["__setattr__"] = guarded_setattr
|
||||
|
||||
_cls = super().__new__(mcs, name, bases, namespace)
|
||||
|
||||
for _fname, _fvalue in mcs.modified_field.items():
|
||||
_cls.__DABSchema__[_fname] = deepcopy(bases[0].__DABSchema__[_fname])
|
||||
_cls.__DABSchema__[_fname].update_value(_fvalue)
|
||||
|
||||
for _fname, _fvalue in mcs.new_fields.items():
|
||||
_fvalue.add_source(mcs)
|
||||
_cls.__DABSchema__[_fname] = _fvalue
|
||||
|
||||
mcs.call_initializer(_cls, name, bases, namespace)
|
||||
|
||||
return _cls
|
||||
|
||||
def __call__(cls, *args: Any, **kw: Any): # intentionally untyped
|
||||
"""BaseElement new instance"""
|
||||
obj = super().__call__(*args, **kw)
|
||||
|
||||
for _fname, _fvalue in cls.__DABSchema__.items():
|
||||
setattr(obj, _fname, cls.__DABSchema__[_fname].value)
|
||||
inst_schema: dict[str, Any] = {}
|
||||
for _fname, _fvalue in cls.__DABSchema__.items():
|
||||
inst_schema[_fname] = FrozenDABField(_fvalue)
|
||||
setattr(obj, "__DABSchema__", inst_schema)
|
||||
return obj
|
||||
return orig_setattr(_self, key, value)
|
||||
|
||||
setattr(cls, "__setattr__", guarded_setattr)
|
||||
|
||||
class BaseElement(metaclass=BaseMeta):
|
||||
"""BaseElement class
|
||||
Base class to apply metaclass and set common Fields.
|
||||
"""
|
||||
|
||||
class BaseMetaFeature(BaseMeta):
|
||||
pass
|
||||
|
||||
class BaseFeature(BaseElement):
|
||||
class BaseFeature(BaseElement,metaclass=BaseMetaFeature):
|
||||
"""BaseFeature class
|
||||
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
|
||||
extensions : dict[str,Any]
|
||||
) -> None:
|
||||
"""early BaseElement checks"""
|
||||
super().pre_check(name,bases,namespace,extensions)
|
||||
if "features" not in namespace["__DABSchema__"]:
|
||||
namespace["__DABSchema__"]["features"]={}
|
||||
else:
|
||||
namespace["__DABSchema__"]["features"] = copy(namespace["__DABSchema__"]["features"])
|
||||
return
|
||||
|
||||
|
||||
class BaseAppliance(BaseElement):
|
||||
@classmethod
|
||||
def pre_processing(mcs: type["BaseMeta"], name: str, bases: tuple[type[Any], ...], namespace: dict[str, Any],extensions : dict[str,Any]):
|
||||
extensions["new_features"]: dict[str,type[BaseFeature]] = {}
|
||||
extensions["modified_features"]: dict[str,type[BaseFeature]] = {}
|
||||
super().pre_processing(name,bases,namespace,extensions)
|
||||
|
||||
|
||||
@classmethod
|
||||
def pre_processing_new_fields(
|
||||
mcs: type["BaseMeta"], name: str, bases: tuple[type[Any], ...], namespace: dict[str, Any], _fname: str, _fvalue: Any,
|
||||
extensions : dict[str,Any]
|
||||
): # pylint: disable=unused-argument
|
||||
"""preprocessing BaseElement new Fields"""
|
||||
#print('pre_processing_new_fields')
|
||||
if _fname in namespace["__DABSchema__"]["features"].keys():
|
||||
if not issubclass(_fvalue,namespace["__DABSchema__"]["features"][_fname]):
|
||||
raise InvalidFeatureInheritance(f"Feature {_fname} is not an instance of {bases[0]}.{_fname}")
|
||||
extensions["modified_features"][_fname]=_fvalue
|
||||
elif isinstance(_fvalue,BaseMetaFeature):
|
||||
extensions["new_features"][_fname]=_fvalue
|
||||
else:
|
||||
super().pre_processing_new_fields(name,bases,namespace,_fname,_fvalue,extensions)
|
||||
|
||||
@classmethod
|
||||
def save_values(
|
||||
mcs: type["BaseMeta"], cls, name: str, bases: tuple[type[Any], ...], namespace: dict[str, Any], # pylint: disable=unused-argument
|
||||
extensions : dict[str,Any]
|
||||
):
|
||||
super().save_values(cls,name,bases,namespace,extensions)
|
||||
|
||||
for _ftname,_ftvalue in extensions["modified_features"].items():
|
||||
cls.__DABSchema__["features"][_ftname] = _ftvalue
|
||||
for _ftname,_ftvalue in extensions["new_features"].items():
|
||||
cls.__DABSchema__["features"][_ftname] = _ftvalue
|
||||
|
||||
def modify_object(cls:Type, obj,extensions : dict[str,Any]): # intentionally untyped
|
||||
for _ftname,_ftvalue in cls.__DABSchema__["features"].items():
|
||||
instft = _ftvalue()
|
||||
object.__setattr__(obj, _ftname,instft )
|
||||
|
||||
|
||||
class BaseAppliance(BaseElement,metaclass=BaseMetaAppliance):
|
||||
"""BaseFeature class
|
||||
Base class for Appliance.
|
||||
An appliance is a server configuration / image that is built using appliance's code and Fields.
|
||||
|
||||
@@ -31,7 +31,7 @@ def test_initializer_safe_testfc():
|
||||
eval("print('hi')")
|
||||
|
||||
|
||||
class TestConfigWithoutEnabledFlag(unittest.TestCase):
|
||||
class MainTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
print("\n->", unittest.TestCase.id(self))
|
||||
|
||||
@@ -219,7 +219,7 @@ class TestConfigWithoutEnabledFlag(unittest.TestCase):
|
||||
self.immutable_vars__test_field(app2, "StrVar5", "default value", "123")
|
||||
self.immutable_vars__test_field(app2, "StrVar6", None, "123")
|
||||
|
||||
# @unittest.skip
|
||||
@unittest.skip
|
||||
def test_containers__set(self):
|
||||
"""Testing first appliance level, and Field types (Set)"""
|
||||
|
||||
@@ -298,7 +298,7 @@ class TestConfigWithoutEnabledFlag(unittest.TestCase):
|
||||
res = subprocess.run([sys.executable, "-c", code], env=env)
|
||||
self.assertEqual(res.returncode, 2)
|
||||
|
||||
# @unittest.skip
|
||||
@unittest.skip
|
||||
def test_containers__frozenset(self):
|
||||
"""Testing first appliance level, and Field types (FrozenSet)"""
|
||||
|
||||
@@ -1175,7 +1175,168 @@ class TestConfigWithoutEnabledFlag(unittest.TestCase):
|
||||
def __initializer(cls):
|
||||
test_initializer_safe_testfc()
|
||||
|
||||
def test_feature(self):
|
||||
"""Testing first appliance feature, and Field types (simple)"""
|
||||
|
||||
|
||||
|
||||
# class can be created
|
||||
class Appliance1(dm.BaseAppliance):
|
||||
VarStrOuter: str = "testvalue APPLIANCE"
|
||||
class Feature1(dm.BaseFeature):
|
||||
VarStrInner: str = "testvalue FEATURE"
|
||||
app1 = Appliance1()
|
||||
|
||||
self.assertIsInstance(Appliance1.__DABSchema__["VarStrOuter"],dm.DABField)
|
||||
self.assertIsInstance(app1.__DABSchema__["VarStrOuter"],dm.FrozenDABField)
|
||||
self.assertIn("Feature1",app1.__DABSchema__["features"])
|
||||
self.assertIn("VarStrInner",app1.__DABSchema__["features"]["Feature1"].__DABSchema__)
|
||||
self.assertIsInstance(app1.__DABSchema__["features"]["Feature1"].__DABSchema__["VarStrInner"],dm.DABField)
|
||||
self.assertTrue(hasattr(app1, "Feature1"))
|
||||
self.assertIsInstance(app1.Feature1.__DABSchema__["VarStrInner"],dm.FrozenDABField)
|
||||
self.assertTrue(hasattr(app1.Feature1, "VarStrInner"))
|
||||
|
||||
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(Appliance1.__DABSchema__["VarStrOuter"],dm.DABField)
|
||||
self.assertIsInstance(app1.__DABSchema__["VarStrOuter"],dm.FrozenDABField)
|
||||
self.assertIn("Feature1",app1.__DABSchema__["features"])
|
||||
self.assertIn("VarStrInner",app1.__DABSchema__["features"]["Feature1"].__DABSchema__)
|
||||
self.assertIsInstance(app1.__DABSchema__["features"]["Feature1"].__DABSchema__["VarStrInner"],dm.DABField)
|
||||
self.assertTrue(hasattr(app1, "Feature1"))
|
||||
self.assertIsInstance(app1.Feature1.__DABSchema__["VarStrInner"],dm.FrozenDABField)
|
||||
self.assertTrue(hasattr(app1.Feature1, "VarStrInner"))
|
||||
self.assertEqual(app1.VarStrOuter,"testvalue APPLIANCE1")
|
||||
self.assertEqual(app1.Feature1.VarStrInner,"testvalue FEATURE1")
|
||||
self.assertEqual(app1.Feature1.VarInt,42)
|
||||
self.assertEqual(app2.VarStrOuter,"testvalue APPLIANCE2")
|
||||
self.assertEqual(app2.Feature2.VarStrInner,"testvalue FEATURE2")
|
||||
self.assertEqual(app3.VarStrOuter,"testvalue APPLIANCE3")
|
||||
self.assertEqual(app3.Feature1.VarStrInner,"testvalue FEATURE1 modded")
|
||||
self.assertEqual(app3.Feature1.VarInt,42)
|
||||
self.assertEqual(app3.Feature3.VarStrInner,"testvalue FEATURE3")
|
||||
|
||||
def test_feature_inheritance2(self):
|
||||
"""Testing first appliance feature, and Field types (simple)"""
|
||||
|
||||
# class can be created
|
||||
class Appliance1(dm.BaseAppliance):
|
||||
class Feature1(dm.BaseFeature):
|
||||
VarStrInner: str = "testvalue FEATURE1"
|
||||
|
||||
# check cannot REdefine a feature from BaseFeature
|
||||
with self.assertRaises(dm.InvalidFeatureInheritance):
|
||||
class Appliance2(Appliance1):
|
||||
class Feature1(dm.BaseFeature):
|
||||
...
|
||||
|
||||
class Appliance2b(Appliance1):
|
||||
class Feature1(Appliance1.Feature1):
|
||||
...
|
||||
|
||||
# check only REdefine a feature from highest parent
|
||||
with self.assertRaises(dm.InvalidFeatureInheritance):
|
||||
class Appliance3(Appliance2b):
|
||||
class Feature1(Appliance1.Feature1):
|
||||
...
|
||||
|
||||
class Appliance3b(Appliance2b):
|
||||
class Feature1(Appliance2b.Feature1):
|
||||
...
|
||||
|
||||
app1 = Appliance1()
|
||||
app2 = Appliance2b()
|
||||
app3 = Appliance3b()
|
||||
|
||||
self.assertEqual(app1.Feature1.VarStrInner,"testvalue FEATURE1")
|
||||
self.assertEqual(app2.Feature1.VarStrInner,"testvalue FEATURE1")
|
||||
self.assertEqual(app3.Feature1.VarStrInner,"testvalue FEATURE1")
|
||||
|
||||
class Appliance4(Appliance3b):
|
||||
class Feature1(Appliance3b.Feature1):
|
||||
VarStrInner = "testvalue FEATURE4"
|
||||
|
||||
self.assertEqual(app1.Feature1.VarStrInner,"testvalue FEATURE1")
|
||||
self.assertEqual(app2.Feature1.VarStrInner,"testvalue FEATURE1")
|
||||
self.assertEqual(app3.Feature1.VarStrInner,"testvalue FEATURE1")
|
||||
|
||||
app4 = Appliance4()
|
||||
|
||||
self.assertEqual(app1.Feature1.VarStrInner,"testvalue FEATURE1")
|
||||
self.assertEqual(app2.Feature1.VarStrInner,"testvalue FEATURE1")
|
||||
self.assertEqual(app3.Feature1.VarStrInner,"testvalue FEATURE1")
|
||||
self.assertEqual(app4.Feature1.VarStrInner,"testvalue FEATURE4")
|
||||
|
||||
def test_inheritance_chain(self):
|
||||
|
||||
# class can be created
|
||||
class Appliance1(dm.BaseAppliance):
|
||||
VarStr: str = "testvalue1"
|
||||
|
||||
class Appliance2(Appliance1):
|
||||
pass
|
||||
|
||||
class Appliance3(Appliance2):
|
||||
pass
|
||||
|
||||
app1 = Appliance1()
|
||||
app2 = Appliance2()
|
||||
app3 = Appliance3()
|
||||
|
||||
self.assertEqual(app1.VarStr,"testvalue1")
|
||||
self.assertEqual(app2.VarStr,"testvalue1")
|
||||
self.assertEqual(app3.VarStr,"testvalue1")
|
||||
|
||||
class Appliance4(Appliance3):
|
||||
VarStr = "testvalue moded"
|
||||
|
||||
|
||||
app4 = Appliance4()
|
||||
|
||||
self.assertEqual(app1.VarStr,"testvalue1")
|
||||
self.assertEqual(app2.VarStr,"testvalue1")
|
||||
self.assertEqual(app3.VarStr,"testvalue1")
|
||||
self.assertEqual(app4.VarStr,"testvalue moded")
|
||||
|
||||
app1b = Appliance1()
|
||||
app2b = Appliance2()
|
||||
app3b = Appliance3()
|
||||
self.assertEqual(app1b.VarStr,"testvalue1")
|
||||
self.assertEqual(app2b.VarStr,"testvalue1")
|
||||
self.assertEqual(app3b.VarStr,"testvalue1")
|
||||
|
||||
# ---------- main ----------
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user