Skip to content

Commit 1d251b8

Browse files
gh-128184: Fix display of signatures with ForwardRefs (#130815)
Co-authored-by: sobolevn <[email protected]>
1 parent 80e6d3e commit 1d251b8

File tree

5 files changed

+46
-2
lines changed

5 files changed

+46
-2
lines changed

Lib/dataclasses.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1163,7 +1163,10 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen,
11631163
try:
11641164
# In some cases fetching a signature is not possible.
11651165
# But, we surely should not fail in this case.
1166-
text_sig = str(inspect.signature(cls)).replace(' -> None', '')
1166+
text_sig = str(inspect.signature(
1167+
cls,
1168+
annotation_format=annotationlib.Format.FORWARDREF,
1169+
)).replace(' -> None', '')
11671170
except (TypeError, ValueError):
11681171
text_sig = ''
11691172
cls.__doc__ = (cls.__name__ + text_sig)

Lib/inspect.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@
143143

144144

145145
import abc
146-
from annotationlib import Format
146+
from annotationlib import Format, ForwardRef
147147
from annotationlib import get_annotations # re-exported
148148
import ast
149149
import dis
@@ -1342,6 +1342,8 @@ def repl(match):
13421342
if annotation.__module__ in ('builtins', base_module):
13431343
return annotation.__qualname__
13441344
return annotation.__module__+'.'+annotation.__qualname__
1345+
if isinstance(annotation, ForwardRef):
1346+
return annotation.__forward_arg__
13451347
return repr(annotation)
13461348

13471349
def formatannotationrelativeto(object):

Lib/test/test_dataclasses/__init__.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import types
1313
import weakref
1414
import traceback
15+
import textwrap
1516
import unittest
1617
from unittest.mock import Mock
1718
from typing import ClassVar, Any, List, Union, Tuple, Dict, Generic, TypeVar, Optional, Protocol, DefaultDict
@@ -2343,6 +2344,31 @@ class C:
23432344

23442345
self.assertDocStrEqual(C.__doc__, "C(x:collections.deque=<factory>)")
23452346

2347+
def test_docstring_undefined_name(self):
2348+
@dataclass
2349+
class C:
2350+
x: undef
2351+
2352+
self.assertDocStrEqual(C.__doc__, "C(x:undef)")
2353+
2354+
def test_docstring_with_unsolvable_forward_ref_in_init(self):
2355+
# See: https://github.com/python/cpython/issues/128184
2356+
ns = {}
2357+
exec(
2358+
textwrap.dedent(
2359+
"""
2360+
from dataclasses import dataclass
2361+
2362+
@dataclass
2363+
class C:
2364+
def __init__(self, x: X, num: int) -> None: ...
2365+
""",
2366+
),
2367+
ns,
2368+
)
2369+
2370+
self.assertDocStrEqual(ns['C'].__doc__, "C(x:X,num:int)")
2371+
23462372
def test_docstring_with_no_signature(self):
23472373
# See https://github.com/python/cpython/issues/103449
23482374
class Meta(type):

Lib/test/test_inspect/test_inspect.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1753,6 +1753,10 @@ def test_typing_replacement(self):
17531753
self.assertEqual(inspect.formatannotation(ann), 'Union[List[str], int]')
17541754
self.assertEqual(inspect.formatannotation(ann1), 'Union[List[testModule.typing.A], int]')
17551755

1756+
def test_forwardref(self):
1757+
fwdref = ForwardRef('fwdref')
1758+
self.assertEqual(inspect.formatannotation(fwdref), 'fwdref')
1759+
17561760

17571761
class TestIsMethodDescriptor(unittest.TestCase):
17581762

@@ -4587,6 +4591,11 @@ def foo(a: list[str]) -> Tuple[str, float]:
45874591
self.assertEqual(str(inspect.signature(foo)),
45884592
inspect.signature(foo).format())
45894593

4594+
def foo(x: undef):
4595+
pass
4596+
sig = inspect.signature(foo, annotation_format=Format.FORWARDREF)
4597+
self.assertEqual(str(sig), '(x: undef)')
4598+
45904599
def test_signature_str_positional_only(self):
45914600
P = inspect.Parameter
45924601
S = inspect.Signature
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Improve display of :class:`annotationlib.ForwardRef` object
2+
within :class:`inspect.Signature` representations.
3+
This also fixes a :exc:`NameError` that was raised when using
4+
:func:`dataclasses.dataclass` on classes with unresolvable forward references.

0 commit comments

Comments
 (0)