blob: 037bf4c22142798b7ce0df66cc24a228691432bc [file] [log] [blame]
Eric V. Smith8e4560a2018-03-21 17:10:22 -04001# Deliberately use "from dataclasses import *". Every name in __all__
2# is tested, so they all must be present. This is a way to catch
3# missing ones.
4
5from dataclasses import *
Eric V. Smithf0db54a2017-12-04 16:58:55 -05006
7import pickle
8import inspect
Vadim Pushtaev4d12e4d2018-08-12 14:46:05 +03009import builtins
Eric V. Smithf0db54a2017-12-04 16:58:55 -050010import unittest
11from unittest.mock import Mock
Ivan Levkivskyi5a7092d2018-03-31 13:41:17 +010012from typing import ClassVar, Any, List, Union, Tuple, Dict, Generic, TypeVar, Optional
Eric V. Smithf0db54a2017-12-04 16:58:55 -050013from collections import deque, OrderedDict, namedtuple
Eric V. Smithea8fc522018-01-27 19:07:40 -050014from functools import total_ordering
Eric V. Smithf0db54a2017-12-04 16:58:55 -050015
Eric V. Smith2a7bacb2018-05-15 22:44:27 -040016import typing # Needed for the string "typing.ClassVar[int]" to work as an annotation.
17import dataclasses # Needed for the string "dataclasses.InitVar[int]" to work as an annotation.
18
Eric V. Smithf0db54a2017-12-04 16:58:55 -050019# Just any custom exception we can catch.
20class CustomError(Exception): pass
21
22class TestCase(unittest.TestCase):
23 def test_no_fields(self):
24 @dataclass
25 class C:
26 pass
27
28 o = C()
29 self.assertEqual(len(fields(C)), 0)
30
Eric V. Smith56970b82018-03-22 16:28:48 -040031 def test_no_fields_but_member_variable(self):
32 @dataclass
33 class C:
34 i = 0
35
36 o = C()
37 self.assertEqual(len(fields(C)), 0)
38
Eric V. Smithf0db54a2017-12-04 16:58:55 -050039 def test_one_field_no_default(self):
40 @dataclass
41 class C:
42 x: int
43
44 o = C(42)
45 self.assertEqual(o.x, 42)
46
47 def test_named_init_params(self):
48 @dataclass
49 class C:
50 x: int
51
52 o = C(x=32)
53 self.assertEqual(o.x, 32)
54
55 def test_two_fields_one_default(self):
56 @dataclass
57 class C:
58 x: int
59 y: int = 0
60
61 o = C(3)
62 self.assertEqual((o.x, o.y), (3, 0))
63
64 # Non-defaults following defaults.
65 with self.assertRaisesRegex(TypeError,
66 "non-default argument 'y' follows "
67 "default argument"):
68 @dataclass
69 class C:
70 x: int = 0
71 y: int
72
73 # A derived class adds a non-default field after a default one.
74 with self.assertRaisesRegex(TypeError,
75 "non-default argument 'y' follows "
76 "default argument"):
77 @dataclass
78 class B:
79 x: int = 0
80
81 @dataclass
82 class C(B):
83 y: int
84
85 # Override a base class field and add a default to
86 # a field which didn't use to have a default.
87 with self.assertRaisesRegex(TypeError,
88 "non-default argument 'y' follows "
89 "default argument"):
90 @dataclass
91 class B:
92 x: int
93 y: int
94
95 @dataclass
96 class C(B):
97 x: int = 0
98
Eric V. Smithdbf9cff2018-02-25 21:30:17 -050099 def test_overwrite_hash(self):
100 # Test that declaring this class isn't an error. It should
101 # use the user-provided __hash__.
Eric V. Smithea8fc522018-01-27 19:07:40 -0500102 @dataclass(frozen=True)
103 class C:
104 x: int
105 def __hash__(self):
Eric V. Smithdbf9cff2018-02-25 21:30:17 -0500106 return 301
107 self.assertEqual(hash(C(100)), 301)
Eric V. Smithf0db54a2017-12-04 16:58:55 -0500108
Eric V. Smithdbf9cff2018-02-25 21:30:17 -0500109 # Test that declaring this class isn't an error. It should
110 # use the generated __hash__.
Eric V. Smithea8fc522018-01-27 19:07:40 -0500111 @dataclass(frozen=True)
112 class C:
113 x: int
Eric V. Smithdbf9cff2018-02-25 21:30:17 -0500114 def __eq__(self, other):
115 return False
116 self.assertEqual(hash(C(100)), hash((100,)))
Eric V. Smithf0db54a2017-12-04 16:58:55 -0500117
Eric V. Smithdbf9cff2018-02-25 21:30:17 -0500118 # But this one should generate an exception, because with
119 # unsafe_hash=True, it's an error to have a __hash__ defined.
120 with self.assertRaisesRegex(TypeError,
121 'Cannot overwrite attribute __hash__'):
122 @dataclass(unsafe_hash=True)
123 class C:
124 def __hash__(self):
125 pass
126
127 # Creating this class should not generate an exception,
128 # because even though __hash__ exists before @dataclass is
129 # called, (due to __eq__ being defined), since it's None
130 # that's okay.
131 @dataclass(unsafe_hash=True)
Eric V. Smithf0db54a2017-12-04 16:58:55 -0500132 class C:
133 x: int
Eric V. Smithdbf9cff2018-02-25 21:30:17 -0500134 def __eq__(self):
135 pass
136 # The generated hash function works as we'd expect.
137 self.assertEqual(hash(C(10)), hash((10,)))
138
139 # Creating this class should generate an exception, because
Eric V. Smith2b75fc22018-03-25 20:37:33 -0400140 # __hash__ exists and is not None, which it would be if it
141 # had been auto-generated due to __eq__ being defined.
Eric V. Smithdbf9cff2018-02-25 21:30:17 -0500142 with self.assertRaisesRegex(TypeError,
143 'Cannot overwrite attribute __hash__'):
144 @dataclass(unsafe_hash=True)
145 class C:
146 x: int
147 def __eq__(self):
148 pass
149 def __hash__(self):
150 pass
151
Eric V. Smithf0db54a2017-12-04 16:58:55 -0500152 def test_overwrite_fields_in_derived_class(self):
153 # Note that x from C1 replaces x in Base, but the order remains
154 # the same as defined in Base.
155 @dataclass
156 class Base:
157 x: Any = 15.0
158 y: int = 0
159
160 @dataclass
161 class C1(Base):
162 z: int = 10
163 x: int = 15
164
165 o = Base()
166 self.assertEqual(repr(o), 'TestCase.test_overwrite_fields_in_derived_class.<locals>.Base(x=15.0, y=0)')
167
168 o = C1()
169 self.assertEqual(repr(o), 'TestCase.test_overwrite_fields_in_derived_class.<locals>.C1(x=15, y=0, z=10)')
170
171 o = C1(x=5)
172 self.assertEqual(repr(o), 'TestCase.test_overwrite_fields_in_derived_class.<locals>.C1(x=5, y=0, z=10)')
173
174 def test_field_named_self(self):
175 @dataclass
176 class C:
177 self: str
178 c=C('foo')
179 self.assertEqual(c.self, 'foo')
180
181 # Make sure the first parameter is not named 'self'.
182 sig = inspect.signature(C.__init__)
183 first = next(iter(sig.parameters))
184 self.assertNotEqual('self', first)
185
186 # But we do use 'self' if no field named self.
187 @dataclass
188 class C:
189 selfx: str
190
191 # Make sure the first parameter is named 'self'.
192 sig = inspect.signature(C.__init__)
193 first = next(iter(sig.parameters))
194 self.assertEqual('self', first)
195
Vadim Pushtaev4d12e4d2018-08-12 14:46:05 +0300196 def test_field_named_object(self):
197 @dataclass
198 class C:
199 object: str
200 c = C('foo')
201 self.assertEqual(c.object, 'foo')
202
203 def test_field_named_object_frozen(self):
204 @dataclass(frozen=True)
205 class C:
206 object: str
207 c = C('foo')
208 self.assertEqual(c.object, 'foo')
209
210 def test_field_named_like_builtin(self):
211 # Attribute names can shadow built-in names
212 # since code generation is used.
213 # Ensure that this is not happening.
214 exclusions = {'None', 'True', 'False'}
215 builtins_names = sorted(
216 b for b in builtins.__dict__.keys()
217 if not b.startswith('__') and b not in exclusions
218 )
219 attributes = [(name, str) for name in builtins_names]
220 C = make_dataclass('C', attributes)
221
222 c = C(*[name for name in builtins_names])
223
224 for name in builtins_names:
225 self.assertEqual(getattr(c, name), name)
226
227 def test_field_named_like_builtin_frozen(self):
228 # Attribute names can shadow built-in names
229 # since code generation is used.
230 # Ensure that this is not happening
231 # for frozen data classes.
232 exclusions = {'None', 'True', 'False'}
233 builtins_names = sorted(
234 b for b in builtins.__dict__.keys()
235 if not b.startswith('__') and b not in exclusions
236 )
237 attributes = [(name, str) for name in builtins_names]
238 C = make_dataclass('C', attributes, frozen=True)
239
240 c = C(*[name for name in builtins_names])
241
242 for name in builtins_names:
243 self.assertEqual(getattr(c, name), name)
244
Eric V. Smithf0db54a2017-12-04 16:58:55 -0500245 def test_0_field_compare(self):
246 # Ensure that order=False is the default.
247 @dataclass
248 class C0:
249 pass
250
251 @dataclass(order=False)
252 class C1:
253 pass
254
255 for cls in [C0, C1]:
256 with self.subTest(cls=cls):
257 self.assertEqual(cls(), cls())
258 for idx, fn in enumerate([lambda a, b: a < b,
259 lambda a, b: a <= b,
260 lambda a, b: a > b,
261 lambda a, b: a >= b]):
262 with self.subTest(idx=idx):
263 with self.assertRaisesRegex(TypeError,
264 f"not supported between instances of '{cls.__name__}' and '{cls.__name__}'"):
265 fn(cls(), cls())
266
267 @dataclass(order=True)
268 class C:
269 pass
270 self.assertLessEqual(C(), C())
271 self.assertGreaterEqual(C(), C())
272
273 def test_1_field_compare(self):
274 # Ensure that order=False is the default.
275 @dataclass
276 class C0:
277 x: int
278
279 @dataclass(order=False)
280 class C1:
281 x: int
282
283 for cls in [C0, C1]:
284 with self.subTest(cls=cls):
285 self.assertEqual(cls(1), cls(1))
286 self.assertNotEqual(cls(0), cls(1))
287 for idx, fn in enumerate([lambda a, b: a < b,
288 lambda a, b: a <= b,
289 lambda a, b: a > b,
290 lambda a, b: a >= b]):
291 with self.subTest(idx=idx):
292 with self.assertRaisesRegex(TypeError,
293 f"not supported between instances of '{cls.__name__}' and '{cls.__name__}'"):
294 fn(cls(0), cls(0))
295
296 @dataclass(order=True)
297 class C:
298 x: int
299 self.assertLess(C(0), C(1))
300 self.assertLessEqual(C(0), C(1))
301 self.assertLessEqual(C(1), C(1))
302 self.assertGreater(C(1), C(0))
303 self.assertGreaterEqual(C(1), C(0))
304 self.assertGreaterEqual(C(1), C(1))
305
306 def test_simple_compare(self):
307 # Ensure that order=False is the default.
308 @dataclass
309 class C0:
310 x: int
311 y: int
312
313 @dataclass(order=False)
314 class C1:
315 x: int
316 y: int
317
318 for cls in [C0, C1]:
319 with self.subTest(cls=cls):
320 self.assertEqual(cls(0, 0), cls(0, 0))
321 self.assertEqual(cls(1, 2), cls(1, 2))
322 self.assertNotEqual(cls(1, 0), cls(0, 0))
323 self.assertNotEqual(cls(1, 0), cls(1, 1))
324 for idx, fn in enumerate([lambda a, b: a < b,
325 lambda a, b: a <= b,
326 lambda a, b: a > b,
327 lambda a, b: a >= b]):
328 with self.subTest(idx=idx):
329 with self.assertRaisesRegex(TypeError,
330 f"not supported between instances of '{cls.__name__}' and '{cls.__name__}'"):
331 fn(cls(0, 0), cls(0, 0))
332
333 @dataclass(order=True)
334 class C:
335 x: int
336 y: int
337
338 for idx, fn in enumerate([lambda a, b: a == b,
339 lambda a, b: a <= b,
340 lambda a, b: a >= b]):
341 with self.subTest(idx=idx):
342 self.assertTrue(fn(C(0, 0), C(0, 0)))
343
344 for idx, fn in enumerate([lambda a, b: a < b,
345 lambda a, b: a <= b,
346 lambda a, b: a != b]):
347 with self.subTest(idx=idx):
348 self.assertTrue(fn(C(0, 0), C(0, 1)))
349 self.assertTrue(fn(C(0, 1), C(1, 0)))
350 self.assertTrue(fn(C(1, 0), C(1, 1)))
351
352 for idx, fn in enumerate([lambda a, b: a > b,
353 lambda a, b: a >= b,
354 lambda a, b: a != b]):
355 with self.subTest(idx=idx):
356 self.assertTrue(fn(C(0, 1), C(0, 0)))
357 self.assertTrue(fn(C(1, 0), C(0, 1)))
358 self.assertTrue(fn(C(1, 1), C(1, 0)))
359
360 def test_compare_subclasses(self):
361 # Comparisons fail for subclasses, even if no fields
362 # are added.
363 @dataclass
364 class B:
365 i: int
366
367 @dataclass
368 class C(B):
369 pass
370
371 for idx, (fn, expected) in enumerate([(lambda a, b: a == b, False),
372 (lambda a, b: a != b, True)]):
373 with self.subTest(idx=idx):
374 self.assertEqual(fn(B(0), C(0)), expected)
375
376 for idx, fn in enumerate([lambda a, b: a < b,
377 lambda a, b: a <= b,
378 lambda a, b: a > b,
379 lambda a, b: a >= b]):
380 with self.subTest(idx=idx):
381 with self.assertRaisesRegex(TypeError,
382 "not supported between instances of 'B' and 'C'"):
383 fn(B(0), C(0))
384
Eric V. Smithf0db54a2017-12-04 16:58:55 -0500385 def test_eq_order(self):
Eric V. Smithea8fc522018-01-27 19:07:40 -0500386 # Test combining eq and order.
Eric V. Smithf0db54a2017-12-04 16:58:55 -0500387 for (eq, order, result ) in [
388 (False, False, 'neither'),
389 (False, True, 'exception'),
390 (True, False, 'eq_only'),
391 (True, True, 'both'),
392 ]:
393 with self.subTest(eq=eq, order=order):
394 if result == 'exception':
395 with self.assertRaisesRegex(ValueError, 'eq must be true if order is true'):
396 @dataclass(eq=eq, order=order)
397 class C:
398 pass
399 else:
400 @dataclass(eq=eq, order=order)
401 class C:
402 pass
403
404 if result == 'neither':
405 self.assertNotIn('__eq__', C.__dict__)
Eric V. Smithf0db54a2017-12-04 16:58:55 -0500406 self.assertNotIn('__lt__', C.__dict__)
407 self.assertNotIn('__le__', C.__dict__)
408 self.assertNotIn('__gt__', C.__dict__)
409 self.assertNotIn('__ge__', C.__dict__)
410 elif result == 'both':
411 self.assertIn('__eq__', C.__dict__)
Eric V. Smithf0db54a2017-12-04 16:58:55 -0500412 self.assertIn('__lt__', C.__dict__)
413 self.assertIn('__le__', C.__dict__)
414 self.assertIn('__gt__', C.__dict__)
415 self.assertIn('__ge__', C.__dict__)
416 elif result == 'eq_only':
417 self.assertIn('__eq__', C.__dict__)
Eric V. Smithf0db54a2017-12-04 16:58:55 -0500418 self.assertNotIn('__lt__', C.__dict__)
419 self.assertNotIn('__le__', C.__dict__)
420 self.assertNotIn('__gt__', C.__dict__)
421 self.assertNotIn('__ge__', C.__dict__)
422 else:
423 assert False, f'unknown result {result!r}'
424
425 def test_field_no_default(self):
426 @dataclass
427 class C:
428 x: int = field()
429
430 self.assertEqual(C(5).x, 5)
431
432 with self.assertRaisesRegex(TypeError,
433 r"__init__\(\) missing 1 required "
434 "positional argument: 'x'"):
435 C()
436
437 def test_field_default(self):
438 default = object()
439 @dataclass
440 class C:
441 x: object = field(default=default)
442
443 self.assertIs(C.x, default)
444 c = C(10)
445 self.assertEqual(c.x, 10)
446
447 # If we delete the instance attribute, we should then see the
448 # class attribute.
449 del c.x
450 self.assertIs(c.x, default)
451
452 self.assertIs(C().x, default)
453
454 def test_not_in_repr(self):
455 @dataclass
456 class C:
457 x: int = field(repr=False)
458 with self.assertRaises(TypeError):
459 C()
460 c = C(10)
461 self.assertEqual(repr(c), 'TestCase.test_not_in_repr.<locals>.C()')
462
463 @dataclass
464 class C:
465 x: int = field(repr=False)
466 y: int
467 c = C(10, 20)
468 self.assertEqual(repr(c), 'TestCase.test_not_in_repr.<locals>.C(y=20)')
469
470 def test_not_in_compare(self):
471 @dataclass
472 class C:
473 x: int = 0
474 y: int = field(compare=False, default=4)
475
476 self.assertEqual(C(), C(0, 20))
477 self.assertEqual(C(1, 10), C(1, 20))
478 self.assertNotEqual(C(3), C(4, 10))
479 self.assertNotEqual(C(3, 10), C(4, 10))
480
481 def test_hash_field_rules(self):
482 # Test all 6 cases of:
483 # hash=True/False/None
484 # compare=True/False
Eric V. Smithdbf9cff2018-02-25 21:30:17 -0500485 for (hash_, compare, result ) in [
Eric V. Smithf0db54a2017-12-04 16:58:55 -0500486 (True, False, 'field' ),
487 (True, True, 'field' ),
488 (False, False, 'absent'),
489 (False, True, 'absent'),
490 (None, False, 'absent'),
491 (None, True, 'field' ),
Eric V. Smithdbf9cff2018-02-25 21:30:17 -0500492 ]:
493 with self.subTest(hash=hash_, compare=compare):
494 @dataclass(unsafe_hash=True)
Eric V. Smithf0db54a2017-12-04 16:58:55 -0500495 class C:
Eric V. Smithdbf9cff2018-02-25 21:30:17 -0500496 x: int = field(compare=compare, hash=hash_, default=5)
Eric V. Smithf0db54a2017-12-04 16:58:55 -0500497
498 if result == 'field':
499 # __hash__ contains the field.
Eric V. Smithdbf9cff2018-02-25 21:30:17 -0500500 self.assertEqual(hash(C(5)), hash((5,)))
Eric V. Smithf0db54a2017-12-04 16:58:55 -0500501 elif result == 'absent':
502 # The field is not present in the hash.
Eric V. Smithdbf9cff2018-02-25 21:30:17 -0500503 self.assertEqual(hash(C(5)), hash(()))
Eric V. Smithf0db54a2017-12-04 16:58:55 -0500504 else:
505 assert False, f'unknown result {result!r}'
506
507 def test_init_false_no_default(self):
508 # If init=False and no default value, then the field won't be
509 # present in the instance.
510 @dataclass
511 class C:
512 x: int = field(init=False)
513
514 self.assertNotIn('x', C().__dict__)
515
516 @dataclass
517 class C:
518 x: int
519 y: int = 0
520 z: int = field(init=False)
521 t: int = 10
522
523 self.assertNotIn('z', C(0).__dict__)
524 self.assertEqual(vars(C(5)), {'t': 10, 'x': 5, 'y': 0})
525
526 def test_class_marker(self):
527 @dataclass
528 class C:
529 x: int
530 y: str = field(init=False, default=None)
531 z: str = field(repr=False)
532
533 the_fields = fields(C)
534 # the_fields is a tuple of 3 items, each value
535 # is in __annotations__.
536 self.assertIsInstance(the_fields, tuple)
537 for f in the_fields:
538 self.assertIs(type(f), Field)
539 self.assertIn(f.name, C.__annotations__)
540
541 self.assertEqual(len(the_fields), 3)
542
543 self.assertEqual(the_fields[0].name, 'x')
544 self.assertEqual(the_fields[0].type, int)
545 self.assertFalse(hasattr(C, 'x'))
546 self.assertTrue (the_fields[0].init)
547 self.assertTrue (the_fields[0].repr)
548 self.assertEqual(the_fields[1].name, 'y')
549 self.assertEqual(the_fields[1].type, str)
550 self.assertIsNone(getattr(C, 'y'))
551 self.assertFalse(the_fields[1].init)
552 self.assertTrue (the_fields[1].repr)
553 self.assertEqual(the_fields[2].name, 'z')
554 self.assertEqual(the_fields[2].type, str)
555 self.assertFalse(hasattr(C, 'z'))
556 self.assertTrue (the_fields[2].init)
557 self.assertFalse(the_fields[2].repr)
558
559 def test_field_order(self):
560 @dataclass
561 class B:
562 a: str = 'B:a'
563 b: str = 'B:b'
564 c: str = 'B:c'
565
566 @dataclass
567 class C(B):
568 b: str = 'C:b'
569
570 self.assertEqual([(f.name, f.default) for f in fields(C)],
571 [('a', 'B:a'),
572 ('b', 'C:b'),
573 ('c', 'B:c')])
574
575 @dataclass
576 class D(B):
577 c: str = 'D:c'
578
579 self.assertEqual([(f.name, f.default) for f in fields(D)],
580 [('a', 'B:a'),
581 ('b', 'B:b'),
582 ('c', 'D:c')])
583
584 @dataclass
585 class E(D):
586 a: str = 'E:a'
587 d: str = 'E:d'
588
589 self.assertEqual([(f.name, f.default) for f in fields(E)],
590 [('a', 'E:a'),
591 ('b', 'B:b'),
592 ('c', 'D:c'),
593 ('d', 'E:d')])
594
595 def test_class_attrs(self):
596 # We only have a class attribute if a default value is
597 # specified, either directly or via a field with a default.
598 default = object()
599 @dataclass
600 class C:
601 x: int
602 y: int = field(repr=False)
603 z: object = default
604 t: int = field(default=100)
605
606 self.assertFalse(hasattr(C, 'x'))
607 self.assertFalse(hasattr(C, 'y'))
608 self.assertIs (C.z, default)
609 self.assertEqual(C.t, 100)
610
611 def test_disallowed_mutable_defaults(self):
612 # For the known types, don't allow mutable default values.
613 for typ, empty, non_empty in [(list, [], [1]),
614 (dict, {}, {0:1}),
615 (set, set(), set([1])),
616 ]:
617 with self.subTest(typ=typ):
618 # Can't use a zero-length value.
619 with self.assertRaisesRegex(ValueError,
620 f'mutable default {typ} for field '
621 'x is not allowed'):
622 @dataclass
623 class Point:
624 x: typ = empty
625
626
627 # Nor a non-zero-length value
628 with self.assertRaisesRegex(ValueError,
629 f'mutable default {typ} for field '
630 'y is not allowed'):
631 @dataclass
632 class Point:
633 y: typ = non_empty
634
635 # Check subtypes also fail.
636 class Subclass(typ): pass
637
638 with self.assertRaisesRegex(ValueError,
639 f"mutable default .*Subclass'>"
640 ' for field z is not allowed'
641 ):
642 @dataclass
643 class Point:
644 z: typ = Subclass()
645
646 # Because this is a ClassVar, it can be mutable.
647 @dataclass
648 class C:
649 z: ClassVar[typ] = typ()
650
651 # Because this is a ClassVar, it can be mutable.
652 @dataclass
653 class C:
654 x: ClassVar[typ] = Subclass()
655
Eric V. Smithf0db54a2017-12-04 16:58:55 -0500656 def test_deliberately_mutable_defaults(self):
657 # If a mutable default isn't in the known list of
658 # (list, dict, set), then it's okay.
659 class Mutable:
660 def __init__(self):
661 self.l = []
662
663 @dataclass
664 class C:
665 x: Mutable
666
667 # These 2 instances will share this value of x.
668 lst = Mutable()
669 o1 = C(lst)
670 o2 = C(lst)
671 self.assertEqual(o1, o2)
672 o1.x.l.extend([1, 2])
673 self.assertEqual(o1, o2)
674 self.assertEqual(o1.x.l, [1, 2])
675 self.assertIs(o1.x, o2.x)
676
677 def test_no_options(self):
Eric V. Smith2b75fc22018-03-25 20:37:33 -0400678 # Call with dataclass().
Eric V. Smithf0db54a2017-12-04 16:58:55 -0500679 @dataclass()
680 class C:
681 x: int
682
683 self.assertEqual(C(42).x, 42)
684
685 def test_not_tuple(self):
686 # Make sure we can't be compared to a tuple.
687 @dataclass
688 class Point:
689 x: int
690 y: int
691 self.assertNotEqual(Point(1, 2), (1, 2))
692
Eric V. Smith2b75fc22018-03-25 20:37:33 -0400693 # And that we can't compare to another unrelated dataclass.
Eric V. Smithf0db54a2017-12-04 16:58:55 -0500694 @dataclass
695 class C:
696 x: int
697 y: int
698 self.assertNotEqual(Point(1, 3), C(1, 3))
699
Windson yangbe372d72019-04-23 02:45:34 +0800700 def test_not_other_dataclass(self):
Eric V. Smithf0db54a2017-12-04 16:58:55 -0500701 # Test that some of the problems with namedtuple don't happen
702 # here.
703 @dataclass
704 class Point3D:
705 x: int
706 y: int
707 z: int
708
709 @dataclass
710 class Date:
711 year: int
712 month: int
713 day: int
714
715 self.assertNotEqual(Point3D(2017, 6, 3), Date(2017, 6, 3))
716 self.assertNotEqual(Point3D(1, 2, 3), (1, 2, 3))
717
Eric V. Smith2b75fc22018-03-25 20:37:33 -0400718 # Make sure we can't unpack.
Serhiy Storchaka13a6c092017-12-26 12:30:41 +0200719 with self.assertRaisesRegex(TypeError, 'unpack'):
Eric V. Smithf0db54a2017-12-04 16:58:55 -0500720 x, y, z = Point3D(4, 5, 6)
721
Eric V. Smith7c99e932018-01-28 19:18:55 -0500722 # Make sure another class with the same field names isn't
Eric V. Smithf0db54a2017-12-04 16:58:55 -0500723 # equal.
724 @dataclass
725 class Point3Dv1:
726 x: int = 0
727 y: int = 0
728 z: int = 0
729 self.assertNotEqual(Point3D(0, 0, 0), Point3Dv1())
730
731 def test_function_annotations(self):
732 # Some dummy class and instance to use as a default.
733 class F:
734 pass
735 f = F()
736
737 def validate_class(cls):
738 # First, check __annotations__, even though they're not
739 # function annotations.
740 self.assertEqual(cls.__annotations__['i'], int)
741 self.assertEqual(cls.__annotations__['j'], str)
742 self.assertEqual(cls.__annotations__['k'], F)
743 self.assertEqual(cls.__annotations__['l'], float)
744 self.assertEqual(cls.__annotations__['z'], complex)
745
746 # Verify __init__.
747
748 signature = inspect.signature(cls.__init__)
Eric V. Smith2b75fc22018-03-25 20:37:33 -0400749 # Check the return type, should be None.
Eric V. Smithf0db54a2017-12-04 16:58:55 -0500750 self.assertIs(signature.return_annotation, None)
751
752 # Check each parameter.
753 params = iter(signature.parameters.values())
754 param = next(params)
755 # This is testing an internal name, and probably shouldn't be tested.
756 self.assertEqual(param.name, 'self')
757 param = next(params)
758 self.assertEqual(param.name, 'i')
759 self.assertIs (param.annotation, int)
760 self.assertEqual(param.default, inspect.Parameter.empty)
761 self.assertEqual(param.kind, inspect.Parameter.POSITIONAL_OR_KEYWORD)
762 param = next(params)
763 self.assertEqual(param.name, 'j')
764 self.assertIs (param.annotation, str)
765 self.assertEqual(param.default, inspect.Parameter.empty)
766 self.assertEqual(param.kind, inspect.Parameter.POSITIONAL_OR_KEYWORD)
767 param = next(params)
768 self.assertEqual(param.name, 'k')
769 self.assertIs (param.annotation, F)
Eric V. Smith2b75fc22018-03-25 20:37:33 -0400770 # Don't test for the default, since it's set to MISSING.
Eric V. Smithf0db54a2017-12-04 16:58:55 -0500771 self.assertEqual(param.kind, inspect.Parameter.POSITIONAL_OR_KEYWORD)
772 param = next(params)
773 self.assertEqual(param.name, 'l')
774 self.assertIs (param.annotation, float)
Eric V. Smith2b75fc22018-03-25 20:37:33 -0400775 # Don't test for the default, since it's set to MISSING.
Eric V. Smithf0db54a2017-12-04 16:58:55 -0500776 self.assertEqual(param.kind, inspect.Parameter.POSITIONAL_OR_KEYWORD)
777 self.assertRaises(StopIteration, next, params)
778
779
780 @dataclass
781 class C:
782 i: int
783 j: str
784 k: F = f
785 l: float=field(default=None)
786 z: complex=field(default=3+4j, init=False)
787
788 validate_class(C)
789
790 # Now repeat with __hash__.
Eric V. Smithdbf9cff2018-02-25 21:30:17 -0500791 @dataclass(frozen=True, unsafe_hash=True)
Eric V. Smithf0db54a2017-12-04 16:58:55 -0500792 class C:
793 i: int
794 j: str
795 k: F = f
796 l: float=field(default=None)
797 z: complex=field(default=3+4j, init=False)
798
799 validate_class(C)
800
Eric V. Smith03220fd2017-12-29 13:59:58 -0500801 def test_missing_default(self):
802 # Test that MISSING works the same as a default not being
803 # specified.
804 @dataclass
805 class C:
806 x: int=field(default=MISSING)
807 with self.assertRaisesRegex(TypeError,
808 r'__init__\(\) missing 1 required '
809 'positional argument'):
810 C()
811 self.assertNotIn('x', C.__dict__)
812
813 @dataclass
814 class D:
815 x: int
816 with self.assertRaisesRegex(TypeError,
817 r'__init__\(\) missing 1 required '
818 'positional argument'):
819 D()
820 self.assertNotIn('x', D.__dict__)
821
822 def test_missing_default_factory(self):
823 # Test that MISSING works the same as a default factory not
824 # being specified (which is really the same as a default not
825 # being specified, too).
826 @dataclass
827 class C:
828 x: int=field(default_factory=MISSING)
829 with self.assertRaisesRegex(TypeError,
830 r'__init__\(\) missing 1 required '
831 'positional argument'):
832 C()
833 self.assertNotIn('x', C.__dict__)
834
835 @dataclass
836 class D:
837 x: int=field(default=MISSING, default_factory=MISSING)
838 with self.assertRaisesRegex(TypeError,
839 r'__init__\(\) missing 1 required '
840 'positional argument'):
841 D()
842 self.assertNotIn('x', D.__dict__)
843
844 def test_missing_repr(self):
845 self.assertIn('MISSING_TYPE object', repr(MISSING))
846
Eric V. Smithf0db54a2017-12-04 16:58:55 -0500847 def test_dont_include_other_annotations(self):
848 @dataclass
849 class C:
850 i: int
851 def foo(self) -> int:
852 return 4
853 @property
854 def bar(self) -> int:
855 return 5
856 self.assertEqual(list(C.__annotations__), ['i'])
857 self.assertEqual(C(10).foo(), 4)
858 self.assertEqual(C(10).bar, 5)
Eric V. Smith51c9ab42018-03-25 09:04:32 -0400859 self.assertEqual(C(10).i, 10)
Eric V. Smithf0db54a2017-12-04 16:58:55 -0500860
861 def test_post_init(self):
862 # Just make sure it gets called
863 @dataclass
864 class C:
865 def __post_init__(self):
866 raise CustomError()
867 with self.assertRaises(CustomError):
868 C()
869
870 @dataclass
871 class C:
872 i: int = 10
873 def __post_init__(self):
874 if self.i == 10:
875 raise CustomError()
876 with self.assertRaises(CustomError):
877 C()
878 # post-init gets called, but doesn't raise. This is just
879 # checking that self is used correctly.
880 C(5)
881
882 # If there's not an __init__, then post-init won't get called.
883 @dataclass(init=False)
884 class C:
885 def __post_init__(self):
886 raise CustomError()
887 # Creating the class won't raise
888 C()
889
890 @dataclass
891 class C:
892 x: int = 0
893 def __post_init__(self):
894 self.x *= 2
895 self.assertEqual(C().x, 0)
896 self.assertEqual(C(2).x, 4)
897
Mike53f7a7c2017-12-14 14:04:53 +0300898 # Make sure that if we're frozen, post-init can't set
Eric V. Smithf0db54a2017-12-04 16:58:55 -0500899 # attributes.
900 @dataclass(frozen=True)
901 class C:
902 x: int = 0
903 def __post_init__(self):
904 self.x *= 2
905 with self.assertRaises(FrozenInstanceError):
906 C()
907
908 def test_post_init_super(self):
909 # Make sure super() post-init isn't called by default.
910 class B:
911 def __post_init__(self):
912 raise CustomError()
913
914 @dataclass
915 class C(B):
916 def __post_init__(self):
917 self.x = 5
918
919 self.assertEqual(C().x, 5)
920
Eric V. Smith2b75fc22018-03-25 20:37:33 -0400921 # Now call super(), and it will raise.
Eric V. Smithf0db54a2017-12-04 16:58:55 -0500922 @dataclass
923 class C(B):
924 def __post_init__(self):
925 super().__post_init__()
926
927 with self.assertRaises(CustomError):
928 C()
929
930 # Make sure post-init is called, even if not defined in our
931 # class.
932 @dataclass
933 class C(B):
934 pass
935
936 with self.assertRaises(CustomError):
937 C()
938
939 def test_post_init_staticmethod(self):
940 flag = False
941 @dataclass
942 class C:
943 x: int
944 y: int
945 @staticmethod
946 def __post_init__():
947 nonlocal flag
948 flag = True
949
950 self.assertFalse(flag)
951 c = C(3, 4)
952 self.assertEqual((c.x, c.y), (3, 4))
953 self.assertTrue(flag)
954
955 def test_post_init_classmethod(self):
956 @dataclass
957 class C:
958 flag = False
959 x: int
960 y: int
961 @classmethod
962 def __post_init__(cls):
963 cls.flag = True
964
965 self.assertFalse(C.flag)
966 c = C(3, 4)
967 self.assertEqual((c.x, c.y), (3, 4))
968 self.assertTrue(C.flag)
969
970 def test_class_var(self):
971 # Make sure ClassVars are ignored in __init__, __repr__, etc.
972 @dataclass
973 class C:
974 x: int
975 y: int = 10
976 z: ClassVar[int] = 1000
977 w: ClassVar[int] = 2000
978 t: ClassVar[int] = 3000
Eric V. Smith2a7bacb2018-05-15 22:44:27 -0400979 s: ClassVar = 4000
Eric V. Smithf0db54a2017-12-04 16:58:55 -0500980
981 c = C(5)
982 self.assertEqual(repr(c), 'TestCase.test_class_var.<locals>.C(x=5, y=10)')
Eric V. Smith2b75fc22018-03-25 20:37:33 -0400983 self.assertEqual(len(fields(C)), 2) # We have 2 fields.
Eric V. Smith2a7bacb2018-05-15 22:44:27 -0400984 self.assertEqual(len(C.__annotations__), 6) # And 4 ClassVars.
Eric V. Smithf0db54a2017-12-04 16:58:55 -0500985 self.assertEqual(c.z, 1000)
986 self.assertEqual(c.w, 2000)
987 self.assertEqual(c.t, 3000)
Eric V. Smith2a7bacb2018-05-15 22:44:27 -0400988 self.assertEqual(c.s, 4000)
Eric V. Smithf0db54a2017-12-04 16:58:55 -0500989 C.z += 1
990 self.assertEqual(c.z, 1001)
991 c = C(20)
992 self.assertEqual((c.x, c.y), (20, 10))
993 self.assertEqual(c.z, 1001)
994 self.assertEqual(c.w, 2000)
995 self.assertEqual(c.t, 3000)
Eric V. Smith2a7bacb2018-05-15 22:44:27 -0400996 self.assertEqual(c.s, 4000)
Eric V. Smithf0db54a2017-12-04 16:58:55 -0500997
998 def test_class_var_no_default(self):
999 # If a ClassVar has no default value, it should not be set on the class.
1000 @dataclass
1001 class C:
1002 x: ClassVar[int]
1003
1004 self.assertNotIn('x', C.__dict__)
1005
1006 def test_class_var_default_factory(self):
1007 # It makes no sense for a ClassVar to have a default factory. When
1008 # would it be called? Call it yourself, since it's class-wide.
1009 with self.assertRaisesRegex(TypeError,
1010 'cannot have a default factory'):
1011 @dataclass
1012 class C:
1013 x: ClassVar[int] = field(default_factory=int)
1014
1015 self.assertNotIn('x', C.__dict__)
1016
1017 def test_class_var_with_default(self):
1018 # If a ClassVar has a default value, it should be set on the class.
1019 @dataclass
1020 class C:
1021 x: ClassVar[int] = 10
1022 self.assertEqual(C.x, 10)
1023
1024 @dataclass
1025 class C:
1026 x: ClassVar[int] = field(default=10)
1027 self.assertEqual(C.x, 10)
1028
1029 def test_class_var_frozen(self):
1030 # Make sure ClassVars work even if we're frozen.
1031 @dataclass(frozen=True)
1032 class C:
1033 x: int
1034 y: int = 10
1035 z: ClassVar[int] = 1000
1036 w: ClassVar[int] = 2000
1037 t: ClassVar[int] = 3000
1038
1039 c = C(5)
1040 self.assertEqual(repr(C(5)), 'TestCase.test_class_var_frozen.<locals>.C(x=5, y=10)')
1041 self.assertEqual(len(fields(C)), 2) # We have 2 fields
1042 self.assertEqual(len(C.__annotations__), 5) # And 3 ClassVars
1043 self.assertEqual(c.z, 1000)
1044 self.assertEqual(c.w, 2000)
1045 self.assertEqual(c.t, 3000)
1046 # We can still modify the ClassVar, it's only instances that are
1047 # frozen.
1048 C.z += 1
1049 self.assertEqual(c.z, 1001)
1050 c = C(20)
1051 self.assertEqual((c.x, c.y), (20, 10))
1052 self.assertEqual(c.z, 1001)
1053 self.assertEqual(c.w, 2000)
1054 self.assertEqual(c.t, 3000)
1055
1056 def test_init_var_no_default(self):
1057 # If an InitVar has no default value, it should not be set on the class.
1058 @dataclass
1059 class C:
1060 x: InitVar[int]
1061
1062 self.assertNotIn('x', C.__dict__)
1063
1064 def test_init_var_default_factory(self):
1065 # It makes no sense for an InitVar to have a default factory. When
1066 # would it be called? Call it yourself, since it's class-wide.
1067 with self.assertRaisesRegex(TypeError,
1068 'cannot have a default factory'):
1069 @dataclass
1070 class C:
1071 x: InitVar[int] = field(default_factory=int)
1072
1073 self.assertNotIn('x', C.__dict__)
1074
1075 def test_init_var_with_default(self):
1076 # If an InitVar has a default value, it should be set on the class.
1077 @dataclass
1078 class C:
1079 x: InitVar[int] = 10
1080 self.assertEqual(C.x, 10)
1081
1082 @dataclass
1083 class C:
1084 x: InitVar[int] = field(default=10)
1085 self.assertEqual(C.x, 10)
1086
1087 def test_init_var(self):
1088 @dataclass
1089 class C:
1090 x: int = None
1091 init_param: InitVar[int] = None
1092
1093 def __post_init__(self, init_param):
1094 if self.x is None:
1095 self.x = init_param*2
1096
1097 c = C(init_param=10)
1098 self.assertEqual(c.x, 20)
1099
Augusto Hack01ee12b2019-06-02 23:14:48 -03001100 def test_init_var_preserve_type(self):
1101 self.assertEqual(InitVar[int].type, int)
1102
1103 # Make sure the repr is correct.
1104 self.assertEqual(repr(InitVar[int]), 'dataclasses.InitVar[int]')
1105
Eric V. Smithf0db54a2017-12-04 16:58:55 -05001106 def test_init_var_inheritance(self):
1107 # Note that this deliberately tests that a dataclass need not
1108 # have a __post_init__ function if it has an InitVar field.
1109 # It could just be used in a derived class, as shown here.
1110 @dataclass
1111 class Base:
1112 x: int
1113 init_base: InitVar[int]
1114
1115 # We can instantiate by passing the InitVar, even though
1116 # it's not used.
1117 b = Base(0, 10)
1118 self.assertEqual(vars(b), {'x': 0})
1119
1120 @dataclass
1121 class C(Base):
1122 y: int
1123 init_derived: InitVar[int]
1124
1125 def __post_init__(self, init_base, init_derived):
1126 self.x = self.x + init_base
1127 self.y = self.y + init_derived
1128
1129 c = C(10, 11, 50, 51)
1130 self.assertEqual(vars(c), {'x': 21, 'y': 101})
1131
1132 def test_default_factory(self):
1133 # Test a factory that returns a new list.
1134 @dataclass
1135 class C:
1136 x: int
1137 y: list = field(default_factory=list)
1138
1139 c0 = C(3)
1140 c1 = C(3)
1141 self.assertEqual(c0.x, 3)
1142 self.assertEqual(c0.y, [])
1143 self.assertEqual(c0, c1)
1144 self.assertIsNot(c0.y, c1.y)
1145 self.assertEqual(astuple(C(5, [1])), (5, [1]))
1146
1147 # Test a factory that returns a shared list.
1148 l = []
1149 @dataclass
1150 class C:
1151 x: int
1152 y: list = field(default_factory=lambda: l)
1153
1154 c0 = C(3)
1155 c1 = C(3)
1156 self.assertEqual(c0.x, 3)
1157 self.assertEqual(c0.y, [])
1158 self.assertEqual(c0, c1)
1159 self.assertIs(c0.y, c1.y)
1160 self.assertEqual(astuple(C(5, [1])), (5, [1]))
1161
1162 # Test various other field flags.
1163 # repr
1164 @dataclass
1165 class C:
1166 x: list = field(default_factory=list, repr=False)
1167 self.assertEqual(repr(C()), 'TestCase.test_default_factory.<locals>.C()')
1168 self.assertEqual(C().x, [])
1169
1170 # hash
Eric V. Smithdbf9cff2018-02-25 21:30:17 -05001171 @dataclass(unsafe_hash=True)
Eric V. Smithf0db54a2017-12-04 16:58:55 -05001172 class C:
1173 x: list = field(default_factory=list, hash=False)
1174 self.assertEqual(astuple(C()), ([],))
1175 self.assertEqual(hash(C()), hash(()))
1176
1177 # init (see also test_default_factory_with_no_init)
1178 @dataclass
1179 class C:
1180 x: list = field(default_factory=list, init=False)
1181 self.assertEqual(astuple(C()), ([],))
1182
1183 # compare
1184 @dataclass
1185 class C:
1186 x: list = field(default_factory=list, compare=False)
1187 self.assertEqual(C(), C([1]))
1188
1189 def test_default_factory_with_no_init(self):
1190 # We need a factory with a side effect.
1191 factory = Mock()
1192
1193 @dataclass
1194 class C:
1195 x: list = field(default_factory=factory, init=False)
1196
1197 # Make sure the default factory is called for each new instance.
1198 C().x
1199 self.assertEqual(factory.call_count, 1)
1200 C().x
1201 self.assertEqual(factory.call_count, 2)
1202
1203 def test_default_factory_not_called_if_value_given(self):
1204 # We need a factory that we can test if it's been called.
1205 factory = Mock()
1206
1207 @dataclass
1208 class C:
1209 x: int = field(default_factory=factory)
1210
1211 # Make sure that if a field has a default factory function,
1212 # it's not called if a value is specified.
1213 C().x
1214 self.assertEqual(factory.call_count, 1)
1215 self.assertEqual(C(10).x, 10)
1216 self.assertEqual(factory.call_count, 1)
1217 C().x
1218 self.assertEqual(factory.call_count, 2)
1219
Eric V. Smith8f6eccd2018-03-20 22:00:23 -04001220 def test_default_factory_derived(self):
1221 # See bpo-32896.
1222 @dataclass
1223 class Foo:
1224 x: dict = field(default_factory=dict)
1225
1226 @dataclass
1227 class Bar(Foo):
1228 y: int = 1
1229
1230 self.assertEqual(Foo().x, {})
1231 self.assertEqual(Bar().x, {})
1232 self.assertEqual(Bar().y, 1)
1233
1234 @dataclass
1235 class Baz(Foo):
1236 pass
1237 self.assertEqual(Baz().x, {})
1238
1239 def test_intermediate_non_dataclass(self):
1240 # Test that an intermediate class that defines
1241 # annotations does not define fields.
1242
1243 @dataclass
1244 class A:
1245 x: int
1246
1247 class B(A):
1248 y: int
1249
1250 @dataclass
1251 class C(B):
1252 z: int
1253
1254 c = C(1, 3)
1255 self.assertEqual((c.x, c.z), (1, 3))
1256
1257 # .y was not initialized.
1258 with self.assertRaisesRegex(AttributeError,
1259 'object has no attribute'):
1260 c.y
1261
1262 # And if we again derive a non-dataclass, no fields are added.
1263 class D(C):
1264 t: int
1265 d = D(4, 5)
1266 self.assertEqual((d.x, d.z), (4, 5))
1267
Eric V. Smith2b75fc22018-03-25 20:37:33 -04001268 def test_classvar_default_factory(self):
1269 # It's an error for a ClassVar to have a factory function.
1270 with self.assertRaisesRegex(TypeError,
1271 'cannot have a default factory'):
1272 @dataclass
1273 class C:
1274 x: ClassVar[int] = field(default_factory=int)
Eric V. Smithf0db54a2017-12-04 16:58:55 -05001275
Eric V. Smithe7ba0132018-01-06 12:41:53 -05001276 def test_is_dataclass(self):
1277 class NotDataClass:
1278 pass
Eric V. Smithf0db54a2017-12-04 16:58:55 -05001279
Eric V. Smithe7ba0132018-01-06 12:41:53 -05001280 self.assertFalse(is_dataclass(0))
1281 self.assertFalse(is_dataclass(int))
1282 self.assertFalse(is_dataclass(NotDataClass))
1283 self.assertFalse(is_dataclass(NotDataClass()))
Eric V. Smithf0db54a2017-12-04 16:58:55 -05001284
1285 @dataclass
1286 class C:
1287 x: int
1288
Eric V. Smithe7ba0132018-01-06 12:41:53 -05001289 @dataclass
1290 class D:
1291 d: C
1292 e: int
1293
1294 c = C(10)
1295 d = D(c, 4)
1296
1297 self.assertTrue(is_dataclass(C))
1298 self.assertTrue(is_dataclass(c))
1299 self.assertFalse(is_dataclass(c.x))
1300 self.assertTrue(is_dataclass(d.d))
1301 self.assertFalse(is_dataclass(d.e))
Eric V. Smithf0db54a2017-12-04 16:58:55 -05001302
Eric V. Smithb0f4dab2019-08-20 01:40:28 -04001303 def test_is_dataclass_when_getattr_always_returns(self):
1304 # See bpo-37868.
1305 class A:
1306 def __getattr__(self, key):
1307 return 0
1308 self.assertFalse(is_dataclass(A))
1309 a = A()
1310
1311 # Also test for an instance attribute.
1312 class B:
1313 pass
1314 b = B()
1315 b.__dataclass_fields__ = []
1316
1317 for obj in a, b:
1318 with self.subTest(obj=obj):
1319 self.assertFalse(is_dataclass(obj))
1320
1321 # Indirect tests for _is_dataclass_instance().
1322 with self.assertRaisesRegex(TypeError, 'should be called on dataclass instances'):
1323 asdict(obj)
1324 with self.assertRaisesRegex(TypeError, 'should be called on dataclass instances'):
1325 astuple(obj)
1326 with self.assertRaisesRegex(TypeError, 'should be called on dataclass instances'):
1327 replace(obj, x=0)
1328
Eric V. Smithf0db54a2017-12-04 16:58:55 -05001329 def test_helper_fields_with_class_instance(self):
1330 # Check that we can call fields() on either a class or instance,
1331 # and get back the same thing.
1332 @dataclass
1333 class C:
1334 x: int
1335 y: float
1336
1337 self.assertEqual(fields(C), fields(C(0, 0.0)))
1338
1339 def test_helper_fields_exception(self):
1340 # Check that TypeError is raised if not passed a dataclass or
1341 # instance.
1342 with self.assertRaisesRegex(TypeError, 'dataclass type or instance'):
1343 fields(0)
1344
1345 class C: pass
1346 with self.assertRaisesRegex(TypeError, 'dataclass type or instance'):
1347 fields(C)
1348 with self.assertRaisesRegex(TypeError, 'dataclass type or instance'):
1349 fields(C())
1350
1351 def test_helper_asdict(self):
Eric V. Smith2b75fc22018-03-25 20:37:33 -04001352 # Basic tests for asdict(), it should return a new dictionary.
Eric V. Smithf0db54a2017-12-04 16:58:55 -05001353 @dataclass
1354 class C:
1355 x: int
1356 y: int
1357 c = C(1, 2)
1358
1359 self.assertEqual(asdict(c), {'x': 1, 'y': 2})
1360 self.assertEqual(asdict(c), asdict(c))
1361 self.assertIsNot(asdict(c), asdict(c))
1362 c.x = 42
1363 self.assertEqual(asdict(c), {'x': 42, 'y': 2})
1364 self.assertIs(type(asdict(c)), dict)
1365
1366 def test_helper_asdict_raises_on_classes(self):
Eric V. Smith2b75fc22018-03-25 20:37:33 -04001367 # asdict() should raise on a class object.
Eric V. Smithf0db54a2017-12-04 16:58:55 -05001368 @dataclass
1369 class C:
1370 x: int
1371 y: int
1372 with self.assertRaisesRegex(TypeError, 'dataclass instance'):
1373 asdict(C)
1374 with self.assertRaisesRegex(TypeError, 'dataclass instance'):
1375 asdict(int)
1376
1377 def test_helper_asdict_copy_values(self):
1378 @dataclass
1379 class C:
1380 x: int
1381 y: List[int] = field(default_factory=list)
1382 initial = []
1383 c = C(1, initial)
1384 d = asdict(c)
1385 self.assertEqual(d['y'], initial)
1386 self.assertIsNot(d['y'], initial)
1387 c = C(1)
1388 d = asdict(c)
1389 d['y'].append(1)
1390 self.assertEqual(c.y, [])
1391
1392 def test_helper_asdict_nested(self):
1393 @dataclass
1394 class UserId:
1395 token: int
1396 group: int
1397 @dataclass
1398 class User:
1399 name: str
1400 id: UserId
1401 u = User('Joe', UserId(123, 1))
1402 d = asdict(u)
1403 self.assertEqual(d, {'name': 'Joe', 'id': {'token': 123, 'group': 1}})
1404 self.assertIsNot(asdict(u), asdict(u))
1405 u.id.group = 2
1406 self.assertEqual(asdict(u), {'name': 'Joe',
1407 'id': {'token': 123, 'group': 2}})
1408
1409 def test_helper_asdict_builtin_containers(self):
1410 @dataclass
1411 class User:
1412 name: str
1413 id: int
1414 @dataclass
1415 class GroupList:
1416 id: int
1417 users: List[User]
1418 @dataclass
1419 class GroupTuple:
1420 id: int
1421 users: Tuple[User, ...]
1422 @dataclass
1423 class GroupDict:
1424 id: int
1425 users: Dict[str, User]
1426 a = User('Alice', 1)
1427 b = User('Bob', 2)
1428 gl = GroupList(0, [a, b])
1429 gt = GroupTuple(0, (a, b))
1430 gd = GroupDict(0, {'first': a, 'second': b})
1431 self.assertEqual(asdict(gl), {'id': 0, 'users': [{'name': 'Alice', 'id': 1},
1432 {'name': 'Bob', 'id': 2}]})
1433 self.assertEqual(asdict(gt), {'id': 0, 'users': ({'name': 'Alice', 'id': 1},
1434 {'name': 'Bob', 'id': 2})})
1435 self.assertEqual(asdict(gd), {'id': 0, 'users': {'first': {'name': 'Alice', 'id': 1},
1436 'second': {'name': 'Bob', 'id': 2}}})
1437
Windson yangbe372d72019-04-23 02:45:34 +08001438 def test_helper_asdict_builtin_object_containers(self):
Eric V. Smithf0db54a2017-12-04 16:58:55 -05001439 @dataclass
1440 class Child:
1441 d: object
1442
1443 @dataclass
1444 class Parent:
1445 child: Child
1446
1447 self.assertEqual(asdict(Parent(Child([1]))), {'child': {'d': [1]}})
1448 self.assertEqual(asdict(Parent(Child({1: 2}))), {'child': {'d': {1: 2}}})
1449
1450 def test_helper_asdict_factory(self):
1451 @dataclass
1452 class C:
1453 x: int
1454 y: int
1455 c = C(1, 2)
1456 d = asdict(c, dict_factory=OrderedDict)
1457 self.assertEqual(d, OrderedDict([('x', 1), ('y', 2)]))
1458 self.assertIsNot(d, asdict(c, dict_factory=OrderedDict))
1459 c.x = 42
1460 d = asdict(c, dict_factory=OrderedDict)
1461 self.assertEqual(d, OrderedDict([('x', 42), ('y', 2)]))
1462 self.assertIs(type(d), OrderedDict)
1463
Eric V. Smith9b9d97d2018-09-14 11:32:16 -04001464 def test_helper_asdict_namedtuple(self):
1465 T = namedtuple('T', 'a b c')
1466 @dataclass
1467 class C:
1468 x: str
1469 y: T
1470 c = C('outer', T(1, C('inner', T(11, 12, 13)), 2))
1471
1472 d = asdict(c)
1473 self.assertEqual(d, {'x': 'outer',
1474 'y': T(1,
1475 {'x': 'inner',
1476 'y': T(11, 12, 13)},
1477 2),
1478 }
1479 )
1480
1481 # Now with a dict_factory. OrderedDict is convenient, but
1482 # since it compares to dicts, we also need to have separate
1483 # assertIs tests.
1484 d = asdict(c, dict_factory=OrderedDict)
1485 self.assertEqual(d, {'x': 'outer',
1486 'y': T(1,
1487 {'x': 'inner',
1488 'y': T(11, 12, 13)},
1489 2),
1490 }
1491 )
1492
penguindustin96466302019-05-06 14:57:17 -04001493 # Make sure that the returned dicts are actually OrderedDicts.
Eric V. Smith9b9d97d2018-09-14 11:32:16 -04001494 self.assertIs(type(d), OrderedDict)
1495 self.assertIs(type(d['y'][1]), OrderedDict)
1496
1497 def test_helper_asdict_namedtuple_key(self):
1498 # Ensure that a field that contains a dict which has a
1499 # namedtuple as a key works with asdict().
1500
1501 @dataclass
1502 class C:
1503 f: dict
1504 T = namedtuple('T', 'a')
1505
1506 c = C({T('an a'): 0})
1507
1508 self.assertEqual(asdict(c), {'f': {T(a='an a'): 0}})
1509
1510 def test_helper_asdict_namedtuple_derived(self):
1511 class T(namedtuple('Tbase', 'a')):
1512 def my_a(self):
1513 return self.a
1514
1515 @dataclass
1516 class C:
1517 f: T
1518
1519 t = T(6)
1520 c = C(t)
1521
1522 d = asdict(c)
1523 self.assertEqual(d, {'f': T(a=6)})
1524 # Make sure that t has been copied, not used directly.
1525 self.assertIsNot(d['f'], t)
1526 self.assertEqual(d['f'].my_a(), 6)
1527
Eric V. Smithf0db54a2017-12-04 16:58:55 -05001528 def test_helper_astuple(self):
Eric V. Smith2b75fc22018-03-25 20:37:33 -04001529 # Basic tests for astuple(), it should return a new tuple.
Eric V. Smithf0db54a2017-12-04 16:58:55 -05001530 @dataclass
1531 class C:
1532 x: int
1533 y: int = 0
1534 c = C(1)
1535
1536 self.assertEqual(astuple(c), (1, 0))
1537 self.assertEqual(astuple(c), astuple(c))
1538 self.assertIsNot(astuple(c), astuple(c))
1539 c.y = 42
1540 self.assertEqual(astuple(c), (1, 42))
1541 self.assertIs(type(astuple(c)), tuple)
1542
1543 def test_helper_astuple_raises_on_classes(self):
Eric V. Smith2b75fc22018-03-25 20:37:33 -04001544 # astuple() should raise on a class object.
Eric V. Smithf0db54a2017-12-04 16:58:55 -05001545 @dataclass
1546 class C:
1547 x: int
1548 y: int
1549 with self.assertRaisesRegex(TypeError, 'dataclass instance'):
1550 astuple(C)
1551 with self.assertRaisesRegex(TypeError, 'dataclass instance'):
1552 astuple(int)
1553
1554 def test_helper_astuple_copy_values(self):
1555 @dataclass
1556 class C:
1557 x: int
1558 y: List[int] = field(default_factory=list)
1559 initial = []
1560 c = C(1, initial)
1561 t = astuple(c)
1562 self.assertEqual(t[1], initial)
1563 self.assertIsNot(t[1], initial)
1564 c = C(1)
1565 t = astuple(c)
1566 t[1].append(1)
1567 self.assertEqual(c.y, [])
1568
1569 def test_helper_astuple_nested(self):
1570 @dataclass
1571 class UserId:
1572 token: int
1573 group: int
1574 @dataclass
1575 class User:
1576 name: str
1577 id: UserId
1578 u = User('Joe', UserId(123, 1))
1579 t = astuple(u)
1580 self.assertEqual(t, ('Joe', (123, 1)))
1581 self.assertIsNot(astuple(u), astuple(u))
1582 u.id.group = 2
1583 self.assertEqual(astuple(u), ('Joe', (123, 2)))
1584
1585 def test_helper_astuple_builtin_containers(self):
1586 @dataclass
1587 class User:
1588 name: str
1589 id: int
1590 @dataclass
1591 class GroupList:
1592 id: int
1593 users: List[User]
1594 @dataclass
1595 class GroupTuple:
1596 id: int
1597 users: Tuple[User, ...]
1598 @dataclass
1599 class GroupDict:
1600 id: int
1601 users: Dict[str, User]
1602 a = User('Alice', 1)
1603 b = User('Bob', 2)
1604 gl = GroupList(0, [a, b])
1605 gt = GroupTuple(0, (a, b))
1606 gd = GroupDict(0, {'first': a, 'second': b})
1607 self.assertEqual(astuple(gl), (0, [('Alice', 1), ('Bob', 2)]))
1608 self.assertEqual(astuple(gt), (0, (('Alice', 1), ('Bob', 2))))
1609 self.assertEqual(astuple(gd), (0, {'first': ('Alice', 1), 'second': ('Bob', 2)}))
1610
Windson yangbe372d72019-04-23 02:45:34 +08001611 def test_helper_astuple_builtin_object_containers(self):
Eric V. Smithf0db54a2017-12-04 16:58:55 -05001612 @dataclass
1613 class Child:
1614 d: object
1615
1616 @dataclass
1617 class Parent:
1618 child: Child
1619
1620 self.assertEqual(astuple(Parent(Child([1]))), (([1],),))
1621 self.assertEqual(astuple(Parent(Child({1: 2}))), (({1: 2},),))
1622
1623 def test_helper_astuple_factory(self):
1624 @dataclass
1625 class C:
1626 x: int
1627 y: int
1628 NT = namedtuple('NT', 'x y')
1629 def nt(lst):
1630 return NT(*lst)
1631 c = C(1, 2)
1632 t = astuple(c, tuple_factory=nt)
1633 self.assertEqual(t, NT(1, 2))
1634 self.assertIsNot(t, astuple(c, tuple_factory=nt))
1635 c.x = 42
1636 t = astuple(c, tuple_factory=nt)
1637 self.assertEqual(t, NT(42, 2))
1638 self.assertIs(type(t), NT)
1639
Eric V. Smith9b9d97d2018-09-14 11:32:16 -04001640 def test_helper_astuple_namedtuple(self):
1641 T = namedtuple('T', 'a b c')
1642 @dataclass
1643 class C:
1644 x: str
1645 y: T
1646 c = C('outer', T(1, C('inner', T(11, 12, 13)), 2))
1647
1648 t = astuple(c)
1649 self.assertEqual(t, ('outer', T(1, ('inner', (11, 12, 13)), 2)))
1650
1651 # Now, using a tuple_factory. list is convenient here.
1652 t = astuple(c, tuple_factory=list)
1653 self.assertEqual(t, ['outer', T(1, ['inner', T(11, 12, 13)], 2)])
1654
Eric V. Smithf0db54a2017-12-04 16:58:55 -05001655 def test_dynamic_class_creation(self):
Eric V. Smith2b75fc22018-03-25 20:37:33 -04001656 cls_dict = {'__annotations__': {'x': int, 'y': int},
Eric V. Smithf0db54a2017-12-04 16:58:55 -05001657 }
1658
1659 # Create the class.
1660 cls = type('C', (), cls_dict)
1661
1662 # Make it a dataclass.
1663 cls1 = dataclass(cls)
1664
1665 self.assertEqual(cls1, cls)
1666 self.assertEqual(asdict(cls(1, 2)), {'x': 1, 'y': 2})
1667
1668 def test_dynamic_class_creation_using_field(self):
Eric V. Smith2b75fc22018-03-25 20:37:33 -04001669 cls_dict = {'__annotations__': {'x': int, 'y': int},
Eric V. Smithf0db54a2017-12-04 16:58:55 -05001670 'y': field(default=5),
1671 }
1672
1673 # Create the class.
1674 cls = type('C', (), cls_dict)
1675
1676 # Make it a dataclass.
1677 cls1 = dataclass(cls)
1678
1679 self.assertEqual(cls1, cls)
1680 self.assertEqual(asdict(cls1(1)), {'x': 1, 'y': 5})
1681
1682 def test_init_in_order(self):
1683 @dataclass
1684 class C:
1685 a: int
1686 b: int = field()
1687 c: list = field(default_factory=list, init=False)
1688 d: list = field(default_factory=list)
1689 e: int = field(default=4, init=False)
1690 f: int = 4
1691
1692 calls = []
1693 def setattr(self, name, value):
1694 calls.append((name, value))
1695
1696 C.__setattr__ = setattr
1697 c = C(0, 1)
1698 self.assertEqual(('a', 0), calls[0])
1699 self.assertEqual(('b', 1), calls[1])
1700 self.assertEqual(('c', []), calls[2])
1701 self.assertEqual(('d', []), calls[3])
1702 self.assertNotIn(('e', 4), calls)
1703 self.assertEqual(('f', 4), calls[4])
1704
1705 def test_items_in_dicts(self):
1706 @dataclass
1707 class C:
1708 a: int
1709 b: list = field(default_factory=list, init=False)
1710 c: list = field(default_factory=list)
1711 d: int = field(default=4, init=False)
1712 e: int = 0
1713
1714 c = C(0)
1715 # Class dict
1716 self.assertNotIn('a', C.__dict__)
1717 self.assertNotIn('b', C.__dict__)
1718 self.assertNotIn('c', C.__dict__)
1719 self.assertIn('d', C.__dict__)
1720 self.assertEqual(C.d, 4)
1721 self.assertIn('e', C.__dict__)
1722 self.assertEqual(C.e, 0)
1723 # Instance dict
1724 self.assertIn('a', c.__dict__)
1725 self.assertEqual(c.a, 0)
1726 self.assertIn('b', c.__dict__)
1727 self.assertEqual(c.b, [])
1728 self.assertIn('c', c.__dict__)
1729 self.assertEqual(c.c, [])
1730 self.assertNotIn('d', c.__dict__)
1731 self.assertIn('e', c.__dict__)
1732 self.assertEqual(c.e, 0)
1733
1734 def test_alternate_classmethod_constructor(self):
1735 # Since __post_init__ can't take params, use a classmethod
Eric V. Smith2b75fc22018-03-25 20:37:33 -04001736 # alternate constructor. This is mostly an example to show
1737 # how to use this technique.
Eric V. Smithf0db54a2017-12-04 16:58:55 -05001738 @dataclass
1739 class C:
1740 x: int
1741 @classmethod
1742 def from_file(cls, filename):
1743 # In a real example, create a new instance
1744 # and populate 'x' from contents of a file.
1745 value_in_file = 20
1746 return cls(value_in_file)
1747
1748 self.assertEqual(C.from_file('filename').x, 20)
1749
1750 def test_field_metadata_default(self):
1751 # Make sure the default metadata is read-only and of
1752 # zero length.
1753 @dataclass
1754 class C:
1755 i: int
1756
1757 self.assertFalse(fields(C)[0].metadata)
1758 self.assertEqual(len(fields(C)[0].metadata), 0)
1759 with self.assertRaisesRegex(TypeError,
1760 'does not support item assignment'):
1761 fields(C)[0].metadata['test'] = 3
1762
1763 def test_field_metadata_mapping(self):
1764 # Make sure only a mapping can be passed as metadata
1765 # zero length.
1766 with self.assertRaises(TypeError):
1767 @dataclass
1768 class C:
1769 i: int = field(metadata=0)
1770
Eric V. Smith2b75fc22018-03-25 20:37:33 -04001771 # Make sure an empty dict works.
Christopher Huntb01786c2019-02-12 06:50:49 -05001772 d = {}
Eric V. Smithf0db54a2017-12-04 16:58:55 -05001773 @dataclass
1774 class C:
Christopher Huntb01786c2019-02-12 06:50:49 -05001775 i: int = field(metadata=d)
Eric V. Smithf0db54a2017-12-04 16:58:55 -05001776 self.assertFalse(fields(C)[0].metadata)
1777 self.assertEqual(len(fields(C)[0].metadata), 0)
Christopher Huntb01786c2019-02-12 06:50:49 -05001778 # Update should work (see bpo-35960).
1779 d['foo'] = 1
1780 self.assertEqual(len(fields(C)[0].metadata), 1)
1781 self.assertEqual(fields(C)[0].metadata['foo'], 1)
Eric V. Smithf0db54a2017-12-04 16:58:55 -05001782 with self.assertRaisesRegex(TypeError,
1783 'does not support item assignment'):
1784 fields(C)[0].metadata['test'] = 3
1785
1786 # Make sure a non-empty dict works.
Christopher Huntb01786c2019-02-12 06:50:49 -05001787 d = {'test': 10, 'bar': '42', 3: 'three'}
Eric V. Smithf0db54a2017-12-04 16:58:55 -05001788 @dataclass
1789 class C:
Christopher Huntb01786c2019-02-12 06:50:49 -05001790 i: int = field(metadata=d)
Eric V. Smithf0db54a2017-12-04 16:58:55 -05001791 self.assertEqual(len(fields(C)[0].metadata), 3)
1792 self.assertEqual(fields(C)[0].metadata['test'], 10)
1793 self.assertEqual(fields(C)[0].metadata['bar'], '42')
1794 self.assertEqual(fields(C)[0].metadata[3], 'three')
Christopher Huntb01786c2019-02-12 06:50:49 -05001795 # Update should work.
1796 d['foo'] = 1
1797 self.assertEqual(len(fields(C)[0].metadata), 4)
1798 self.assertEqual(fields(C)[0].metadata['foo'], 1)
Eric V. Smithf0db54a2017-12-04 16:58:55 -05001799 with self.assertRaises(KeyError):
1800 # Non-existent key.
1801 fields(C)[0].metadata['baz']
1802 with self.assertRaisesRegex(TypeError,
1803 'does not support item assignment'):
1804 fields(C)[0].metadata['test'] = 3
1805
1806 def test_field_metadata_custom_mapping(self):
1807 # Try a custom mapping.
1808 class SimpleNameSpace:
1809 def __init__(self, **kw):
1810 self.__dict__.update(kw)
1811
1812 def __getitem__(self, item):
1813 if item == 'xyzzy':
1814 return 'plugh'
1815 return getattr(self, item)
1816
1817 def __len__(self):
1818 return self.__dict__.__len__()
1819
1820 @dataclass
1821 class C:
1822 i: int = field(metadata=SimpleNameSpace(a=10))
1823
1824 self.assertEqual(len(fields(C)[0].metadata), 1)
1825 self.assertEqual(fields(C)[0].metadata['a'], 10)
1826 with self.assertRaises(AttributeError):
1827 fields(C)[0].metadata['b']
1828 # Make sure we're still talking to our custom mapping.
1829 self.assertEqual(fields(C)[0].metadata['xyzzy'], 'plugh')
1830
1831 def test_generic_dataclasses(self):
1832 T = TypeVar('T')
1833
1834 @dataclass
1835 class LabeledBox(Generic[T]):
1836 content: T
1837 label: str = '<unknown>'
1838
1839 box = LabeledBox(42)
1840 self.assertEqual(box.content, 42)
1841 self.assertEqual(box.label, '<unknown>')
1842
Eric V. Smith2b75fc22018-03-25 20:37:33 -04001843 # Subscripting the resulting class should work, etc.
Eric V. Smithf0db54a2017-12-04 16:58:55 -05001844 Alias = List[LabeledBox[int]]
1845
1846 def test_generic_extending(self):
1847 S = TypeVar('S')
1848 T = TypeVar('T')
1849
1850 @dataclass
1851 class Base(Generic[T, S]):
1852 x: T
1853 y: S
1854
1855 @dataclass
1856 class DataDerived(Base[int, T]):
1857 new_field: str
1858 Alias = DataDerived[str]
1859 c = Alias(0, 'test1', 'test2')
1860 self.assertEqual(astuple(c), (0, 'test1', 'test2'))
1861
1862 class NonDataDerived(Base[int, T]):
1863 def new_method(self):
1864 return self.y
1865 Alias = NonDataDerived[float]
1866 c = Alias(10, 1.0)
1867 self.assertEqual(c.new_method(), 1.0)
1868
Ivan Levkivskyi5a7092d2018-03-31 13:41:17 +01001869 def test_generic_dynamic(self):
1870 T = TypeVar('T')
1871
1872 @dataclass
1873 class Parent(Generic[T]):
1874 x: T
1875 Child = make_dataclass('Child', [('y', T), ('z', Optional[T], None)],
1876 bases=(Parent[int], Generic[T]), namespace={'other': 42})
1877 self.assertIs(Child[int](1, 2).z, None)
1878 self.assertEqual(Child[int](1, 2, 3).z, 3)
1879 self.assertEqual(Child[int](1, 2, 3).other, 42)
1880 # Check that type aliases work correctly.
1881 Alias = Child[T]
1882 self.assertEqual(Alias[int](1, 2).x, 1)
1883 # Check MRO resolution.
1884 self.assertEqual(Child.__mro__, (Child, Parent, Generic, object))
1885
Eric V. Smithf0db54a2017-12-04 16:58:55 -05001886 def test_dataclassses_pickleable(self):
1887 global P, Q, R
1888 @dataclass
1889 class P:
1890 x: int
1891 y: int = 0
1892 @dataclass
1893 class Q:
1894 x: int
1895 y: int = field(default=0, init=False)
1896 @dataclass
1897 class R:
1898 x: int
1899 y: List[int] = field(default_factory=list)
1900 q = Q(1)
1901 q.y = 2
1902 samples = [P(1), P(1, 2), Q(1), q, R(1), R(1, [2, 3, 4])]
1903 for sample in samples:
1904 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1905 with self.subTest(sample=sample, proto=proto):
1906 new_sample = pickle.loads(pickle.dumps(sample, proto))
1907 self.assertEqual(sample.x, new_sample.x)
1908 self.assertEqual(sample.y, new_sample.y)
1909 self.assertIsNot(sample, new_sample)
1910 new_sample.x = 42
1911 another_new_sample = pickle.loads(pickle.dumps(new_sample, proto))
1912 self.assertEqual(new_sample.x, another_new_sample.x)
1913 self.assertEqual(sample.y, another_new_sample.y)
1914
Eric V. Smithea8fc522018-01-27 19:07:40 -05001915
Eric V. Smith56970b82018-03-22 16:28:48 -04001916class TestFieldNoAnnotation(unittest.TestCase):
1917 def test_field_without_annotation(self):
1918 with self.assertRaisesRegex(TypeError,
1919 "'f' is a field but has no type annotation"):
1920 @dataclass
1921 class C:
1922 f = field()
1923
1924 def test_field_without_annotation_but_annotation_in_base(self):
1925 @dataclass
1926 class B:
1927 f: int
1928
1929 with self.assertRaisesRegex(TypeError,
1930 "'f' is a field but has no type annotation"):
1931 # This is still an error: make sure we don't pick up the
Eric V. Smith2b75fc22018-03-25 20:37:33 -04001932 # type annotation in the base class.
Eric V. Smith56970b82018-03-22 16:28:48 -04001933 @dataclass
1934 class C(B):
1935 f = field()
1936
1937 def test_field_without_annotation_but_annotation_in_base_not_dataclass(self):
1938 # Same test, but with the base class not a dataclass.
1939 class B:
1940 f: int
1941
1942 with self.assertRaisesRegex(TypeError,
1943 "'f' is a field but has no type annotation"):
1944 # This is still an error: make sure we don't pick up the
Eric V. Smith2b75fc22018-03-25 20:37:33 -04001945 # type annotation in the base class.
Eric V. Smith56970b82018-03-22 16:28:48 -04001946 @dataclass
1947 class C(B):
1948 f = field()
1949
1950
Eric V. Smithf0db54a2017-12-04 16:58:55 -05001951class TestDocString(unittest.TestCase):
1952 def assertDocStrEqual(self, a, b):
1953 # Because 3.6 and 3.7 differ in how inspect.signature work
1954 # (see bpo #32108), for the time being just compare them with
1955 # whitespace stripped.
1956 self.assertEqual(a.replace(' ', ''), b.replace(' ', ''))
1957
1958 def test_existing_docstring_not_overridden(self):
1959 @dataclass
1960 class C:
1961 """Lorem ipsum"""
1962 x: int
1963
1964 self.assertEqual(C.__doc__, "Lorem ipsum")
1965
1966 def test_docstring_no_fields(self):
1967 @dataclass
1968 class C:
1969 pass
1970
1971 self.assertDocStrEqual(C.__doc__, "C()")
1972
1973 def test_docstring_one_field(self):
1974 @dataclass
1975 class C:
1976 x: int
1977
1978 self.assertDocStrEqual(C.__doc__, "C(x:int)")
1979
1980 def test_docstring_two_fields(self):
1981 @dataclass
1982 class C:
1983 x: int
1984 y: int
1985
1986 self.assertDocStrEqual(C.__doc__, "C(x:int, y:int)")
1987
1988 def test_docstring_three_fields(self):
1989 @dataclass
1990 class C:
1991 x: int
1992 y: int
1993 z: str
1994
1995 self.assertDocStrEqual(C.__doc__, "C(x:int, y:int, z:str)")
1996
1997 def test_docstring_one_field_with_default(self):
1998 @dataclass
1999 class C:
2000 x: int = 3
2001
2002 self.assertDocStrEqual(C.__doc__, "C(x:int=3)")
2003
2004 def test_docstring_one_field_with_default_none(self):
2005 @dataclass
2006 class C:
2007 x: Union[int, type(None)] = None
2008
2009 self.assertDocStrEqual(C.__doc__, "C(x:Union[int, NoneType]=None)")
2010
2011 def test_docstring_list_field(self):
2012 @dataclass
2013 class C:
2014 x: List[int]
2015
2016 self.assertDocStrEqual(C.__doc__, "C(x:List[int])")
2017
2018 def test_docstring_list_field_with_default_factory(self):
2019 @dataclass
2020 class C:
2021 x: List[int] = field(default_factory=list)
2022
2023 self.assertDocStrEqual(C.__doc__, "C(x:List[int]=<factory>)")
2024
2025 def test_docstring_deque_field(self):
2026 @dataclass
2027 class C:
2028 x: deque
2029
2030 self.assertDocStrEqual(C.__doc__, "C(x:collections.deque)")
2031
2032 def test_docstring_deque_field_with_default_factory(self):
2033 @dataclass
2034 class C:
2035 x: deque = field(default_factory=deque)
2036
2037 self.assertDocStrEqual(C.__doc__, "C(x:collections.deque=<factory>)")
2038
2039
Eric V. Smithea8fc522018-01-27 19:07:40 -05002040class TestInit(unittest.TestCase):
2041 def test_base_has_init(self):
2042 class B:
2043 def __init__(self):
2044 self.z = 100
2045 pass
2046
2047 # Make sure that declaring this class doesn't raise an error.
2048 # The issue is that we can't override __init__ in our class,
2049 # but it should be okay to add __init__ to us if our base has
2050 # an __init__.
2051 @dataclass
2052 class C(B):
2053 x: int = 0
2054 c = C(10)
2055 self.assertEqual(c.x, 10)
2056 self.assertNotIn('z', vars(c))
2057
2058 # Make sure that if we don't add an init, the base __init__
2059 # gets called.
2060 @dataclass(init=False)
2061 class C(B):
2062 x: int = 10
2063 c = C()
2064 self.assertEqual(c.x, 10)
2065 self.assertEqual(c.z, 100)
2066
2067 def test_no_init(self):
2068 dataclass(init=False)
2069 class C:
2070 i: int = 0
2071 self.assertEqual(C().i, 0)
2072
2073 dataclass(init=False)
2074 class C:
2075 i: int = 2
2076 def __init__(self):
2077 self.i = 3
2078 self.assertEqual(C().i, 3)
2079
2080 def test_overwriting_init(self):
2081 # If the class has __init__, use it no matter the value of
2082 # init=.
2083
2084 @dataclass
2085 class C:
2086 x: int
2087 def __init__(self, x):
2088 self.x = 2 * x
2089 self.assertEqual(C(3).x, 6)
2090
2091 @dataclass(init=True)
2092 class C:
2093 x: int
2094 def __init__(self, x):
2095 self.x = 2 * x
2096 self.assertEqual(C(4).x, 8)
2097
2098 @dataclass(init=False)
2099 class C:
2100 x: int
2101 def __init__(self, x):
2102 self.x = 2 * x
2103 self.assertEqual(C(5).x, 10)
2104
2105
2106class TestRepr(unittest.TestCase):
2107 def test_repr(self):
2108 @dataclass
2109 class B:
2110 x: int
2111
2112 @dataclass
2113 class C(B):
2114 y: int = 10
2115
2116 o = C(4)
2117 self.assertEqual(repr(o), 'TestRepr.test_repr.<locals>.C(x=4, y=10)')
2118
2119 @dataclass
2120 class D(C):
2121 x: int = 20
2122 self.assertEqual(repr(D()), 'TestRepr.test_repr.<locals>.D(x=20, y=10)')
2123
2124 @dataclass
2125 class C:
2126 @dataclass
2127 class D:
2128 i: int
2129 @dataclass
2130 class E:
2131 pass
2132 self.assertEqual(repr(C.D(0)), 'TestRepr.test_repr.<locals>.C.D(i=0)')
2133 self.assertEqual(repr(C.E()), 'TestRepr.test_repr.<locals>.C.E()')
2134
2135 def test_no_repr(self):
2136 # Test a class with no __repr__ and repr=False.
2137 @dataclass(repr=False)
2138 class C:
2139 x: int
Serhiy Storchaka3fe5ccc2018-07-23 23:37:55 +03002140 self.assertIn(f'{__name__}.TestRepr.test_no_repr.<locals>.C object at',
Eric V. Smithea8fc522018-01-27 19:07:40 -05002141 repr(C(3)))
2142
2143 # Test a class with a __repr__ and repr=False.
2144 @dataclass(repr=False)
2145 class C:
2146 x: int
2147 def __repr__(self):
2148 return 'C-class'
2149 self.assertEqual(repr(C(3)), 'C-class')
2150
2151 def test_overwriting_repr(self):
2152 # If the class has __repr__, use it no matter the value of
2153 # repr=.
2154
2155 @dataclass
2156 class C:
2157 x: int
2158 def __repr__(self):
2159 return 'x'
2160 self.assertEqual(repr(C(0)), 'x')
2161
2162 @dataclass(repr=True)
2163 class C:
2164 x: int
2165 def __repr__(self):
2166 return 'x'
2167 self.assertEqual(repr(C(0)), 'x')
2168
2169 @dataclass(repr=False)
2170 class C:
2171 x: int
2172 def __repr__(self):
2173 return 'x'
2174 self.assertEqual(repr(C(0)), 'x')
2175
2176
Eric V. Smithea8fc522018-01-27 19:07:40 -05002177class TestEq(unittest.TestCase):
2178 def test_no_eq(self):
2179 # Test a class with no __eq__ and eq=False.
2180 @dataclass(eq=False)
2181 class C:
2182 x: int
2183 self.assertNotEqual(C(0), C(0))
2184 c = C(3)
2185 self.assertEqual(c, c)
2186
2187 # Test a class with an __eq__ and eq=False.
2188 @dataclass(eq=False)
2189 class C:
2190 x: int
2191 def __eq__(self, other):
2192 return other == 10
2193 self.assertEqual(C(3), 10)
2194
2195 def test_overwriting_eq(self):
2196 # If the class has __eq__, use it no matter the value of
2197 # eq=.
2198
2199 @dataclass
2200 class C:
2201 x: int
2202 def __eq__(self, other):
2203 return other == 3
2204 self.assertEqual(C(1), 3)
2205 self.assertNotEqual(C(1), 1)
2206
2207 @dataclass(eq=True)
2208 class C:
2209 x: int
2210 def __eq__(self, other):
2211 return other == 4
2212 self.assertEqual(C(1), 4)
2213 self.assertNotEqual(C(1), 1)
2214
2215 @dataclass(eq=False)
2216 class C:
2217 x: int
2218 def __eq__(self, other):
2219 return other == 5
2220 self.assertEqual(C(1), 5)
2221 self.assertNotEqual(C(1), 1)
2222
2223
2224class TestOrdering(unittest.TestCase):
2225 def test_functools_total_ordering(self):
2226 # Test that functools.total_ordering works with this class.
2227 @total_ordering
2228 @dataclass
2229 class C:
2230 x: int
2231 def __lt__(self, other):
2232 # Perform the test "backward", just to make
2233 # sure this is being called.
2234 return self.x >= other
2235
2236 self.assertLess(C(0), -1)
2237 self.assertLessEqual(C(0), -1)
2238 self.assertGreater(C(0), 1)
2239 self.assertGreaterEqual(C(0), 1)
2240
2241 def test_no_order(self):
2242 # Test that no ordering functions are added by default.
2243 @dataclass(order=False)
2244 class C:
2245 x: int
2246 # Make sure no order methods are added.
2247 self.assertNotIn('__le__', C.__dict__)
2248 self.assertNotIn('__lt__', C.__dict__)
2249 self.assertNotIn('__ge__', C.__dict__)
2250 self.assertNotIn('__gt__', C.__dict__)
2251
2252 # Test that __lt__ is still called
2253 @dataclass(order=False)
2254 class C:
2255 x: int
2256 def __lt__(self, other):
2257 return False
2258 # Make sure other methods aren't added.
2259 self.assertNotIn('__le__', C.__dict__)
2260 self.assertNotIn('__ge__', C.__dict__)
2261 self.assertNotIn('__gt__', C.__dict__)
2262
2263 def test_overwriting_order(self):
2264 with self.assertRaisesRegex(TypeError,
2265 'Cannot overwrite attribute __lt__'
2266 '.*using functools.total_ordering'):
2267 @dataclass(order=True)
2268 class C:
2269 x: int
2270 def __lt__(self):
2271 pass
2272
2273 with self.assertRaisesRegex(TypeError,
2274 'Cannot overwrite attribute __le__'
2275 '.*using functools.total_ordering'):
2276 @dataclass(order=True)
2277 class C:
2278 x: int
2279 def __le__(self):
2280 pass
2281
2282 with self.assertRaisesRegex(TypeError,
2283 'Cannot overwrite attribute __gt__'
2284 '.*using functools.total_ordering'):
2285 @dataclass(order=True)
2286 class C:
2287 x: int
2288 def __gt__(self):
2289 pass
2290
2291 with self.assertRaisesRegex(TypeError,
2292 'Cannot overwrite attribute __ge__'
2293 '.*using functools.total_ordering'):
2294 @dataclass(order=True)
2295 class C:
2296 x: int
2297 def __ge__(self):
2298 pass
2299
2300class TestHash(unittest.TestCase):
Eric V. Smithdbf9cff2018-02-25 21:30:17 -05002301 def test_unsafe_hash(self):
2302 @dataclass(unsafe_hash=True)
Eric V. Smithea8fc522018-01-27 19:07:40 -05002303 class C:
2304 x: int
2305 y: str
2306 self.assertEqual(hash(C(1, 'foo')), hash((1, 'foo')))
2307
Eric V. Smithea8fc522018-01-27 19:07:40 -05002308 def test_hash_rules(self):
2309 def non_bool(value):
2310 # Map to something else that's True, but not a bool.
2311 if value is None:
2312 return None
2313 if value:
2314 return (3,)
2315 return 0
2316
Eric V. Smithdbf9cff2018-02-25 21:30:17 -05002317 def test(case, unsafe_hash, eq, frozen, with_hash, result):
2318 with self.subTest(case=case, unsafe_hash=unsafe_hash, eq=eq,
2319 frozen=frozen):
2320 if result != 'exception':
2321 if with_hash:
2322 @dataclass(unsafe_hash=unsafe_hash, eq=eq, frozen=frozen)
2323 class C:
2324 def __hash__(self):
2325 return 0
2326 else:
2327 @dataclass(unsafe_hash=unsafe_hash, eq=eq, frozen=frozen)
2328 class C:
2329 pass
Eric V. Smithea8fc522018-01-27 19:07:40 -05002330
2331 # See if the result matches what's expected.
Eric V. Smithdbf9cff2018-02-25 21:30:17 -05002332 if result == 'fn':
Eric V. Smithea8fc522018-01-27 19:07:40 -05002333 # __hash__ contains the function we generated.
2334 self.assertIn('__hash__', C.__dict__)
2335 self.assertIsNotNone(C.__dict__['__hash__'])
2336
Eric V. Smithea8fc522018-01-27 19:07:40 -05002337 elif result == '':
2338 # __hash__ is not present in our class.
2339 if not with_hash:
2340 self.assertNotIn('__hash__', C.__dict__)
Eric V. Smithdbf9cff2018-02-25 21:30:17 -05002341
Eric V. Smithea8fc522018-01-27 19:07:40 -05002342 elif result == 'none':
2343 # __hash__ is set to None.
2344 self.assertIn('__hash__', C.__dict__)
2345 self.assertIsNone(C.__dict__['__hash__'])
Eric V. Smithdbf9cff2018-02-25 21:30:17 -05002346
2347 elif result == 'exception':
2348 # Creating the class should cause an exception.
2349 # This only happens with with_hash==True.
2350 assert(with_hash)
2351 with self.assertRaisesRegex(TypeError, 'Cannot overwrite attribute __hash__'):
2352 @dataclass(unsafe_hash=unsafe_hash, eq=eq, frozen=frozen)
2353 class C:
2354 def __hash__(self):
2355 return 0
2356
Eric V. Smithea8fc522018-01-27 19:07:40 -05002357 else:
2358 assert False, f'unknown result {result!r}'
2359
Eric V. Smithdbf9cff2018-02-25 21:30:17 -05002360 # There are 8 cases of:
2361 # unsafe_hash=True/False
Eric V. Smithea8fc522018-01-27 19:07:40 -05002362 # eq=True/False
2363 # frozen=True/False
2364 # And for each of these, a different result if
2365 # __hash__ is defined or not.
Eric V. Smithdbf9cff2018-02-25 21:30:17 -05002366 for case, (unsafe_hash, eq, frozen, res_no_defined_hash, res_defined_hash) in enumerate([
2367 (False, False, False, '', ''),
2368 (False, False, True, '', ''),
2369 (False, True, False, 'none', ''),
2370 (False, True, True, 'fn', ''),
2371 (True, False, False, 'fn', 'exception'),
2372 (True, False, True, 'fn', 'exception'),
2373 (True, True, False, 'fn', 'exception'),
2374 (True, True, True, 'fn', 'exception'),
2375 ], 1):
2376 test(case, unsafe_hash, eq, frozen, False, res_no_defined_hash)
2377 test(case, unsafe_hash, eq, frozen, True, res_defined_hash)
Eric V. Smithea8fc522018-01-27 19:07:40 -05002378
2379 # Test non-bool truth values, too. This is just to
2380 # make sure the data-driven table in the decorator
2381 # handles non-bool values.
Eric V. Smithdbf9cff2018-02-25 21:30:17 -05002382 test(case, non_bool(unsafe_hash), non_bool(eq), non_bool(frozen), False, res_no_defined_hash)
2383 test(case, non_bool(unsafe_hash), non_bool(eq), non_bool(frozen), True, res_defined_hash)
Eric V. Smithea8fc522018-01-27 19:07:40 -05002384
2385
2386 def test_eq_only(self):
2387 # If a class defines __eq__, __hash__ is automatically added
2388 # and set to None. This is normal Python behavior, not
2389 # related to dataclasses. Make sure we don't interfere with
2390 # that (see bpo=32546).
2391
2392 @dataclass
2393 class C:
2394 i: int
2395 def __eq__(self, other):
2396 return self.i == other.i
2397 self.assertEqual(C(1), C(1))
2398 self.assertNotEqual(C(1), C(4))
2399
2400 # And make sure things work in this case if we specify
Eric V. Smithdbf9cff2018-02-25 21:30:17 -05002401 # unsafe_hash=True.
2402 @dataclass(unsafe_hash=True)
Eric V. Smithea8fc522018-01-27 19:07:40 -05002403 class C:
2404 i: int
2405 def __eq__(self, other):
2406 return self.i == other.i
2407 self.assertEqual(C(1), C(1.0))
2408 self.assertEqual(hash(C(1)), hash(C(1.0)))
2409
2410 # And check that the classes __eq__ is being used, despite
2411 # specifying eq=True.
Eric V. Smithdbf9cff2018-02-25 21:30:17 -05002412 @dataclass(unsafe_hash=True, eq=True)
Eric V. Smithea8fc522018-01-27 19:07:40 -05002413 class C:
2414 i: int
2415 def __eq__(self, other):
2416 return self.i == 3 and self.i == other.i
2417 self.assertEqual(C(3), C(3))
2418 self.assertNotEqual(C(1), C(1))
2419 self.assertEqual(hash(C(1)), hash(C(1.0)))
2420
Eric V. Smithdbf9cff2018-02-25 21:30:17 -05002421 def test_0_field_hash(self):
2422 @dataclass(frozen=True)
2423 class C:
2424 pass
2425 self.assertEqual(hash(C()), hash(()))
2426
2427 @dataclass(unsafe_hash=True)
2428 class C:
2429 pass
2430 self.assertEqual(hash(C()), hash(()))
2431
2432 def test_1_field_hash(self):
2433 @dataclass(frozen=True)
2434 class C:
2435 x: int
2436 self.assertEqual(hash(C(4)), hash((4,)))
2437 self.assertEqual(hash(C(42)), hash((42,)))
2438
2439 @dataclass(unsafe_hash=True)
2440 class C:
2441 x: int
2442 self.assertEqual(hash(C(4)), hash((4,)))
2443 self.assertEqual(hash(C(42)), hash((42,)))
2444
Eric V. Smith718070d2018-02-23 13:01:31 -05002445 def test_hash_no_args(self):
2446 # Test dataclasses with no hash= argument. This exists to
Eric V. Smith2b75fc22018-03-25 20:37:33 -04002447 # make sure that if the @dataclass parameter name is changed
2448 # or the non-default hashing behavior changes, the default
2449 # hashability keeps working the same way.
Eric V. Smith718070d2018-02-23 13:01:31 -05002450
2451 class Base:
2452 def __hash__(self):
2453 return 301
2454
2455 # If frozen or eq is None, then use the default value (do not
Eric V. Smith2b75fc22018-03-25 20:37:33 -04002456 # specify any value in the decorator).
Eric V. Smith718070d2018-02-23 13:01:31 -05002457 for frozen, eq, base, expected in [
2458 (None, None, object, 'unhashable'),
2459 (None, None, Base, 'unhashable'),
2460 (None, False, object, 'object'),
2461 (None, False, Base, 'base'),
2462 (None, True, object, 'unhashable'),
2463 (None, True, Base, 'unhashable'),
2464 (False, None, object, 'unhashable'),
2465 (False, None, Base, 'unhashable'),
2466 (False, False, object, 'object'),
2467 (False, False, Base, 'base'),
2468 (False, True, object, 'unhashable'),
2469 (False, True, Base, 'unhashable'),
2470 (True, None, object, 'tuple'),
2471 (True, None, Base, 'tuple'),
2472 (True, False, object, 'object'),
2473 (True, False, Base, 'base'),
2474 (True, True, object, 'tuple'),
2475 (True, True, Base, 'tuple'),
2476 ]:
2477
2478 with self.subTest(frozen=frozen, eq=eq, base=base, expected=expected):
2479 # First, create the class.
2480 if frozen is None and eq is None:
2481 @dataclass
2482 class C(base):
2483 i: int
2484 elif frozen is None:
2485 @dataclass(eq=eq)
2486 class C(base):
2487 i: int
2488 elif eq is None:
2489 @dataclass(frozen=frozen)
2490 class C(base):
2491 i: int
2492 else:
2493 @dataclass(frozen=frozen, eq=eq)
2494 class C(base):
2495 i: int
2496
2497 # Now, make sure it hashes as expected.
2498 if expected == 'unhashable':
2499 c = C(10)
2500 with self.assertRaisesRegex(TypeError, 'unhashable type'):
2501 hash(c)
2502
2503 elif expected == 'base':
2504 self.assertEqual(hash(C(10)), 301)
2505
2506 elif expected == 'object':
2507 # I'm not sure what test to use here. object's
Eric V. Smith2b75fc22018-03-25 20:37:33 -04002508 # hash isn't based on id(), so calling hash()
2509 # won't tell us much. So, just check the
2510 # function used is object's.
Eric V. Smith718070d2018-02-23 13:01:31 -05002511 self.assertIs(C.__hash__, object.__hash__)
2512
2513 elif expected == 'tuple':
2514 self.assertEqual(hash(C(42)), hash((42,)))
2515
2516 else:
2517 assert False, f'unknown value for expected={expected!r}'
2518
Eric V. Smithea8fc522018-01-27 19:07:40 -05002519
Eric V. Smith2fa6b9e2018-02-26 20:38:33 -05002520class TestFrozen(unittest.TestCase):
2521 def test_frozen(self):
2522 @dataclass(frozen=True)
2523 class C:
2524 i: int
2525
2526 c = C(10)
2527 self.assertEqual(c.i, 10)
2528 with self.assertRaises(FrozenInstanceError):
2529 c.i = 5
2530 self.assertEqual(c.i, 10)
2531
2532 def test_inherit(self):
2533 @dataclass(frozen=True)
2534 class C:
2535 i: int
2536
2537 @dataclass(frozen=True)
2538 class D(C):
2539 j: int
2540
2541 d = D(0, 10)
2542 with self.assertRaises(FrozenInstanceError):
2543 d.i = 5
Eric V. Smithf199bc62018-03-18 20:40:34 -04002544 with self.assertRaises(FrozenInstanceError):
2545 d.j = 6
Eric V. Smith2fa6b9e2018-02-26 20:38:33 -05002546 self.assertEqual(d.i, 0)
Eric V. Smithf199bc62018-03-18 20:40:34 -04002547 self.assertEqual(d.j, 10)
Eric V. Smith2fa6b9e2018-02-26 20:38:33 -05002548
Eric V. Smithf199bc62018-03-18 20:40:34 -04002549 # Test both ways: with an intermediate normal (non-dataclass)
2550 # class and without an intermediate class.
2551 def test_inherit_nonfrozen_from_frozen(self):
2552 for intermediate_class in [True, False]:
2553 with self.subTest(intermediate_class=intermediate_class):
2554 @dataclass(frozen=True)
2555 class C:
2556 i: int
Eric V. Smith2fa6b9e2018-02-26 20:38:33 -05002557
Eric V. Smithf199bc62018-03-18 20:40:34 -04002558 if intermediate_class:
2559 class I(C): pass
2560 else:
2561 I = C
Eric V. Smith2fa6b9e2018-02-26 20:38:33 -05002562
Eric V. Smithf199bc62018-03-18 20:40:34 -04002563 with self.assertRaisesRegex(TypeError,
2564 'cannot inherit non-frozen dataclass from a frozen one'):
2565 @dataclass
2566 class D(I):
2567 pass
Eric V. Smith2fa6b9e2018-02-26 20:38:33 -05002568
Eric V. Smithf199bc62018-03-18 20:40:34 -04002569 def test_inherit_frozen_from_nonfrozen(self):
2570 for intermediate_class in [True, False]:
2571 with self.subTest(intermediate_class=intermediate_class):
2572 @dataclass
2573 class C:
2574 i: int
2575
2576 if intermediate_class:
2577 class I(C): pass
2578 else:
2579 I = C
2580
2581 with self.assertRaisesRegex(TypeError,
2582 'cannot inherit frozen dataclass from a non-frozen one'):
2583 @dataclass(frozen=True)
2584 class D(I):
2585 pass
Eric V. Smith2fa6b9e2018-02-26 20:38:33 -05002586
2587 def test_inherit_from_normal_class(self):
Eric V. Smithf199bc62018-03-18 20:40:34 -04002588 for intermediate_class in [True, False]:
2589 with self.subTest(intermediate_class=intermediate_class):
2590 class C:
2591 pass
2592
2593 if intermediate_class:
2594 class I(C): pass
2595 else:
2596 I = C
2597
2598 @dataclass(frozen=True)
2599 class D(I):
2600 i: int
2601
2602 d = D(10)
2603 with self.assertRaises(FrozenInstanceError):
2604 d.i = 5
2605
2606 def test_non_frozen_normal_derived(self):
2607 # See bpo-32953.
Eric V. Smith2fa6b9e2018-02-26 20:38:33 -05002608
2609 @dataclass(frozen=True)
Eric V. Smithf199bc62018-03-18 20:40:34 -04002610 class D:
2611 x: int
2612 y: int = 10
Eric V. Smith2fa6b9e2018-02-26 20:38:33 -05002613
Eric V. Smithf199bc62018-03-18 20:40:34 -04002614 class S(D):
2615 pass
2616
2617 s = S(3)
2618 self.assertEqual(s.x, 3)
2619 self.assertEqual(s.y, 10)
2620 s.cached = True
2621
2622 # But can't change the frozen attributes.
Eric V. Smith2fa6b9e2018-02-26 20:38:33 -05002623 with self.assertRaises(FrozenInstanceError):
Eric V. Smithf199bc62018-03-18 20:40:34 -04002624 s.x = 5
2625 with self.assertRaises(FrozenInstanceError):
2626 s.y = 5
2627 self.assertEqual(s.x, 3)
2628 self.assertEqual(s.y, 10)
2629 self.assertEqual(s.cached, True)
Eric V. Smith2fa6b9e2018-02-26 20:38:33 -05002630
Eric V. Smith74940912018-04-05 06:50:18 -04002631 def test_overwriting_frozen(self):
2632 # frozen uses __setattr__ and __delattr__.
2633 with self.assertRaisesRegex(TypeError,
2634 'Cannot overwrite attribute __setattr__'):
2635 @dataclass(frozen=True)
2636 class C:
2637 x: int
2638 def __setattr__(self):
2639 pass
2640
2641 with self.assertRaisesRegex(TypeError,
2642 'Cannot overwrite attribute __delattr__'):
2643 @dataclass(frozen=True)
2644 class C:
2645 x: int
2646 def __delattr__(self):
2647 pass
2648
2649 @dataclass(frozen=False)
2650 class C:
2651 x: int
2652 def __setattr__(self, name, value):
2653 self.__dict__['x'] = value * 2
2654 self.assertEqual(C(10).x, 20)
2655
2656 def test_frozen_hash(self):
2657 @dataclass(frozen=True)
2658 class C:
2659 x: Any
2660
2661 # If x is immutable, we can compute the hash. No exception is
2662 # raised.
2663 hash(C(3))
2664
2665 # If x is mutable, computing the hash is an error.
2666 with self.assertRaisesRegex(TypeError, 'unhashable type'):
2667 hash(C({}))
2668
Eric V. Smith2fa6b9e2018-02-26 20:38:33 -05002669
Eric V. Smith7389fd92018-03-19 21:07:51 -04002670class TestSlots(unittest.TestCase):
2671 def test_simple(self):
2672 @dataclass
2673 class C:
2674 __slots__ = ('x',)
2675 x: Any
2676
Eric V. Smith2b75fc22018-03-25 20:37:33 -04002677 # There was a bug where a variable in a slot was assumed to
2678 # also have a default value (of type
2679 # types.MemberDescriptorType).
Eric V. Smith7389fd92018-03-19 21:07:51 -04002680 with self.assertRaisesRegex(TypeError,
Eric V. Smithc42e7aa2018-03-24 23:02:21 -04002681 r"__init__\(\) missing 1 required positional argument: 'x'"):
Eric V. Smith7389fd92018-03-19 21:07:51 -04002682 C()
2683
2684 # We can create an instance, and assign to x.
2685 c = C(10)
2686 self.assertEqual(c.x, 10)
2687 c.x = 5
2688 self.assertEqual(c.x, 5)
2689
2690 # We can't assign to anything else.
2691 with self.assertRaisesRegex(AttributeError, "'C' object has no attribute 'y'"):
2692 c.y = 5
2693
2694 def test_derived_added_field(self):
2695 # See bpo-33100.
2696 @dataclass
2697 class Base:
2698 __slots__ = ('x',)
2699 x: Any
2700
2701 @dataclass
2702 class Derived(Base):
2703 x: int
2704 y: int
2705
2706 d = Derived(1, 2)
2707 self.assertEqual((d.x, d.y), (1, 2))
2708
2709 # We can add a new field to the derived instance.
2710 d.z = 10
2711
Eric V. Smithde7a2f02018-03-26 13:29:16 -04002712class TestDescriptors(unittest.TestCase):
2713 def test_set_name(self):
2714 # See bpo-33141.
2715
2716 # Create a descriptor.
2717 class D:
2718 def __set_name__(self, owner, name):
Eric V. Smith52199522018-03-29 11:07:48 -04002719 self.name = name + 'x'
Eric V. Smithde7a2f02018-03-26 13:29:16 -04002720 def __get__(self, instance, owner):
2721 if instance is not None:
2722 return 1
2723 return self
2724
2725 # This is the case of just normal descriptor behavior, no
2726 # dataclass code is involved in initializing the descriptor.
2727 @dataclass
2728 class C:
2729 c: int=D()
Eric V. Smith52199522018-03-29 11:07:48 -04002730 self.assertEqual(C.c.name, 'cx')
Eric V. Smithde7a2f02018-03-26 13:29:16 -04002731
2732 # Now test with a default value and init=False, which is the
2733 # only time this is really meaningful. If not using
2734 # init=False, then the descriptor will be overwritten, anyway.
2735 @dataclass
2736 class C:
2737 c: int=field(default=D(), init=False)
Eric V. Smith52199522018-03-29 11:07:48 -04002738 self.assertEqual(C.c.name, 'cx')
Eric V. Smithde7a2f02018-03-26 13:29:16 -04002739 self.assertEqual(C().c, 1)
2740
2741 def test_non_descriptor(self):
2742 # PEP 487 says __set_name__ should work on non-descriptors.
2743 # Create a descriptor.
2744
2745 class D:
2746 def __set_name__(self, owner, name):
Eric V. Smith52199522018-03-29 11:07:48 -04002747 self.name = name + 'x'
Eric V. Smithde7a2f02018-03-26 13:29:16 -04002748
2749 @dataclass
2750 class C:
2751 c: int=field(default=D(), init=False)
Eric V. Smith52199522018-03-29 11:07:48 -04002752 self.assertEqual(C.c.name, 'cx')
2753
2754 def test_lookup_on_instance(self):
2755 # See bpo-33175.
2756 class D:
2757 pass
2758
2759 d = D()
2760 # Create an attribute on the instance, not type.
2761 d.__set_name__ = Mock()
2762
2763 # Make sure d.__set_name__ is not called.
2764 @dataclass
2765 class C:
2766 i: int=field(default=d, init=False)
2767
2768 self.assertEqual(d.__set_name__.call_count, 0)
2769
2770 def test_lookup_on_class(self):
2771 # See bpo-33175.
2772 class D:
2773 pass
2774 D.__set_name__ = Mock()
2775
2776 # Make sure D.__set_name__ is called.
2777 @dataclass
2778 class C:
2779 i: int=field(default=D(), init=False)
2780
2781 self.assertEqual(D.__set_name__.call_count, 1)
Eric V. Smithde7a2f02018-03-26 13:29:16 -04002782
Eric V. Smith7389fd92018-03-19 21:07:51 -04002783
Eric V. Smith2a7bacb2018-05-15 22:44:27 -04002784class TestStringAnnotations(unittest.TestCase):
2785 def test_classvar(self):
2786 # Some expressions recognized as ClassVar really aren't. But
2787 # if you're using string annotations, it's not an exact
2788 # science.
2789 # These tests assume that both "import typing" and "from
2790 # typing import *" have been run in this file.
2791 for typestr in ('ClassVar[int]',
2792 'ClassVar [int]'
2793 ' ClassVar [int]',
2794 'ClassVar',
2795 ' ClassVar ',
2796 'typing.ClassVar[int]',
2797 'typing.ClassVar[str]',
2798 ' typing.ClassVar[str]',
2799 'typing .ClassVar[str]',
2800 'typing. ClassVar[str]',
2801 'typing.ClassVar [str]',
2802 'typing.ClassVar [ str]',
2803
2804 # Not syntactically valid, but these will
2805 # be treated as ClassVars.
2806 'typing.ClassVar.[int]',
2807 'typing.ClassVar+',
2808 ):
2809 with self.subTest(typestr=typestr):
2810 @dataclass
2811 class C:
2812 x: typestr
2813
2814 # x is a ClassVar, so C() takes no args.
2815 C()
2816
2817 # And it won't appear in the class's dict because it doesn't
2818 # have a default.
2819 self.assertNotIn('x', C.__dict__)
2820
2821 def test_isnt_classvar(self):
2822 for typestr in ('CV',
2823 't.ClassVar',
2824 't.ClassVar[int]',
2825 'typing..ClassVar[int]',
2826 'Classvar',
2827 'Classvar[int]',
2828 'typing.ClassVarx[int]',
2829 'typong.ClassVar[int]',
2830 'dataclasses.ClassVar[int]',
2831 'typingxClassVar[str]',
2832 ):
2833 with self.subTest(typestr=typestr):
2834 @dataclass
2835 class C:
2836 x: typestr
2837
2838 # x is not a ClassVar, so C() takes one arg.
2839 self.assertEqual(C(10).x, 10)
2840
2841 def test_initvar(self):
2842 # These tests assume that both "import dataclasses" and "from
2843 # dataclasses import *" have been run in this file.
2844 for typestr in ('InitVar[int]',
2845 'InitVar [int]'
2846 ' InitVar [int]',
2847 'InitVar',
2848 ' InitVar ',
2849 'dataclasses.InitVar[int]',
2850 'dataclasses.InitVar[str]',
2851 ' dataclasses.InitVar[str]',
2852 'dataclasses .InitVar[str]',
2853 'dataclasses. InitVar[str]',
2854 'dataclasses.InitVar [str]',
2855 'dataclasses.InitVar [ str]',
2856
2857 # Not syntactically valid, but these will
2858 # be treated as InitVars.
2859 'dataclasses.InitVar.[int]',
2860 'dataclasses.InitVar+',
2861 ):
2862 with self.subTest(typestr=typestr):
2863 @dataclass
2864 class C:
2865 x: typestr
2866
2867 # x is an InitVar, so doesn't create a member.
2868 with self.assertRaisesRegex(AttributeError,
2869 "object has no attribute 'x'"):
2870 C(1).x
2871
2872 def test_isnt_initvar(self):
2873 for typestr in ('IV',
2874 'dc.InitVar',
2875 'xdataclasses.xInitVar',
2876 'typing.xInitVar[int]',
2877 ):
2878 with self.subTest(typestr=typestr):
2879 @dataclass
2880 class C:
2881 x: typestr
2882
2883 # x is not an InitVar, so there will be a member x.
2884 self.assertEqual(C(10).x, 10)
2885
2886 def test_classvar_module_level_import(self):
Serhiy Storchaka3fe5ccc2018-07-23 23:37:55 +03002887 from test import dataclass_module_1
2888 from test import dataclass_module_1_str
2889 from test import dataclass_module_2
2890 from test import dataclass_module_2_str
Eric V. Smith2a7bacb2018-05-15 22:44:27 -04002891
2892 for m in (dataclass_module_1, dataclass_module_1_str,
2893 dataclass_module_2, dataclass_module_2_str,
2894 ):
2895 with self.subTest(m=m):
2896 # There's a difference in how the ClassVars are
2897 # interpreted when using string annotations or
2898 # not. See the imported modules for details.
2899 if m.USING_STRINGS:
2900 c = m.CV(10)
2901 else:
2902 c = m.CV()
2903 self.assertEqual(c.cv0, 20)
2904
2905
2906 # There's a difference in how the InitVars are
2907 # interpreted when using string annotations or
2908 # not. See the imported modules for details.
2909 c = m.IV(0, 1, 2, 3, 4)
2910
2911 for field_name in ('iv0', 'iv1', 'iv2', 'iv3'):
2912 with self.subTest(field_name=field_name):
2913 with self.assertRaisesRegex(AttributeError, f"object has no attribute '{field_name}'"):
2914 # Since field_name is an InitVar, it's
2915 # not an instance field.
2916 getattr(c, field_name)
2917
2918 if m.USING_STRINGS:
2919 # iv4 is interpreted as a normal field.
2920 self.assertIn('not_iv4', c.__dict__)
2921 self.assertEqual(c.not_iv4, 4)
2922 else:
2923 # iv4 is interpreted as an InitVar, so it
2924 # won't exist on the instance.
2925 self.assertNotIn('not_iv4', c.__dict__)
2926
2927
Eric V. Smith4e812962018-05-16 11:31:29 -04002928class TestMakeDataclass(unittest.TestCase):
2929 def test_simple(self):
2930 C = make_dataclass('C',
2931 [('x', int),
2932 ('y', int, field(default=5))],
2933 namespace={'add_one': lambda self: self.x + 1})
2934 c = C(10)
2935 self.assertEqual((c.x, c.y), (10, 5))
2936 self.assertEqual(c.add_one(), 11)
2937
2938
2939 def test_no_mutate_namespace(self):
2940 # Make sure a provided namespace isn't mutated.
2941 ns = {}
2942 C = make_dataclass('C',
2943 [('x', int),
2944 ('y', int, field(default=5))],
2945 namespace=ns)
2946 self.assertEqual(ns, {})
2947
2948 def test_base(self):
2949 class Base1:
2950 pass
2951 class Base2:
2952 pass
2953 C = make_dataclass('C',
2954 [('x', int)],
2955 bases=(Base1, Base2))
2956 c = C(2)
2957 self.assertIsInstance(c, C)
2958 self.assertIsInstance(c, Base1)
2959 self.assertIsInstance(c, Base2)
2960
2961 def test_base_dataclass(self):
2962 @dataclass
2963 class Base1:
2964 x: int
2965 class Base2:
2966 pass
2967 C = make_dataclass('C',
2968 [('y', int)],
2969 bases=(Base1, Base2))
2970 with self.assertRaisesRegex(TypeError, 'required positional'):
2971 c = C(2)
2972 c = C(1, 2)
2973 self.assertIsInstance(c, C)
2974 self.assertIsInstance(c, Base1)
2975 self.assertIsInstance(c, Base2)
2976
2977 self.assertEqual((c.x, c.y), (1, 2))
2978
2979 def test_init_var(self):
2980 def post_init(self, y):
2981 self.x *= y
2982
2983 C = make_dataclass('C',
2984 [('x', int),
2985 ('y', InitVar[int]),
2986 ],
2987 namespace={'__post_init__': post_init},
2988 )
2989 c = C(2, 3)
2990 self.assertEqual(vars(c), {'x': 6})
2991 self.assertEqual(len(fields(c)), 1)
2992
2993 def test_class_var(self):
2994 C = make_dataclass('C',
2995 [('x', int),
2996 ('y', ClassVar[int], 10),
2997 ('z', ClassVar[int], field(default=20)),
2998 ])
2999 c = C(1)
3000 self.assertEqual(vars(c), {'x': 1})
3001 self.assertEqual(len(fields(c)), 1)
3002 self.assertEqual(C.y, 10)
3003 self.assertEqual(C.z, 20)
3004
3005 def test_other_params(self):
3006 C = make_dataclass('C',
3007 [('x', int),
3008 ('y', ClassVar[int], 10),
3009 ('z', ClassVar[int], field(default=20)),
3010 ],
3011 init=False)
3012 # Make sure we have a repr, but no init.
3013 self.assertNotIn('__init__', vars(C))
3014 self.assertIn('__repr__', vars(C))
3015
3016 # Make sure random other params don't work.
3017 with self.assertRaisesRegex(TypeError, 'unexpected keyword argument'):
3018 C = make_dataclass('C',
3019 [],
3020 xxinit=False)
3021
3022 def test_no_types(self):
3023 C = make_dataclass('Point', ['x', 'y', 'z'])
3024 c = C(1, 2, 3)
3025 self.assertEqual(vars(c), {'x': 1, 'y': 2, 'z': 3})
3026 self.assertEqual(C.__annotations__, {'x': 'typing.Any',
3027 'y': 'typing.Any',
3028 'z': 'typing.Any'})
3029
3030 C = make_dataclass('Point', ['x', ('y', int), 'z'])
3031 c = C(1, 2, 3)
3032 self.assertEqual(vars(c), {'x': 1, 'y': 2, 'z': 3})
3033 self.assertEqual(C.__annotations__, {'x': 'typing.Any',
3034 'y': int,
3035 'z': 'typing.Any'})
3036
3037 def test_invalid_type_specification(self):
3038 for bad_field in [(),
3039 (1, 2, 3, 4),
3040 ]:
3041 with self.subTest(bad_field=bad_field):
3042 with self.assertRaisesRegex(TypeError, r'Invalid field: '):
3043 make_dataclass('C', ['a', bad_field])
3044
3045 # And test for things with no len().
3046 for bad_field in [float,
3047 lambda x:x,
3048 ]:
3049 with self.subTest(bad_field=bad_field):
3050 with self.assertRaisesRegex(TypeError, r'has no len\(\)'):
3051 make_dataclass('C', ['a', bad_field])
3052
3053 def test_duplicate_field_names(self):
3054 for field in ['a', 'ab']:
3055 with self.subTest(field=field):
3056 with self.assertRaisesRegex(TypeError, 'Field name duplicated'):
3057 make_dataclass('C', [field, 'a', field])
3058
3059 def test_keyword_field_names(self):
3060 for field in ['for', 'async', 'await', 'as']:
3061 with self.subTest(field=field):
3062 with self.assertRaisesRegex(TypeError, 'must not be keywords'):
3063 make_dataclass('C', ['a', field])
3064 with self.assertRaisesRegex(TypeError, 'must not be keywords'):
3065 make_dataclass('C', [field])
3066 with self.assertRaisesRegex(TypeError, 'must not be keywords'):
3067 make_dataclass('C', [field, 'a'])
3068
3069 def test_non_identifier_field_names(self):
3070 for field in ['()', 'x,y', '*', '2@3', '', 'little johnny tables']:
3071 with self.subTest(field=field):
Min ho Kim96e12d52019-07-22 06:12:33 +10003072 with self.assertRaisesRegex(TypeError, 'must be valid identifiers'):
Eric V. Smith4e812962018-05-16 11:31:29 -04003073 make_dataclass('C', ['a', field])
Min ho Kim96e12d52019-07-22 06:12:33 +10003074 with self.assertRaisesRegex(TypeError, 'must be valid identifiers'):
Eric V. Smith4e812962018-05-16 11:31:29 -04003075 make_dataclass('C', [field])
Min ho Kim96e12d52019-07-22 06:12:33 +10003076 with self.assertRaisesRegex(TypeError, 'must be valid identifiers'):
Eric V. Smith4e812962018-05-16 11:31:29 -04003077 make_dataclass('C', [field, 'a'])
3078
3079 def test_underscore_field_names(self):
3080 # Unlike namedtuple, it's okay if dataclass field names have
3081 # an underscore.
3082 make_dataclass('C', ['_', '_a', 'a_a', 'a_'])
3083
3084 def test_funny_class_names_names(self):
3085 # No reason to prevent weird class names, since
3086 # types.new_class allows them.
3087 for classname in ['()', 'x,y', '*', '2@3', '']:
3088 with self.subTest(classname=classname):
3089 C = make_dataclass(classname, ['a', 'b'])
3090 self.assertEqual(C.__name__, classname)
3091
Eric V. Smithe7adf2b2018-06-07 14:43:59 -04003092class TestReplace(unittest.TestCase):
3093 def test(self):
3094 @dataclass(frozen=True)
3095 class C:
3096 x: int
3097 y: int
3098
3099 c = C(1, 2)
3100 c1 = replace(c, x=3)
3101 self.assertEqual(c1.x, 3)
3102 self.assertEqual(c1.y, 2)
3103
3104 def test_frozen(self):
3105 @dataclass(frozen=True)
3106 class C:
3107 x: int
3108 y: int
3109 z: int = field(init=False, default=10)
3110 t: int = field(init=False, default=100)
3111
3112 c = C(1, 2)
3113 c1 = replace(c, x=3)
3114 self.assertEqual((c.x, c.y, c.z, c.t), (1, 2, 10, 100))
3115 self.assertEqual((c1.x, c1.y, c1.z, c1.t), (3, 2, 10, 100))
3116
3117
3118 with self.assertRaisesRegex(ValueError, 'init=False'):
3119 replace(c, x=3, z=20, t=50)
3120 with self.assertRaisesRegex(ValueError, 'init=False'):
3121 replace(c, z=20)
3122 replace(c, x=3, z=20, t=50)
3123
3124 # Make sure the result is still frozen.
3125 with self.assertRaisesRegex(FrozenInstanceError, "cannot assign to field 'x'"):
3126 c1.x = 3
3127
3128 # Make sure we can't replace an attribute that doesn't exist,
3129 # if we're also replacing one that does exist. Test this
3130 # here, because setting attributes on frozen instances is
3131 # handled slightly differently from non-frozen ones.
3132 with self.assertRaisesRegex(TypeError, r"__init__\(\) got an unexpected "
3133 "keyword argument 'a'"):
3134 c1 = replace(c, x=20, a=5)
3135
3136 def test_invalid_field_name(self):
3137 @dataclass(frozen=True)
3138 class C:
3139 x: int
3140 y: int
3141
3142 c = C(1, 2)
3143 with self.assertRaisesRegex(TypeError, r"__init__\(\) got an unexpected "
3144 "keyword argument 'z'"):
3145 c1 = replace(c, z=3)
3146
3147 def test_invalid_object(self):
3148 @dataclass(frozen=True)
3149 class C:
3150 x: int
3151 y: int
3152
3153 with self.assertRaisesRegex(TypeError, 'dataclass instance'):
3154 replace(C, x=3)
3155
3156 with self.assertRaisesRegex(TypeError, 'dataclass instance'):
3157 replace(0, x=3)
3158
3159 def test_no_init(self):
3160 @dataclass
3161 class C:
3162 x: int
3163 y: int = field(init=False, default=10)
3164
3165 c = C(1)
3166 c.y = 20
3167
3168 # Make sure y gets the default value.
3169 c1 = replace(c, x=5)
3170 self.assertEqual((c1.x, c1.y), (5, 10))
3171
3172 # Trying to replace y is an error.
3173 with self.assertRaisesRegex(ValueError, 'init=False'):
3174 replace(c, x=2, y=30)
3175
3176 with self.assertRaisesRegex(ValueError, 'init=False'):
3177 replace(c, y=30)
3178
3179 def test_classvar(self):
3180 @dataclass
3181 class C:
3182 x: int
3183 y: ClassVar[int] = 1000
3184
3185 c = C(1)
3186 d = C(2)
3187
3188 self.assertIs(c.y, d.y)
3189 self.assertEqual(c.y, 1000)
3190
3191 # Trying to replace y is an error: can't replace ClassVars.
3192 with self.assertRaisesRegex(TypeError, r"__init__\(\) got an "
3193 "unexpected keyword argument 'y'"):
3194 replace(c, y=30)
3195
3196 replace(c, x=5)
3197
Dong-hee Na3d70f7a2018-06-23 23:46:32 +09003198 def test_initvar_is_specified(self):
3199 @dataclass
3200 class C:
3201 x: int
3202 y: InitVar[int]
3203
3204 def __post_init__(self, y):
3205 self.x *= y
3206
3207 c = C(1, 10)
3208 self.assertEqual(c.x, 10)
3209 with self.assertRaisesRegex(ValueError, r"InitVar 'y' must be "
3210 "specified with replace()"):
3211 replace(c, x=3)
3212 c = replace(c, x=3, y=5)
3213 self.assertEqual(c.x, 15)
Srinivas Thatiparthy (శ్రీనివాస్ తాటిపర్తి)dd13c882018-10-19 22:24:50 +05303214
3215 def test_recursive_repr(self):
3216 @dataclass
3217 class C:
3218 f: "C"
3219
3220 c = C(None)
3221 c.f = c
3222 self.assertEqual(repr(c), "TestReplace.test_recursive_repr.<locals>.C(f=...)")
3223
3224 def test_recursive_repr_two_attrs(self):
3225 @dataclass
3226 class C:
3227 f: "C"
3228 g: "C"
3229
3230 c = C(None, None)
3231 c.f = c
3232 c.g = c
3233 self.assertEqual(repr(c), "TestReplace.test_recursive_repr_two_attrs"
3234 ".<locals>.C(f=..., g=...)")
3235
3236 def test_recursive_repr_indirection(self):
3237 @dataclass
3238 class C:
3239 f: "D"
3240
3241 @dataclass
3242 class D:
3243 f: "C"
3244
3245 c = C(None)
3246 d = D(None)
3247 c.f = d
3248 d.f = c
3249 self.assertEqual(repr(c), "TestReplace.test_recursive_repr_indirection"
3250 ".<locals>.C(f=TestReplace.test_recursive_repr_indirection"
3251 ".<locals>.D(f=...))")
3252
3253 def test_recursive_repr_indirection_two(self):
3254 @dataclass
3255 class C:
3256 f: "D"
3257
3258 @dataclass
3259 class D:
3260 f: "E"
3261
3262 @dataclass
3263 class E:
3264 f: "C"
3265
3266 c = C(None)
3267 d = D(None)
3268 e = E(None)
3269 c.f = d
3270 d.f = e
3271 e.f = c
3272 self.assertEqual(repr(c), "TestReplace.test_recursive_repr_indirection_two"
3273 ".<locals>.C(f=TestReplace.test_recursive_repr_indirection_two"
3274 ".<locals>.D(f=TestReplace.test_recursive_repr_indirection_two"
3275 ".<locals>.E(f=...)))")
3276
Srinivas Thatiparthy (శ్రీనివాస్ తాటిపర్తి)dd13c882018-10-19 22:24:50 +05303277 def test_recursive_repr_misc_attrs(self):
3278 @dataclass
3279 class C:
3280 f: "C"
3281 g: int
3282
3283 c = C(None, 1)
3284 c.f = c
3285 self.assertEqual(repr(c), "TestReplace.test_recursive_repr_misc_attrs"
3286 ".<locals>.C(f=..., g=1)")
3287
Eric V. Smithe7adf2b2018-06-07 14:43:59 -04003288 ## def test_initvar(self):
3289 ## @dataclass
3290 ## class C:
3291 ## x: int
3292 ## y: InitVar[int]
3293
3294 ## c = C(1, 10)
3295 ## d = C(2, 20)
3296
3297 ## # In our case, replacing an InitVar is a no-op
3298 ## self.assertEqual(c, replace(c, y=5))
3299
3300 ## replace(c, x=5)
3301
Eric V. Smith4e812962018-05-16 11:31:29 -04003302
Eric V. Smithf0db54a2017-12-04 16:58:55 -05003303if __name__ == '__main__':
3304 unittest.main()