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

Co-authored-by: sobolevn <mail@sobolevn.me>
This commit is contained in:
Jelle Zijlstra 2025-03-04 06:58:37 -08:00 committed by GitHub
parent 80e6d3ec49
commit 1d251b8339
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 46 additions and 2 deletions

View 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)

View 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):

View 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):

View 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

View file

@ -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.