From f2676badc548837350a99b9c0d333bc42edfa487 Mon Sep 17 00:00:00 2001 From: GBeauregard Date: Fri, 28 Jan 2022 13:17:44 -0800 Subject: [PATCH 1/9] Let typing.Annotated wrap dataclasses annotations --- Lib/dataclasses.py | 43 ++++++++++++++++++----- Lib/test/test_dataclasses.py | 66 ++++++++++++++++++++++++++---------- 2 files changed, 83 insertions(+), 26 deletions(-) diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py index b327462080f993..9151327cb2e4e9 100644 --- a/Lib/dataclasses.py +++ b/Lib/dataclasses.py @@ -188,7 +188,8 @@ class _MISSING_TYPE: # A sentinel object to indicate that following fields are keyword-only by # default. Use a class to give it a better repr. class _KW_ONLY_TYPE: - pass + def __call__(self, *args, **kwds): + raise TypeError(f"Cannot instantiate {self!r}") KW_ONLY = _KW_ONLY_TYPE() # Since most per-field metadata will be unused, create an empty @@ -220,7 +221,8 @@ def __repr__(self): # String regex that string annotations for ClassVar or InitVar must match. # Allows "identifier.identifier[" or "identifier[". # https://bugs.python.org/issue33453 for details. -_MODULE_IDENTIFIER_RE = re.compile(r'^(?:\s*(\w+)\s*\.)?\s*(\w+)') +_MODULE_IDENTIFIER_RE = re.compile( + r'^(?:(?:\w+\s*\.\s*)?Annotated\s*\[\s*)*(?:(\w+)\s*\.)?\s*(\w+)') class InitVar: __slots__ = ('type', ) @@ -239,6 +241,9 @@ def __repr__(self): def __class_getitem__(cls, type): return InitVar(type) + def __call__(self, *args, **kwds): + raise TypeError(f"Cannot instantiate {self!r}") + # Instances of Field are only ever created from within this module, # and only from the field() function, although Field instances are # exposed externally as (conceptually) read-only objects. @@ -643,21 +648,41 @@ def _hash_fn(fields, globals): globals=globals) -def _is_classvar(a_type, typing): +def _is_classvar(cls, a_type, typing): # This test uses a typing internal class, but it's the best way to # test if this is a ClassVar. + if isinstance(a_type, typing._AnnotatedAlias): + a_type = a_type.__origin__ + return (_is_classvar(cls, a_type, typing) + or isinstance(a_type, typing.ForwardRef) + and _is_type(a_type.__forward_arg__, cls, typing, + typing.ClassVar, _is_classvar)) return (a_type is typing.ClassVar or (type(a_type) is typing._GenericAlias and a_type.__origin__ is typing.ClassVar)) -def _is_initvar(a_type, dataclasses): +def _is_initvar(cls, a_type, dataclasses): # The module we're checking against is the module we're # currently in (dataclasses.py). + typing = sys.modules.get('typing') + if typing and isinstance(a_type, typing._AnnotatedAlias): + a_type = a_type.__origin__ + return (_is_initvar(cls, a_type, dataclasses) + or isinstance(a_type, typing.ForwardRef) + and _is_type(a_type.__forward_arg__, cls, dataclasses, + dataclasses.InitVar, _is_initvar)) return (a_type is dataclasses.InitVar or type(a_type) is dataclasses.InitVar) -def _is_kw_only(a_type, dataclasses): +def _is_kw_only(cls, a_type, dataclasses): + typing = sys.modules.get('typing') + if typing and isinstance(a_type, typing._AnnotatedAlias): + a_type = a_type.__origin__ + return (_is_kw_only(cls, a_type, dataclasses) + or isinstance(a_type, typing.ForwardRef) + and _is_type(a_type.__forward_arg__, cls, dataclasses, + dataclasses.KW_ONLY, _is_kw_only)) return a_type is dataclasses.KW_ONLY @@ -715,7 +740,7 @@ def _is_type(annotation, cls, a_module, a_type, is_type_predicate): module = sys.modules.get(cls.__module__) if module and module.__dict__.get(module_name) is a_module: ns = sys.modules.get(a_type.__module__).__dict__ - if ns and is_type_predicate(ns.get(match.group(2)), a_module): + if ns and is_type_predicate(cls, ns.get(match.group(2)), a_module): return True return False @@ -762,7 +787,7 @@ def _get_field(cls, a_name, a_type, default_kw_only): # module). typing = sys.modules.get('typing') if typing: - if (_is_classvar(a_type, typing) + if (_is_classvar(cls, a_type, typing) or (isinstance(f.type, str) and _is_type(f.type, cls, typing, typing.ClassVar, _is_classvar))): @@ -774,7 +799,7 @@ def _get_field(cls, a_name, a_type, default_kw_only): # The module we're checking against is the module we're # currently in (dataclasses.py). dataclasses = sys.modules[__name__] - if (_is_initvar(a_type, dataclasses) + if (_is_initvar(cls, a_type, dataclasses) or (isinstance(f.type, str) and _is_type(f.type, cls, dataclasses, dataclasses.InitVar, _is_initvar))): @@ -942,7 +967,7 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen, dataclasses = sys.modules[__name__] for name, type in cls_annotations.items(): # See if this is a marker to change the value of kw_only. - if (_is_kw_only(type, dataclasses) + if (_is_kw_only(cls, type, dataclasses) or (isinstance(type, str) and _is_type(type, cls, dataclasses, dataclasses.KW_ONLY, _is_kw_only))): diff --git a/Lib/test/test_dataclasses.py b/Lib/test/test_dataclasses.py index 69e76850830091..be81fbf9395142 100644 --- a/Lib/test/test_dataclasses.py +++ b/Lib/test/test_dataclasses.py @@ -11,7 +11,7 @@ import types import unittest from unittest.mock import Mock -from typing import ClassVar, Any, List, Union, Tuple, Dict, Generic, TypeVar, Optional, Protocol +from typing import Annotated, ClassVar, Any, List, Union, Tuple, Dict, Generic, TypeVar, Optional, Protocol from typing import get_type_hints from collections import deque, OrderedDict, namedtuple from functools import total_ordering @@ -1022,19 +1022,25 @@ def test_class_var(self): class C: x: int y: int = 10 - z: ClassVar[int] = 1000 - w: ClassVar[int] = 2000 - t: ClassVar[int] = 3000 - s: ClassVar = 4000 + z: ClassVar[int] = 1000 + w: ClassVar[int] = 2000 + t: ClassVar[int] = 3000 + s: ClassVar = 4000 + a: Annotated[ClassVar, "meta"] = 5000 + b: Annotated[ClassVar[int], 'meta'] = 6000 + c: Annotated['ClassVar', "meta"] = 7000 c = C(5) self.assertEqual(repr(c), 'TestCase.test_class_var..C(x=5, y=10)') self.assertEqual(len(fields(C)), 2) # We have 2 fields. - self.assertEqual(len(C.__annotations__), 6) # And 4 ClassVars. + self.assertEqual(len(C.__annotations__), 9) # And 7 ClassVars. self.assertEqual(c.z, 1000) self.assertEqual(c.w, 2000) self.assertEqual(c.t, 3000) self.assertEqual(c.s, 4000) + self.assertEqual(c.a, 5000) + self.assertEqual(c.b, 6000) + self.assertEqual(c.c, 7000) C.z += 1 self.assertEqual(c.z, 1001) c = C(20) @@ -1043,6 +1049,9 @@ class C: self.assertEqual(c.w, 2000) self.assertEqual(c.t, 3000) self.assertEqual(c.s, 4000) + self.assertEqual(c.a, 5000) + self.assertEqual(c.b, 6000) + self.assertEqual(c.c, 7000) def test_class_var_no_default(self): # If a ClassVar has no default value, it should not be set on the class. @@ -1138,13 +1147,14 @@ def test_init_var(self): class C: x: int = None init_param: InitVar[int] = None + annotated_init_param: Annotated[InitVar[int], 'meta'] = None - def __post_init__(self, init_param): + def __post_init__(self, init_param, annotated_init_param): if self.x is None: - self.x = init_param*2 + self.x = init_param*2 + annotated_init_param - c = C(init_param=10) - self.assertEqual(c.x, 20) + c = C(init_param=10, annotated_init_param=5) + self.assertEqual(c.x, 25) def test_init_var_preserve_type(self): self.assertEqual(InitVar[int].type, int) @@ -3025,16 +3035,21 @@ def test_classvar(self): # typing import *" have been run in this file. for typestr in ('ClassVar[int]', 'ClassVar [int]', - ' ClassVar [int]', 'ClassVar', - ' ClassVar ', 'typing.ClassVar[int]', 'typing.ClassVar[str]', - ' typing.ClassVar[str]', 'typing .ClassVar[str]', 'typing. ClassVar[str]', 'typing.ClassVar [str]', 'typing.ClassVar [ str]', + 'Annotated[ClassVar[int], (3, 5)]', + 'Annotated[Annotated[ClassVar[int], (3, 5)], (3, 6)]', + 'Annotated[typing.ClassVar[int], (3, 5)]', + 'Annotated [ClassVar[int], (3, 5)]', + 'Annotated[ ClassVar[int], (3, 5)]', + 'typing.Annotated[ClassVar[int], (3, 5)]', + 'typing .Annotated[ClassVar[int], (3, 5)]', + 'typing. Annotated[ClassVar[int], (3, 5)]', # Not syntactically valid, but these will # be treated as ClassVars. @@ -3077,17 +3092,22 @@ def test_initvar(self): # These tests assume that both "import dataclasses" and "from # dataclasses import *" have been run in this file. for typestr in ('InitVar[int]', - 'InitVar [int]' - ' InitVar [int]', + 'InitVar [int]', 'InitVar', - ' InitVar ', 'dataclasses.InitVar[int]', 'dataclasses.InitVar[str]', - ' dataclasses.InitVar[str]', 'dataclasses .InitVar[str]', 'dataclasses. InitVar[str]', 'dataclasses.InitVar [str]', 'dataclasses.InitVar [ str]', + 'Annotated[InitVar[int], (3, 5)]', + 'Annotated[Annotated[InitVar[int], (3, 5)], (3, 6)]', + 'Annotated[dataclasses.InitVar[int], (3, 5)]', + 'Annotated [InitVar[int], (3, 5)]', + 'Annotated[ InitVar[int], (3, 5)]', + 'typing.Annotated[InitVar[int], (3, 5)]', + 'typing .Annotated[InitVar[int], (3, 5)]', + 'typing. Annotated[InitVar[int], (3, 5)]', # Not syntactically valid, but these will # be treated as InitVars. @@ -3808,6 +3828,18 @@ class A: with self.assertRaisesRegex(TypeError, msg): A(3, 4, 5) + def test_KW_ONLY_annotated(self): + @dataclass + class A: + a: int + _: Annotated[KW_ONLY, 'meta'] + 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) + def test_KW_ONLY_twice(self): msg = "'Y' is KW_ONLY, but KW_ONLY has already been specified" From d86d24b62dfd575e185104480cc6fdd3fb0215eb Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Fri, 28 Jan 2022 21:27:09 +0000 Subject: [PATCH 2/9] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../NEWS.d/next/Library/2022-01-28-21-27-09.bpo-46511.092D9i.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2022-01-28-21-27-09.bpo-46511.092D9i.rst diff --git a/Misc/NEWS.d/next/Library/2022-01-28-21-27-09.bpo-46511.092D9i.rst b/Misc/NEWS.d/next/Library/2022-01-28-21-27-09.bpo-46511.092D9i.rst new file mode 100644 index 00000000000000..55ecc832e8d3b5 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-01-28-21-27-09.bpo-46511.092D9i.rst @@ -0,0 +1 @@ +Allow :data:`typing.Annotated` to wrap :data:`typing.ClassVar`, :data:`dataclasses.InitVar`, and :data:`dataclasses.KW_ONLY` in dataclasses. Patch by Gregory Beauregard. \ No newline at end of file From d5937e92b3bc043667a1988b156f8fc0310bcf8e Mon Sep 17 00:00:00 2001 From: GBeauregard Date: Fri, 28 Jan 2022 15:04:47 -0800 Subject: [PATCH 3/9] less invasive patch --- Lib/dataclasses.py | 50 ++++++++++++++++++++-------------------------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py index 9151327cb2e4e9..6961b679c07871 100644 --- a/Lib/dataclasses.py +++ b/Lib/dataclasses.py @@ -648,41 +648,21 @@ def _hash_fn(fields, globals): globals=globals) -def _is_classvar(cls, a_type, typing): +def _is_classvar(a_type, typing): # This test uses a typing internal class, but it's the best way to # test if this is a ClassVar. - if isinstance(a_type, typing._AnnotatedAlias): - a_type = a_type.__origin__ - return (_is_classvar(cls, a_type, typing) - or isinstance(a_type, typing.ForwardRef) - and _is_type(a_type.__forward_arg__, cls, typing, - typing.ClassVar, _is_classvar)) return (a_type is typing.ClassVar or (type(a_type) is typing._GenericAlias and a_type.__origin__ is typing.ClassVar)) -def _is_initvar(cls, a_type, dataclasses): +def _is_initvar(a_type, dataclasses): # The module we're checking against is the module we're # currently in (dataclasses.py). - typing = sys.modules.get('typing') - if typing and isinstance(a_type, typing._AnnotatedAlias): - a_type = a_type.__origin__ - return (_is_initvar(cls, a_type, dataclasses) - or isinstance(a_type, typing.ForwardRef) - and _is_type(a_type.__forward_arg__, cls, dataclasses, - dataclasses.InitVar, _is_initvar)) return (a_type is dataclasses.InitVar or type(a_type) is dataclasses.InitVar) -def _is_kw_only(cls, a_type, dataclasses): - typing = sys.modules.get('typing') - if typing and isinstance(a_type, typing._AnnotatedAlias): - a_type = a_type.__origin__ - return (_is_kw_only(cls, a_type, dataclasses) - or isinstance(a_type, typing.ForwardRef) - and _is_type(a_type.__forward_arg__, cls, dataclasses, - dataclasses.KW_ONLY, _is_kw_only)) +def _is_kw_only(a_type, dataclasses): return a_type is dataclasses.KW_ONLY @@ -740,7 +720,7 @@ def _is_type(annotation, cls, a_module, a_type, is_type_predicate): module = sys.modules.get(cls.__module__) if module and module.__dict__.get(module_name) is a_module: ns = sys.modules.get(a_type.__module__).__dict__ - if ns and is_type_predicate(cls, ns.get(match.group(2)), a_module): + if ns and is_type_predicate(ns.get(match.group(2)), a_module): return True return False @@ -762,10 +742,18 @@ def _get_field(cls, a_name, a_type, default_kw_only): default = MISSING f = field(default=default) + typing = sys.modules.get('typing') + if typing: + while isinstance(a_type, typing._AnnotatedAlias): + a_type = a_type.__origin__ + if isinstance(a_type, typing.ForwardRef): + a_type = a_type.__forward_arg__ + # Only at this point do we know the name and the type. Set them. f.name = a_name f.type = a_type + # Assume it's a normal field until proven otherwise. We're next # going to decide if it's a ClassVar or InitVar, everything else # is just a normal field. @@ -785,9 +773,9 @@ def _get_field(cls, a_name, a_type, default_kw_only): # annotation to be a ClassVar. So, only look for ClassVar if # typing has been imported by any module (not necessarily cls's # module). - typing = sys.modules.get('typing') + if typing: - if (_is_classvar(cls, a_type, typing) + if (_is_classvar(a_type, typing) or (isinstance(f.type, str) and _is_type(f.type, cls, typing, typing.ClassVar, _is_classvar))): @@ -799,7 +787,7 @@ def _get_field(cls, a_name, a_type, default_kw_only): # The module we're checking against is the module we're # currently in (dataclasses.py). dataclasses = sys.modules[__name__] - if (_is_initvar(cls, a_type, dataclasses) + if (_is_initvar(a_type, dataclasses) or (isinstance(f.type, str) and _is_type(f.type, cls, dataclasses, dataclasses.InitVar, _is_initvar))): @@ -965,9 +953,15 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen, # Get a reference to this module for the _is_kw_only() test. KW_ONLY_seen = False dataclasses = sys.modules[__name__] + typing = sys.modules.get('typing') for name, type in cls_annotations.items(): + if typing: + while isinstance(type, typing._AnnotatedAlias): + type = type.__origin__ + if isinstance(type, typing.ForwardRef): + type = type.__forward_arg__ # See if this is a marker to change the value of kw_only. - if (_is_kw_only(cls, type, dataclasses) + if (_is_kw_only(type, dataclasses) or (isinstance(type, str) and _is_type(type, cls, dataclasses, dataclasses.KW_ONLY, _is_kw_only))): From 090c6f13cd75320d22cbc488d8e74ace78bd3a76 Mon Sep 17 00:00:00 2001 From: GBeauregard Date: Fri, 28 Jan 2022 15:08:43 -0800 Subject: [PATCH 4/9] remove extra space --- Lib/dataclasses.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py index 6961b679c07871..d14812890e6c41 100644 --- a/Lib/dataclasses.py +++ b/Lib/dataclasses.py @@ -753,7 +753,6 @@ def _get_field(cls, a_name, a_type, default_kw_only): f.name = a_name f.type = a_type - # Assume it's a normal field until proven otherwise. We're next # going to decide if it's a ClassVar or InitVar, everything else # is just a normal field. From 6bd856cfe8a032b76c172e4b469d849585f3932b Mon Sep 17 00:00:00 2001 From: GBeauregard Date: Fri, 28 Jan 2022 18:35:56 -0800 Subject: [PATCH 5/9] remove support for Annotated[KW_ONLY, ...] --- Lib/dataclasses.py | 9 +-------- Lib/test/test_dataclasses.py | 12 ------------ 2 files changed, 1 insertion(+), 20 deletions(-) diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py index d14812890e6c41..931f4e6a120ed1 100644 --- a/Lib/dataclasses.py +++ b/Lib/dataclasses.py @@ -188,8 +188,7 @@ class _MISSING_TYPE: # A sentinel object to indicate that following fields are keyword-only by # default. Use a class to give it a better repr. class _KW_ONLY_TYPE: - def __call__(self, *args, **kwds): - raise TypeError(f"Cannot instantiate {self!r}") + pass KW_ONLY = _KW_ONLY_TYPE() # Since most per-field metadata will be unused, create an empty @@ -952,13 +951,7 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen, # Get a reference to this module for the _is_kw_only() test. KW_ONLY_seen = False dataclasses = sys.modules[__name__] - typing = sys.modules.get('typing') for name, type in cls_annotations.items(): - if typing: - while isinstance(type, typing._AnnotatedAlias): - type = type.__origin__ - if isinstance(type, typing.ForwardRef): - type = type.__forward_arg__ # See if this is a marker to change the value of kw_only. if (_is_kw_only(type, dataclasses) or (isinstance(type, str) diff --git a/Lib/test/test_dataclasses.py b/Lib/test/test_dataclasses.py index be81fbf9395142..778130a40e68ea 100644 --- a/Lib/test/test_dataclasses.py +++ b/Lib/test/test_dataclasses.py @@ -3828,18 +3828,6 @@ class A: with self.assertRaisesRegex(TypeError, msg): A(3, 4, 5) - def test_KW_ONLY_annotated(self): - @dataclass - class A: - a: int - _: Annotated[KW_ONLY, 'meta'] - 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) - def test_KW_ONLY_twice(self): msg = "'Y' is KW_ONLY, but KW_ONLY has already been specified" From eff831a6ad13adc983416c5e595e9699122838ae Mon Sep 17 00:00:00 2001 From: GBeauregard Date: Fri, 28 Jan 2022 18:41:32 -0800 Subject: [PATCH 6/9] add tests to address review comments --- Lib/test/test_dataclasses.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/Lib/test/test_dataclasses.py b/Lib/test/test_dataclasses.py index 778130a40e68ea..1481ac3edb293c 100644 --- a/Lib/test/test_dataclasses.py +++ b/Lib/test/test_dataclasses.py @@ -1022,18 +1022,20 @@ def test_class_var(self): class C: x: int y: int = 10 - z: ClassVar[int] = 1000 - w: ClassVar[int] = 2000 - t: ClassVar[int] = 3000 - s: ClassVar = 4000 - a: Annotated[ClassVar, "meta"] = 5000 - b: Annotated[ClassVar[int], 'meta'] = 6000 - c: Annotated['ClassVar', "meta"] = 7000 + z: ClassVar[int] = 1000 + w: ClassVar[int] = 2000 + t: ClassVar[int] = 3000 + s: ClassVar = 4000 + a: Annotated[ClassVar, 'meta'] = 5000 + b: Annotated[ClassVar[int], 'meta'] = 6000 + c: Annotated['ClassVar', 'meta'] = 7000 + d: Annotated[Annotated[ClassVar, 'meta'], 'meta'] = 8000 + e: Annotated['Annotated[ClassVar, "meta"]', 'meta'] = 9000 c = C(5) self.assertEqual(repr(c), 'TestCase.test_class_var..C(x=5, y=10)') self.assertEqual(len(fields(C)), 2) # We have 2 fields. - self.assertEqual(len(C.__annotations__), 9) # And 7 ClassVars. + self.assertEqual(len(C.__annotations__), 11) # And 9 ClassVars. self.assertEqual(c.z, 1000) self.assertEqual(c.w, 2000) self.assertEqual(c.t, 3000) @@ -1041,6 +1043,8 @@ class C: self.assertEqual(c.a, 5000) self.assertEqual(c.b, 6000) self.assertEqual(c.c, 7000) + self.assertEqual(c.d, 8000) + self.assertEqual(c.e, 9000) C.z += 1 self.assertEqual(c.z, 1001) c = C(20) @@ -1052,6 +1056,8 @@ class C: self.assertEqual(c.a, 5000) self.assertEqual(c.b, 6000) self.assertEqual(c.c, 7000) + self.assertEqual(c.d, 8000) + self.assertEqual(c.e, 9000) def test_class_var_no_default(self): # If a ClassVar has no default value, it should not be set on the class. From bd78d34e3bc1ad9c0582316d52899000386ffbe1 Mon Sep 17 00:00:00 2001 From: Gregory Beauregard Date: Fri, 28 Jan 2022 18:48:13 -0800 Subject: [PATCH 7/9] Update 2022-01-28-21-27-09.bpo-46511.092D9i.rst --- .../next/Library/2022-01-28-21-27-09.bpo-46511.092D9i.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2022-01-28-21-27-09.bpo-46511.092D9i.rst b/Misc/NEWS.d/next/Library/2022-01-28-21-27-09.bpo-46511.092D9i.rst index 55ecc832e8d3b5..00f57d68db061e 100644 --- a/Misc/NEWS.d/next/Library/2022-01-28-21-27-09.bpo-46511.092D9i.rst +++ b/Misc/NEWS.d/next/Library/2022-01-28-21-27-09.bpo-46511.092D9i.rst @@ -1 +1 @@ -Allow :data:`typing.Annotated` to wrap :data:`typing.ClassVar`, :data:`dataclasses.InitVar`, and :data:`dataclasses.KW_ONLY` in dataclasses. Patch by Gregory Beauregard. \ No newline at end of file +Allow :data:`typing.Annotated` to wrap :data:`typing.ClassVar` and :data:`dataclasses.InitVar` in dataclasses. Patch by Gregory Beauregard. From e5cdf7d9c6e18c5341607f88f75e7050943a625a Mon Sep 17 00:00:00 2001 From: GBeauregard Date: Fri, 28 Jan 2022 21:01:44 -0800 Subject: [PATCH 8/9] add comment to explain code --- Lib/dataclasses.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py index 931f4e6a120ed1..96d894162bcd6f 100644 --- a/Lib/dataclasses.py +++ b/Lib/dataclasses.py @@ -240,6 +240,9 @@ def __repr__(self): def __class_getitem__(cls, type): return InitVar(type) + # This is needed to pass the callable() check in typing._type_check, + # which is run whenever the InitVar is contained within an Annotated + # annotation. def __call__(self, *args, **kwds): raise TypeError(f"Cannot instantiate {self!r}") From e099a4a68dcf4ca05b09c027233e3f5fcb802c04 Mon Sep 17 00:00:00 2001 From: GBeauregard Date: Sat, 19 Mar 2022 10:20:28 -0500 Subject: [PATCH 9/9] remove no longer needed __call__ --- Lib/dataclasses.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py index 96d894162bcd6f..429f1cf3f7b4e2 100644 --- a/Lib/dataclasses.py +++ b/Lib/dataclasses.py @@ -240,12 +240,6 @@ def __repr__(self): def __class_getitem__(cls, type): return InitVar(type) - # This is needed to pass the callable() check in typing._type_check, - # which is run whenever the InitVar is contained within an Annotated - # annotation. - def __call__(self, *args, **kwds): - raise TypeError(f"Cannot instantiate {self!r}") - # Instances of Field are only ever created from within this module, # and only from the field() function, although Field instances are # exposed externally as (conceptually) read-only objects.