Repository URL to install this package:
# Deliberately use "from dataclasses import *". Every name in __all__
# is tested, so they all must be present. This is a way to catch
# missing ones.
from dataclasses import *
import pickle
import inspect
import builtins
import unittest
from unittest.mock import Mock
from typing import ClassVar, Any, List, Union, Tuple, Dict, Generic, TypeVar, Optional
from typing import get_type_hints
from collections import deque, OrderedDict, namedtuple
from functools import total_ordering
import typing # Needed for the string "typing.ClassVar[int]" to work as an annotation.
import dataclasses # Needed for the string "dataclasses.InitVar[int]" to work as an annotation.
# Just any custom exception we can catch.
class CustomError(Exception): pass
class TestCase(unittest.TestCase):
def test_no_fields(self):
@dataclass
class C:
pass
o = C()
self.assertEqual(len(fields(C)), 0)
def test_no_fields_but_member_variable(self):
@dataclass
class C:
i = 0
o = C()
self.assertEqual(len(fields(C)), 0)
def test_one_field_no_default(self):
@dataclass
class C:
x: int
o = C(42)
self.assertEqual(o.x, 42)
def test_field_default_default_factory_error(self):
msg = "cannot specify both default and default_factory"
with self.assertRaisesRegex(ValueError, msg):
@dataclass
class C:
x: int = field(default=1, default_factory=int)
def test_field_repr(self):
int_field = field(default=1, init=True, repr=False)
int_field.name = "id"
repr_output = repr(int_field)
expected_output = "Field(name='id',type=None," \
f"default=1,default_factory={MISSING!r}," \
"init=True,repr=False,hash=None," \
"compare=True,metadata=mappingproxy({})," \
"_field_type=None)"
self.assertEqual(repr_output, expected_output)
def test_named_init_params(self):
@dataclass
class C:
x: int
o = C(x=32)
self.assertEqual(o.x, 32)
def test_two_fields_one_default(self):
@dataclass
class C:
x: int
y: int = 0
o = C(3)
self.assertEqual((o.x, o.y), (3, 0))
# Non-defaults following defaults.
with self.assertRaisesRegex(TypeError,
"non-default argument 'y' follows "
"default argument"):
@dataclass
class C:
x: int = 0
y: int
# A derived class adds a non-default field after a default one.
with self.assertRaisesRegex(TypeError,
"non-default argument 'y' follows "
"default argument"):
@dataclass
class B:
x: int = 0
@dataclass
class C(B):
y: int
# Override a base class field and add a default to
# a field which didn't use to have a default.
with self.assertRaisesRegex(TypeError,
"non-default argument 'y' follows "
"default argument"):
@dataclass
class B:
x: int
y: int
@dataclass
class C(B):
x: int = 0
def test_overwrite_hash(self):
# Test that declaring this class isn't an error. It should
# use the user-provided __hash__.
@dataclass(frozen=True)
class C:
x: int
def __hash__(self):
return 301
self.assertEqual(hash(C(100)), 301)
# Test that declaring this class isn't an error. It should
# use the generated __hash__.
@dataclass(frozen=True)
class C:
x: int
def __eq__(self, other):
return False
self.assertEqual(hash(C(100)), hash((100,)))
# But this one should generate an exception, because with
# unsafe_hash=True, it's an error to have a __hash__ defined.
with self.assertRaisesRegex(TypeError,
'Cannot overwrite attribute __hash__'):
@dataclass(unsafe_hash=True)
class C:
def __hash__(self):
pass
# Creating this class should not generate an exception,
# because even though __hash__ exists before @dataclass is
# called, (due to __eq__ being defined), since it's None
# that's okay.
@dataclass(unsafe_hash=True)
class C:
x: int
def __eq__(self):
pass
# The generated hash function works as we'd expect.
self.assertEqual(hash(C(10)), hash((10,)))
# Creating this class should generate an exception, because
# __hash__ exists and is not None, which it would be if it
# had been auto-generated due to __eq__ being defined.
with self.assertRaisesRegex(TypeError,
'Cannot overwrite attribute __hash__'):
@dataclass(unsafe_hash=True)
class C:
x: int
def __eq__(self):
pass
def __hash__(self):
pass
def test_overwrite_fields_in_derived_class(self):
# Note that x from C1 replaces x in Base, but the order remains
# the same as defined in Base.
@dataclass
class Base:
x: Any = 15.0
y: int = 0
@dataclass
class C1(Base):
z: int = 10
x: int = 15
o = Base()
self.assertEqual(repr(o), 'TestCase.test_overwrite_fields_in_derived_class.<locals>.Base(x=15.0, y=0)')
o = C1()
self.assertEqual(repr(o), 'TestCase.test_overwrite_fields_in_derived_class.<locals>.C1(x=15, y=0, z=10)')
o = C1(x=5)
self.assertEqual(repr(o), 'TestCase.test_overwrite_fields_in_derived_class.<locals>.C1(x=5, y=0, z=10)')
def test_field_named_self(self):
@dataclass
class C:
self: str
c=C('foo')
self.assertEqual(c.self, 'foo')
# Make sure the first parameter is not named 'self'.
sig = inspect.signature(C.__init__)
first = next(iter(sig.parameters))
self.assertNotEqual('self', first)
# But we do use 'self' if no field named self.
@dataclass
class C:
selfx: str
# Make sure the first parameter is named 'self'.
sig = inspect.signature(C.__init__)
first = next(iter(sig.parameters))
self.assertEqual('self', first)
def test_field_named_object(self):
@dataclass
class C:
object: str
c = C('foo')
self.assertEqual(c.object, 'foo')
def test_field_named_object_frozen(self):
@dataclass(frozen=True)
class C:
object: str
c = C('foo')
self.assertEqual(c.object, 'foo')
def test_field_named_like_builtin(self):
# Attribute names can shadow built-in names
# since code generation is used.
# Ensure that this is not happening.
exclusions = {'None', 'True', 'False'}
builtins_names = sorted(
b for b in builtins.__dict__.keys()
if not b.startswith('__') and b not in exclusions
)
attributes = [(name, str) for name in builtins_names]
C = make_dataclass('C', attributes)
c = C(*[name for name in builtins_names])
for name in builtins_names:
self.assertEqual(getattr(c, name), name)
def test_field_named_like_builtin_frozen(self):
# Attribute names can shadow built-in names
# since code generation is used.
# Ensure that this is not happening
# for frozen data classes.
exclusions = {'None', 'True', 'False'}
builtins_names = sorted(
b for b in builtins.__dict__.keys()
if not b.startswith('__') and b not in exclusions
)
attributes = [(name, str) for name in builtins_names]
C = make_dataclass('C', attributes, frozen=True)
c = C(*[name for name in builtins_names])
for name in builtins_names:
self.assertEqual(getattr(c, name), name)
def test_0_field_compare(self):
# Ensure that order=False is the default.
@dataclass
class C0:
pass
@dataclass(order=False)
class C1:
pass
for cls in [C0, C1]:
with self.subTest(cls=cls):
self.assertEqual(cls(), cls())
for idx, fn in enumerate([lambda a, b: a < b,
lambda a, b: a <= b,
lambda a, b: a > b,
lambda a, b: a >= b]):
with self.subTest(idx=idx):
with self.assertRaisesRegex(TypeError,
f"not supported between instances of '{cls.__name__}' and '{cls.__name__}'"):
fn(cls(), cls())
@dataclass(order=True)
class C:
pass
self.assertLessEqual(C(), C())
self.assertGreaterEqual(C(), C())
def test_1_field_compare(self):
# Ensure that order=False is the default.
@dataclass
class C0:
x: int
@dataclass(order=False)
class C1:
x: int
for cls in [C0, C1]:
with self.subTest(cls=cls):
self.assertEqual(cls(1), cls(1))
self.assertNotEqual(cls(0), cls(1))
for idx, fn in enumerate([lambda a, b: a < b,
lambda a, b: a <= b,
lambda a, b: a > b,
lambda a, b: a >= b]):
with self.subTest(idx=idx):
with self.assertRaisesRegex(TypeError,
f"not supported between instances of '{cls.__name__}' and '{cls.__name__}'"):
fn(cls(0), cls(0))
@dataclass(order=True)
class C:
x: int
self.assertLess(C(0), C(1))
self.assertLessEqual(C(0), C(1))
self.assertLessEqual(C(1), C(1))
self.assertGreater(C(1), C(0))
self.assertGreaterEqual(C(1), C(0))
self.assertGreaterEqual(C(1), C(1))
def test_simple_compare(self):
# Ensure that order=False is the default.
@dataclass
class C0:
x: int
y: int
@dataclass(order=False)
class C1:
x: int
y: int
for cls in [C0, C1]:
with self.subTest(cls=cls):
self.assertEqual(cls(0, 0), cls(0, 0))
self.assertEqual(cls(1, 2), cls(1, 2))
self.assertNotEqual(cls(1, 0), cls(0, 0))
self.assertNotEqual(cls(1, 0), cls(1, 1))
for idx, fn in enumerate([lambda a, b: a < b,
lambda a, b: a <= b,
Loading ...