Module pedantic.tests.test_frozen_dataclass
Expand source code
import unittest
from abc import ABC
from dataclasses import dataclass, FrozenInstanceError
from typing import List, Dict, Set, Tuple, Awaitable, Callable, Generic, TypeVar, Optional
from pedantic.decorators.cls_deco_frozen_dataclass import frozen_dataclass, frozen_type_safe_dataclass
from pedantic.exceptions import PedanticTypeCheckException
@frozen_dataclass
class Foo:
a: int
b: str
c: bool
@frozen_type_safe_dataclass
class B:
v: Set[int]
@frozen_type_safe_dataclass
class A:
foo: List[int]
bar: Dict[str, str]
values: Tuple[B, B]
class TestFrozenDataclass(unittest.TestCase):
def test_equals_and_hash(self):
a = Foo(a=6, b='hi', c=True)
b = Foo(a=6, b='hi', c=True)
c = Foo(a=7, b='hi', c=True)
self.assertEqual(a, b)
self.assertEqual(hash(a), hash(b))
self.assertNotEqual(a, c)
self.assertNotEqual(hash(a), hash(c))
def test_copy_with(self):
foo = Foo(a=6, b='hi', c=True)
copy_1 = foo.copy_with()
self.assertEqual(foo, copy_1)
copy_2 = foo.copy_with(a=42)
self.assertNotEqual(foo, copy_2)
self.assertEqual(42, copy_2.a)
self.assertEqual(foo.b, copy_2.b)
self.assertEqual(foo.c, copy_2.c)
copy_3 = foo.copy_with(b='Hello')
self.assertNotEqual(foo, copy_3)
self.assertEqual(foo.a, copy_3.a)
self.assertEqual('Hello', copy_3.b)
self.assertEqual(foo.c, copy_3.c)
copy_4 = foo.copy_with(c=False)
self.assertNotEqual(foo, copy_4)
self.assertEqual(foo.a, copy_4.a)
self.assertEqual(foo.b, copy_4.b)
self.assertEqual(False, copy_4.c)
copy_5 = foo.copy_with(a=676676, b='new', c=False)
self.assertNotEqual(foo, copy_5)
self.assertEqual(676676, copy_5.a)
self.assertEqual('new', copy_5.b)
self.assertEqual(False, copy_5.c)
def test_validate_types(self):
foo = Foo(a=6, b='hi', c=True)
foo.validate_types()
bar = Foo(a=6.6, b='hi', c=True)
with self.assertRaises(expected_exception=PedanticTypeCheckException) as exc:
bar.validate_types()
expected = 'In dataclass "Foo" in field "a": Type hint is incorrect: Argument 6.6 of type <class \'float\'> ' \
'does not match expected type <class \'int\'>.'
self.assertEqual(str(exc.exception), expected)
def test_frozen_dataclass_above_dataclass(self):
# This is the same behavior like
# >>> @dataclass(frozen=True)
# ... @dataclass
# ... class C:
# ... foo: int
@frozen_dataclass
@dataclass
class A:
foo: int
with self.assertRaises(expected_exception=TypeError):
A()
with self.assertRaises(expected_exception=FrozenInstanceError):
A(foo=3)
def test_frozen_dataclass_below_dataclass(self):
@dataclass
@frozen_dataclass
class A:
foo: int
with self.assertRaises(expected_exception=TypeError):
A()
a = A(foo=3)
with self.assertRaises(expected_exception=FrozenInstanceError):
a.foo = 4
b = a.copy_with(foo=4)
self.assertEqual(4, b.foo)
def test_frozen_typesafe_dataclass_with_post_init(self):
b = 3
@frozen_dataclass(type_safe=True)
class A:
foo: int
def __post_init__(self) -> None:
nonlocal b
b = 33
with self.assertRaises(expected_exception=PedanticTypeCheckException) as exc:
A(foo=42.7)
self.assertEqual(
'In dataclass "A" in field "foo": Type hint is incorrect: Argument 42.7 of type'
' <class \'float\'> does not match expected type <class \'int\'>.',
str(exc.exception)
)
# we check that the __post_init__ method is executed
self.assertEqual(33, b)
def test_frozen_typesafe_dataclass_without_post_init(self):
@frozen_dataclass(type_safe=True)
class A:
foo: int
with self.assertRaises(expected_exception=PedanticTypeCheckException) as exc:
A(foo=42.7)
self.assertEqual(
'In dataclass "A" in field "foo": Type hint is incorrect: Argument 42.7 of type'
' <class \'float\'> does not match expected type <class \'int\'>.',
str(exc.exception)
)
def test_frozen_dataclass_with_empty_braces(self):
@frozen_dataclass()
class A:
foo: int
a = A(foo=42)
self.assertEqual(42, a.foo)
def test_frozen_dataclass_no_braces(self):
@frozen_dataclass
class A:
foo: int
a = A(foo=42)
self.assertEqual(42, a.foo)
def test_frozen_dataclass_order(self):
@frozen_dataclass(order=True)
class A:
foo: int
bar: int
a = A(foo=42, bar=43)
b = A(foo=42, bar=42)
c = A(foo=41, bar=44)
d = A(foo=44, bar=0)
self.assertLess(b, a)
self.assertLess(c, b)
self.assertLess(a, d)
def test_frozen_type_safe_dataclass_copy_with_check(self):
@frozen_type_safe_dataclass
class A:
foo: int
bar: bool
a = A(foo=42, bar=False)
with self.assertRaises(expected_exception=PedanticTypeCheckException):
a.copy_with(foo=1.1)
with self.assertRaises(expected_exception=PedanticTypeCheckException):
a.copy_with(bar=11)
a.copy_with(foo=11, bar=True)
def test_copy_with_is_shallow(self):
a = A(foo=[1, 2], bar={'hello': 'world'}, values=(B(v={4, 5}), B(v={6})))
shallow = a.copy_with()
# manipulation
shallow.bar['hello'] = 'pedantic'
shallow.foo.append(3)
self.assertEqual([1, 2, 3], a.foo)
self.assertEqual([1, 2, 3], shallow.foo)
self.assertEqual('pedantic', a.bar['hello'])
self.assertEqual('pedantic', shallow.bar['hello'])
def test_copy_with_and_deep_copy_with(self):
a = A(foo=[1, 2], bar={'hello': 'world'}, values=(B(v={4, 5}), B(v={6})))
deep = a.deep_copy_with()
# manipulation
deep.bar['hello'] = 'pedantic'
deep.foo.append(3)
self.assertEqual([1, 2], a.foo)
self.assertEqual([1, 2, 3], deep.foo)
self.assertEqual('world', a.bar['hello'])
self.assertEqual('pedantic', deep.bar['hello'])
def test_frozen_dataclass_inheritance_override_post_init(self):
i = 1
@frozen_type_safe_dataclass
class A:
bar: int
def __post_init__(self):
nonlocal i
i += 1
print('hello a')
@frozen_type_safe_dataclass
class B(A):
foo: int
def __post_init__(self):
nonlocal i
i *= 10
print('hello b')
A(bar=3)
self.assertEqual(2, i)
b = B(bar=3, foo=42)
self.assertEqual(20, i) # post init of A was not called
self.assertEqual(3, b.bar)
self.assertEqual(42, b.foo)
a = b.copy_with()
self.assertEqual(b, a)
self.assertEqual(200, i)
def test_frozen_dataclass_inheritance_not_override_post_init(self):
i = 1
@frozen_type_safe_dataclass
class A:
bar: int
def __post_init__(self):
nonlocal i
i += 1
print('hello a')
@frozen_type_safe_dataclass
class B(A):
foo: int
A(bar=3)
self.assertEqual(2, i)
b = B(bar=3, foo=42)
self.assertEqual(3, i) # post init of A was called
self.assertEqual(3, b.bar)
self.assertEqual(42, b.foo)
a = b.copy_with()
self.assertEqual(b, a)
self.assertEqual(4, i)
def test_type_safe_frozen_dataclass_with_awaitable(self):
@frozen_type_safe_dataclass
class A:
f: Callable[..., Awaitable[int]]
async def _cb() -> int:
return 42
async def _cb_2() -> str:
return '42'
A(f=_cb)
with self.assertRaises(expected_exception=PedanticTypeCheckException):
A(f=_cb_2)
def test_type_safe_frozen_dataclass_with_forward_ref(self):
T = TypeVar('T')
class State(Generic[T], ABC):
pass
class StateMachine(Generic[T], ABC):
pass
@frozen_type_safe_dataclass
class StateChangeResult:
new_state: Optional['MachineState']
class MachineState(State['MachineStateMachine']):
pass
class OfflineMachineState(MachineState):
pass
class OnlineMachineState:
pass
class MachineStateMachine(StateMachine[MachineState]):
pass
s = StateChangeResult(new_state=OfflineMachineState())
with self.assertRaises(expected_exception=PedanticTypeCheckException):
StateChangeResult(new_state=OnlineMachineState())
s.validate_types()
def test_forward_ref_to_itself(self):
""" Regression test for https://github.com/LostInDarkMath/pedantic-python-decorators/issues/72 """
@frozen_type_safe_dataclass
class Comment:
replies: List['Comment']
comment = Comment(replies=[Comment(replies=[])])
comment.copy_with(replies=[Comment(replies=[])])
comment.validate_types()
def test_forward_ref_to_itself_while_class_not_in_scope(self):
""" Regression test for https://github.com/LostInDarkMath/pedantic-python-decorators/issues/72 """
def _scope():
@frozen_type_safe_dataclass
class Comment:
replies: List['Comment']
def _make(replies=None):
return Comment(replies=replies or [])
return _make
make = _scope()
comment = make(replies=[make(replies=[])])
comment.copy_with(replies=[make(replies=[])])
comment.validate_types()
def test_slots_work_with_equals(self):
@frozen_dataclass(slots=True)
class Foo:
a: int
o = Foo(a=0)
assert o == o.copy_with()
Classes
class A (*, foo: List[int], bar: Dict[str, str], values: Tuple[B, B])
-
A(*, foo: List[int], bar: Dict[str, str], values: Tuple[pedantic.tests.test_frozen_dataclass.B, pedantic.tests.test_frozen_dataclass.B])
Expand source code
@frozen_type_safe_dataclass class A: foo: List[int] bar: Dict[str, str] values: Tuple[B, B]
Class variables
var bar : Dict[str, str]
var foo : List[int]
var values : Tuple[B, B]
Methods
def copy_with(self, **kwargs: Any) ‑> ~T
-
Creates a new immutable instance that by copying all fields of this instance replaced by the new values. Keep in mind that this is a shallow copy!
Expand source code
def copy_with(self, **kwargs: Any) -> T: """ Creates a new immutable instance that by copying all fields of this instance replaced by the new values. Keep in mind that this is a shallow copy! """ return replace(self, **kwargs)
def deep_copy_with(self, **kwargs: Any) ‑> ~T
-
Creates a new immutable instance that by deep copying all fields of this instance replaced by the new values.
Expand source code
def deep_copy_with(self, **kwargs: Any) -> T: """ Creates a new immutable instance that by deep copying all fields of this instance replaced by the new values. """ current_values = {field.name: deepcopy(getattr(self, field.name)) for field in fields(self)} return new_class(**{**current_values, **kwargs})
def validate_types(self) ‑> None
-
Checks that all instance variable have the correct type. Raises a [PedanticTypeCheckException] if at least one type is incorrect.
Expand source code
def validate_types(self, *, _context: Dict[str, Type] = None) -> None: """ Checks that all instance variable have the correct type. Raises a [PedanticTypeCheckException] if at least one type is incorrect. """ props = fields(new_class) if _context is None: # method was called by user _context = get_context(depth=2) _context = {**_context, **self.__init__.__globals__, self.__class__.__name__: self.__class__} for field in props: assert_value_matches_type( value=getattr(self, field.name), type_=field.type, err=f'In dataclass "{cls_.__name__}" in field "{field.name}": ', type_vars={}, context=_context, )
class B (*, v: Set[int])
-
B(*, v: Set[int])
Expand source code
@frozen_type_safe_dataclass class B: v: Set[int]
Class variables
var v : Set[int]
Methods
def copy_with(self, **kwargs: Any) ‑> ~T
-
Creates a new immutable instance that by copying all fields of this instance replaced by the new values. Keep in mind that this is a shallow copy!
Expand source code
def copy_with(self, **kwargs: Any) -> T: """ Creates a new immutable instance that by copying all fields of this instance replaced by the new values. Keep in mind that this is a shallow copy! """ return replace(self, **kwargs)
def deep_copy_with(self, **kwargs: Any) ‑> ~T
-
Creates a new immutable instance that by deep copying all fields of this instance replaced by the new values.
Expand source code
def deep_copy_with(self, **kwargs: Any) -> T: """ Creates a new immutable instance that by deep copying all fields of this instance replaced by the new values. """ current_values = {field.name: deepcopy(getattr(self, field.name)) for field in fields(self)} return new_class(**{**current_values, **kwargs})
def validate_types(self) ‑> None
-
Checks that all instance variable have the correct type. Raises a [PedanticTypeCheckException] if at least one type is incorrect.
Expand source code
def validate_types(self, *, _context: Dict[str, Type] = None) -> None: """ Checks that all instance variable have the correct type. Raises a [PedanticTypeCheckException] if at least one type is incorrect. """ props = fields(new_class) if _context is None: # method was called by user _context = get_context(depth=2) _context = {**_context, **self.__init__.__globals__, self.__class__.__name__: self.__class__} for field in props: assert_value_matches_type( value=getattr(self, field.name), type_=field.type, err=f'In dataclass "{cls_.__name__}" in field "{field.name}": ', type_vars={}, context=_context, )
class Foo (*, a: int, b: str, c: bool)
-
Foo(*, a: int, b: str, c: bool)
Expand source code
@frozen_dataclass class Foo: a: int b: str c: bool
Class variables
var a : int
var b : str
var c : bool
Methods
def copy_with(self, **kwargs: Any) ‑> ~T
-
Creates a new immutable instance that by copying all fields of this instance replaced by the new values. Keep in mind that this is a shallow copy!
Expand source code
def copy_with(self, **kwargs: Any) -> T: """ Creates a new immutable instance that by copying all fields of this instance replaced by the new values. Keep in mind that this is a shallow copy! """ return replace(self, **kwargs)
def deep_copy_with(self, **kwargs: Any) ‑> ~T
-
Creates a new immutable instance that by deep copying all fields of this instance replaced by the new values.
Expand source code
def deep_copy_with(self, **kwargs: Any) -> T: """ Creates a new immutable instance that by deep copying all fields of this instance replaced by the new values. """ current_values = {field.name: deepcopy(getattr(self, field.name)) for field in fields(self)} return new_class(**{**current_values, **kwargs})
def validate_types(self) ‑> None
-
Checks that all instance variable have the correct type. Raises a [PedanticTypeCheckException] if at least one type is incorrect.
Expand source code
def validate_types(self, *, _context: Dict[str, Type] = None) -> None: """ Checks that all instance variable have the correct type. Raises a [PedanticTypeCheckException] if at least one type is incorrect. """ props = fields(new_class) if _context is None: # method was called by user _context = get_context(depth=2) _context = {**_context, **self.__init__.__globals__, self.__class__.__name__: self.__class__} for field in props: assert_value_matches_type( value=getattr(self, field.name), type_=field.type, err=f'In dataclass "{cls_.__name__}" in field "{field.name}": ', type_vars={}, context=_context, )
class TestFrozenDataclass (methodName='runTest')
-
A class whose instances are single test cases.
By default, the test code itself should be placed in a method named 'runTest'.
If the fixture may be used for many test cases, create as many test methods as are needed. When instantiating such a TestCase subclass, specify in the constructor arguments the name of the test method that the instance is to execute.
Test authors should subclass TestCase for their own tests. Construction and deconstruction of the test's environment ('fixture') can be implemented by overriding the 'setUp' and 'tearDown' methods respectively.
If it is necessary to override the init method, the base class init method must always be called. It is important that subclasses should not change the signature of their init method, since instances of the classes are instantiated automatically by parts of the framework in order to be run.
When subclassing TestCase, you can set these attributes: * failureException: determines which exception will be raised when the instance's assertion methods fail; test methods raising this exception will be deemed to have 'failed' rather than 'errored'. * longMessage: determines whether long messages (including repr of objects used in assert methods) will be printed on failure in addition to any explicit message passed. * maxDiff: sets the maximum length of a diff in failure messages by assert methods using difflib. It is looked up as an instance attribute so can be configured by individual tests if required.
Create an instance of the class that will use the named test method when executed. Raises a ValueError if the instance does not have a method with the specified name.
Expand source code
class TestFrozenDataclass(unittest.TestCase): def test_equals_and_hash(self): a = Foo(a=6, b='hi', c=True) b = Foo(a=6, b='hi', c=True) c = Foo(a=7, b='hi', c=True) self.assertEqual(a, b) self.assertEqual(hash(a), hash(b)) self.assertNotEqual(a, c) self.assertNotEqual(hash(a), hash(c)) def test_copy_with(self): foo = Foo(a=6, b='hi', c=True) copy_1 = foo.copy_with() self.assertEqual(foo, copy_1) copy_2 = foo.copy_with(a=42) self.assertNotEqual(foo, copy_2) self.assertEqual(42, copy_2.a) self.assertEqual(foo.b, copy_2.b) self.assertEqual(foo.c, copy_2.c) copy_3 = foo.copy_with(b='Hello') self.assertNotEqual(foo, copy_3) self.assertEqual(foo.a, copy_3.a) self.assertEqual('Hello', copy_3.b) self.assertEqual(foo.c, copy_3.c) copy_4 = foo.copy_with(c=False) self.assertNotEqual(foo, copy_4) self.assertEqual(foo.a, copy_4.a) self.assertEqual(foo.b, copy_4.b) self.assertEqual(False, copy_4.c) copy_5 = foo.copy_with(a=676676, b='new', c=False) self.assertNotEqual(foo, copy_5) self.assertEqual(676676, copy_5.a) self.assertEqual('new', copy_5.b) self.assertEqual(False, copy_5.c) def test_validate_types(self): foo = Foo(a=6, b='hi', c=True) foo.validate_types() bar = Foo(a=6.6, b='hi', c=True) with self.assertRaises(expected_exception=PedanticTypeCheckException) as exc: bar.validate_types() expected = 'In dataclass "Foo" in field "a": Type hint is incorrect: Argument 6.6 of type <class \'float\'> ' \ 'does not match expected type <class \'int\'>.' self.assertEqual(str(exc.exception), expected) def test_frozen_dataclass_above_dataclass(self): # This is the same behavior like # >>> @dataclass(frozen=True) # ... @dataclass # ... class C: # ... foo: int @frozen_dataclass @dataclass class A: foo: int with self.assertRaises(expected_exception=TypeError): A() with self.assertRaises(expected_exception=FrozenInstanceError): A(foo=3) def test_frozen_dataclass_below_dataclass(self): @dataclass @frozen_dataclass class A: foo: int with self.assertRaises(expected_exception=TypeError): A() a = A(foo=3) with self.assertRaises(expected_exception=FrozenInstanceError): a.foo = 4 b = a.copy_with(foo=4) self.assertEqual(4, b.foo) def test_frozen_typesafe_dataclass_with_post_init(self): b = 3 @frozen_dataclass(type_safe=True) class A: foo: int def __post_init__(self) -> None: nonlocal b b = 33 with self.assertRaises(expected_exception=PedanticTypeCheckException) as exc: A(foo=42.7) self.assertEqual( 'In dataclass "A" in field "foo": Type hint is incorrect: Argument 42.7 of type' ' <class \'float\'> does not match expected type <class \'int\'>.', str(exc.exception) ) # we check that the __post_init__ method is executed self.assertEqual(33, b) def test_frozen_typesafe_dataclass_without_post_init(self): @frozen_dataclass(type_safe=True) class A: foo: int with self.assertRaises(expected_exception=PedanticTypeCheckException) as exc: A(foo=42.7) self.assertEqual( 'In dataclass "A" in field "foo": Type hint is incorrect: Argument 42.7 of type' ' <class \'float\'> does not match expected type <class \'int\'>.', str(exc.exception) ) def test_frozen_dataclass_with_empty_braces(self): @frozen_dataclass() class A: foo: int a = A(foo=42) self.assertEqual(42, a.foo) def test_frozen_dataclass_no_braces(self): @frozen_dataclass class A: foo: int a = A(foo=42) self.assertEqual(42, a.foo) def test_frozen_dataclass_order(self): @frozen_dataclass(order=True) class A: foo: int bar: int a = A(foo=42, bar=43) b = A(foo=42, bar=42) c = A(foo=41, bar=44) d = A(foo=44, bar=0) self.assertLess(b, a) self.assertLess(c, b) self.assertLess(a, d) def test_frozen_type_safe_dataclass_copy_with_check(self): @frozen_type_safe_dataclass class A: foo: int bar: bool a = A(foo=42, bar=False) with self.assertRaises(expected_exception=PedanticTypeCheckException): a.copy_with(foo=1.1) with self.assertRaises(expected_exception=PedanticTypeCheckException): a.copy_with(bar=11) a.copy_with(foo=11, bar=True) def test_copy_with_is_shallow(self): a = A(foo=[1, 2], bar={'hello': 'world'}, values=(B(v={4, 5}), B(v={6}))) shallow = a.copy_with() # manipulation shallow.bar['hello'] = 'pedantic' shallow.foo.append(3) self.assertEqual([1, 2, 3], a.foo) self.assertEqual([1, 2, 3], shallow.foo) self.assertEqual('pedantic', a.bar['hello']) self.assertEqual('pedantic', shallow.bar['hello']) def test_copy_with_and_deep_copy_with(self): a = A(foo=[1, 2], bar={'hello': 'world'}, values=(B(v={4, 5}), B(v={6}))) deep = a.deep_copy_with() # manipulation deep.bar['hello'] = 'pedantic' deep.foo.append(3) self.assertEqual([1, 2], a.foo) self.assertEqual([1, 2, 3], deep.foo) self.assertEqual('world', a.bar['hello']) self.assertEqual('pedantic', deep.bar['hello']) def test_frozen_dataclass_inheritance_override_post_init(self): i = 1 @frozen_type_safe_dataclass class A: bar: int def __post_init__(self): nonlocal i i += 1 print('hello a') @frozen_type_safe_dataclass class B(A): foo: int def __post_init__(self): nonlocal i i *= 10 print('hello b') A(bar=3) self.assertEqual(2, i) b = B(bar=3, foo=42) self.assertEqual(20, i) # post init of A was not called self.assertEqual(3, b.bar) self.assertEqual(42, b.foo) a = b.copy_with() self.assertEqual(b, a) self.assertEqual(200, i) def test_frozen_dataclass_inheritance_not_override_post_init(self): i = 1 @frozen_type_safe_dataclass class A: bar: int def __post_init__(self): nonlocal i i += 1 print('hello a') @frozen_type_safe_dataclass class B(A): foo: int A(bar=3) self.assertEqual(2, i) b = B(bar=3, foo=42) self.assertEqual(3, i) # post init of A was called self.assertEqual(3, b.bar) self.assertEqual(42, b.foo) a = b.copy_with() self.assertEqual(b, a) self.assertEqual(4, i) def test_type_safe_frozen_dataclass_with_awaitable(self): @frozen_type_safe_dataclass class A: f: Callable[..., Awaitable[int]] async def _cb() -> int: return 42 async def _cb_2() -> str: return '42' A(f=_cb) with self.assertRaises(expected_exception=PedanticTypeCheckException): A(f=_cb_2) def test_type_safe_frozen_dataclass_with_forward_ref(self): T = TypeVar('T') class State(Generic[T], ABC): pass class StateMachine(Generic[T], ABC): pass @frozen_type_safe_dataclass class StateChangeResult: new_state: Optional['MachineState'] class MachineState(State['MachineStateMachine']): pass class OfflineMachineState(MachineState): pass class OnlineMachineState: pass class MachineStateMachine(StateMachine[MachineState]): pass s = StateChangeResult(new_state=OfflineMachineState()) with self.assertRaises(expected_exception=PedanticTypeCheckException): StateChangeResult(new_state=OnlineMachineState()) s.validate_types() def test_forward_ref_to_itself(self): """ Regression test for https://github.com/LostInDarkMath/pedantic-python-decorators/issues/72 """ @frozen_type_safe_dataclass class Comment: replies: List['Comment'] comment = Comment(replies=[Comment(replies=[])]) comment.copy_with(replies=[Comment(replies=[])]) comment.validate_types() def test_forward_ref_to_itself_while_class_not_in_scope(self): """ Regression test for https://github.com/LostInDarkMath/pedantic-python-decorators/issues/72 """ def _scope(): @frozen_type_safe_dataclass class Comment: replies: List['Comment'] def _make(replies=None): return Comment(replies=replies or []) return _make make = _scope() comment = make(replies=[make(replies=[])]) comment.copy_with(replies=[make(replies=[])]) comment.validate_types() def test_slots_work_with_equals(self): @frozen_dataclass(slots=True) class Foo: a: int o = Foo(a=0) assert o == o.copy_with()
Ancestors
- unittest.case.TestCase
Methods
def test_copy_with(self)
-
Expand source code
def test_copy_with(self): foo = Foo(a=6, b='hi', c=True) copy_1 = foo.copy_with() self.assertEqual(foo, copy_1) copy_2 = foo.copy_with(a=42) self.assertNotEqual(foo, copy_2) self.assertEqual(42, copy_2.a) self.assertEqual(foo.b, copy_2.b) self.assertEqual(foo.c, copy_2.c) copy_3 = foo.copy_with(b='Hello') self.assertNotEqual(foo, copy_3) self.assertEqual(foo.a, copy_3.a) self.assertEqual('Hello', copy_3.b) self.assertEqual(foo.c, copy_3.c) copy_4 = foo.copy_with(c=False) self.assertNotEqual(foo, copy_4) self.assertEqual(foo.a, copy_4.a) self.assertEqual(foo.b, copy_4.b) self.assertEqual(False, copy_4.c) copy_5 = foo.copy_with(a=676676, b='new', c=False) self.assertNotEqual(foo, copy_5) self.assertEqual(676676, copy_5.a) self.assertEqual('new', copy_5.b) self.assertEqual(False, copy_5.c)
def test_copy_with_and_deep_copy_with(self)
-
Expand source code
def test_copy_with_and_deep_copy_with(self): a = A(foo=[1, 2], bar={'hello': 'world'}, values=(B(v={4, 5}), B(v={6}))) deep = a.deep_copy_with() # manipulation deep.bar['hello'] = 'pedantic' deep.foo.append(3) self.assertEqual([1, 2], a.foo) self.assertEqual([1, 2, 3], deep.foo) self.assertEqual('world', a.bar['hello']) self.assertEqual('pedantic', deep.bar['hello'])
def test_copy_with_is_shallow(self)
-
Expand source code
def test_copy_with_is_shallow(self): a = A(foo=[1, 2], bar={'hello': 'world'}, values=(B(v={4, 5}), B(v={6}))) shallow = a.copy_with() # manipulation shallow.bar['hello'] = 'pedantic' shallow.foo.append(3) self.assertEqual([1, 2, 3], a.foo) self.assertEqual([1, 2, 3], shallow.foo) self.assertEqual('pedantic', a.bar['hello']) self.assertEqual('pedantic', shallow.bar['hello'])
def test_equals_and_hash(self)
-
Expand source code
def test_equals_and_hash(self): a = Foo(a=6, b='hi', c=True) b = Foo(a=6, b='hi', c=True) c = Foo(a=7, b='hi', c=True) self.assertEqual(a, b) self.assertEqual(hash(a), hash(b)) self.assertNotEqual(a, c) self.assertNotEqual(hash(a), hash(c))
def test_forward_ref_to_itself(self)
-
Regression test for https://github.com/LostInDarkMath/pedantic-python-decorators/issues/72
Expand source code
def test_forward_ref_to_itself(self): """ Regression test for https://github.com/LostInDarkMath/pedantic-python-decorators/issues/72 """ @frozen_type_safe_dataclass class Comment: replies: List['Comment'] comment = Comment(replies=[Comment(replies=[])]) comment.copy_with(replies=[Comment(replies=[])]) comment.validate_types()
def test_forward_ref_to_itself_while_class_not_in_scope(self)
-
Regression test for https://github.com/LostInDarkMath/pedantic-python-decorators/issues/72
Expand source code
def test_forward_ref_to_itself_while_class_not_in_scope(self): """ Regression test for https://github.com/LostInDarkMath/pedantic-python-decorators/issues/72 """ def _scope(): @frozen_type_safe_dataclass class Comment: replies: List['Comment'] def _make(replies=None): return Comment(replies=replies or []) return _make make = _scope() comment = make(replies=[make(replies=[])]) comment.copy_with(replies=[make(replies=[])]) comment.validate_types()
def test_frozen_dataclass_above_dataclass(self)
-
Expand source code
def test_frozen_dataclass_above_dataclass(self): # This is the same behavior like # >>> @dataclass(frozen=True) # ... @dataclass # ... class C: # ... foo: int @frozen_dataclass @dataclass class A: foo: int with self.assertRaises(expected_exception=TypeError): A() with self.assertRaises(expected_exception=FrozenInstanceError): A(foo=3)
def test_frozen_dataclass_below_dataclass(self)
-
Expand source code
def test_frozen_dataclass_below_dataclass(self): @dataclass @frozen_dataclass class A: foo: int with self.assertRaises(expected_exception=TypeError): A() a = A(foo=3) with self.assertRaises(expected_exception=FrozenInstanceError): a.foo = 4 b = a.copy_with(foo=4) self.assertEqual(4, b.foo)
def test_frozen_dataclass_inheritance_not_override_post_init(self)
-
Expand source code
def test_frozen_dataclass_inheritance_not_override_post_init(self): i = 1 @frozen_type_safe_dataclass class A: bar: int def __post_init__(self): nonlocal i i += 1 print('hello a') @frozen_type_safe_dataclass class B(A): foo: int A(bar=3) self.assertEqual(2, i) b = B(bar=3, foo=42) self.assertEqual(3, i) # post init of A was called self.assertEqual(3, b.bar) self.assertEqual(42, b.foo) a = b.copy_with() self.assertEqual(b, a) self.assertEqual(4, i)
def test_frozen_dataclass_inheritance_override_post_init(self)
-
Expand source code
def test_frozen_dataclass_inheritance_override_post_init(self): i = 1 @frozen_type_safe_dataclass class A: bar: int def __post_init__(self): nonlocal i i += 1 print('hello a') @frozen_type_safe_dataclass class B(A): foo: int def __post_init__(self): nonlocal i i *= 10 print('hello b') A(bar=3) self.assertEqual(2, i) b = B(bar=3, foo=42) self.assertEqual(20, i) # post init of A was not called self.assertEqual(3, b.bar) self.assertEqual(42, b.foo) a = b.copy_with() self.assertEqual(b, a) self.assertEqual(200, i)
def test_frozen_dataclass_no_braces(self)
-
Expand source code
def test_frozen_dataclass_no_braces(self): @frozen_dataclass class A: foo: int a = A(foo=42) self.assertEqual(42, a.foo)
def test_frozen_dataclass_order(self)
-
Expand source code
def test_frozen_dataclass_order(self): @frozen_dataclass(order=True) class A: foo: int bar: int a = A(foo=42, bar=43) b = A(foo=42, bar=42) c = A(foo=41, bar=44) d = A(foo=44, bar=0) self.assertLess(b, a) self.assertLess(c, b) self.assertLess(a, d)
def test_frozen_dataclass_with_empty_braces(self)
-
Expand source code
def test_frozen_dataclass_with_empty_braces(self): @frozen_dataclass() class A: foo: int a = A(foo=42) self.assertEqual(42, a.foo)
def test_frozen_type_safe_dataclass_copy_with_check(self)
-
Expand source code
def test_frozen_type_safe_dataclass_copy_with_check(self): @frozen_type_safe_dataclass class A: foo: int bar: bool a = A(foo=42, bar=False) with self.assertRaises(expected_exception=PedanticTypeCheckException): a.copy_with(foo=1.1) with self.assertRaises(expected_exception=PedanticTypeCheckException): a.copy_with(bar=11) a.copy_with(foo=11, bar=True)
def test_frozen_typesafe_dataclass_with_post_init(self)
-
Expand source code
def test_frozen_typesafe_dataclass_with_post_init(self): b = 3 @frozen_dataclass(type_safe=True) class A: foo: int def __post_init__(self) -> None: nonlocal b b = 33 with self.assertRaises(expected_exception=PedanticTypeCheckException) as exc: A(foo=42.7) self.assertEqual( 'In dataclass "A" in field "foo": Type hint is incorrect: Argument 42.7 of type' ' <class \'float\'> does not match expected type <class \'int\'>.', str(exc.exception) ) # we check that the __post_init__ method is executed self.assertEqual(33, b)
def test_frozen_typesafe_dataclass_without_post_init(self)
-
Expand source code
def test_frozen_typesafe_dataclass_without_post_init(self): @frozen_dataclass(type_safe=True) class A: foo: int with self.assertRaises(expected_exception=PedanticTypeCheckException) as exc: A(foo=42.7) self.assertEqual( 'In dataclass "A" in field "foo": Type hint is incorrect: Argument 42.7 of type' ' <class \'float\'> does not match expected type <class \'int\'>.', str(exc.exception) )
def test_slots_work_with_equals(self)
-
Expand source code
def test_slots_work_with_equals(self): @frozen_dataclass(slots=True) class Foo: a: int o = Foo(a=0) assert o == o.copy_with()
def test_type_safe_frozen_dataclass_with_awaitable(self)
-
Expand source code
def test_type_safe_frozen_dataclass_with_awaitable(self): @frozen_type_safe_dataclass class A: f: Callable[..., Awaitable[int]] async def _cb() -> int: return 42 async def _cb_2() -> str: return '42' A(f=_cb) with self.assertRaises(expected_exception=PedanticTypeCheckException): A(f=_cb_2)
def test_type_safe_frozen_dataclass_with_forward_ref(self)
-
Expand source code
def test_type_safe_frozen_dataclass_with_forward_ref(self): T = TypeVar('T') class State(Generic[T], ABC): pass class StateMachine(Generic[T], ABC): pass @frozen_type_safe_dataclass class StateChangeResult: new_state: Optional['MachineState'] class MachineState(State['MachineStateMachine']): pass class OfflineMachineState(MachineState): pass class OnlineMachineState: pass class MachineStateMachine(StateMachine[MachineState]): pass s = StateChangeResult(new_state=OfflineMachineState()) with self.assertRaises(expected_exception=PedanticTypeCheckException): StateChangeResult(new_state=OnlineMachineState()) s.validate_types()
def test_validate_types(self)
-
Expand source code
def test_validate_types(self): foo = Foo(a=6, b='hi', c=True) foo.validate_types() bar = Foo(a=6.6, b='hi', c=True) with self.assertRaises(expected_exception=PedanticTypeCheckException) as exc: bar.validate_types() expected = 'In dataclass "Foo" in field "a": Type hint is incorrect: Argument 6.6 of type <class \'float\'> ' \ 'does not match expected type <class \'int\'>.' self.assertEqual(str(exc.exception), expected)