Compare commits
4 Commits
0.0.1.post
...
0.0.1.post
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f45c9cc8f3 | ||
|
|
95b0c298ce | ||
|
|
04a4cf7b36 | ||
|
|
f42a839cff |
@@ -35,8 +35,8 @@ classifiers = [
|
||||
dependencies = [
|
||||
'importlib-metadata; python_version<"3.9"',
|
||||
'packaging',
|
||||
'pydantic',
|
||||
'runtype'
|
||||
'frozendict',
|
||||
'typeguard'
|
||||
]
|
||||
dynamic = ["version"]
|
||||
|
||||
|
||||
@@ -25,4 +25,6 @@ from .model import (
|
||||
InvalidFieldValue,
|
||||
InvalidFieldAnnotation,
|
||||
IncompletelyAnnotatedField,
|
||||
ImportForbidden,
|
||||
FunctionForbidden,
|
||||
)
|
||||
|
||||
@@ -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
|
||||
@@ -195,6 +259,10 @@ class DABField(Generic[TV_ALLOWED_MODEL_FIELDS_TYPES]):
|
||||
def value(self):
|
||||
return _deepfreeze(self._value)
|
||||
|
||||
@property
|
||||
def raw_value(self):
|
||||
return self._value
|
||||
|
||||
@property
|
||||
def annotations(self) -> Any:
|
||||
return self._annotations
|
||||
@@ -225,6 +293,53 @@ class FrozenDABField(Generic[TV_ALLOWED_MODEL_FIELDS_TYPES]):
|
||||
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))
|
||||
@@ -248,12 +363,19 @@ 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
|
||||
@@ -291,8 +413,8 @@ class BaseMeta(type):
|
||||
|
||||
_finfo: Optional[DABFieldInfo] = DABFieldInfo()
|
||||
origin = get_origin(namespace["__annotations__"][_fname])
|
||||
name = getattr(origin, "__name__", "") or getattr(origin, "__qualname__", "") or str(origin)
|
||||
if "Annotated" in name:
|
||||
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:
|
||||
@@ -319,6 +441,8 @@ class BaseMeta(type):
|
||||
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__)
|
||||
|
||||
@@ -346,6 +470,33 @@ 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):
|
||||
@@ -353,8 +504,6 @@ class BaseMeta(type):
|
||||
|
||||
for _fname in cls.__DABSchema__.keys():
|
||||
setattr(obj, _fname, cls.__DABSchema__[_fname].value)
|
||||
# obj.__DABSchema__ = deepfreeze(obj.__DABSchema__)
|
||||
# setattr(obj, "__DABSchema__", deepfreeze(obj.__DABSchema__))
|
||||
inst_schema = dict()
|
||||
for _fname, _fvalue in cls.__DABSchema__.items():
|
||||
inst_schema[_fname] = FrozenDABField(_fvalue)
|
||||
|
||||
@@ -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))
|
||||
@@ -214,7 +218,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)"""
|
||||
|
||||
@@ -293,7 +297,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)"""
|
||||
|
||||
@@ -896,6 +900,280 @@ class TestConfigWithoutEnabledFlag(unittest.TestCase):
|
||||
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})
|
||||
|
||||
with self.assertRaises(NameError):
|
||||
|
||||
class _(dm.BaseAppliance):
|
||||
_: int = 0
|
||||
|
||||
@classmethod
|
||||
def __initializer(cls):
|
||||
test_initializer_safe_testfc()
|
||||
|
||||
|
||||
# ---------- main ----------
|
||||
|
||||
|
||||
Reference in New Issue
Block a user