]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-128184: Fix display of signatures with ForwardRefs (#130815)
authorJelle Zijlstra <jelle.zijlstra@gmail.com>
Tue, 4 Mar 2025 14:58:37 +0000 (06:58 -0800)
committerGitHub <noreply@github.com>
Tue, 4 Mar 2025 14:58:37 +0000 (06:58 -0800)
Co-authored-by: sobolevn <mail@sobolevn.me>
Lib/dataclasses.py
Lib/inspect.py
Lib/test/test_dataclasses/__init__.py
Lib/test/test_inspect/test_inspect.py
Misc/NEWS.d/next/Library/2024-12-23-17-00-35.gh-issue-128184.cRQvgM.rst [new file with mode: 0644]

index 7a24f8a9e5ccee3554d40b56ae2f73d1e7d3e44b..0f7dc9ae6b82f51e781c2b046169935197d4be95 100644 (file)
@@ -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)
index f143c89674b7b2ec69018dc168a4b372f1c17aab..182c2eedbaba643176b1dcc279876bf2cc8c5c9e 100644 (file)
@@ -143,7 +143,7 @@ __all__ = [
 
 
 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 formatannotation(annotation, base_module=None, *, quote_annotation_strings=T
         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):
index 2e6c49e29ce828c2d512e4de8aeaf605e173aa50..8209374c36bca2cba31ac079d9239bd0d05f063d 100644 (file)
@@ -12,6 +12,7 @@ import builtins
 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 TestDocString(unittest.TestCase):
 
         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):
index 6a562108c3a34b4d1830c12e215627558ce2b4fa..03f2bacb3a4e881923cf21c4c2b36b55cbd62ba2 100644 (file)
@@ -1753,6 +1753,10 @@ class TestFormatAnnotation(unittest.TestCase):
         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 @@ class TestSignatureObject(unittest.TestCase):
         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 (file)
index 0000000..448dcfe
--- /dev/null
@@ -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.