gh-132491: Rename annotationlib.value_to_string to type_repr (#132492)

This commit is contained in:
Jelle Zijlstra 2025-04-15 13:10:53 -07:00 committed by GitHub
parent 5e80fee41a
commit 11f6603845
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 37 additions and 26 deletions

View file

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

View file

@ -485,10 +485,10 @@ class _CallableGenericAlias(GenericAlias):
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__

View file

@ -15,7 +15,7 @@ __all__ = [
"get_annotate_function",
"get_annotations",
"annotations_to_string",
"value_to_string",
"type_repr",
]
@ -795,29 +795,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
This is intended 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()
}

View file

@ -13,7 +13,7 @@ from annotationlib import (
get_annotations,
get_annotate_function,
annotations_to_string,
value_to_string,
type_repr,
)
from typing import Unpack
@ -1173,18 +1173,28 @@ class TestGetAnnotateFunction(unittest.TestCase):
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({}), {})

View file

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

View file

@ -0,0 +1,3 @@
Rename ``annotationlib.value_to_string`` to
:func:`annotationlib.type_repr` and provide better handling for function
objects.