Skip to content

Commit 10c8ecc

Browse files
authored
Enable pretty-printing for PEP-649 annotated functions (ipython#15095)
This PR detects the possibility of such functions by importing annotationlib.Format from inspect, and wraps inspect.signature in a partial that sets annotation_format=Format.FORWARDREF. This may be more proof-of-concept than final form; I won't be offended if you want to scrap this and tackle the issue a different way :) I'm also not sure this is actually enough to support all possibilities, but it at least gets us from ```python In [1]: def test(p: Annotated): pass In [2]: test <huge traceback elided> NameError: name 'Annotated' is not defined In [3]: ``` to ```python In [1]: def test(p: Annotated): pass In [2]: test Out[2]: <function __main__.test(p: Annotated)> ```
2 parents 94fb0eb + 1e7227b commit 10c8ecc

File tree

2 files changed

+33
-0
lines changed

2 files changed

+33
-0
lines changed

IPython/lib/pretty.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,14 @@ def _repr_pretty_(self, p, cycle):
109109

110110
from typing import Dict
111111

112+
# Allow pretty-printing of functions with PEP-649 annotations
113+
if sys.version_info >= (3, 14):
114+
from annotationlib import Format
115+
from functools import partial
116+
117+
signature = partial(signature, annotation_format=Format.FORWARDREF)
118+
119+
112120
__all__ = ['pretty', 'pprint', 'PrettyPrinter', 'RepresentationPrinter',
113121
'for_type', 'for_type_by_name', 'RawText', 'RawStringLiteral', 'CallExpression']
114122

tests/test_pretty.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,31 @@ def meaning_of_life(question=None):
543543
assert "meaning_of_life(question=None)" in pretty.pretty(meaning_of_life)
544544

545545

546+
def test_annotated_function_pretty():
547+
"Test pretty print of a function with PEP-649 annotations"
548+
# Confirm that the name is not in scope
549+
try:
550+
b
551+
except NameError:
552+
pass
553+
else:
554+
assert False, "Unexpected 'b' found in scope"
555+
try:
556+
def f(a: b): pass
557+
except NameError as e:
558+
if e.name == 'b':
559+
# PEP-649 not available
560+
return
561+
raise
562+
# Check whether __future__.annotations is in effect
563+
# It probably shouldn't be, but to be sure:
564+
import __future__
565+
if f.__code__.co_flags & __future__.annotations.compiler_flag:
566+
assert "f(a: 'b')" in pretty.pretty(f)
567+
else:
568+
assert "f(a: b)" in pretty.pretty(f)
569+
570+
546571
class OrderedCounter(Counter, OrderedDict):
547572
"Counter that remembers the order elements are first encountered"
548573

0 commit comments

Comments
 (0)