Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Doc/library/annotationlib.rst
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ Functions

Convert an annotations dict containing runtime values to a
dict containing only strings. If the values are not already strings,
they are converted using :func:`value_to_string`.
they are converted using :func:`type_repr`.
This is meant as a helper for user-provided
annotate functions that support the :attr:`~Format.STRING` format but
do not have access to the code creating the annotations.
Expand Down Expand Up @@ -393,7 +393,7 @@ Functions

.. versionadded:: 3.14

.. function:: value_to_string(value)
.. function:: type_repr(value)

Convert an arbitrary Python value to a format suitable for use by the
:attr:`~Format.STRING` format. This calls :func:`repr` for most
Expand Down
6 changes: 3 additions & 3 deletions Lib/_collections_abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -485,10 +485,10 @@ def __new__(cls, origin, args):
def __repr__(self):
if len(self.__args__) == 2 and _is_param_expr(self.__args__[0]):
return super().__repr__()
from annotationlib import value_to_string
from annotationlib import type_repr
return (f'collections.abc.Callable'
f'[[{", ".join([value_to_string(a) for a in self.__args__[:-1]])}], '
f'{value_to_string(self.__args__[-1])}]')
f'[[{", ".join([type_repr(a) for a in self.__args__[:-1]])}], '
f'{type_repr(self.__args__[-1])}]')

def __reduce__(self):
args = self.__args__
Expand Down
10 changes: 4 additions & 6 deletions Lib/annotationlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"get_annotate_function",
"get_annotations",
"annotations_to_string",
"value_to_string",
"type_repr",
]


Expand Down Expand Up @@ -799,29 +799,27 @@ def get_annotations(
return return_value


def value_to_string(value):
def type_repr(value):
"""Convert a Python value to a format suitable for use with the STRING format.

This is inteded as a helper for tools that support the STRING format but do
not have access to the code that originally produced the annotations. It uses
repr() for most objects.

"""
if isinstance(value, type):
if isinstance(value, (type, types.FunctionType, types.BuiltinFunctionType)):
if value.__module__ == "builtins":
return value.__qualname__
return f"{value.__module__}.{value.__qualname__}"
if value is ...:
return "..."
if isinstance(value, (types.FunctionType, types.BuiltinFunctionType)):
return value.__name__
return repr(value)


def annotations_to_string(annotations):
"""Convert an annotation dict containing values to approximately the STRING format."""
return {
n: t if isinstance(t, str) else value_to_string(t)
n: t if isinstance(t, str) else type_repr(t)
for n, t in annotations.items()
}

Expand Down
36 changes: 23 additions & 13 deletions Lib/test/test_annotationlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
get_annotations,
get_annotate_function,
annotations_to_string,
value_to_string,
type_repr,
)
from typing import Unpack

Expand Down Expand Up @@ -1173,18 +1173,28 @@ class C:


class TestToSource(unittest.TestCase):
def test_value_to_string(self):
self.assertEqual(value_to_string(int), "int")
self.assertEqual(value_to_string(MyClass), "test.test_annotationlib.MyClass")
self.assertEqual(value_to_string(len), "len")
self.assertEqual(value_to_string(value_to_string), "value_to_string")
self.assertEqual(value_to_string(times_three), "times_three")
self.assertEqual(value_to_string(...), "...")
self.assertEqual(value_to_string(None), "None")
self.assertEqual(value_to_string(1), "1")
self.assertEqual(value_to_string("1"), "'1'")
self.assertEqual(value_to_string(Format.VALUE), repr(Format.VALUE))
self.assertEqual(value_to_string(MyClass()), "my repr")
def test_type_repr(self):
class Nested:
pass

def nested():
pass

self.assertEqual(type_repr(int), "int")
self.assertEqual(type_repr(MyClass), f"{__name__}.MyClass")
self.assertEqual(
type_repr(Nested), f"{__name__}.TestToSource.test_type_repr.<locals>.Nested")
self.assertEqual(
type_repr(nested), f"{__name__}.TestToSource.test_type_repr.<locals>.nested")
self.assertEqual(type_repr(len), "len")
self.assertEqual(type_repr(type_repr), "annotationlib.type_repr")
self.assertEqual(type_repr(times_three), f"{__name__}.times_three")
self.assertEqual(type_repr(...), "...")
self.assertEqual(type_repr(None), "None")
self.assertEqual(type_repr(1), "1")
self.assertEqual(type_repr("1"), "'1'")
self.assertEqual(type_repr(Format.VALUE), repr(Format.VALUE))
self.assertEqual(type_repr(MyClass()), "my repr")

def test_annotations_to_string(self):
self.assertEqual(annotations_to_string({}), {})
Expand Down
2 changes: 1 addition & 1 deletion Lib/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ def _type_repr(obj):
if isinstance(obj, tuple):
# Special case for `repr` of types with `ParamSpec`:
return '[' + ', '.join(_type_repr(t) for t in obj) + ']'
return _lazy_annotationlib.value_to_string(obj)
return _lazy_annotationlib.type_repr(obj)


def _collect_type_parameters(args, *, enforce_default_ordering: bool = True):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Rename ``annotationlib.value_to_string`` to
:func:`annotationlib.type_repr` and provide better handling for function
objects.
Loading