Skip to content

Commit 7541742

Browse files
Add backport for annotationlib.type_repr (#641)
Closes: #544 Co-authored-by: Brian Schubert <[email protected]>
1 parent 7fcfecc commit 7541742

File tree

4 files changed

+77
-0
lines changed

4 files changed

+77
-0
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
# Unreleased
2+
3+
- Add `typing_extensions.type_repr`, a backport of
4+
[`annotationlib.type_repr`](https://docs.python.org/3.14/library/annotationlib.html#annotationlib.type_repr),
5+
introduced in Python 3.14 (CPython PR [#124551](https://github.com/python/cpython/pull/124551),
6+
originally by Jelle Zijlstra). Patch by Semyon Moroz.
7+
8+
19
# Release 4.14.1 (July 4, 2025)
210

311
- Fix usage of `typing_extensions.TypedDict` nested inside other types

doc/index.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -933,6 +933,15 @@ Functions
933933

934934
.. versionadded:: 4.1.0
935935

936+
.. function:: type_repr(value)
937+
938+
See :py:func:`annotationlib.type_repr`. In ``annotationlib`` since 3.14.
939+
940+
Convert an arbitrary Python value to a format suitable for use by
941+
the :attr:`Format.STRING`.
942+
943+
.. versionadded:: 4.15.0
944+
936945
Enums
937946
~~~~~
938947

src/test_typing_extensions.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@
101101
reveal_type,
102102
runtime,
103103
runtime_checkable,
104+
type_repr,
104105
)
105106

106107
NoneType = type(None)
@@ -8368,6 +8369,44 @@ def test_capsule_type(self):
83688369
self.assertIsInstance(_datetime.datetime_CAPI, typing_extensions.CapsuleType)
83698370

83708371

8372+
class MyClass:
8373+
def __repr__(self):
8374+
return "my repr"
8375+
8376+
8377+
class TestTypeRepr(BaseTestCase):
8378+
def test_custom_types(self):
8379+
8380+
class Nested:
8381+
pass
8382+
8383+
def nested():
8384+
pass
8385+
8386+
self.assertEqual(type_repr(MyClass), f"{__name__}.MyClass")
8387+
self.assertEqual(
8388+
type_repr(Nested),
8389+
f"{__name__}.TestTypeRepr.test_custom_types.<locals>.Nested",
8390+
)
8391+
self.assertEqual(
8392+
type_repr(nested),
8393+
f"{__name__}.TestTypeRepr.test_custom_types.<locals>.nested",
8394+
)
8395+
self.assertEqual(type_repr(times_three), f"{__name__}.times_three")
8396+
self.assertEqual(type_repr(Format.VALUE), repr(Format.VALUE))
8397+
self.assertEqual(type_repr(MyClass()), "my repr")
8398+
8399+
def test_builtin_types(self):
8400+
self.assertEqual(type_repr(int), "int")
8401+
self.assertEqual(type_repr(object), "object")
8402+
self.assertEqual(type_repr(None), "None")
8403+
self.assertEqual(type_repr(len), "len")
8404+
self.assertEqual(type_repr(1), "1")
8405+
self.assertEqual(type_repr("1"), "'1'")
8406+
self.assertEqual(type_repr(''), "''")
8407+
self.assertEqual(type_repr(...), "...")
8408+
8409+
83718410
def times_three(fn):
83728411
@functools.wraps(fn)
83738412
def wrapper(a, b):

src/typing_extensions.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@
100100
'TypeGuard',
101101
'TypeIs',
102102
'TYPE_CHECKING',
103+
'type_repr',
103104
'Never',
104105
'NoReturn',
105106
'ReadOnly',
@@ -4192,6 +4193,26 @@ def __getstate__(self):
41924193
raise TypeError(f"Cannot pickle {type(self).__name__!r} object")
41934194

41944195

4196+
if sys.version_info >= (3, 14, 0, "beta"):
4197+
type_repr = annotationlib.type_repr
4198+
else:
4199+
def type_repr(value):
4200+
"""Convert a Python value to a format suitable for use with the STRING format.
4201+
4202+
This is intended as a helper for tools that support the STRING format but do
4203+
not have access to the code that originally produced the annotations. It uses
4204+
repr() for most objects.
4205+
4206+
"""
4207+
if isinstance(value, (type, _types.FunctionType, _types.BuiltinFunctionType)):
4208+
if value.__module__ == "builtins":
4209+
return value.__qualname__
4210+
return f"{value.__module__}.{value.__qualname__}"
4211+
if value is ...:
4212+
return "..."
4213+
return repr(value)
4214+
4215+
41954216
# Aliases for items that are in typing in all supported versions.
41964217
# We use hasattr() checks so this library will continue to import on
41974218
# future versions of Python that may remove these names.

0 commit comments

Comments
 (0)