https://github.com/python/cpython/commit/1d251b8339a34a54e5d82d912d90bc0ca96e8757
commit: 1d251b8339a34a54e5d82d912d90bc0ca96e8757
branch: main
author: Jelle Zijlstra <jelle.zijls...@gmail.com>
committer: JelleZijlstra <jelle.zijls...@gmail.com>
date: 2025-03-04T06:58:37-08:00
summary:

gh-128184: Fix display of signatures with ForwardRefs (#130815)

Co-authored-by: sobolevn <m...@sobolevn.me>

files:
A Misc/NEWS.d/next/Library/2024-12-23-17-00-35.gh-issue-128184.cRQvgM.rst
M Lib/dataclasses.py
M Lib/inspect.py
M Lib/test/test_dataclasses/__init__.py
M Lib/test/test_inspect/test_inspect.py

diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py
index 7a24f8a9e5ccee..0f7dc9ae6b82f5 100644
--- a/Lib/dataclasses.py
+++ b/Lib/dataclasses.py
@@ -1163,7 +1163,10 @@ def _process_class(cls, init, repr, eq, order, 
unsafe_hash, frozen,
         try:
             # 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', '')
+            text_sig = str(inspect.signature(
+                cls,
+                annotation_format=annotationlib.Format.FORWARDREF,
+            )).replace(' -> None', '')
         except (TypeError, ValueError):
             text_sig = ''
         cls.__doc__ = (cls.__name__ + text_sig)
diff --git a/Lib/inspect.py b/Lib/inspect.py
index f143c89674b7b2..182c2eedbaba64 100644
--- a/Lib/inspect.py
+++ b/Lib/inspect.py
@@ -143,7 +143,7 @@
 
 
 import abc
-from annotationlib import Format
+from annotationlib import Format, ForwardRef
 from annotationlib import get_annotations  # re-exported
 import ast
 import dis
@@ -1342,6 +1342,8 @@ def repl(match):
         if annotation.__module__ in ('builtins', base_module):
             return annotation.__qualname__
         return annotation.__module__+'.'+annotation.__qualname__
+    if isinstance(annotation, ForwardRef):
+        return annotation.__forward_arg__
     return repr(annotation)
 
 def formatannotationrelativeto(object):
diff --git a/Lib/test/test_dataclasses/__init__.py 
b/Lib/test/test_dataclasses/__init__.py
index 2e6c49e29ce828..8209374c36bca2 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,31 @@ class C:
 
         self.assertDocStrEqual(C.__doc__, "C(x:collections.deque=<factory>)")
 
+    def test_docstring_undefined_name(self):
+        @dataclass
+        class C:
+            x: undef
+
+        self.assertDocStrEqual(C.__doc__, "C(x:undef)")
+
+    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, num: int) -> None: ...
+                """,
+            ),
+            ns,
+        )
+
+        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
         class Meta(type):
diff --git a/Lib/test/test_inspect/test_inspect.py 
b/Lib/test/test_inspect/test_inspect.py
index 6a562108c3a34b..03f2bacb3a4e88 100644
--- a/Lib/test/test_inspect/test_inspect.py
+++ b/Lib/test/test_inspect/test_inspect.py
@@ -1753,6 +1753,10 @@ def test_typing_replacement(self):
         self.assertEqual(inspect.formatannotation(ann), 'Union[List[str], 
int]')
         self.assertEqual(inspect.formatannotation(ann1), 
'Union[List[testModule.typing.A], int]')
 
+    def test_forwardref(self):
+        fwdref = ForwardRef('fwdref')
+        self.assertEqual(inspect.formatannotation(fwdref), 'fwdref')
+
 
 class TestIsMethodDescriptor(unittest.TestCase):
 
@@ -4587,6 +4591,11 @@ def foo(a: list[str]) -> Tuple[str, float]:
         self.assertEqual(str(inspect.signature(foo)),
                          inspect.signature(foo).format())
 
+        def foo(x: undef):
+            pass
+        sig = inspect.signature(foo, annotation_format=Format.FORWARDREF)
+        self.assertEqual(str(sig), '(x: undef)')
+
     def test_signature_str_positional_only(self):
         P = inspect.Parameter
         S = inspect.Signature
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..448dcfe5a7ccdb
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-12-23-17-00-35.gh-issue-128184.cRQvgM.rst
@@ -0,0 +1,4 @@
+Improve display of :class:`annotationlib.ForwardRef` object
+within :class:`inspect.Signature` representations.
+This also fixes a :exc:`NameError` that was raised when using
+:func:`dataclasses.dataclass` on classes with unresolvable forward references.

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com

Reply via email to