From a96efef16694ae78fc8d9201e28d26f8c7619ec8 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Wed, 24 Jul 2024 15:45:03 +0300 Subject: [PATCH 1/3] gh-82129: Fix `NameError` on `get_type_hints` in `dataclasses` --- Lib/dataclasses.py | 6 +++++- Lib/test/test_dataclasses/__init__.py | 23 ++++++++++++++++++----- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py index 4cba606dd8dd4d..ef50f8aa20847e 100644 --- a/Lib/dataclasses.py +++ b/Lib/dataclasses.py @@ -1530,7 +1530,11 @@ class C(Base): for item in fields: if isinstance(item, str): name = item - tp = 'typing.Any' + typing = sys.modules.get('typing') + if typing: + tp = typing.Any + else: + tp = 'typing.Any' elif len(item) == 2: name, tp, = item elif len(item) == 3: diff --git a/Lib/test/test_dataclasses/__init__.py b/Lib/test/test_dataclasses/__init__.py index b93c99d8c90bf3..404574dcdf8d8b 100644 --- a/Lib/test/test_dataclasses/__init__.py +++ b/Lib/test/test_dataclasses/__init__.py @@ -13,6 +13,7 @@ import weakref import traceback import unittest +import sys from unittest.mock import Mock from typing import ClassVar, Any, List, Union, Tuple, Dict, Generic, TypeVar, Optional, Protocol, DefaultDict from typing import get_type_hints @@ -23,6 +24,7 @@ import dataclasses # Needed for the string "dataclasses.InitVar[int]" to work as an annotation. from test import support +from test.support import import_helper # Just any custom exception we can catch. class CustomError(Exception): pass @@ -4108,16 +4110,27 @@ def test_no_types(self): C = make_dataclass('Point', ['x', 'y', 'z']) c = C(1, 2, 3) self.assertEqual(vars(c), {'x': 1, 'y': 2, 'z': 3}) - self.assertEqual(C.__annotations__, {'x': 'typing.Any', - 'y': 'typing.Any', - 'z': 'typing.Any'}) + self.assertEqual(C.__annotations__, {'x': typing.Any, + 'y': typing.Any, + 'z': typing.Any}) C = make_dataclass('Point', ['x', ('y', int), 'z']) c = C(1, 2, 3) self.assertEqual(vars(c), {'x': 1, 'y': 2, 'z': 3}) - self.assertEqual(C.__annotations__, {'x': 'typing.Any', + self.assertEqual(C.__annotations__, {'x': typing.Any, 'y': int, - 'z': 'typing.Any'}) + 'z': typing.Any}) + + def test_no_types_no_NameError(self): + C = make_dataclass('Point', ['x']) + self.assertEqual(C.__annotations__, {'x': typing.Any}) + self.assertEqual(get_type_hints(C), {'x': typing.Any}) + + def test_no_types_no_typing_fallback(self): + with import_helper.isolated_modules(): + del sys.modules['typing'] + C = make_dataclass('Point', ['x']) + self.assertEqual(C.__annotations__, {'x': 'typing.Any'}) def test_module_attr(self): self.assertEqual(ByMakeDataClass.__module__, __name__) From 0d1081237a8022bd5704c5389442edae6c46a580 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Wed, 24 Jul 2024 15:47:18 +0300 Subject: [PATCH 2/3] gh-82129: Fix `NameError` on `get_type_hints` in `dataclasses` --- .../next/Library/2024-07-24-15-47-11.gh-issue-82129.7z5g8K.rst | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2024-07-24-15-47-11.gh-issue-82129.7z5g8K.rst diff --git a/Misc/NEWS.d/next/Library/2024-07-24-15-47-11.gh-issue-82129.7z5g8K.rst b/Misc/NEWS.d/next/Library/2024-07-24-15-47-11.gh-issue-82129.7z5g8K.rst new file mode 100644 index 00000000000000..4ba6c79994911c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-07-24-15-47-11.gh-issue-82129.7z5g8K.rst @@ -0,0 +1,3 @@ +Fix name error in :func:`typing.get_type_hints` when calling it in a +dataclass created without an annotation via +:func:`dataclasses.make_dataclass` function. From 7af17e395b0dd8aaacc8aa58786f9bd2baf53632 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Wed, 24 Jul 2024 16:43:25 +0300 Subject: [PATCH 3/3] Address review --- Lib/dataclasses.py | 2 +- Lib/test/test_dataclasses/__init__.py | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py index ef50f8aa20847e..a8c1af6f6ec4f0 100644 --- a/Lib/dataclasses.py +++ b/Lib/dataclasses.py @@ -1534,7 +1534,7 @@ class C(Base): if typing: tp = typing.Any else: - tp = 'typing.Any' + tp = "__import__('typing').Any" elif len(item) == 2: name, tp, = item elif len(item) == 3: diff --git a/Lib/test/test_dataclasses/__init__.py b/Lib/test/test_dataclasses/__init__.py index 404574dcdf8d8b..9c06058260eebd 100644 --- a/Lib/test/test_dataclasses/__init__.py +++ b/Lib/test/test_dataclasses/__init__.py @@ -4126,11 +4126,14 @@ def test_no_types_no_NameError(self): self.assertEqual(C.__annotations__, {'x': typing.Any}) self.assertEqual(get_type_hints(C), {'x': typing.Any}) - def test_no_types_no_typing_fallback(self): + def test_no_types_no_NameError_no_typing_fallback(self): with import_helper.isolated_modules(): del sys.modules['typing'] C = make_dataclass('Point', ['x']) - self.assertEqual(C.__annotations__, {'x': 'typing.Any'}) + self.assertEqual(C.__annotations__, + {'x': "__import__('typing').Any"}) + from typing import Any # since we hack our modules + self.assertEqual(get_type_hints(C), {'x': Any}) def test_module_attr(self): self.assertEqual(ByMakeDataClass.__module__, __name__)