2020 lines
72 KiB
Python
2020 lines
72 KiB
Python
# dabmodel (c) by chacha
|
|
#
|
|
# dabmodel is licensed under a
|
|
# Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Unported License.
|
|
#
|
|
# You should have received a copy of the license along with this
|
|
# work. If not, see <https://creativecommons.org/licenses/by-nc-sa/4.0/>.
|
|
|
|
import unittest
|
|
|
|
import sys
|
|
import subprocess
|
|
from os import chdir, environ
|
|
from pathlib import Path
|
|
import textwrap
|
|
from typing import (
|
|
List,
|
|
Optional,
|
|
Dict,
|
|
Union,
|
|
Tuple,
|
|
Set,
|
|
FrozenSet,
|
|
Any,
|
|
Annotated,
|
|
)
|
|
|
|
import math
|
|
|
|
print(__name__)
|
|
print(__package__)
|
|
|
|
from src import dabmodel as dm
|
|
|
|
|
|
testdir_path = Path(__file__).parent.resolve()
|
|
chdir(testdir_path.parent.resolve())
|
|
|
|
|
|
def test_initializer_safe_testfc():
|
|
eval("print('hi')")
|
|
|
|
|
|
class ApplianceTest(unittest.TestCase):
|
|
def setUp(self):
|
|
print("\n->", unittest.TestCase.id(self))
|
|
|
|
def immutable_vars__test_field(
|
|
self, obj: Any, name: str, default_value: Any, test_value: Any
|
|
):
|
|
# field is not in the class
|
|
self.assertNotIn(name, dir(obj.__class__))
|
|
# field is in the object
|
|
self.assertIn(name, dir(obj))
|
|
# field is in the schema
|
|
self.assertIn(name, obj.__LAMSchema__.keys())
|
|
# field is readable
|
|
self.assertEqual(getattr(obj, name), default_value)
|
|
# field is read only
|
|
with self.assertRaises(dm.ReadOnlyField):
|
|
setattr(obj, name, test_value)
|
|
|
|
def test_immutable_fields(self):
|
|
"""Testing first appliance level, and Field types (simple)"""
|
|
|
|
# class can be created
|
|
class Appliance1(dm.Appliance):
|
|
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.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 "),
|
|
)
|
|
|
|
with self.assertRaises(dm.InvalidFieldValue):
|
|
|
|
class _(dm.Appliance):
|
|
test: str = 12
|
|
|
|
with self.assertRaises(dm.InvalidFieldValue):
|
|
|
|
class _(dm.Appliance):
|
|
test: int = "value"
|
|
|
|
with self.assertRaises(dm.InvalidFieldValue):
|
|
|
|
class _(dm.Appliance):
|
|
test: float = "value"
|
|
|
|
with self.assertRaises(dm.InvalidFieldValue):
|
|
|
|
class _(dm.Appliance):
|
|
test: complex = "value"
|
|
|
|
with self.assertRaises(dm.InvalidFieldValue):
|
|
|
|
class _(dm.Appliance):
|
|
test: bool = "value"
|
|
|
|
with self.assertRaises(dm.InvalidFieldValue):
|
|
|
|
class _(dm.Appliance):
|
|
test: bytes = "value"
|
|
|
|
def test_annotation(self):
|
|
"""Testing first appliance level, and Field types (simple annotations)"""
|
|
|
|
# class can be created if annotation is a string
|
|
class Appliance1(dm.Appliance):
|
|
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.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 "),
|
|
)
|
|
|
|
with self.assertRaises(dm.InvalidFieldValue):
|
|
|
|
class _(dm.Appliance):
|
|
test: "str" = 12
|
|
|
|
with self.assertRaises(dm.InvalidFieldValue):
|
|
|
|
class _(dm.Appliance):
|
|
test: "int" = "value"
|
|
|
|
with self.assertRaises(dm.InvalidFieldValue):
|
|
|
|
class _(dm.Appliance):
|
|
test: "float" = "value"
|
|
|
|
with self.assertRaises(dm.InvalidFieldValue):
|
|
|
|
class _(dm.Appliance):
|
|
test: "complex" = "value"
|
|
|
|
with self.assertRaises(dm.InvalidFieldValue):
|
|
|
|
class _(dm.Appliance):
|
|
test: "bool" = "value"
|
|
|
|
with self.assertRaises(dm.InvalidFieldValue):
|
|
|
|
class _(dm.Appliance):
|
|
test: "bytes" = "value"
|
|
|
|
# class cannot be created if not annotated field
|
|
with self.assertRaises(dm.NotAnnotatedField):
|
|
|
|
class _(dm.Appliance):
|
|
test = "default value"
|
|
|
|
def test_optionnal(self):
|
|
"""Testing first appliance level, and Field types (Optionnal annotations)"""
|
|
|
|
# class can be created with Optionnal (and variant)
|
|
class Appliance1(dm.Appliance):
|
|
StrVar: Optional[str] = "default value"
|
|
StrVar2: Optional[str]
|
|
StrVar3: Union[None | str] = "default value"
|
|
StrVar4: Union[None | str]
|
|
StrVar5: None | str = "default value"
|
|
StrVar6: None | str
|
|
|
|
app1 = Appliance1()
|
|
|
|
self.immutable_vars__test_field(app1, "StrVar", "default value", "123")
|
|
self.immutable_vars__test_field(app1, "StrVar2", None, "123")
|
|
self.immutable_vars__test_field(app1, "StrVar3", "default value", "123")
|
|
self.immutable_vars__test_field(app1, "StrVar4", None, "123")
|
|
self.immutable_vars__test_field(app1, "StrVar5", "default value", "123")
|
|
self.immutable_vars__test_field(app1, "StrVar6", None, "123")
|
|
|
|
# class can be created with Optionnal (and variant), as string annotation
|
|
class Appliance2(dm.Appliance):
|
|
StrVar: "Optional[str]" = "default value"
|
|
StrVar2: "Optional[str]"
|
|
StrVar3: "Union[None | str]" = "default value"
|
|
StrVar4: "Union[None | str]"
|
|
StrVar5: "None | str" = "default value"
|
|
StrVar6: "None | str"
|
|
|
|
app2 = Appliance2()
|
|
|
|
self.immutable_vars__test_field(app2, "StrVar", "default value", "123")
|
|
self.immutable_vars__test_field(app2, "StrVar2", None, "123")
|
|
self.immutable_vars__test_field(app2, "StrVar3", "default value", "123")
|
|
self.immutable_vars__test_field(app2, "StrVar4", None, "123")
|
|
self.immutable_vars__test_field(app2, "StrVar5", "default value", "123")
|
|
self.immutable_vars__test_field(app2, "StrVar6", None, "123")
|
|
|
|
# @unittest.skip
|
|
def test_containers__set(self):
|
|
"""Testing first appliance level, and Field types (Set)"""
|
|
|
|
# class can be created with set
|
|
class Appliance1(dm.Appliance):
|
|
testVar: Set[int] = {1, 2}
|
|
testVar2: Set[str] = {"a", "b"}
|
|
testVar3: Set[float] = {0.5, 0.456, 12}
|
|
testVar4: "Set[str]" = {"a", "c"}
|
|
testVar5: set[str] = {"a", "b"}
|
|
testVar6: "set[str]" = {"a", "b"}
|
|
testVar7: set[int | str] = {1, 2, "abcd", "efg"}
|
|
|
|
app1 = Appliance1()
|
|
self.immutable_vars__test_field(app1, "testVar", {1, 2}, {1, 5})
|
|
self.assertEqual(app1.testVar, {2, 1})
|
|
self.immutable_vars__test_field(app1, "testVar2", {"a", "b"}, {"h", "c"})
|
|
self.assertEqual(app1.testVar2, {"b", "a"})
|
|
self.immutable_vars__test_field(
|
|
app1, "testVar3", {0.5, 0.456, 12}, {0.9, 0.4156, 11}
|
|
)
|
|
self.assertEqual(app1.testVar3, {0.456, 0.5, 12})
|
|
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, "abcd", "efg"}, {"h", "c"}
|
|
)
|
|
|
|
# must work
|
|
sorted(app1.testVar)
|
|
|
|
with self.assertRaises(AttributeError):
|
|
app1.testVar.add(3)
|
|
|
|
with self.assertRaises(AttributeError):
|
|
app1.testVar4.add("coucou")
|
|
|
|
with self.assertRaises(dm.InvalidFieldValue):
|
|
|
|
class A(dm.Appliance):
|
|
a: Set[int] = {"a"}
|
|
|
|
with self.assertRaises(dm.InvalidFieldValue):
|
|
|
|
class B(dm.Appliance):
|
|
b: "Set[int]" = {"a"}
|
|
|
|
with self.assertRaises(dm.IncompletelyAnnotatedField):
|
|
|
|
class C(dm.Appliance):
|
|
c: Set = {1, 2}
|
|
|
|
with self.assertRaises(dm.IncompletelyAnnotatedField):
|
|
|
|
class D(dm.Appliance):
|
|
d: "Set" = {1, 2}
|
|
|
|
# Hacky part !
|
|
# set() are randomly ordered, and typeguard can be configured to only check 1st element of containers.
|
|
# we need to make sure it is properly configured, so we are expecting it to detect wrong type at any element
|
|
# this is why we need to run the code multiple time with a new interpreter that will use a different random seed
|
|
code = textwrap.dedent(
|
|
r"""
|
|
from src import dabmodel as dm
|
|
from typing import Set
|
|
try:
|
|
class Yours(dm.Appliance):
|
|
My1: Set[int] = {99, 1, 3, "a", 5,6}
|
|
except dm.InvalidFieldValue as ex:
|
|
raise SystemExit(2)
|
|
raise SystemExit(0)
|
|
|
|
"""
|
|
)
|
|
|
|
for i in range(15):
|
|
env = environ.copy()
|
|
env["PYTHONHASHSEED"] = str(i)
|
|
res = subprocess.run([sys.executable, "-c", code], env=env)
|
|
self.assertEqual(res.returncode, 2)
|
|
|
|
# @unittest.skip
|
|
def test_containers__frozenset(self):
|
|
"""Testing first appliance level, and Field types (FrozenSet)"""
|
|
|
|
# class can be created with set
|
|
class Appliance1(dm.Appliance):
|
|
testVar: frozenset[int] = frozenset({1, 2})
|
|
testVar2: frozenset[str] = frozenset({"a", "b"})
|
|
testVar3: frozenset[float] = frozenset({0.5, 0.456, 12})
|
|
testVar4: "frozenset[str]" = frozenset({"a", "c"})
|
|
testVar5: FrozenSet[int] = frozenset({1, 2})
|
|
testVar6: "FrozenSet[int]" = frozenset({1, 2})
|
|
testVar7: FrozenSet[int | str] = frozenset({1, 2, "abcd", "efg"})
|
|
|
|
app1 = Appliance1()
|
|
self.immutable_vars__test_field(app1, "testVar", {1, 2}, {1, 5})
|
|
self.assertEqual(app1.testVar, {2, 1})
|
|
self.immutable_vars__test_field(app1, "testVar2", {"a", "b"}, {"h", "c"})
|
|
self.assertEqual(app1.testVar2, {"b", "a"})
|
|
self.immutable_vars__test_field(
|
|
app1, "testVar3", {0.5, 0.456, 12}, {0.9, 0.4156, 11}
|
|
)
|
|
self.assertEqual(app1.testVar3, {0.456, 0.5, 12})
|
|
self.immutable_vars__test_field(app1, "testVar4", {"a", "c"}, {"h", "e"})
|
|
self.immutable_vars__test_field(app1, "testVar5", {1, 2}, {1, 5})
|
|
self.immutable_vars__test_field(app1, "testVar6", {1, 2}, {1, 5})
|
|
self.immutable_vars__test_field(app1, "testVar7", {1, 2, "abcd", "efg"}, {1, 5})
|
|
|
|
# must work
|
|
sorted(app1.testVar)
|
|
|
|
with self.assertRaises(AttributeError):
|
|
app1.testVar.add(3)
|
|
|
|
with self.assertRaises(dm.InvalidFieldValue):
|
|
print("youhou")
|
|
|
|
class A(dm.Appliance):
|
|
a: FrozenSet[int] = {"a"}
|
|
|
|
with self.assertRaises(dm.InvalidFieldValue):
|
|
|
|
class B(dm.Appliance):
|
|
b: "FrozenSet[int]" = {"a"}
|
|
|
|
with self.assertRaises(dm.IncompletelyAnnotatedField):
|
|
|
|
class C(dm.Appliance):
|
|
c: FrozenSet = {1, 2}
|
|
|
|
with self.assertRaises(dm.IncompletelyAnnotatedField):
|
|
|
|
class D(dm.Appliance):
|
|
d: "FrozenSet" = {1, 2}
|
|
|
|
# Hacky part !
|
|
# set() are randomly ordered, and typeguard can be configured to only check 1st element of containers.
|
|
# we need to make sure it is properly configured, so we are expecting it to detect wrong type at any element
|
|
# this is why we need to run the code multiple time with a new interpreter that will use a different random seed
|
|
code = textwrap.dedent(
|
|
r"""
|
|
from src import dabmodel as dm
|
|
from typing import FrozenSet
|
|
try:
|
|
class Yours(dm.Appliance):
|
|
My1: FrozenSet[int] = frozenset({99, 1, 3, "a", 5,6})
|
|
except dm.InvalidFieldValue as ex:
|
|
raise SystemExit(2)
|
|
raise SystemExit(0)
|
|
|
|
"""
|
|
)
|
|
|
|
for i in range(15):
|
|
env = environ.copy()
|
|
env["PYTHONHASHSEED"] = str(i)
|
|
res = subprocess.run([sys.executable, "-c", code], env=env)
|
|
self.assertEqual(res.returncode, 2)
|
|
|
|
def test_containers__list(self):
|
|
"""Testing first appliance level, and Field types (List)"""
|
|
|
|
# class can be created with list
|
|
class Appliance1(dm.Appliance):
|
|
testVar: List[int] = [1, 2]
|
|
testVar2: List[str] = ["a", "b"]
|
|
testVar3: List[float] = [0.5, 0.456, 12]
|
|
testVar4: "List[str]" = ["a", "c"]
|
|
testVar5: list[str] = ["a", "b"]
|
|
testVar6: "list[str]" = ["a", "b"]
|
|
testVar7: List[Union[int, str]] = [1, 2, 3, "one", "two", "three"]
|
|
|
|
app1 = Appliance1()
|
|
# Note: lists are converted to tuples
|
|
self.immutable_vars__test_field(app1, "testVar", (1, 2), [1, 5])
|
|
self.immutable_vars__test_field(app1, "testVar2", ("a", "b"), ["h", "c"])
|
|
self.immutable_vars__test_field(
|
|
app1, "testVar3", (0.5, 0.456, 12), [0.9, 0.4156, 11]
|
|
)
|
|
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"]
|
|
)
|
|
|
|
# must work
|
|
sorted(app1.testVar)
|
|
|
|
with self.assertRaises(AttributeError):
|
|
app1.testVar.append(3)
|
|
|
|
with self.assertRaises(AttributeError):
|
|
app1.testVar.pop()
|
|
|
|
with self.assertRaises(AttributeError):
|
|
app1.testVar.sort()
|
|
|
|
with self.assertRaises(AttributeError):
|
|
app1.testVar4.append("coucou")
|
|
|
|
with self.assertRaises(dm.InvalidFieldValue):
|
|
|
|
class E(dm.Appliance):
|
|
test: List[int] = ["a"]
|
|
|
|
with self.assertRaises(dm.InvalidFieldValue):
|
|
|
|
class F(dm.Appliance):
|
|
test: "List[int]" = ["a"]
|
|
|
|
with self.assertRaises(dm.IncompletelyAnnotatedField):
|
|
|
|
class G(dm.Appliance):
|
|
test: List = [1, 2]
|
|
|
|
with self.assertRaises(dm.IncompletelyAnnotatedField):
|
|
|
|
class H(dm.Appliance):
|
|
test: "List" = [1, 2]
|
|
|
|
def test_containers__dict(self):
|
|
"""Testing first appliance level, and Field types (Dict)"""
|
|
|
|
# class can be created with dict
|
|
class Appliance1(dm.Appliance):
|
|
testVar: Dict[int, str] = {1: "a", 2: "b"}
|
|
testVar2: "Dict[int, str]" = {1: "c", 99: "d"}
|
|
|
|
app1 = Appliance1()
|
|
self.immutable_vars__test_field(
|
|
app1, "testVar", {1: "a", 2: "b"}, {1: "", 99: "i"}
|
|
)
|
|
self.immutable_vars__test_field(
|
|
app1, "testVar2", {1: "c", 99: "d"}, {10: "", 50: "i"}
|
|
)
|
|
|
|
# TODO: wrap exception type
|
|
with self.assertRaises(TypeError):
|
|
app1.testVar[58] = "aaa"
|
|
|
|
# TODO: wrap exception type
|
|
with self.assertRaises(TypeError):
|
|
app1.testVar[1] = "ggg"
|
|
|
|
with self.assertRaises(dm.InvalidFieldValue):
|
|
|
|
class _(dm.Appliance):
|
|
test: Dict[int, str] = {1: 64, 2: "b"}
|
|
|
|
with self.assertRaises(dm.InvalidFieldValue):
|
|
|
|
class _(dm.Appliance):
|
|
test: "Dict[int, str]" = {1: 64, 2: "b"}
|
|
|
|
with self.assertRaises(dm.IncompletelyAnnotatedField):
|
|
|
|
class _(dm.Appliance):
|
|
test: Dict = {1: 64, 2: "b"}
|
|
|
|
# annotation is parsed before the library can do anything, so the exception can only be TypeError
|
|
with self.assertRaises(TypeError):
|
|
|
|
class _(dm.Appliance):
|
|
test: Dict[int] = {1: 64, 2: "b"}
|
|
|
|
# annotation is parsed before the library can do anything, so the exception can only be TypeError
|
|
with self.assertRaises(TypeError):
|
|
|
|
class _(dm.Appliance):
|
|
test: "Dict[int]" = {1: 64, 2: "b"}
|
|
|
|
def test_containers__tuple(self):
|
|
"""Testing first appliance level, and Field types (Tuple)"""
|
|
|
|
# class can be created with list
|
|
class Appliance1(dm.Appliance):
|
|
testVar: Tuple[int, ...] = (1, 2)
|
|
testVar2: Tuple[str, ...] = ("a", "b")
|
|
testVar3: Tuple[float, ...] = (0.5, 0.456, 12)
|
|
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()
|
|
|
|
self.immutable_vars__test_field(app1, "testVar", (1, 2), (1, 5))
|
|
self.immutable_vars__test_field(app1, "testVar2", ("a", "b"), ("h", "c"))
|
|
self.immutable_vars__test_field(
|
|
app1, "testVar3", (0.5, 0.456, 12), (0.9, 0.4156, 11)
|
|
)
|
|
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, "b"), (7, "h"))
|
|
|
|
# must work
|
|
sorted(app1.testVar)
|
|
|
|
with self.assertRaises(dm.InvalidFieldValue):
|
|
|
|
class _(dm.Appliance):
|
|
test: Tuple[int] = "a"
|
|
|
|
with self.assertRaises(dm.InvalidFieldValue):
|
|
|
|
class _(dm.Appliance):
|
|
test: "Tuple[int]" = "a"
|
|
|
|
with self.assertRaises(dm.IncompletelyAnnotatedField):
|
|
|
|
class _(dm.Appliance):
|
|
test: Tuple = (1, 2)
|
|
|
|
with self.assertRaises(dm.IncompletelyAnnotatedField):
|
|
|
|
class _(dm.Appliance):
|
|
test: "Tuple" = (1, 2)
|
|
|
|
with self.assertRaises(dm.InvalidFieldValue):
|
|
|
|
class _(dm.Appliance):
|
|
test: "Tuple[int,...]" = (1, "a")
|
|
|
|
with self.assertRaises(dm.InvalidFieldValue):
|
|
|
|
class _(dm.Appliance):
|
|
test: "tuple[int,...]" = (1, "a")
|
|
|
|
def check_immutable_fields_schema(
|
|
self,
|
|
appliance: dm.Appliance,
|
|
field_name: str,
|
|
expected_value: str,
|
|
expected_default_value: str,
|
|
expected_type: type,
|
|
):
|
|
self.assertIn(field_name, appliance.__LAMSchema__)
|
|
self.assertIn("doc", dir(appliance.__LAMSchema__[field_name]))
|
|
self.assertEqual(appliance.__LAMSchema__[field_name].doc, "")
|
|
self.assertIn("annotations", dir(appliance.__LAMSchema__[field_name]))
|
|
self.assertEqual(appliance.__LAMSchema__[field_name].annotations, expected_type)
|
|
self.assertIn("value", dir(appliance.__LAMSchema__[field_name]))
|
|
self.assertEqual(appliance.__LAMSchema__[field_name].value, expected_value)
|
|
self.assertIn("default_value", dir(appliance.__LAMSchema__[field_name]))
|
|
self.assertEqual(
|
|
appliance.__LAMSchema__[field_name].default_value, expected_default_value
|
|
)
|
|
self.assertIn("constraints", dir(appliance.__LAMSchema__[field_name]))
|
|
self.assertEqual(appliance.__LAMSchema__[field_name].constraints, ())
|
|
|
|
def test_immutable_fields_schema(self):
|
|
"""Testing first appliance level, and Field types (annotated)"""
|
|
|
|
# class can be created
|
|
class Appliance1(dm.Appliance):
|
|
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("__LAMSchema__", dir(app1))
|
|
self.assertIn("__LAMSchema__", 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.Appliance):
|
|
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("__LAMSchema__", dir(app1))
|
|
self.assertIn("__LAMSchema__", 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.Appliance):
|
|
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("__LAMSchema__", dir(app1))
|
|
self.assertIn("__LAMSchema__", 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.Appliance):
|
|
StrVar: Annotated[str, dm.LAMFieldInfo(doc="foo1")] = "default value"
|
|
StrVar2: Annotated[str, dm.LAMFieldInfo(doc="foo2")] = "default value2"
|
|
VarInt: Annotated[int, dm.LAMFieldInfo(doc="foo3")] = 12
|
|
VarInt2: Annotated[int, dm.LAMFieldInfo(doc="foo4")] = 21
|
|
VarFloat: Annotated[float, dm.LAMFieldInfo(doc="foo5")] = 12.1
|
|
VarFloat2: Annotated[float, dm.LAMFieldInfo(doc="foo6")] = 21.2
|
|
VarComplex: Annotated[complex, dm.LAMFieldInfo(doc="foo7")] = complex(3, 5)
|
|
VarComplex2: Annotated[complex, dm.LAMFieldInfo(doc="foo8")] = complex(8, 6)
|
|
VarBool: Annotated[bool, dm.LAMFieldInfo(doc="foo9")] = True
|
|
VarBool2: Annotated[bool, dm.LAMFieldInfo(doc="foo10")] = False
|
|
VarBytes: Annotated[bytes, dm.LAMFieldInfo(doc="foo11")] = bytes.fromhex(
|
|
"2Ef0 F1f2 "
|
|
)
|
|
VarBytes2: Annotated[bytes, dm.LAMFieldInfo(doc="foo12")] = bytes.fromhex(
|
|
"2ff0 F7f2 "
|
|
)
|
|
|
|
app1 = Appliance1()
|
|
|
|
self.immutable_vars__test_field(app1, "StrVar", "default value", "test")
|
|
self.assertEqual(app1.__LAMSchema__["StrVar"].doc, "foo1")
|
|
self.immutable_vars__test_field(app1, "StrVar2", "default value2", "test2")
|
|
self.assertEqual(app1.__LAMSchema__["StrVar2"].doc, "foo2")
|
|
self.immutable_vars__test_field(app1, "VarInt", 12, 13)
|
|
self.assertEqual(app1.__LAMSchema__["VarInt"].doc, "foo3")
|
|
self.immutable_vars__test_field(app1, "VarInt2", 21, 22)
|
|
self.assertEqual(app1.__LAMSchema__["VarInt2"].doc, "foo4")
|
|
self.immutable_vars__test_field(app1, "VarFloat", 12.1, 32)
|
|
self.assertEqual(app1.__LAMSchema__["VarFloat"].doc, "foo5")
|
|
self.immutable_vars__test_field(app1, "VarFloat2", 21.2, 42)
|
|
self.assertEqual(app1.__LAMSchema__["VarFloat2"].doc, "foo6")
|
|
self.immutable_vars__test_field(
|
|
app1, "VarComplex", complex(3, 5), complex(1, 2)
|
|
)
|
|
self.assertEqual(app1.__LAMSchema__["VarComplex"].doc, "foo7")
|
|
self.immutable_vars__test_field(
|
|
app1, "VarComplex2", complex(8, 6), complex(3, 2)
|
|
)
|
|
self.assertEqual(app1.__LAMSchema__["VarComplex2"].doc, "foo8")
|
|
self.immutable_vars__test_field(app1, "VarBool", True, False)
|
|
self.assertEqual(app1.__LAMSchema__["VarBool"].doc, "foo9")
|
|
self.immutable_vars__test_field(app1, "VarBool2", False, True)
|
|
self.assertEqual(app1.__LAMSchema__["VarBool2"].doc, "foo10")
|
|
self.immutable_vars__test_field(
|
|
app1, "VarBytes", bytes.fromhex("2Ef0 F1f2 "), bytes.fromhex("11f0 F1f2 ")
|
|
)
|
|
self.assertEqual(app1.__LAMSchema__["VarBytes"].doc, "foo11")
|
|
self.immutable_vars__test_field(
|
|
app1,
|
|
"VarBytes2",
|
|
bytes.fromhex("2ff0 F7f2 "),
|
|
bytes.fromhex("11f0 F1e2 "),
|
|
)
|
|
self.assertEqual(app1.__LAMSchema__["VarBytes2"].doc, "foo12")
|
|
|
|
with self.assertRaises(dm.InvalidFieldAnnotation):
|
|
|
|
class _(dm.Appliance):
|
|
test: 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.Appliance):
|
|
test: 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.Appliance):
|
|
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.__LAMSchema__)
|
|
self.assertNotIn("NewValue", app2.__LAMSchema__)
|
|
self.assertNotIn("NewValue", Appliance1.__LAMSchema__)
|
|
self.assertNotIn("NewValue", app1.__LAMSchema__)
|
|
|
|
app3 = Appliance3()
|
|
self.immutable_vars__test_field(app3, "StrVar", "moded value", "test")
|
|
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.Appliance):
|
|
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.Appliance):
|
|
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.assertEqual(app1.VarInt, 12)
|
|
self.assertEqual(app1.VarInt2, 13)
|
|
self.assertEqual(app1.set1, frozenset({0}))
|
|
self.assertEqual(app1.set2, frozenset((1.43, 1234)))
|
|
self.assertEqual(app1.VarStr1, "ABCD")
|
|
|
|
# class can be created
|
|
class _(dm.Appliance):
|
|
VarInt: int = 41
|
|
|
|
@classmethod
|
|
def __initializer(cls):
|
|
|
|
cls.VarInt = cls.VarInt + 1
|
|
|
|
app2 = _()
|
|
self.assertEqual(app2.VarInt, 42)
|
|
|
|
def test_initializer_dont_leak(self):
|
|
|
|
class A(dm.Appliance):
|
|
VarInt: int = 12
|
|
|
|
class B(A):
|
|
|
|
@classmethod
|
|
def __initializer(cls):
|
|
cls.VarInt = cls.VarInt + 1
|
|
|
|
a = A()
|
|
b = B()
|
|
|
|
self.assertEqual(a.VarInt, 12)
|
|
self.assertEqual(b.VarInt, 13)
|
|
|
|
def test_initializer_safe(self):
|
|
|
|
with self.assertRaises(dm.ImportForbidden):
|
|
|
|
class test(dm.Appliance):
|
|
_: int = 0
|
|
|
|
@classmethod
|
|
def __initializer(cls):
|
|
import pprint
|
|
|
|
with self.assertRaises(NameError):
|
|
|
|
class _(dm.Appliance):
|
|
_: int = 0
|
|
|
|
@classmethod
|
|
def __initializer(cls):
|
|
eval("2 ** 8")
|
|
|
|
with self.assertRaises(NameError):
|
|
|
|
class _(dm.Appliance):
|
|
_: int = 0
|
|
|
|
@classmethod
|
|
def __initializer(cls):
|
|
open("foo")
|
|
|
|
with self.assertRaises(NameError):
|
|
|
|
class _(dm.Appliance):
|
|
_: int = 0
|
|
|
|
@classmethod
|
|
def __initializer(cls):
|
|
exec("exit()")
|
|
|
|
with self.assertRaises(NameError):
|
|
|
|
class _(dm.Appliance):
|
|
_: int = 0
|
|
|
|
@classmethod
|
|
def __initializer(cls):
|
|
compile("print(55)", "test", "eval")
|
|
|
|
with self.assertRaises(NameError):
|
|
|
|
class _(dm.Appliance):
|
|
_: int = 0
|
|
|
|
@classmethod
|
|
def __initializer(cls):
|
|
compile("print(55)", "test", "eval")
|
|
|
|
def testfc():
|
|
eval("print('test')")
|
|
|
|
with self.assertRaises(dm.FunctionForbidden):
|
|
|
|
class _(dm.Appliance):
|
|
_: int = 0
|
|
|
|
@classmethod
|
|
def __initializer(cls):
|
|
testfc()
|
|
|
|
with self.assertRaises(dm.FunctionForbidden):
|
|
|
|
class _(dm.Appliance):
|
|
_: int = 0
|
|
|
|
@classmethod
|
|
def __initializer(cls):
|
|
if True:
|
|
testfc()
|
|
|
|
# class can be created
|
|
class Appliance2(dm.Appliance):
|
|
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.assertEqual(app2.VarInt2, 56)
|
|
self.assertEqual(app2.VarFloat, 1)
|
|
self.assertEqual(app2.VarInt, 0)
|
|
self.assertEqual(app2.VarInt3, 4)
|
|
self.assertEqual(app2.VarInt4, 7)
|
|
self.assertEqual(app2.VarInt5, 4)
|
|
self.assertEqual(app2.list2, (0, 1, 2, 4))
|
|
self.assertEqual(app2.list3, (0, 1, 2, 3, 4))
|
|
self.assertEqual(app2.set2, {2})
|
|
|
|
with self.assertRaises(NameError):
|
|
|
|
class _(dm.Appliance):
|
|
_: int = 0
|
|
|
|
@classmethod
|
|
def __initializer(cls):
|
|
test_initializer_safe_testfc()
|
|
|
|
def test_inheritance_chain(self):
|
|
|
|
# class can be created
|
|
class Appliance1(dm.Appliance):
|
|
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")
|
|
|
|
def test_override(self):
|
|
"""Testing first appliance level, and Field types (List)"""
|
|
|
|
# class can be created with list
|
|
class Appliance1(dm.Appliance):
|
|
testVar: List[int] = [1, 2]
|
|
testVar2: List[str] = ["a", "b"]
|
|
testVar3: float = 0.5
|
|
testVar4: "List[str]" = ["a", "c"]
|
|
testVar5: list[str] = ["a", "b"]
|
|
testVar6: "list[str]" = ["a", "b"]
|
|
|
|
app1 = Appliance1(testVar=[5, 6], testVar2=[], testVar3=45.8)
|
|
|
|
self.immutable_vars__test_field(app1, "testVar", (5, 6), (1, 5))
|
|
self.immutable_vars__test_field(app1, "testVar2", (), ("h", "c"))
|
|
self.immutable_vars__test_field(app1, "testVar3", 45.8, 43.9)
|
|
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"))
|
|
|
|
# test no leaks
|
|
app2 = Appliance1()
|
|
self.immutable_vars__test_field(app2, "testVar", (1, 2), (1, 5))
|
|
self.immutable_vars__test_field(app2, "testVar2", ("a", "b"), ("h", "c"))
|
|
self.immutable_vars__test_field(app2, "testVar3", 0.5, 43.9)
|
|
self.immutable_vars__test_field(app2, "testVar4", ("a", "c"), ("h", "e"))
|
|
self.immutable_vars__test_field(app2, "testVar5", ("a", "b"), ("h", "c"))
|
|
self.immutable_vars__test_field(app2, "testVar6", ("a", "b"), ("h", "c"))
|
|
|
|
def test_new_field_forbidden(self):
|
|
class App(dm.Appliance):
|
|
x: int = 1
|
|
|
|
app = App()
|
|
with self.assertRaises(dm.NewFieldForbidden):
|
|
app.y = 2
|
|
|
|
def test_private_field_allowed(self):
|
|
class App(dm.Appliance):
|
|
x: int = 1
|
|
|
|
app = App()
|
|
app._internal = 42 # should be allowed
|
|
self.assertEqual(app._internal, 42)
|
|
|
|
def test_field_cannot_be_reassigned(self):
|
|
class App(dm.Appliance):
|
|
x: int = 1
|
|
|
|
app = App()
|
|
with self.assertRaises(dm.ReadOnlyField):
|
|
app.x = 99
|
|
|
|
def test_schema_fields_are_frozen(self):
|
|
class App(dm.Appliance):
|
|
x: list[int] = [1, 2]
|
|
|
|
app = App()
|
|
with self.assertRaises(AttributeError): # frozendict / tuple immutability
|
|
app.x.insert(3)
|
|
|
|
with self.assertRaises(dm.ReadOnlyField): # frozendict / tuple immutability
|
|
app.x = (1, 3)
|
|
|
|
def test_initializer_invalid_type_raises(self):
|
|
with self.assertRaises(dm.InvalidFieldValue):
|
|
|
|
class App(dm.Appliance):
|
|
x: int = 1
|
|
|
|
@classmethod
|
|
def __initializer(cls):
|
|
cls.x = "wrong" # wrong type
|
|
|
|
def test_initializer_forbid_nested_functions(self):
|
|
with self.assertRaises(dm.FunctionForbidden):
|
|
|
|
class App(dm.Appliance):
|
|
x: int = 1
|
|
|
|
@classmethod
|
|
def __initializer(cls):
|
|
def inner():
|
|
return 2
|
|
|
|
cls.x = inner()
|
|
|
|
with self.assertRaises(dm.FunctionForbidden):
|
|
|
|
class _(dm.Appliance):
|
|
x: int = 1
|
|
|
|
@classmethod
|
|
def __initializer(cls):
|
|
y = 2
|
|
|
|
def inner():
|
|
return y + 1
|
|
|
|
cls.x = inner()
|
|
|
|
with self.assertRaises(dm.FunctionForbidden):
|
|
|
|
class _(dm.Appliance):
|
|
x: int = 1
|
|
|
|
@classmethod
|
|
def __initializer(cls):
|
|
y = 2
|
|
|
|
def inner():
|
|
return y + 1
|
|
|
|
cls.x = lambda: inner()
|
|
|
|
def test_initializer_lambda_forbidden(self):
|
|
|
|
class App(dm.Appliance):
|
|
x: int = 1
|
|
|
|
@classmethod
|
|
def __initializer(cls):
|
|
f = lambda: 42
|
|
cls.x = f()
|
|
|
|
def test_multiple_inheritance_forbidden(self):
|
|
with self.assertRaises(dm.MultipleInheritanceForbidden):
|
|
|
|
class A(dm.Appliance):
|
|
pass
|
|
|
|
class B(dm.Appliance):
|
|
pass
|
|
|
|
class C(A, B):
|
|
pass
|
|
|
|
def test_initializer_can_use_math(self):
|
|
|
|
class App(dm.Appliance):
|
|
val: float = 0.0
|
|
|
|
@classmethod
|
|
def __initializer(cls):
|
|
# should be able to access math without NameError
|
|
cls.val = math.ceil(1.2) + math.floor(2.8)
|
|
|
|
app = App()
|
|
# math.ceil(1.2)=2, math.floor(2.8)=2 → 4
|
|
self.assertEqual(app.val, 4)
|
|
|
|
def test_deepfreeze_nested_mixed_tuple_list(self):
|
|
class App(dm.Appliance):
|
|
data: tuple[list[int], tuple[int, list[int]]] = ([1, 2], (3, [4, 5]))
|
|
|
|
app = App()
|
|
|
|
# Top-level: must be tuple
|
|
self.assertIsInstance(app.data, tuple)
|
|
|
|
# First element of tuple: should have been frozen to tuple, not list
|
|
self.assertIsInstance(app.data[0], tuple)
|
|
|
|
# Nested second element: itself a tuple
|
|
self.assertIsInstance(app.data[1], tuple)
|
|
|
|
# Deepest element: inner list should also be frozen to tuple
|
|
self.assertIsInstance(app.data[1][1], tuple)
|
|
|
|
# Check immutability
|
|
with self.assertRaises(TypeError):
|
|
app.data[0] += (99,) # tuples are immutable
|
|
|
|
with self.assertRaises(TypeError):
|
|
app.data[1][1] += (42,) # inner tuple also immutable
|
|
|
|
def test_inacurate_type(self):
|
|
with self.assertRaises(dm.InvalidFieldAnnotation):
|
|
|
|
class Appliance1(dm.Appliance):
|
|
SomeVar: list = []
|
|
|
|
with self.assertRaises(dm.InvalidFieldAnnotation):
|
|
|
|
class Appliance2(dm.Appliance):
|
|
SomeVar: list[Any] = []
|
|
|
|
with self.assertRaises(dm.InvalidFieldAnnotation):
|
|
|
|
class Appliance3(dm.Appliance):
|
|
SomeVar: list[object] = []
|
|
|
|
with self.assertRaises(dm.InvalidFieldAnnotation):
|
|
|
|
class Appliance4(dm.Appliance):
|
|
SomeVar: dict = {}
|
|
|
|
with self.assertRaises(dm.InvalidFieldAnnotation):
|
|
|
|
class Appliance5(dm.Appliance):
|
|
SomeVar: dict[str, Any] = {}
|
|
|
|
with self.assertRaises(dm.InvalidFieldAnnotation):
|
|
|
|
class Appliance6(dm.Appliance):
|
|
SomeVar: dict[Any, Any] = {}
|
|
|
|
with self.assertRaises(dm.InvalidFieldAnnotation):
|
|
|
|
class Appliance7(dm.Appliance):
|
|
SomeVar: dict[Any, str] = {}
|
|
|
|
with self.assertRaises(dm.InvalidFieldAnnotation):
|
|
|
|
class Appliance8(dm.Appliance):
|
|
SomeVar: dict[str, object] = {}
|
|
|
|
def test_deepfreeze_nested_dict_list_set(self):
|
|
class App(dm.Appliance):
|
|
data: dict[str, list[int] | set[str] | dict[str, list[int] | set[str]]] = {
|
|
"numbers": [1, 2, 3],
|
|
"letters": {"a", "b", "c"},
|
|
"mixed": {"x": [4, 5], "y": {"z"}},
|
|
}
|
|
|
|
app = App()
|
|
|
|
# Top-level: should be frozendict
|
|
self.assertEqual(type(app.data).__name__, "frozendict")
|
|
|
|
# Lists must be frozen to tuple
|
|
self.assertIsInstance(app.data["numbers"], tuple)
|
|
self.assertIsInstance(app.data["mixed"]["x"], tuple)
|
|
|
|
# Sets must be frozen to frozenset
|
|
self.assertIsInstance(app.data["letters"], frozenset)
|
|
self.assertIsInstance(app.data["mixed"]["y"], frozenset)
|
|
|
|
# Check immutability
|
|
with self.assertRaises(TypeError):
|
|
app.data["numbers"] += (99,)
|
|
|
|
with self.assertRaises(AttributeError):
|
|
app.data["letters"].add("d")
|
|
|
|
with self.assertRaises(TypeError):
|
|
app.data["mixed"]["x"] += (6,)
|
|
|
|
def test_unknown_parameters_raise(self):
|
|
class App(dm.Appliance):
|
|
a: int = 1
|
|
|
|
with self.assertRaises(dm.InvalidFieldValue) as cm:
|
|
App(foo=123)
|
|
|
|
self.assertIn("Unknown parameters:", str(cm.exception))
|
|
self.assertIn("foo", str(cm.exception))
|
|
|
|
def test_variadic_tuple_annotation_ok_and_errors(self):
|
|
class A(dm.Appliance):
|
|
xs: tuple[int, ...] = (1, 2, 3)
|
|
|
|
self.assertEqual(A().xs, (1, 2, 3))
|
|
|
|
class B(dm.Appliance):
|
|
xs: "Tuple[int, ...]" = (4, 5)
|
|
|
|
self.assertEqual(B().xs, (4, 5))
|
|
|
|
# wrong inner type should fail
|
|
with self.assertRaises(dm.InvalidFieldValue):
|
|
|
|
class _Bad(dm.Appliance):
|
|
xs: tuple[int, ...] = (1, "x")
|
|
|
|
def test_field_override_typechecked_and_instance_local(self):
|
|
class App(dm.Appliance):
|
|
a: list[int] = [1]
|
|
|
|
app1 = App(a=[9])
|
|
app2 = App()
|
|
# deepfreeze: lists become tuples
|
|
self.assertEqual(app1.a, (9,))
|
|
self.assertEqual(app2.a, (1,)) # no leakage
|
|
|
|
with self.assertRaises(dm.InvalidFieldValue):
|
|
App(a=["oops"]) # wrong inner type
|
|
|
|
def test_root_field_override_nonexisting_rejected(self):
|
|
class App(dm.Appliance):
|
|
x: int = 1
|
|
|
|
with self.assertRaises(dm.InvalidFieldValue):
|
|
App(y=2) # not in schema -> unknown parameter
|
|
|
|
def test_dict_field_override_with_nested_containers(self):
|
|
class App(dm.Appliance):
|
|
data: dict[str, list[int]] = {"numbers": [1, 2]}
|
|
|
|
app = App(data={"numbers": [9, 8]})
|
|
# Dict must be frozen, lists inside must become tuples
|
|
self.assertEqual(type(app.data).__name__, "frozendict")
|
|
self.assertIsInstance(app.data["numbers"], tuple)
|
|
self.assertEqual(app.data["numbers"], (9, 8))
|
|
|
|
# Wrong type inside list should raise
|
|
with self.assertRaises(dm.InvalidFieldValue):
|
|
App(data={"numbers": [1, "oops"]})
|
|
|
|
def test_initializer_modifies_nested_containers(self):
|
|
class App(dm.Appliance):
|
|
data: dict[str, list[int]] = {"nums": [1]}
|
|
|
|
@classmethod
|
|
def __initializer(cls):
|
|
# Append into nested list
|
|
cls.data["nums"].append(2)
|
|
|
|
app = App()
|
|
self.assertEqual(app.data["nums"], (1, 2)) # frozen tuple after init
|
|
|
|
|
|
# ---------- main ----------
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|