From 1d9d99497220327b23225c0ab5a8d3f8edfa3d34 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Mon, 23 Dec 2024 17:07:59 +0300 Subject: [PATCH 1/2] gh-128184: Fix docstring generation in dataclasses with forward refs --- Lib/dataclasses.py | 7 +++++++ Lib/test/test_dataclasses/__init__.py | 19 +++++++++++++++++++ ...-12-23-17-00-35.gh-issue-128184.cRQvgM.rst | 2 ++ 3 files changed, 28 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2024-12-23-17-00-35.gh-issue-128184.cRQvgM.rst diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py index 7a24f8a9e5ccee..0fc230959a3f73 100644 --- a/Lib/dataclasses.py +++ b/Lib/dataclasses.py @@ -1164,6 +1164,13 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen, # In some cases fetching a signature is not possible. # But, we surely should not fail in this case. text_sig = str(inspect.signature(cls)).replace(' -> None', '') + except NameError: + # This means that some types where not defined, maybe due to + # forward references, etc. In this case, try different format. + text_sig = str(inspect.signature( + cls, + annotation_format=annotationlib.Format.STRING, + )).replace(" -> 'None'", '') except (TypeError, ValueError): text_sig = '' cls.__doc__ = (cls.__name__ + text_sig) diff --git a/Lib/test/test_dataclasses/__init__.py b/Lib/test/test_dataclasses/__init__.py index 2e6c49e29ce828..0050fea2e31e2a 100644 --- a/Lib/test/test_dataclasses/__init__.py +++ b/Lib/test/test_dataclasses/__init__.py @@ -12,6 +12,7 @@ import types import weakref import traceback +import textwrap import unittest from unittest.mock import Mock from typing import ClassVar, Any, List, Union, Tuple, Dict, Generic, TypeVar, Optional, Protocol, DefaultDict @@ -2343,6 +2344,24 @@ class C: self.assertDocStrEqual(C.__doc__, "C(x:collections.deque=)") + def test_docstring_with_unsolvable_forward_ref_in_init(self): + # See: https://github.com/python/cpython/issues/128184 + ns = {} + exec( + textwrap.dedent( + """ + from dataclasses import dataclass + + @dataclass + class C: + def __init__(self, x: X) -> None: ... + """, + ), + ns, + ) + + self.assertDocStrEqual(ns['C'].__doc__, "C(x:'X')") + def test_docstring_with_no_signature(self): # See https://github.com/python/cpython/issues/103449 class Meta(type): diff --git a/Misc/NEWS.d/next/Library/2024-12-23-17-00-35.gh-issue-128184.cRQvgM.rst b/Misc/NEWS.d/next/Library/2024-12-23-17-00-35.gh-issue-128184.cRQvgM.rst new file mode 100644 index 00000000000000..3d194b12c16ae4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-12-23-17-00-35.gh-issue-128184.cRQvgM.rst @@ -0,0 +1,2 @@ +Fixes :exc:`NameError` when using :func:`dataclasses.dataclass` on classes +with unresolvable forward references. From bdfea9fc94464c82f133c4de448ba9ab1da28390 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Wed, 25 Dec 2024 10:54:52 +0300 Subject: [PATCH 2/2] Also check existing type --- Lib/test/test_dataclasses/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_dataclasses/__init__.py b/Lib/test/test_dataclasses/__init__.py index 0050fea2e31e2a..04c894f8b493e1 100644 --- a/Lib/test/test_dataclasses/__init__.py +++ b/Lib/test/test_dataclasses/__init__.py @@ -2354,13 +2354,13 @@ def test_docstring_with_unsolvable_forward_ref_in_init(self): @dataclass class C: - def __init__(self, x: X) -> None: ... + def __init__(self, x: X, num: int) -> None: ... """, ), ns, ) - self.assertDocStrEqual(ns['C'].__doc__, "C(x:'X')") + self.assertDocStrEqual(ns['C'].__doc__, "C(x:'X',num:'int')") def test_docstring_with_no_signature(self): # See https://github.com/python/cpython/issues/103449