#!/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()