Add keyword-only fields to dataclasses. (GH=25608)

This commit is contained in:
Eric V. Smith 2021-04-25 20:42:39 -04:00 committed by GitHub
parent 7f8e072c6d
commit c0280532dc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 298 additions and 38 deletions

View file

@ -61,6 +61,7 @@ class TestCase(unittest.TestCase):
f"default=1,default_factory={MISSING!r}," \
"init=True,repr=False,hash=None," \
"compare=True,metadata=mappingproxy({})," \
f"kw_only={MISSING!r}," \
"_field_type=None)"
self.assertEqual(repr_output, expected_output)
@ -3501,5 +3502,163 @@ class TestMatchArgs(unittest.TestCase):
self.assertEqual(C.__match_args__, ('z',))
class TestKwArgs(unittest.TestCase):
def test_no_classvar_kwarg(self):
msg = 'field a is a ClassVar but specifies kw_only'
with self.assertRaisesRegex(TypeError, msg):
@dataclass
class A:
a: ClassVar[int] = field(kw_only=True)
with self.assertRaisesRegex(TypeError, msg):
@dataclass
class A:
a: ClassVar[int] = field(kw_only=False)
with self.assertRaisesRegex(TypeError, msg):
@dataclass(kw_only=True)
class A:
a: ClassVar[int] = field(kw_only=False)
def test_field_marked_as_kwonly(self):
#######################
# Using dataclass(kw_only=True)
@dataclass(kw_only=True)
class A:
a: int
self.assertTrue(fields(A)[0].kw_only)
@dataclass(kw_only=True)
class A:
a: int = field(kw_only=True)
self.assertTrue(fields(A)[0].kw_only)
@dataclass(kw_only=True)
class A:
a: int = field(kw_only=False)
self.assertFalse(fields(A)[0].kw_only)
#######################
# Using dataclass(kw_only=False)
@dataclass(kw_only=False)
class A:
a: int
self.assertFalse(fields(A)[0].kw_only)
@dataclass(kw_only=False)
class A:
a: int = field(kw_only=True)
self.assertTrue(fields(A)[0].kw_only)
@dataclass(kw_only=False)
class A:
a: int = field(kw_only=False)
self.assertFalse(fields(A)[0].kw_only)
#######################
# Not specifying dataclass(kw_only)
@dataclass
class A:
a: int
self.assertFalse(fields(A)[0].kw_only)
@dataclass
class A:
a: int = field(kw_only=True)
self.assertTrue(fields(A)[0].kw_only)
@dataclass
class A:
a: int = field(kw_only=False)
self.assertFalse(fields(A)[0].kw_only)
def test_match_args(self):
# kw fields don't show up in __match_args__.
@dataclass(kw_only=True)
class C:
a: int
self.assertEqual(C(a=42).__match_args__, ())
@dataclass
class C:
a: int
b: int = field(kw_only=True)
self.assertEqual(C(42, b=10).__match_args__, ('a',))
def test_KW_ONLY(self):
@dataclass
class A:
a: int
_: KW_ONLY
b: int
c: int
A(3, c=5, b=4)
msg = "takes 2 positional arguments but 4 were given"
with self.assertRaisesRegex(TypeError, msg):
A(3, 4, 5)
@dataclass(kw_only=True)
class B:
a: int
_: KW_ONLY
b: int
c: int
B(a=3, b=4, c=5)
msg = "takes 1 positional argument but 4 were given"
with self.assertRaisesRegex(TypeError, msg):
B(3, 4, 5)
# Explicitely make a field that follows KW_ONLY be non-keyword-only.
@dataclass
class C:
a: int
_: KW_ONLY
b: int
c: int = field(kw_only=False)
c = C(1, 2, b=3)
self.assertEqual(c.a, 1)
self.assertEqual(c.b, 3)
self.assertEqual(c.c, 2)
c = C(1, b=3, c=2)
self.assertEqual(c.a, 1)
self.assertEqual(c.b, 3)
self.assertEqual(c.c, 2)
c = C(1, b=3, c=2)
self.assertEqual(c.a, 1)
self.assertEqual(c.b, 3)
self.assertEqual(c.c, 2)
c = C(c=2, b=3, a=1)
self.assertEqual(c.a, 1)
self.assertEqual(c.b, 3)
self.assertEqual(c.c, 2)
def test_post_init(self):
@dataclass
class A:
a: int
_: KW_ONLY
b: InitVar[int]
c: int
d: InitVar[int]
def __post_init__(self, b, d):
raise CustomError(f'{b=} {d=}')
with self.assertRaisesRegex(CustomError, 'b=3 d=4'):
A(1, c=2, b=3, d=4)
@dataclass
class B:
a: int
_: KW_ONLY
b: InitVar[int]
c: int
d: InitVar[int]
def __post_init__(self, b, d):
self.a = b
self.c = d
b = B(1, c=2, b=3, d=4)
self.assertEqual(asdict(b), {'a': 3, 'c': 4})
if __name__ == '__main__':
unittest.main()