294 lines
7.7 KiB
Python
294 lines
7.7 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
|
||
|
|
import defl
|
||
|
|
from defl import log, cl, Assert
|
||
|
|
from defl.testing_ import Tester, Test, TestState, testFail
|
||
|
|
|
||
|
|
import dataclasses, contextlib
|
||
|
|
import itertools
|
||
|
|
import inspect
|
||
|
|
from typing import *
|
||
|
|
from types import *
|
||
|
|
import typing
|
||
|
|
from defl._typeCheck_ import *
|
||
|
|
from defl._dataclass_ import *
|
||
|
|
|
||
|
|
tester = Tester(name=__file__)
|
||
|
|
|
||
|
|
# log.setLevel('debug')
|
||
|
|
|
||
|
|
@tester.add()
|
||
|
|
def isSetup():
|
||
|
|
unpatchDataClassWithRunTimeValidator()
|
||
|
|
assert not hasattr(dataclasses, '_process_class_hidden'), dataclasses._process_class_hidden
|
||
|
|
assert dataclasses._process_class.__name__ == '_process_class'
|
||
|
|
|
||
|
|
patchDataClassWithRunTimeValidator()
|
||
|
|
assert hasattr(dataclasses, '_process_class_hidden')
|
||
|
|
assert dataclasses._process_class_hidden
|
||
|
|
assert dataclasses._process_class.__name__ == '_dataclassWrap'
|
||
|
|
|
||
|
|
unpatchDataClassWithRunTimeValidator()
|
||
|
|
assert not hasattr(dataclasses, '_process_class_hidden')
|
||
|
|
assert dataclasses._process_class.__name__ == '_process_class'
|
||
|
|
|
||
|
|
@tester.add()
|
||
|
|
def test():
|
||
|
|
patchDataClassWithRunTimeValidator()
|
||
|
|
# == validated after __post_init__
|
||
|
|
@dataclasses.dataclass(slots=True, kw_only=True, frozen=False)
|
||
|
|
class Test:
|
||
|
|
a: list
|
||
|
|
|
||
|
|
def __post_init__(_) -> None:
|
||
|
|
if _.a is None:
|
||
|
|
_.a = []
|
||
|
|
|
||
|
|
Test(a=None)
|
||
|
|
Test(a=[])
|
||
|
|
with testFail(expt=ObjectTypeValidationError):
|
||
|
|
Test(a=1)
|
||
|
|
|
||
|
|
@tester.add()
|
||
|
|
def test():
|
||
|
|
patchDataClassWithRunTimeValidator()
|
||
|
|
# == unions
|
||
|
|
@dataclasses.dataclass(slots=True, kw_only=True, frozen=False)
|
||
|
|
class Test:
|
||
|
|
a: list | dict
|
||
|
|
|
||
|
|
Test(a=[])
|
||
|
|
Test(a={})
|
||
|
|
with testFail(expt=ObjectTypeValidationError):
|
||
|
|
Test(a=1)
|
||
|
|
|
||
|
|
@tester.add()
|
||
|
|
def test():
|
||
|
|
patchDataClassWithRunTimeValidator()
|
||
|
|
|
||
|
|
# == genericAlias
|
||
|
|
def genericAliasTests():
|
||
|
|
Test(a=[])
|
||
|
|
Test(a=[{}, {}])
|
||
|
|
Test(a=[{}, {'t': 1, 't': 2}])
|
||
|
|
|
||
|
|
with testFail(expt=ObjectTypeValidationError):
|
||
|
|
Test(a={})
|
||
|
|
with testFail(expt=ObjectTypeValidationError):
|
||
|
|
Test(a=[{}, {}, 1])
|
||
|
|
with testFail(expt=ObjectTypeValidationError):
|
||
|
|
Test(a=[{}, {'t': 1, 't': 2, 't': 'fail'}])
|
||
|
|
with testFail(expt=ObjectTypeValidationError):
|
||
|
|
Test(a=[None])
|
||
|
|
|
||
|
|
@dataclasses.dataclass(slots=True, kw_only=True, frozen=False)
|
||
|
|
class Test:
|
||
|
|
a: list[dict[str, int]]
|
||
|
|
|
||
|
|
genericAliasTests()
|
||
|
|
|
||
|
|
@dataclasses.dataclass(slots=True, kw_only=True, frozen=False)
|
||
|
|
class Test:
|
||
|
|
a: List[Dict[str, int]]
|
||
|
|
|
||
|
|
genericAliasTests()
|
||
|
|
|
||
|
|
@tester.add()
|
||
|
|
def test():
|
||
|
|
patchDataClassWithRunTimeValidator()
|
||
|
|
# unpatchDataClassWithRunTimeValidator()
|
||
|
|
# print(dataclasses._process_class_hidden)
|
||
|
|
# == custom class
|
||
|
|
@dataclasses.dataclass(slots=True, kw_only=True, frozen=False)
|
||
|
|
class A:
|
||
|
|
a: int
|
||
|
|
|
||
|
|
def __hash__(_) -> None:
|
||
|
|
return _.a
|
||
|
|
|
||
|
|
class Tup(tuple):
|
||
|
|
...
|
||
|
|
|
||
|
|
tuple([1, 2, 3])
|
||
|
|
Tup([1, 2, 3])
|
||
|
|
|
||
|
|
@dataclasses.dataclass(slots=True, kw_only=True, frozen=False)
|
||
|
|
class B:
|
||
|
|
b: list[dict[A, Tup[Dict[int | str, int | str | A]]]]
|
||
|
|
# _TYPECHECK_Debug: bool = True
|
||
|
|
|
||
|
|
B(b=[{A(a=1): Tup([{1: 't'}, {'t': 1}, {'t': A(a=2)}])}])
|
||
|
|
B(b=[{A(a=1): Tup([])}])
|
||
|
|
B(b=[{}])
|
||
|
|
B(b=[])
|
||
|
|
|
||
|
|
with testFail(expt=ObjectTypeValidationError):
|
||
|
|
B(b=None)
|
||
|
|
|
||
|
|
with testFail(expt=ObjectTypeValidationError):
|
||
|
|
B(b=[{A(a=1): Tup([{'t': B(b=2)}])}])
|
||
|
|
|
||
|
|
@tester.add()
|
||
|
|
def test():
|
||
|
|
assert typeMap(1, mp={int: True}) is True
|
||
|
|
assert typeMap(1, mp={}, default=True) is True
|
||
|
|
assert typeMap([1], mp={list[int]: True}) is True
|
||
|
|
assert typeMap([None, 1], mp={list[int | None]: True}) is True
|
||
|
|
assert typeMap([None, 1], mp={list: True}) is True
|
||
|
|
|
||
|
|
with testFail(expt=ValueError):
|
||
|
|
typeMap(None, mp={int: True})
|
||
|
|
with testFail(expt=ValueError):
|
||
|
|
typeMap([None], mp={list[int]: True})
|
||
|
|
|
||
|
|
@tester.add()
|
||
|
|
def test():
|
||
|
|
return True if 'OOO is trash' else False
|
||
|
|
|
||
|
|
#| class hierarchy is trash
|
||
|
|
@dataclass(slots=True, kw_only=True, frozen=False)
|
||
|
|
class A:
|
||
|
|
a: list = field(default_factory=list, kw_only=True)
|
||
|
|
|
||
|
|
def __post_init__(_):
|
||
|
|
_.a.append(1)
|
||
|
|
|
||
|
|
a = A()
|
||
|
|
Assert(a.a) == [1]
|
||
|
|
|
||
|
|
@dataclass(slots=True, kw_only=True, frozen=False)
|
||
|
|
class B(A):
|
||
|
|
b: list = field(default_factory=list, kw_only=True)
|
||
|
|
|
||
|
|
def __post_init__(_):
|
||
|
|
sup = inspect.getmro(type(_))
|
||
|
|
print(sup)
|
||
|
|
sup[1].__post_init__(_)
|
||
|
|
_.a.append(2)
|
||
|
|
|
||
|
|
b = B()
|
||
|
|
Assert(b.a) == [1, 2]
|
||
|
|
|
||
|
|
@dataclass(slots=True, kw_only=True, frozen=False)
|
||
|
|
class C(B):
|
||
|
|
c: list = field(default_factory=list, kw_only=True)
|
||
|
|
|
||
|
|
def __post_init__(_):
|
||
|
|
type(_).__bases__
|
||
|
|
# sup = inspect.getmro(type(_))
|
||
|
|
# print(sup)
|
||
|
|
# sup[1].__post_init__(_)
|
||
|
|
# _.a.append(3)
|
||
|
|
|
||
|
|
c = C()
|
||
|
|
Assert(c.a) == [1, 2, 3]
|
||
|
|
|
||
|
|
@tester.add()
|
||
|
|
def test():
|
||
|
|
@dataclass(slots=True, kw_only=True, frozen=False)
|
||
|
|
class A:
|
||
|
|
base: list = field(default_factory=list, kw_only=True)
|
||
|
|
|
||
|
|
@dataclass(slots=True, kw_only=True, frozen=False)
|
||
|
|
class B:
|
||
|
|
a: A = field(default_factory=A, kw_only=True)
|
||
|
|
|
||
|
|
@dataclass(slots=True, kw_only=True, frozen=False)
|
||
|
|
class C:
|
||
|
|
myLongField: B = field(default_factory=B, kw_only=True)
|
||
|
|
|
||
|
|
@property
|
||
|
|
def b(_) -> B:
|
||
|
|
return _.myLongField
|
||
|
|
|
||
|
|
c = C()
|
||
|
|
Assert(c.b.a.base) == []
|
||
|
|
|
||
|
|
@tester.add()
|
||
|
|
def test():
|
||
|
|
patchDataClassWithRunTimeValidator()
|
||
|
|
|
||
|
|
#/ ---------------------------------------------------------------
|
||
|
|
@dataclasses.dataclass(slots=True, kw_only=True, frozen=False)
|
||
|
|
class Test:
|
||
|
|
a: ClassVar = 1
|
||
|
|
|
||
|
|
test = Test()
|
||
|
|
|
||
|
|
#/ ---------------------------------------------------------------
|
||
|
|
@dataclasses.dataclass(slots=True, kw_only=True, frozen=False)
|
||
|
|
class Test:
|
||
|
|
a: ClassVar[str] = 1
|
||
|
|
|
||
|
|
try:
|
||
|
|
test = Test()
|
||
|
|
raise ValueError()
|
||
|
|
except ObjectTypeValidationError:
|
||
|
|
...
|
||
|
|
|
||
|
|
#/ ---------------------------------------------------------------
|
||
|
|
@dataclasses.dataclass(slots=True, kw_only=True, frozen=False)
|
||
|
|
class Test:
|
||
|
|
_TYPECHECK_Debug: ClassVar[int] = 1
|
||
|
|
|
||
|
|
test = Test()
|
||
|
|
|
||
|
|
#/ ---------------------------------------------------------------
|
||
|
|
@dataclasses.dataclass(slots=True, kw_only=True, frozen=False)
|
||
|
|
class Test:
|
||
|
|
a: ClassVar[dict[int, int] | list[int]] = {1: 2, 3: 4}
|
||
|
|
b: ClassVar[dict[int] | list[int]] = [1, 2, 3]
|
||
|
|
|
||
|
|
test = Test()
|
||
|
|
|
||
|
|
@tester.add()
|
||
|
|
def factory():
|
||
|
|
# https://www.geeksforgeeks.org/class-factories-a-powerful-pattern-in-python/
|
||
|
|
# https://www.geeksforgeeks.org/python-class-members/
|
||
|
|
|
||
|
|
C = dataclasses.make_dataclass(
|
||
|
|
'C', [('x', int), 'y', ('z', int, field(default=5))], namespace={'add_one': lambda self: self.x + 1}
|
||
|
|
)
|
||
|
|
|
||
|
|
@dataclass(slots=T, kw_only=T, frozen=F)
|
||
|
|
class A:
|
||
|
|
a: str = field(kw_only=T, repr=T)
|
||
|
|
|
||
|
|
print(type(A.a), A.a)
|
||
|
|
|
||
|
|
f = {'a': 1, 'b': 2}
|
||
|
|
C = dataclasses.make_dataclass('C', [(x, int) for x in f.keys()])
|
||
|
|
c = C(**f)
|
||
|
|
Assert(c.a) == 1
|
||
|
|
Assert(c.b) == 2
|
||
|
|
|
||
|
|
try:
|
||
|
|
f = {'fail': "not an int"}
|
||
|
|
C = dataclasses.make_dataclass('C', [(x, int) for x in f.keys()])
|
||
|
|
c = C(**f)
|
||
|
|
raise ValueError()
|
||
|
|
except ObjectTypeValidationError:
|
||
|
|
...
|
||
|
|
|
||
|
|
@tester.add()
|
||
|
|
def descriptors():
|
||
|
|
# https://realpython.com/python-descriptors/
|
||
|
|
class Verbose_attribute():
|
||
|
|
def __get__(self, obj, type=None) -> object:
|
||
|
|
print("accessing the attribute to get the value")
|
||
|
|
return 42
|
||
|
|
|
||
|
|
def __set__(self, obj, value) -> None:
|
||
|
|
print("accessing the attribute to set the value")
|
||
|
|
raise AttributeError("Cannot change the value")
|
||
|
|
|
||
|
|
class Foo():
|
||
|
|
attribute1 = Verbose_attribute()
|
||
|
|
|
||
|
|
my_foo_object = Foo()
|
||
|
|
x = my_foo_object.attribute1
|
||
|
|
Assert(x) == 42
|
||
|
|
|
||
|
|
log.info(tester.run())
|
||
|
|
tester.exitWithStatus()
|