Compare commits

...

4 Commits

Author SHA1 Message Date
cclecle
95b0c298ce update deps 2025-09-05 23:00:36 +02:00
cclecle
04a4cf7b36 add deps 2025-09-05 22:56:17 +02:00
cclecle
f42a839cff work 2025-09-05 22:53:47 +02:00
cclecle
7f3a4ef545 work 2025-09-03 23:15:04 +02:00
4 changed files with 915 additions and 41 deletions

View File

@@ -35,8 +35,8 @@ classifiers = [
dependencies = [
'importlib-metadata; python_version<"3.9"',
'packaging',
'pydantic',
'runtype'
'frozendict',
'typeguard'
]
dynamic = ["version"]

View File

@@ -12,6 +12,7 @@ Main module __init__ file.
from .__metadata__ import __version__, __Summuary__, __Name__
from .model import (
DABFieldInfo,
BaseAppliance,
BaseFeature,
DABModelException,
@@ -24,4 +25,6 @@ from .model import (
InvalidFieldValue,
InvalidFieldAnnotation,
IncompletelyAnnotatedField,
ImportForbidden,
FunctionForbidden,
)

View File

@@ -1,10 +1,10 @@
from typing import Optional, TypeVar, Generic, Union, get_origin, get_args, List, Dict, Any, Tuple, Set, Annotated, FrozenSet
from types import UnionType
from types import UnionType, FunctionType, SimpleNamespace
from frozendict import deepfreeze
from copy import deepcopy, copy
from pprint import pprint
from typeguard import check_type, TypeCheckError, CollectionCheckStrategy
import math
ALLOWED_ANNOTATIONS = {
"Union": Union,
@@ -55,6 +55,9 @@ class NewFieldForbidden(DABModelException): ...
class InvalidFieldAnnotation(DABModelException): ...
class InvalidInitializerType(DABModelException): ...
class NotAnnotatedField(InvalidFieldAnnotation): ...
@@ -67,6 +70,67 @@ class ReadOnlyFieldAnnotation(DABModelException): ...
class InvalidFieldValue(DABModelException): ...
class NonExistingField(DABModelException): ...
class ImportForbidden(DABModelException): ...
class FunctionForbidden(DABModelException): ...
ALLOWED_HELPERS_MATH = SimpleNamespace(
sqrt=math.sqrt,
floor=math.floor,
ceil=math.ceil,
trunc=math.trunc,
fabs=math.fabs,
copysign=math.copysign,
hypot=math.hypot,
exp=math.exp,
log=math.log,
log10=math.log10,
sin=math.sin,
cos=math.cos,
tan=math.tan,
atan2=math.atan2,
radians=math.radians,
degrees=math.degrees,
)
ALLOWED_HELPERS_DEFAULT = {
"math": ALLOWED_HELPERS_MATH,
"print": print,
# Numbers & reducers (pure, deterministic)
"abs": abs,
"round": round,
"min": min,
"max": max,
"sum": sum,
# Introspection-free basics
"len": len,
"sorted": sorted,
# Basic constructors (for copy-on-write patterns)
"tuple": tuple,
"list": list,
"dict": dict,
"set": set,
# Simple casts if they need to normalize types
"int": int,
"float": float,
"str": str,
"bool": bool,
"bytes": bytes,
"complex": complex,
# Easy iteration helpers (optional but handy)
"range": range,
}
def _blocked_import(*args, **kwargs):
raise ImportForbidden("imports disabled in __initializer")
def _resolve_annotation(ann):
if isinstance(ann, str):
# Safe eval against a **whitelist** only
@@ -88,36 +152,50 @@ def _peel_annotated(t: Any) -> Any:
return t
def __check_annotation_definition__(_type) -> bool:
def _check_annotation_definition(_type) -> bool:
_type = _peel_annotated(_type)
# handle Optional[] and Union[None,...]
if (get_origin(_type) is Union or get_origin(_type) is UnionType) and type(None) in get_args(_type):
return all([__check_annotation_definition__(_) for _ in get_args(_type) if _ is not type(None)])
return all([_check_annotation_definition(_) for _ in get_args(_type) if _ is not type(None)])
# handle other Union[...]
if get_origin(_type) is Union or get_origin(_type) is UnionType:
return all([__check_annotation_definition__(_) for _ in get_args(_type)])
return all([_check_annotation_definition(_) for _ in get_args(_type)])
# handle Dict[...]
if get_origin(_type) is dict:
inner = get_args(_type)
if len(inner) != 2:
raise IncompletelyAnnotatedField(f"Dict Annotation requires 2 inner definitions: {_type}")
return _peel_annotated(inner[0]) in ALLOWED_MODEL_FIELDS_TYPES and __check_annotation_definition__(inner[1])
return _peel_annotated(inner[0]) in ALLOWED_MODEL_FIELDS_TYPES and _check_annotation_definition(inner[1])
# handle Tuple[]
if get_origin(_type) in [tuple]:
inner_types = get_args(_type)
if len(inner_types) == 0:
raise IncompletelyAnnotatedField(f"Annotation requires inner definition: {_type}")
if len(inner_types) == 2 and inner_types[1] is Ellipsis:
return _check_annotation_definition(inner_types[0])
return all([_check_annotation_definition(_) for _ in inner_types])
# handle Set[],Tuple[],FrozenSet[],List[]
if get_origin(_type) in [set, frozenset, tuple, list]:
inner_types = get_args(_type)
if len(inner_types) == 0:
raise IncompletelyAnnotatedField(f"Annotation requires inner definition: {_type}")
return all([__check_annotation_definition__(_) for _ in inner_types])
return all([_check_annotation_definition(_) for _ in inner_types])
if _type in ALLOWED_MODEL_FIELDS_TYPES:
return True
return False
class Constraint: ...
class BaseConstraint(Generic[TV_ALLOWED_MODEL_FIELDS_TYPES]):
_bound_type: type
def __init__(self): ...
def check(self, value: TV_ALLOWED_MODEL_FIELDS_TYPES) -> bool: ...
def _deepfreeze(value):
@@ -132,38 +210,136 @@ def _deepfreeze(value):
return value
class DABFieldInfo:
def __init__(self, *, doc: str = "", constraints: list[BaseConstraint] = []):
self._doc: str = doc
self._constraints: list[BaseConstraint] = constraints
@property
def doc(self):
return self._doc
@property
def constraints(self):
return self._constraints
class DABField(Generic[TV_ALLOWED_MODEL_FIELDS_TYPES]):
def __init__(self, name: str, v: Optional[TV_ALLOWED_MODEL_FIELDS_TYPES], a: Any):
self._constraints: List[Constraint] = []
def __init__(self, name: str, v: Optional[TV_ALLOWED_MODEL_FIELDS_TYPES], a: Any, i: str):
self._name: str = name
self._source: Optional[type] = None
self._default_value: Optional[TV_ALLOWED_MODEL_FIELDS_TYPES] = v
self._value: Optional[TV_ALLOWED_MODEL_FIELDS_TYPES] = v
self._annotations: Any = a
self._documentation: str = ""
def add_documentation(self, d: str) -> None:
self._documentation = d
self._info: DABFieldInfo = i
self._constraints: List[BaseConstraint] = i.constraints
def add_source(self, s: type) -> None:
self._source = s
def add_constraint(self, c: Constraint) -> None:
@property
def doc(self):
return self._info.doc
def add_constraint(self, c: BaseConstraint) -> None:
self._constraints.append(c)
@property
def constraints(self) -> list[BaseConstraint]:
return self._info.constraints
@property
def default_value(self):
return _deepfreeze(self._default_value)
def update_value(self, v: Optional[TV_ALLOWED_MODEL_FIELDS_TYPES] = None) -> None:
self._value = v
def render(self) -> TV_ALLOWED_MODEL_FIELDS_TYPES:
@property
def value(self):
return _deepfreeze(self._value)
def render_default(self) -> TV_ALLOWED_MODEL_FIELDS_TYPES:
return _deepfreeze(self._default_value)
@property
def raw_value(self):
return self._value
def get_annotation(self) -> Any:
@property
def annotations(self) -> Any:
return self._annotations
class FrozenDABField(Generic[TV_ALLOWED_MODEL_FIELDS_TYPES]):
def __init__(self, inner_field: DABField):
self._inner_field = inner_field
@property
def doc(self):
return self._inner_field.doc
@property
def constraints(self):
return _deepfreeze(self._inner_field.constraints)
@property
def default_value(self):
return self._inner_field.default_value
@property
def value(self):
return self._inner_field.value
@property
def annotations(self) -> Any:
return _deepfreeze(self._inner_field.annotations)
class ModelSpecView:
__slots__ = ("_vals", "_types", "_touched", "_name", "_module")
def __init__(self, values: dict, types_map: dict[str, type], name, module):
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)
@property
def __name__(self) -> str:
return self._name
@property
def __module__(self) -> str:
return self._module
def __getattr__(self, name):
if name not in self._types:
raise AttributeError(f"Unknown field {name}")
return self._vals[name]
def __setattr__(self, name, value):
if name not in self._types:
raise NonExistingField(f"Cannot set unknown field {name}")
T = self._types[name]
try:
check_type(
value,
T,
collection_check_strategy=CollectionCheckStrategy.ALL_ITEMS,
)
except TypeCheckError as exp:
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:
return dict(self._vals)
def diff(self) -> dict:
return {k: self._vals[k] for k in self._touched}
class BaseMeta(type):
def __new__(mcls, name, bases, namespace):
# print("__NEW__ Defining:", name, "with keys:", list(namespace))
@@ -187,23 +363,30 @@ class BaseMeta(type):
# iterating new and modified fields
modified_field: Dict[str, Any] = {}
new_fields: Dict[str, DABField] = {}
initializer: Optional[type] = None
initializer_name: Optional[str] = None
for _fname, _fvalue in namespace.items():
if _fname.startswith("__"):
if _fname == f"_{name}__initializer" or (name.startswith("_") and _fname == "__initializer"):
if not isinstance(_fvalue, classmethod):
raise InvalidInitializerType()
initializer = _fvalue.__func__
if name.startswith("_"):
initializer_name = "__initializer"
else:
initializer_name = f"_{name}__initializer"
elif _fname.startswith("__"):
pass
elif _fname == "Constraints" and type(_fvalue) is type:
...
# print("FOUND Constraints")
else:
# print(f"Parsing Field: {_fname} / {_fvalue}")
# Modified fields
if len(bases) == 1 and _fname in namespace["__DABSchema__"].keys():
# print(f"Modified field: {_fname}")
if _fname in namespace["__annotations__"]:
if "__annotations__" in namespace and _fname in namespace["__annotations__"]:
raise ReadOnlyFieldAnnotation("annotations cannot be modified on derived classes")
try:
check_type(
_fvalue,
namespace["__DABSchema__"][_fname].get_annotation(),
namespace["__DABSchema__"][_fname].annotations,
collection_check_strategy=CollectionCheckStrategy.ALL_ITEMS,
)
except TypeCheckError as exp:
@@ -225,9 +408,22 @@ class BaseMeta(type):
if isinstance(namespace["__annotations__"][_fname], str):
namespace["__annotations__"][_fname] = _resolve_annotation(namespace["__annotations__"][_fname])
if not __check_annotation_definition__(namespace["__annotations__"][_fname]):
if not _check_annotation_definition(namespace["__annotations__"][_fname]):
raise InvalidFieldAnnotation(f"Field <{_fname}> has not an allowed or valid annotation.")
_finfo: Optional[DABFieldInfo] = DABFieldInfo()
origin = get_origin(namespace["__annotations__"][_fname])
tname = getattr(origin, "__name__", "") or getattr(origin, "__qualname__", "") or str(origin)
if "Annotated" in tname:
args = get_args(namespace["__annotations__"][_fname])
if args:
if len(args) > 2:
raise InvalidFieldAnnotation(f"Field <{_fname}> had invalid Annotated value.")
if len(args) == 2 and not isinstance(args[1], DABFieldInfo):
raise InvalidFieldAnnotation(f"Only DABFieldInfo object is allowed as Annotated data.")
_finfo = args[1]
# print(f"annotation is: {namespace['__annotations__'][_fname]}")
# check if value is valid
try:
@@ -238,14 +434,15 @@ class BaseMeta(type):
raise InvalidFieldValue(
f"Value of Field <{_fname}> is not of expected type {namespace['__annotations__'][_fname]}."
) from exp
new_fields[_fname] = DABField(_fname, _fvalue, namespace["__annotations__"][_fname])
# namespace[_fname].add_documentation()
new_fields[_fname] = DABField(_fname, _fvalue, namespace["__annotations__"][_fname], _finfo)
# removing modified fields from class (will add them back later)
for _fname in new_fields.keys():
del namespace[_fname]
for _fname in modified_field.keys():
del namespace[_fname]
if initializer is not None:
del namespace[initializer_name]
orig_setattr = namespace.get("__setattr__", object.__setattr__)
@@ -273,15 +470,44 @@ class BaseMeta(type):
_fvalue.add_source(cls)
cls.__DABSchema__[_fname] = _fvalue
if initializer is not None:
init_fieldvalues = dict()
init_fieldtypes = dict()
for _fname, _fvalue in cls.__DABSchema__.items():
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 initializer.__code__.co_freevars:
raise FunctionForbidden("__initializer must not use closures")
safe_initializer = FunctionType(
initializer.__code__,
safe_globals,
name=initializer.__name__,
argdefs=initializer.__defaults__,
closure=None,
)
safe_initializer(fakecls)
print(fakecls.diff())
for _fname, _fvalue in fakecls.export().items():
try:
check_type(_fvalue, cls.__DABSchema__[_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)
return cls
def __call__(cls, *args, **kw):
obj = super().__call__(*args, **kw)
for _fname in cls.__DABSchema__.keys():
setattr(obj, _fname, cls.__DABSchema__[_fname].render())
# obj.__DABSchema__ = deepfreeze(obj.__DABSchema__)
# setattr(obj, "__DABSchema__", deepfreeze(obj.__DABSchema__))
setattr(obj, _fname, cls.__DABSchema__[_fname].value)
inst_schema = dict()
for _fname, _fvalue in cls.__DABSchema__.items():
inst_schema[_fname] = FrozenDABField(_fvalue)
setattr(obj, "__DABSchema__", inst_schema)
return obj

View File

@@ -26,6 +26,10 @@ testdir_path = Path(__file__).parent.resolve()
chdir(testdir_path.parent.resolve())
def test_initializer_safe_testfc():
eval("print('hi')")
class TestConfigWithoutEnabledFlag(unittest.TestCase):
def setUp(self):
print("\n->", unittest.TestCase.id(self))
@@ -175,13 +179,6 @@ class TestConfigWithoutEnabledFlag(unittest.TestCase):
class _(dm.BaseAppliance):
_ = "default value"
def test_annotated(self):
"""Testing first appliance level, and Field types (annotated one)"""
# class can be created if annotation is a string
class Appliance1(dm.BaseAppliance):
StrVar: Annotated[str, "my string"] = "default value"
def test_optionnal(self):
"""Testing first appliance level, and Field types (Optionnal annotations)"""
@@ -492,6 +489,7 @@ class TestConfigWithoutEnabledFlag(unittest.TestCase):
testVar4: "Tuple[str,...]" = ("a", "c")
testVar5: tuple[str, ...] = ("a", "b")
testVar6: "tuple[str,...]" = ("a", "b")
testVar7: Tuple[int, str] = (1, "b")
# testVar7: Tuple[Union[int, str]] = (1, 2, 3, "one", "two", "three")
app1 = Appliance1()
@@ -502,7 +500,7 @@ class TestConfigWithoutEnabledFlag(unittest.TestCase):
self.immutable_vars__test_field(app1, "testVar4", ("a", "c"), ("h", "e"))
self.immutable_vars__test_field(app1, "testVar5", ("a", "b"), ("h", "c"))
self.immutable_vars__test_field(app1, "testVar6", ("a", "b"), ("h", "c"))
# self.immutable_vars__test_field(app1, "testVar7", (1, 2, 3, "one", "two", "three"), ("h", "c"))
self.immutable_vars__test_field(app1, "testVar7", (1, "b"), (7, "h"))
# must work
sorted(app1.testVar)
@@ -527,6 +525,653 @@ class TestConfigWithoutEnabledFlag(unittest.TestCase):
class _(dm.BaseAppliance):
_: "Tuple" = (1, 2)
with self.assertRaises(dm.InvalidFieldValue):
class _(dm.BaseAppliance):
_: "Tuple[int,...]" = (1, "a")
with self.assertRaises(dm.InvalidFieldValue):
class _(dm.BaseAppliance):
_: "tuple[int,...]" = (1, "a")
def check_immutable_fields_schema(
self, appliance: dm.BaseAppliance, field_name: str, expected_value: str, 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.assertEqual(appliance.__DABSchema__[field_name].default_value, expected_default_value)
self.assertIn("constraints", dir(appliance.__DABSchema__[field_name]))
self.assertEqual(appliance.__DABSchema__[field_name].constraints, ())
def test_immutable_fields_schema(self):
"""Testing first appliance level, and Field types (annotated)"""
# class can be created
class Appliance1(dm.BaseAppliance):
StrVar: "str" = "default value"
StrVar2: "str" = "default value2"
VarInt: "int" = 12
VarInt2: "int" = 21
VarFloat: "float" = 12.1
VarFloat2: "float" = 21.2
VarComplex: "complex" = complex(3, 5)
VarComplex2: "complex" = complex(8, 6)
VarBool: "bool" = True
VarBool2: "bool" = False
VarBytes: "bytes" = bytes.fromhex("2Ef0 F1f2 ")
VarBytes2: "bytes" = bytes.fromhex("2ff0 F7f2 ")
app1 = Appliance1()
self.assertIn("__DABSchema__", dir(app1))
self.assertIn("__DABSchema__", app1.__dict__)
self.check_immutable_fields_schema(app1, "StrVar", "default value", "default value", str)
self.check_immutable_fields_schema(app1, "StrVar2", "default value2", "default value2", str)
self.check_immutable_fields_schema(app1, "VarInt", 12, 12, int)
self.check_immutable_fields_schema(app1, "VarInt2", 21, 21, int)
self.check_immutable_fields_schema(app1, "VarFloat", 12.1, 12.1, float)
self.check_immutable_fields_schema(app1, "VarFloat2", 21.2, 21.2, float)
self.check_immutable_fields_schema(app1, "VarComplex", complex(3, 5), complex(3, 5), complex)
self.check_immutable_fields_schema(app1, "VarComplex2", complex(8, 6), complex(8, 6), complex)
self.check_immutable_fields_schema(app1, "VarBool", True, True, bool)
self.check_immutable_fields_schema(app1, "VarBool2", False, False, bool)
self.check_immutable_fields_schema(app1, "VarBytes", bytes.fromhex("2Ef0 F1f2 "), bytes.fromhex("2Ef0 F1f2 "), bytes)
self.check_immutable_fields_schema(app1, "VarBytes2", bytes.fromhex("2ff0 F7f2 "), bytes.fromhex("2ff0 F7f2 "), bytes)
def test_container_field_schema(self):
"""Testing first appliance level, and Field types (annotated)"""
# class can be created
class Appliance1(dm.BaseAppliance):
ListStr: list[str] = ["val1", "val2"]
ListStr2: "list[str]" = ["val3", "val4"]
Dict1: dict[int, float] = {1: 1.1, 4: 7.6, 91: 23.6}
Dict2: "dict[str, str]" = {"1": "1.1", "4": "7.6", "91": "23.6"}
Tuple1: "tuple[str,...]" = ("a", "c")
Tuple2: tuple[str, ...] = ("a", "b")
FrozenSet1: frozenset[int] = frozenset({1, 2})
FrozenSet2: "frozenset[int]" = frozenset({1, 2})
Set1: set[int] = set({1, 2})
Set2: "set[int]" = set({1, 2})
app1 = Appliance1()
self.assertIn("__DABSchema__", dir(app1))
self.assertIn("__DABSchema__", app1.__dict__)
self.check_immutable_fields_schema(app1, "ListStr", ("val1", "val2"), ("val1", "val2"), list[str])
self.check_immutable_fields_schema(app1, "ListStr2", ("val3", "val4"), ("val3", "val4"), list[str])
self.check_immutable_fields_schema(app1, "Dict1", {1: 1.1, 4: 7.6, 91: 23.6}, {1: 1.1, 4: 7.6, 91: 23.6}, dict[int, float])
self.check_immutable_fields_schema(
app1, "Dict2", {"1": "1.1", "4": "7.6", "91": "23.6"}, {"1": "1.1", "4": "7.6", "91": "23.6"}, dict[str, str]
)
self.check_immutable_fields_schema(app1, "Tuple1", ("a", "c"), ("a", "c"), tuple[str, ...])
self.check_immutable_fields_schema(app1, "Tuple2", ("a", "b"), ("a", "b"), tuple[str, ...])
self.check_immutable_fields_schema(app1, "FrozenSet1", frozenset({1, 2}), frozenset({1, 2}), frozenset[int])
self.check_immutable_fields_schema(app1, "FrozenSet2", frozenset({1, 2}), frozenset({1, 2}), frozenset[int])
self.check_immutable_fields_schema(app1, "Set1", frozenset({1, 2}), frozenset({1, 2}), set[int])
self.check_immutable_fields_schema(app1, "Set2", frozenset({1, 2}), frozenset({1, 2}), set[int])
# same test with Typing types (list -> List ...)
# class can be created
class Appliance1(dm.BaseAppliance):
ListStr: List[str] = ["val1", "val2"]
ListStr2: "List[str]" = ["val3", "val4"]
Dict1: Dict[int, float] = {1: 1.1, 4: 7.6, 91: 23.6}
Dict2: "Dict[str, str]" = {"1": "1.1", "4": "7.6", "91": "23.6"}
Tuple1: "Tuple[str,...]" = ("a", "c")
Tuple2: Tuple[str, ...] = ("a", "b")
FrozenSet1: FrozenSet[int] = frozenset({1, 2})
FrozenSet2: "FrozenSet[int]" = frozenset({1, 2})
Set1: Set[int] = set({1, 2})
Set2: "Set[int]" = set({1, 2})
app1 = Appliance1()
self.assertIn("__DABSchema__", dir(app1))
self.assertIn("__DABSchema__", app1.__dict__)
self.check_immutable_fields_schema(app1, "ListStr", ("val1", "val2"), ("val1", "val2"), List[str])
self.check_immutable_fields_schema(app1, "ListStr2", ("val3", "val4"), ("val3", "val4"), List[str])
self.check_immutable_fields_schema(app1, "Dict1", {1: 1.1, 4: 7.6, 91: 23.6}, {1: 1.1, 4: 7.6, 91: 23.6}, Dict[int, float])
self.check_immutable_fields_schema(
app1, "Dict2", {"1": "1.1", "4": "7.6", "91": "23.6"}, {"1": "1.1", "4": "7.6", "91": "23.6"}, Dict[str, str]
)
self.check_immutable_fields_schema(app1, "Tuple1", ("a", "c"), ("a", "c"), Tuple[str, ...])
self.check_immutable_fields_schema(app1, "Tuple2", ("a", "b"), ("a", "b"), Tuple[str, ...])
self.check_immutable_fields_schema(app1, "FrozenSet1", frozenset({1, 2}), frozenset({1, 2}), FrozenSet[int])
self.check_immutable_fields_schema(app1, "FrozenSet2", frozenset({1, 2}), frozenset({1, 2}), FrozenSet[int])
self.check_immutable_fields_schema(app1, "Set1", frozenset({1, 2}), frozenset({1, 2}), Set[int])
self.check_immutable_fields_schema(app1, "Set2", frozenset({1, 2}), frozenset({1, 2}), Set[int])
def test_immutable_fields_annotated(self):
"""Testing first appliance level, and Field types (annotated)"""
# class can be created
class Appliance1(dm.BaseAppliance):
StrVar: Annotated[str, dm.DABFieldInfo(doc="foo1")] = "default value"
StrVar2: Annotated[str, dm.DABFieldInfo(doc="foo2")] = "default value2"
VarInt: Annotated[int, dm.DABFieldInfo(doc="foo3")] = 12
VarInt2: Annotated[int, dm.DABFieldInfo(doc="foo4")] = 21
VarFloat: Annotated[float, dm.DABFieldInfo(doc="foo5")] = 12.1
VarFloat2: Annotated[float, dm.DABFieldInfo(doc="foo6")] = 21.2
VarComplex: Annotated[complex, dm.DABFieldInfo(doc="foo7")] = complex(3, 5)
VarComplex2: Annotated[complex, dm.DABFieldInfo(doc="foo8")] = complex(8, 6)
VarBool: Annotated[bool, dm.DABFieldInfo(doc="foo9")] = True
VarBool2: Annotated[bool, dm.DABFieldInfo(doc="foo10")] = False
VarBytes: Annotated[bytes, dm.DABFieldInfo(doc="foo11")] = bytes.fromhex("2Ef0 F1f2 ")
VarBytes2: Annotated[bytes, dm.DABFieldInfo(doc="foo12")] = bytes.fromhex("2ff0 F7f2 ")
app1 = Appliance1()
self.immutable_vars__test_field(app1, "StrVar", "default value", "test")
self.assertEqual(app1.__DABSchema__["StrVar"].doc, "foo1")
self.immutable_vars__test_field(app1, "StrVar2", "default value2", "test2")
self.assertEqual(app1.__DABSchema__["StrVar2"].doc, "foo2")
self.immutable_vars__test_field(app1, "VarInt", 12, 13)
self.assertEqual(app1.__DABSchema__["VarInt"].doc, "foo3")
self.immutable_vars__test_field(app1, "VarInt2", 21, 22)
self.assertEqual(app1.__DABSchema__["VarInt2"].doc, "foo4")
self.immutable_vars__test_field(app1, "VarFloat", 12.1, 32)
self.assertEqual(app1.__DABSchema__["VarFloat"].doc, "foo5")
self.immutable_vars__test_field(app1, "VarFloat2", 21.2, 42)
self.assertEqual(app1.__DABSchema__["VarFloat2"].doc, "foo6")
self.immutable_vars__test_field(app1, "VarComplex", complex(3, 5), complex(1, 2))
self.assertEqual(app1.__DABSchema__["VarComplex"].doc, "foo7")
self.immutable_vars__test_field(app1, "VarComplex2", complex(8, 6), complex(3, 2))
self.assertEqual(app1.__DABSchema__["VarComplex2"].doc, "foo8")
self.immutable_vars__test_field(app1, "VarBool", True, False)
self.assertEqual(app1.__DABSchema__["VarBool"].doc, "foo9")
self.immutable_vars__test_field(app1, "VarBool2", False, True)
self.assertEqual(app1.__DABSchema__["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.immutable_vars__test_field(app1, "VarBytes2", bytes.fromhex("2ff0 F7f2 "), bytes.fromhex("11f0 F1e2 "))
self.assertEqual(app1.__DABSchema__["VarBytes2"].doc, "foo12")
with self.assertRaises(dm.InvalidFieldAnnotation):
class _(dm.BaseAppliance):
_: Annotated[str, "foo2"] = "default value2"
# annotation is parsed before the library can do anything, so the exception can only be TypeError
with self.assertRaises(TypeError):
class _(dm.BaseAppliance):
_: Annotated[str] = "default value2"
def test_immutable_fields_inheritance(self):
"""Testing first appliance level, and Field types (simple)"""
# class can be created
class Appliance1(dm.BaseAppliance):
StrVar: str = "default value"
StrVar2: str = "default value2"
VarInt: int = 12
VarInt2: int = 21
VarFloat: float = 12.1
VarFloat2: float = 21.2
VarComplex: complex = complex(3, 5)
VarComplex2: complex = complex(8, 6)
VarBool: bool = True
VarBool2: bool = False
VarBytes: bytes = bytes.fromhex("2Ef0 F1f2 ")
VarBytes2: bytes = bytes.fromhex("2ff0 F7f2 ")
class Appliance2(Appliance1):
StrVar = "moded value"
StrVar2 = "moded value2"
VarInt = 54
VarInt2 = 23
VarFloat = 2.6
VarFloat2 = 1.5
VarComplex = complex(7, 1)
VarComplex2 = complex(3, 0)
VarBool = False
VarBool2 = True
VarBytes = bytes.fromhex("21f0 e1f2 ")
VarBytes2 = bytes.fromhex("2df0 F672 ")
app1 = Appliance1()
self.immutable_vars__test_field(app1, "StrVar", "default value", "test")
self.immutable_vars__test_field(app1, "StrVar2", "default value2", "test2")
self.immutable_vars__test_field(app1, "VarInt", 12, 13)
self.immutable_vars__test_field(app1, "VarInt2", 21, 22)
self.immutable_vars__test_field(app1, "VarFloat", 12.1, 32)
self.immutable_vars__test_field(app1, "VarFloat2", 21.2, 42)
self.immutable_vars__test_field(app1, "VarComplex", complex(3, 5), complex(1, 2))
self.immutable_vars__test_field(app1, "VarComplex2", complex(8, 6), complex(3, 2))
self.immutable_vars__test_field(app1, "VarBool", True, False)
self.immutable_vars__test_field(app1, "VarBool2", False, True)
self.immutable_vars__test_field(app1, "VarBytes", bytes.fromhex("2Ef0 F1f2 "), bytes.fromhex("11f0 F1f2 "))
self.immutable_vars__test_field(app1, "VarBytes2", bytes.fromhex("2ff0 F7f2 "), bytes.fromhex("11f0 F1e2 "))
self.check_immutable_fields_schema(app1, "StrVar", "default value", "default value", str)
self.check_immutable_fields_schema(app1, "StrVar2", "default value2", "default value2", str)
self.check_immutable_fields_schema(app1, "VarInt", 12, 12, int)
self.check_immutable_fields_schema(app1, "VarInt2", 21, 21, int)
self.check_immutable_fields_schema(app1, "VarFloat", 12.1, 12.1, float)
self.check_immutable_fields_schema(app1, "VarFloat2", 21.2, 21.2, float)
self.check_immutable_fields_schema(app1, "VarComplex", complex(3, 5), complex(3, 5), complex)
self.check_immutable_fields_schema(app1, "VarComplex2", complex(8, 6), complex(8, 6), complex)
self.check_immutable_fields_schema(app1, "VarBool", True, True, bool)
self.check_immutable_fields_schema(app1, "VarBool2", False, False, bool)
self.check_immutable_fields_schema(app1, "VarBytes", bytes.fromhex("2Ef0 F1f2 "), bytes.fromhex("2Ef0 F1f2 "), bytes)
self.check_immutable_fields_schema(app1, "VarBytes2", bytes.fromhex("2ff0 F7f2 "), bytes.fromhex("2ff0 F7f2 "), bytes)
app2 = Appliance2()
self.immutable_vars__test_field(app2, "StrVar", "moded value", "test")
self.immutable_vars__test_field(app2, "StrVar2", "moded value2", "test2")
self.immutable_vars__test_field(app2, "VarInt", 54, 13)
self.immutable_vars__test_field(app2, "VarInt2", 23, 22)
self.immutable_vars__test_field(app2, "VarFloat", 2.6, 32)
self.immutable_vars__test_field(app2, "VarFloat2", 1.5, 42)
self.immutable_vars__test_field(app2, "VarComplex", complex(7, 1), complex(1, 2))
self.immutable_vars__test_field(app2, "VarComplex2", complex(3, 0), complex(3, 2))
self.immutable_vars__test_field(app2, "VarBool", False, False)
self.immutable_vars__test_field(app2, "VarBool2", True, True)
self.immutable_vars__test_field(app2, "VarBytes", bytes.fromhex("21f0 e1f2 "), bytes.fromhex("11f0 F1f2 "))
self.immutable_vars__test_field(app2, "VarBytes2", bytes.fromhex("2df0 F672 "), bytes.fromhex("11f0 F1e2 "))
self.check_immutable_fields_schema(app2, "StrVar", "moded value", "default value", str)
self.check_immutable_fields_schema(app2, "StrVar2", "moded value2", "default value2", str)
self.check_immutable_fields_schema(app2, "VarInt", 54, 12, int)
self.check_immutable_fields_schema(app2, "VarInt2", 23, 21, int)
self.check_immutable_fields_schema(app2, "VarFloat", 2.6, 12.1, float)
self.check_immutable_fields_schema(app2, "VarFloat2", 1.5, 21.2, float)
self.check_immutable_fields_schema(app2, "VarComplex", complex(7, 1), complex(3, 5), complex)
self.check_immutable_fields_schema(app2, "VarComplex2", complex(3, 0), complex(8, 6), complex)
self.check_immutable_fields_schema(app2, "VarBool", False, True, bool)
self.check_immutable_fields_schema(app2, "VarBool2", True, False, bool)
self.check_immutable_fields_schema(app2, "VarBytes", bytes.fromhex("21f0 e1f2 "), bytes.fromhex("2Ef0 F1f2 "), bytes)
self.check_immutable_fields_schema(app2, "VarBytes2", bytes.fromhex("2df0 F672 "), bytes.fromhex("2ff0 F7f2 "), bytes)
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__)
app3 = Appliance3()
self.immutable_vars__test_field(app3, "StrVar", "moded value", "test")
self.immutable_vars__test_field(app3, "StrVar2", "moded value2", "test2")
self.immutable_vars__test_field(app3, "VarInt", 54, 13)
self.immutable_vars__test_field(app3, "VarInt2", 23, 22)
self.immutable_vars__test_field(app3, "VarFloat", 2.6, 32)
self.immutable_vars__test_field(app3, "VarFloat2", 1.5, 42)
self.immutable_vars__test_field(app3, "VarComplex", complex(7, 1), complex(1, 2))
self.immutable_vars__test_field(app3, "VarComplex2", complex(3, 0), complex(3, 2))
self.immutable_vars__test_field(app3, "VarBool", False, False)
self.immutable_vars__test_field(app3, "VarBool2", True, True)
self.immutable_vars__test_field(app3, "VarBytes", bytes.fromhex("21f0 e1f2 "), bytes.fromhex("11f0 F1f2 "))
self.immutable_vars__test_field(app3, "VarBytes2", bytes.fromhex("2df0 F672 "), bytes.fromhex("11f0 F1e2 "))
self.immutable_vars__test_field(app3, "NewValue", "newval", "test")
self.check_immutable_fields_schema(app3, "StrVar", "moded value", "default value", str)
self.check_immutable_fields_schema(app3, "StrVar2", "moded value2", "default value2", str)
self.check_immutable_fields_schema(app3, "VarInt", 54, 12, int)
self.check_immutable_fields_schema(app3, "VarInt2", 23, 21, int)
self.check_immutable_fields_schema(app3, "VarFloat", 2.6, 12.1, float)
self.check_immutable_fields_schema(app3, "VarFloat2", 1.5, 21.2, float)
self.check_immutable_fields_schema(app3, "VarComplex", complex(7, 1), complex(3, 5), complex)
self.check_immutable_fields_schema(app3, "VarComplex2", complex(3, 0), complex(8, 6), complex)
self.check_immutable_fields_schema(app3, "VarBool", False, True, bool)
self.check_immutable_fields_schema(app3, "VarBool2", True, False, bool)
self.check_immutable_fields_schema(app3, "VarBytes", bytes.fromhex("21f0 e1f2 "), bytes.fromhex("2Ef0 F1f2 "), bytes)
self.check_immutable_fields_schema(app3, "VarBytes2", bytes.fromhex("2df0 F672 "), bytes.fromhex("2ff0 F7f2 "), bytes)
self.check_immutable_fields_schema(app3, "NewValue", "newval", "newval", str)
self.immutable_vars__test_field(app1, "StrVar", "default value", "test")
self.immutable_vars__test_field(app1, "StrVar2", "default value2", "test2")
self.immutable_vars__test_field(app1, "VarInt", 12, 13)
self.immutable_vars__test_field(app1, "VarInt2", 21, 22)
self.immutable_vars__test_field(app1, "VarFloat", 12.1, 32)
self.immutable_vars__test_field(app1, "VarFloat2", 21.2, 42)
self.immutable_vars__test_field(app1, "VarComplex", complex(3, 5), complex(1, 2))
self.immutable_vars__test_field(app1, "VarComplex2", complex(8, 6), complex(3, 2))
self.immutable_vars__test_field(app1, "VarBool", True, False)
self.immutable_vars__test_field(app1, "VarBool2", False, True)
self.immutable_vars__test_field(app1, "VarBytes", bytes.fromhex("2Ef0 F1f2 "), bytes.fromhex("11f0 F1f2 "))
self.immutable_vars__test_field(app1, "VarBytes2", bytes.fromhex("2ff0 F7f2 "), bytes.fromhex("11f0 F1e2 "))
self.check_immutable_fields_schema(app1, "StrVar", "default value", "default value", str)
self.check_immutable_fields_schema(app1, "StrVar2", "default value2", "default value2", str)
self.check_immutable_fields_schema(app1, "VarInt", 12, 12, int)
self.check_immutable_fields_schema(app1, "VarInt2", 21, 21, int)
self.check_immutable_fields_schema(app1, "VarFloat", 12.1, 12.1, float)
self.check_immutable_fields_schema(app1, "VarFloat2", 21.2, 21.2, float)
self.check_immutable_fields_schema(app1, "VarComplex", complex(3, 5), complex(3, 5), complex)
self.check_immutable_fields_schema(app1, "VarComplex2", complex(8, 6), complex(8, 6), complex)
self.check_immutable_fields_schema(app1, "VarBool", True, True, bool)
self.check_immutable_fields_schema(app1, "VarBool2", False, False, bool)
self.check_immutable_fields_schema(app1, "VarBytes", bytes.fromhex("2Ef0 F1f2 "), bytes.fromhex("2Ef0 F1f2 "), bytes)
self.check_immutable_fields_schema(app1, "VarBytes2", bytes.fromhex("2ff0 F7f2 "), bytes.fromhex("2ff0 F7f2 "), bytes)
self.immutable_vars__test_field(app2, "StrVar", "moded value", "test")
self.immutable_vars__test_field(app2, "StrVar2", "moded value2", "test2")
self.immutable_vars__test_field(app2, "VarInt", 54, 13)
self.immutable_vars__test_field(app2, "VarInt2", 23, 22)
self.immutable_vars__test_field(app2, "VarFloat", 2.6, 32)
self.immutable_vars__test_field(app2, "VarFloat2", 1.5, 42)
self.immutable_vars__test_field(app2, "VarComplex", complex(7, 1), complex(1, 2))
self.immutable_vars__test_field(app2, "VarComplex2", complex(3, 0), complex(3, 2))
self.immutable_vars__test_field(app2, "VarBool", False, False)
self.immutable_vars__test_field(app2, "VarBool2", True, True)
self.immutable_vars__test_field(app2, "VarBytes", bytes.fromhex("21f0 e1f2 "), bytes.fromhex("11f0 F1f2 "))
self.immutable_vars__test_field(app2, "VarBytes2", bytes.fromhex("2df0 F672 "), bytes.fromhex("11f0 F1e2 "))
self.check_immutable_fields_schema(app2, "StrVar", "moded value", "default value", str)
self.check_immutable_fields_schema(app2, "StrVar2", "moded value2", "default value2", str)
self.check_immutable_fields_schema(app2, "VarInt", 54, 12, int)
self.check_immutable_fields_schema(app2, "VarInt2", 23, 21, int)
self.check_immutable_fields_schema(app2, "VarFloat", 2.6, 12.1, float)
self.check_immutable_fields_schema(app2, "VarFloat2", 1.5, 21.2, float)
self.check_immutable_fields_schema(app2, "VarComplex", complex(7, 1), complex(3, 5), complex)
self.check_immutable_fields_schema(app2, "VarComplex2", complex(3, 0), complex(8, 6), complex)
self.check_immutable_fields_schema(app2, "VarBool", False, True, bool)
self.check_immutable_fields_schema(app2, "VarBool2", True, False, bool)
self.check_immutable_fields_schema(app2, "VarBytes", bytes.fromhex("21f0 e1f2 "), bytes.fromhex("2Ef0 F1f2 "), bytes)
self.check_immutable_fields_schema(app2, "VarBytes2", bytes.fromhex("2df0 F672 "), bytes.fromhex("2ff0 F7f2 "), bytes)
with self.assertRaises(dm.ReadOnlyFieldAnnotation):
class _(Appliance1):
StrVar: int = 12
with self.assertRaises(dm.ReadOnlyFieldAnnotation):
class _(Appliance3):
NewValue: int = 12
with self.assertRaises(dm.ReadOnlyFieldAnnotation):
class _(Appliance3):
StrVar: int = 12
def test_containers_field_inheritance(self):
"""Testing first appliance level, and Field types (annotated)"""
# class can be created
class Appliance1(dm.BaseAppliance):
ListStr: list[str] = ["val1", "val2"]
Dict1: dict[int, float] = {1: 1.1, 4: 7.6, 91: 23.6}
Tuple1: "tuple[str,...]" = ("a", "c")
FrozenSet1: frozenset[int] = frozenset({1, 2})
Set1: set[int] = set({1, 2})
# class can be created
class Appliance2(Appliance1):
ListStr = ["mod val1", "mod val2"]
Dict1 = {4: 1.1, 9: 7.6, 51: 23.6}
Tuple1 = ("aa", "cc")
FrozenSet1 = frozenset({14, 27})
Set1 = set({1, 20})
app1 = Appliance1()
self.immutable_vars__test_field(app1, "ListStr", ("val1", "val2"), ["val2", "val3"])
self.immutable_vars__test_field(app1, "Dict1", {1: 1.1, 4: 7.6, 91: 23.6}, {1: 1.1, 4: 7.6, 91: 23.6})
self.immutable_vars__test_field(app1, "Tuple1", ("a", "c"), ("h", "r"))
self.immutable_vars__test_field(app1, "FrozenSet1", frozenset({1, 2}), frozenset({4, 0}))
self.immutable_vars__test_field(app1, "Set1", frozenset({1, 2}), set({4, 0}))
self.check_immutable_fields_schema(app1, "ListStr", ("val1", "val2"), ("val1", "val2"), list[str])
self.check_immutable_fields_schema(app1, "Dict1", {1: 1.1, 4: 7.6, 91: 23.6}, {1: 1.1, 4: 7.6, 91: 23.6}, dict[int, float])
self.check_immutable_fields_schema(app1, "Tuple1", ("a", "c"), ("a", "c"), tuple[str, ...])
self.check_immutable_fields_schema(app1, "FrozenSet1", frozenset({1, 2}), frozenset({1, 2}), frozenset[int])
self.check_immutable_fields_schema(app1, "Set1", frozenset({1, 2}), frozenset({1, 2}), set[int])
app2 = Appliance2()
self.immutable_vars__test_field(app2, "ListStr", ("mod val1", "mod val2"), ["val2", "val3"])
self.immutable_vars__test_field(app2, "Dict1", {4: 1.1, 9: 7.6, 51: 23.6}, {1: 1.1, 4: 7.6, 91: 23.6})
self.immutable_vars__test_field(app2, "Tuple1", ("aa", "cc"), ("h", "r"))
self.immutable_vars__test_field(app2, "FrozenSet1", frozenset({14, 27}), frozenset({4, 0}))
self.immutable_vars__test_field(app2, "Set1", frozenset({1, 20}), set({4, 0}))
self.check_immutable_fields_schema(app2, "ListStr", ("mod val1", "mod val2"), ("val1", "val2"), list[str])
self.check_immutable_fields_schema(app2, "Dict1", {4: 1.1, 9: 7.6, 51: 23.6}, {1: 1.1, 4: 7.6, 91: 23.6}, dict[int, float])
self.check_immutable_fields_schema(app2, "Tuple1", ("aa", "cc"), ("a", "c"), tuple[str, ...])
self.check_immutable_fields_schema(app2, "FrozenSet1", frozenset({14, 27}), frozenset({1, 2}), frozenset[int])
self.check_immutable_fields_schema(app2, "Set1", frozenset({1, 20}), frozenset({1, 2}), set[int])
self.immutable_vars__test_field(app1, "ListStr", ("val1", "val2"), ["val2", "val3"])
self.immutable_vars__test_field(app1, "Dict1", {1: 1.1, 4: 7.6, 91: 23.6}, {1: 1.1, 4: 7.6, 91: 23.6})
self.immutable_vars__test_field(app1, "Tuple1", ("a", "c"), ("h", "r"))
self.immutable_vars__test_field(app1, "FrozenSet1", frozenset({1, 2}), frozenset({4, 0}))
self.immutable_vars__test_field(app1, "Set1", frozenset({1, 2}), set({4, 0}))
self.check_immutable_fields_schema(app1, "ListStr", ("val1", "val2"), ("val1", "val2"), list[str])
self.check_immutable_fields_schema(app1, "Dict1", {1: 1.1, 4: 7.6, 91: 23.6}, {1: 1.1, 4: 7.6, 91: 23.6}, dict[int, float])
self.check_immutable_fields_schema(app1, "Tuple1", ("a", "c"), ("a", "c"), tuple[str, ...])
self.check_immutable_fields_schema(app1, "FrozenSet1", frozenset({1, 2}), frozenset({1, 2}), frozenset[int])
self.check_immutable_fields_schema(app1, "Set1", frozenset({1, 2}), frozenset({1, 2}), set[int])
# class can be created
class Appliance3(Appliance2):
ListStr2: list[str] = ["mod val3", "mod val3"]
Dict2: dict[int, float] = {9: 8.1, 5: 98.6, 551: 3.6}
Tuple2: "tuple[str,...]" = ("aaa", "ccc")
FrozenSet2: frozenset[int] = frozenset({114, 127})
Set2: set[int] = set({10, 250})
app3 = Appliance3()
self.immutable_vars__test_field(app3, "ListStr", ("mod val1", "mod val2"), ["val2", "val3"])
self.immutable_vars__test_field(app3, "Dict1", {4: 1.1, 9: 7.6, 51: 23.6}, {1: 1.1, 4: 7.6, 91: 23.6})
self.immutable_vars__test_field(app3, "Tuple1", ("aa", "cc"), ("h", "r"))
self.immutable_vars__test_field(app3, "FrozenSet1", frozenset({14, 27}), frozenset({4, 0}))
self.immutable_vars__test_field(app3, "Set1", frozenset({1, 20}), set({4, 0}))
self.immutable_vars__test_field(app3, "ListStr2", ("mod val3", "mod val3"), ["mod val3", "mod val3"])
self.immutable_vars__test_field(app3, "Dict2", {9: 8.1, 5: 98.6, 551: 3.6}, {9: 8.1, 5: 98.6, 551: 3.6})
self.immutable_vars__test_field(app3, "Tuple2", ("aaa", "ccc"), ("aaa", "ccc"))
self.immutable_vars__test_field(app3, "FrozenSet2", frozenset({114, 127}), frozenset({114, 127}))
self.immutable_vars__test_field(app3, "Set2", frozenset({10, 250}), set({10, 250}))
self.check_immutable_fields_schema(app3, "ListStr", ("mod val1", "mod val2"), ("val1", "val2"), list[str])
self.check_immutable_fields_schema(app3, "Dict1", {4: 1.1, 9: 7.6, 51: 23.6}, {1: 1.1, 4: 7.6, 91: 23.6}, dict[int, float])
self.check_immutable_fields_schema(app3, "Tuple1", ("aa", "cc"), ("a", "c"), tuple[str, ...])
self.check_immutable_fields_schema(app3, "FrozenSet1", frozenset({14, 27}), frozenset({1, 2}), frozenset[int])
self.check_immutable_fields_schema(app3, "Set1", frozenset({1, 20}), frozenset({1, 2}), set[int])
self.check_immutable_fields_schema(app3, "ListStr2", ("mod val3", "mod val3"), ("mod val3", "mod val3"), list[str])
self.check_immutable_fields_schema(app3, "Dict2", {9: 8.1, 5: 98.6, 551: 3.6}, {9: 8.1, 5: 98.6, 551: 3.6}, dict[int, float])
self.check_immutable_fields_schema(app3, "Tuple2", ("aaa", "ccc"), ("aaa", "ccc"), tuple[str, ...])
self.check_immutable_fields_schema(app3, "FrozenSet2", frozenset({114, 127}), frozenset({114, 127}), frozenset[int])
self.check_immutable_fields_schema(app3, "Set2", frozenset({10, 250}), frozenset({10, 250}), set[int])
self.immutable_vars__test_field(app2, "ListStr", ("mod val1", "mod val2"), ["val2", "val3"])
self.immutable_vars__test_field(app2, "Dict1", {4: 1.1, 9: 7.6, 51: 23.6}, {1: 1.1, 4: 7.6, 91: 23.6})
self.immutable_vars__test_field(app2, "Tuple1", ("aa", "cc"), ("h", "r"))
self.immutable_vars__test_field(app2, "FrozenSet1", frozenset({14, 27}), frozenset({4, 0}))
self.immutable_vars__test_field(app2, "Set1", frozenset({1, 20}), set({4, 0}))
self.check_immutable_fields_schema(app2, "ListStr", ("mod val1", "mod val2"), ("val1", "val2"), list[str])
self.check_immutable_fields_schema(app2, "Dict1", {4: 1.1, 9: 7.6, 51: 23.6}, {1: 1.1, 4: 7.6, 91: 23.6}, dict[int, float])
self.check_immutable_fields_schema(app2, "Tuple1", ("aa", "cc"), ("a", "c"), tuple[str, ...])
self.check_immutable_fields_schema(app2, "FrozenSet1", frozenset({14, 27}), frozenset({1, 2}), frozenset[int])
self.check_immutable_fields_schema(app2, "Set1", frozenset({1, 20}), frozenset({1, 2}), set[int])
self.immutable_vars__test_field(app1, "ListStr", ("val1", "val2"), ["val2", "val3"])
self.immutable_vars__test_field(app1, "Dict1", {1: 1.1, 4: 7.6, 91: 23.6}, {1: 1.1, 4: 7.6, 91: 23.6})
self.immutable_vars__test_field(app1, "Tuple1", ("a", "c"), ("h", "r"))
self.immutable_vars__test_field(app1, "FrozenSet1", frozenset({1, 2}), frozenset({4, 0}))
self.immutable_vars__test_field(app1, "Set1", frozenset({1, 2}), set({4, 0}))
self.check_immutable_fields_schema(app1, "ListStr", ("val1", "val2"), ("val1", "val2"), list[str])
self.check_immutable_fields_schema(app1, "Dict1", {1: 1.1, 4: 7.6, 91: 23.6}, {1: 1.1, 4: 7.6, 91: 23.6}, dict[int, float])
self.check_immutable_fields_schema(app1, "Tuple1", ("a", "c"), ("a", "c"), tuple[str, ...])
self.check_immutable_fields_schema(app1, "FrozenSet1", frozenset({1, 2}), frozenset({1, 2}), frozenset[int])
self.check_immutable_fields_schema(app1, "Set1", frozenset({1, 2}), frozenset({1, 2}), set[int])
def test_initializer(self):
"""Testing first appliance level, and Field types (simple)"""
# class can be created
class Appliance1(dm.BaseAppliance):
VarInt: int = 12
VarInt2: int = 21
list1: list[int] = [1]
set1: set[float] = {1.43}
set2: set[float] = {5.43}
VarStr1: str = "abcd"
@classmethod
def __initializer(cls):
cls.VarInt2 = cls.VarInt + 1
cls.list1.append(2)
cls.set2 = cls.set1 | {1234}
cls.set1 = {0}
cls.VarStr1 = cls.VarStr1.upper()
app1 = Appliance1()
self.assertEquals(app1.VarInt, 12)
self.assertEquals(app1.VarInt2, 13)
self.assertEquals(app1.set1, frozenset({0}))
self.assertEquals(app1.set2, frozenset((1.43, 1234)))
self.assertEquals(app1.VarStr1, "ABCD")
# class can be created
class _(dm.BaseAppliance):
VarInt: int = 41
@classmethod
def __initializer(cls):
cls.VarInt = cls.VarInt + 1
app2 = _()
self.assertEquals(app2.VarInt, 42)
def test_initializer_safe(self):
with self.assertRaises(dm.ImportForbidden):
class test(dm.BaseAppliance):
_: int = 0
@classmethod
def __initializer(cls):
import pprint
with self.assertRaises(NameError):
class _(dm.BaseAppliance):
_: int = 0
@classmethod
def __initializer(cls):
eval("2 ** 8")
with self.assertRaises(NameError):
class _(dm.BaseAppliance):
_: int = 0
@classmethod
def __initializer(cls):
open("foo")
with self.assertRaises(NameError):
class _(dm.BaseAppliance):
_: int = 0
@classmethod
def __initializer(cls):
exec("exit()")
with self.assertRaises(NameError):
class _(dm.BaseAppliance):
_: int = 0
@classmethod
def __initializer(cls):
compile("print(55)", "test", "eval")
with self.assertRaises(NameError):
class _(dm.BaseAppliance):
_: int = 0
@classmethod
def __initializer(cls):
compile("print(55)", "test", "eval")
with self.assertRaises(dm.FunctionForbidden):
def testfc():
eval("print('test')")
class _(dm.BaseAppliance):
_: int = 0
@classmethod
def __initializer(cls):
testfc()
# class can be created
class Appliance2(dm.BaseAppliance):
VarInt: int = -56
VarInt2: int = 0
VarInt3: int = 0
VarInt4: int = 0
VarInt5: int = 0
VarFloat: float = 1.23
list1: list[int] = [1, 2, 0, 4]
list2: Optional[list[int]] = None
list3: Optional[list[int]] = None
set1: set[float] = {1.43}
set2: set[float] = {5.43}
@classmethod
def __initializer(cls):
cls.VarInt2 = abs(cls.VarInt)
cls.VarFloat = round(cls.VarFloat)
cls.VarInt = min(cls.list1)
cls.VarInt3 = max(cls.list1)
cls.VarInt4 = sum(cls.list1)
cls.VarInt5 = len(cls.list1)
cls.list2 = sorted(cls.list1)
cls.list3 = []
for i in range(5):
cls.list3.append(i)
cls.set2 = {math.ceil(list(cls.set1)[0])}
app2 = Appliance2()
self.assertEquals(app2.VarInt2, 56)
self.assertEquals(app2.VarFloat, 1)
self.assertEquals(app2.VarInt, 0)
self.assertEquals(app2.VarInt3, 4)
self.assertEquals(app2.VarInt4, 7)
self.assertEquals(app2.VarInt5, 4)
self.assertEquals(app2.list2, (0, 1, 2, 4))
self.assertEquals(app2.list3, (0, 1, 2, 3, 4))
self.assertEquals(app2.set2, {2})
class _(dm.BaseAppliance):
_: int = 0
@classmethod
def __initializer(cls):
test_initializer_safe_testfc()
# ---------- main ----------