mirror of
https://github.com/python/cpython.git
synced 2024-11-21 12:59:38 +01:00
gh-119180: Rename SOURCE format to STRING (#124620)
This commit is contained in:
parent
a4d1fdfb15
commit
2c10832887
@ -32,7 +32,7 @@ This module supports retrieving annotations in three main formats
|
||||
for annotations that cannot be resolved, allowing you to inspect the
|
||||
annotations without evaluating them. This is useful when you need to
|
||||
work with annotations that may contain unresolved forward references.
|
||||
* :attr:`~Format.SOURCE` returns the annotations as a string, similar
|
||||
* :attr:`~Format.STRING` returns the annotations as a string, similar
|
||||
to how it would appear in the source file. This is useful for documentation
|
||||
generators that want to display annotations in a readable way.
|
||||
|
||||
@ -135,7 +135,7 @@ Classes
|
||||
values. Real objects may contain references to, :class:`ForwardRef`
|
||||
proxy objects.
|
||||
|
||||
.. attribute:: SOURCE
|
||||
.. attribute:: STRING
|
||||
:value: 3
|
||||
|
||||
Values are the text string of the annotation as it appears in the
|
||||
@ -197,23 +197,23 @@ Classes
|
||||
Functions
|
||||
---------
|
||||
|
||||
.. function:: annotations_to_source(annotations)
|
||||
.. function:: annotations_to_string(annotations)
|
||||
|
||||
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_source`.
|
||||
they are converted using :func:`value_to_string`.
|
||||
This is meant as a helper for user-provided
|
||||
annotate functions that support the :attr:`~Format.SOURCE` format but
|
||||
annotate functions that support the :attr:`~Format.STRING` format but
|
||||
do not have access to the code creating the annotations.
|
||||
|
||||
For example, this is used to implement the :attr:`~Format.SOURCE` for
|
||||
For example, this is used to implement the :attr:`~Format.STRING` for
|
||||
:class:`typing.TypedDict` classes created through the functional syntax:
|
||||
|
||||
.. doctest::
|
||||
|
||||
>>> from typing import TypedDict
|
||||
>>> Movie = TypedDict("movie", {"name": str, "year": int})
|
||||
>>> get_annotations(Movie, format=Format.SOURCE)
|
||||
>>> get_annotations(Movie, format=Format.STRING)
|
||||
{'name': 'str', 'year': 'int'}
|
||||
|
||||
.. versionadded:: 3.14
|
||||
@ -282,7 +282,7 @@ Functions
|
||||
NameError: name 'undefined' is not defined
|
||||
>>> call_evaluate_function(Alias.evaluate_value, Format.FORWARDREF)
|
||||
ForwardRef('undefined')
|
||||
>>> call_evaluate_function(Alias.evaluate_value, Format.SOURCE)
|
||||
>>> call_evaluate_function(Alias.evaluate_value, Format.STRING)
|
||||
'undefined'
|
||||
|
||||
.. versionadded:: 3.14
|
||||
@ -369,14 +369,14 @@ Functions
|
||||
|
||||
.. versionadded:: 3.14
|
||||
|
||||
.. function:: value_to_source(value)
|
||||
.. function:: value_to_string(value)
|
||||
|
||||
Convert an arbitrary Python value to a format suitable for use by the
|
||||
:attr:`~Format.SOURCE` format. This calls :func:`repr` for most
|
||||
:attr:`~Format.STRING` format. This calls :func:`repr` for most
|
||||
objects, but has special handling for some objects, such as type objects.
|
||||
|
||||
This is meant as a helper for user-provided
|
||||
annotate functions that support the :attr:`~Format.SOURCE` format but
|
||||
annotate functions that support the :attr:`~Format.STRING` format but
|
||||
do not have access to the code creating the annotations. It can also
|
||||
be used to provide a user-friendly string representation for other
|
||||
objects that contain values that are commonly encountered in annotations.
|
||||
|
@ -3427,7 +3427,7 @@ Introspection helpers
|
||||
* Replaces type hints that evaluate to :const:`!None` with
|
||||
:class:`types.NoneType`.
|
||||
* Supports the :attr:`~annotationlib.Format.FORWARDREF` and
|
||||
:attr:`~annotationlib.Format.SOURCE` formats.
|
||||
:attr:`~annotationlib.Format.STRING` formats.
|
||||
|
||||
*forward_ref* must be an instance of :class:`~annotationlib.ForwardRef`.
|
||||
*owner*, if given, should be the object that holds the annotations that
|
||||
|
@ -91,7 +91,7 @@ annotations. Annotations may be evaluated in the :attr:`~annotationlib.Format.VA
|
||||
format (which evaluates annotations to runtime values, similar to the behavior in
|
||||
earlier Python versions), the :attr:`~annotationlib.Format.FORWARDREF` format
|
||||
(which replaces undefined names with special markers), and the
|
||||
:attr:`~annotationlib.Format.SOURCE` format (which returns annotations as strings).
|
||||
:attr:`~annotationlib.Format.STRING` format (which returns annotations as strings).
|
||||
|
||||
This example shows how these formats behave:
|
||||
|
||||
@ -106,7 +106,7 @@ This example shows how these formats behave:
|
||||
NameError: name 'Undefined' is not defined
|
||||
>>> get_annotations(func, format=Format.FORWARDREF)
|
||||
{'arg': ForwardRef('Undefined')}
|
||||
>>> get_annotations(func, format=Format.SOURCE)
|
||||
>>> get_annotations(func, format=Format.STRING)
|
||||
{'arg': 'Undefined'}
|
||||
|
||||
Implications for annotated code
|
||||
|
@ -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_source
|
||||
from annotationlib import value_to_string
|
||||
return (f'collections.abc.Callable'
|
||||
f'[[{", ".join([value_to_source(a) for a in self.__args__[:-1]])}], '
|
||||
f'{value_to_source(self.__args__[-1])}]')
|
||||
f'[[{", ".join([value_to_string(a) for a in self.__args__[:-1]])}], '
|
||||
f'{value_to_string(self.__args__[-1])}]')
|
||||
|
||||
def __reduce__(self):
|
||||
args = self.__args__
|
||||
|
@ -15,15 +15,15 @@ __all__ = [
|
||||
"call_evaluate_function",
|
||||
"get_annotate_function",
|
||||
"get_annotations",
|
||||
"annotations_to_source",
|
||||
"value_to_source",
|
||||
"annotations_to_string",
|
||||
"value_to_string",
|
||||
]
|
||||
|
||||
|
||||
class Format(enum.IntEnum):
|
||||
VALUE = 1
|
||||
FORWARDREF = 2
|
||||
SOURCE = 3
|
||||
STRING = 3
|
||||
|
||||
|
||||
_Union = None
|
||||
@ -291,9 +291,21 @@ class _Stringifier:
|
||||
return other.__ast_node__
|
||||
elif isinstance(other, slice):
|
||||
return ast.Slice(
|
||||
lower=self.__convert_to_ast(other.start) if other.start is not None else None,
|
||||
upper=self.__convert_to_ast(other.stop) if other.stop is not None else None,
|
||||
step=self.__convert_to_ast(other.step) if other.step is not None else None,
|
||||
lower=(
|
||||
self.__convert_to_ast(other.start)
|
||||
if other.start is not None
|
||||
else None
|
||||
),
|
||||
upper=(
|
||||
self.__convert_to_ast(other.stop)
|
||||
if other.stop is not None
|
||||
else None
|
||||
),
|
||||
step=(
|
||||
self.__convert_to_ast(other.step)
|
||||
if other.step is not None
|
||||
else None
|
||||
),
|
||||
)
|
||||
else:
|
||||
return ast.Constant(value=other)
|
||||
@ -469,7 +481,7 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False):
|
||||
can be called with any of the format arguments in the Format enum, but
|
||||
compiler-generated __annotate__ functions only support the VALUE format.
|
||||
This function provides additional functionality to call __annotate__
|
||||
functions with the FORWARDREF and SOURCE formats.
|
||||
functions with the FORWARDREF and STRING formats.
|
||||
|
||||
*annotate* must be an __annotate__ function, which takes a single argument
|
||||
and returns a dict of annotations.
|
||||
@ -487,8 +499,8 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False):
|
||||
return annotate(format)
|
||||
except NotImplementedError:
|
||||
pass
|
||||
if format == Format.SOURCE:
|
||||
# SOURCE is implemented by calling the annotate function in a special
|
||||
if format == Format.STRING:
|
||||
# STRING is implemented by calling the annotate function in a special
|
||||
# environment where every name lookup results in an instance of _Stringifier.
|
||||
# _Stringifier supports every dunder operation and returns a new _Stringifier.
|
||||
# At the end, we get a dictionary that mostly contains _Stringifier objects (or
|
||||
@ -524,9 +536,9 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False):
|
||||
for key, val in annos.items()
|
||||
}
|
||||
elif format == Format.FORWARDREF:
|
||||
# FORWARDREF is implemented similarly to SOURCE, but there are two changes,
|
||||
# FORWARDREF is implemented similarly to STRING, but there are two changes,
|
||||
# at the beginning and the end of the process.
|
||||
# First, while SOURCE uses an empty dictionary as the namespace, so that all
|
||||
# First, while STRING uses an empty dictionary as the namespace, so that all
|
||||
# name lookups result in _Stringifier objects, FORWARDREF uses the globals
|
||||
# and builtins, so that defined names map to their real values.
|
||||
# Second, instead of returning strings, we want to return either real values
|
||||
@ -688,14 +700,14 @@ def get_annotations(
|
||||
# __annotations__ threw NameError and there is no __annotate__. In that case,
|
||||
# we fall back to trying __annotations__ again.
|
||||
return dict(_get_dunder_annotations(obj))
|
||||
case Format.SOURCE:
|
||||
# For SOURCE, we try to call __annotate__
|
||||
case Format.STRING:
|
||||
# For STRING, we try to call __annotate__
|
||||
ann = _get_and_call_annotate(obj, format)
|
||||
if ann is not None:
|
||||
return ann
|
||||
# But if we didn't get it, we use __annotations__ instead.
|
||||
ann = _get_dunder_annotations(obj)
|
||||
return annotations_to_source(ann)
|
||||
return annotations_to_string(ann)
|
||||
case _:
|
||||
raise ValueError(f"Unsupported format {format!r}")
|
||||
|
||||
@ -764,10 +776,10 @@ def get_annotations(
|
||||
return return_value
|
||||
|
||||
|
||||
def value_to_source(value):
|
||||
"""Convert a Python value to a format suitable for use with the SOURCE format.
|
||||
def value_to_string(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 SOURCE format but do
|
||||
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.
|
||||
|
||||
@ -783,10 +795,10 @@ def value_to_source(value):
|
||||
return repr(value)
|
||||
|
||||
|
||||
def annotations_to_source(annotations):
|
||||
"""Convert an annotation dict containing values to approximately the SOURCE format."""
|
||||
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_source(t)
|
||||
n: t if isinstance(t, str) else value_to_string(t)
|
||||
for n, t in annotations.items()
|
||||
}
|
||||
|
||||
|
@ -12,8 +12,8 @@ from annotationlib import (
|
||||
ForwardRef,
|
||||
get_annotations,
|
||||
get_annotate_function,
|
||||
annotations_to_source,
|
||||
value_to_source,
|
||||
annotations_to_string,
|
||||
value_to_string,
|
||||
)
|
||||
from typing import Unpack
|
||||
|
||||
@ -39,14 +39,14 @@ class MyClass:
|
||||
|
||||
class TestFormat(unittest.TestCase):
|
||||
def test_enum(self):
|
||||
self.assertEqual(annotationlib.Format.VALUE.value, 1)
|
||||
self.assertEqual(annotationlib.Format.VALUE, 1)
|
||||
self.assertEqual(Format.VALUE.value, 1)
|
||||
self.assertEqual(Format.VALUE, 1)
|
||||
|
||||
self.assertEqual(annotationlib.Format.FORWARDREF.value, 2)
|
||||
self.assertEqual(annotationlib.Format.FORWARDREF, 2)
|
||||
self.assertEqual(Format.FORWARDREF.value, 2)
|
||||
self.assertEqual(Format.FORWARDREF, 2)
|
||||
|
||||
self.assertEqual(annotationlib.Format.SOURCE.value, 3)
|
||||
self.assertEqual(annotationlib.Format.SOURCE, 3)
|
||||
self.assertEqual(Format.STRING.value, 3)
|
||||
self.assertEqual(Format.STRING, 3)
|
||||
|
||||
|
||||
class TestForwardRefFormat(unittest.TestCase):
|
||||
@ -54,9 +54,7 @@ class TestForwardRefFormat(unittest.TestCase):
|
||||
def inner(arg: x):
|
||||
pass
|
||||
|
||||
anno = annotationlib.get_annotations(
|
||||
inner, format=annotationlib.Format.FORWARDREF
|
||||
)
|
||||
anno = annotationlib.get_annotations(inner, format=Format.FORWARDREF)
|
||||
fwdref = anno["arg"]
|
||||
self.assertIsInstance(fwdref, annotationlib.ForwardRef)
|
||||
self.assertEqual(fwdref.__forward_arg__, "x")
|
||||
@ -66,16 +64,14 @@ class TestForwardRefFormat(unittest.TestCase):
|
||||
x = 1
|
||||
self.assertEqual(fwdref.evaluate(), x)
|
||||
|
||||
anno = annotationlib.get_annotations(
|
||||
inner, format=annotationlib.Format.FORWARDREF
|
||||
)
|
||||
anno = annotationlib.get_annotations(inner, format=Format.FORWARDREF)
|
||||
self.assertEqual(anno["arg"], x)
|
||||
|
||||
def test_function(self):
|
||||
def f(x: int, y: doesntexist):
|
||||
pass
|
||||
|
||||
anno = annotationlib.get_annotations(f, format=annotationlib.Format.FORWARDREF)
|
||||
anno = annotationlib.get_annotations(f, format=Format.FORWARDREF)
|
||||
self.assertIs(anno["x"], int)
|
||||
fwdref = anno["y"]
|
||||
self.assertIsInstance(fwdref, annotationlib.ForwardRef)
|
||||
@ -92,14 +88,14 @@ class TestSourceFormat(unittest.TestCase):
|
||||
def inner(arg: x):
|
||||
pass
|
||||
|
||||
anno = annotationlib.get_annotations(inner, format=annotationlib.Format.SOURCE)
|
||||
anno = annotationlib.get_annotations(inner, format=Format.STRING)
|
||||
self.assertEqual(anno, {"arg": "x"})
|
||||
|
||||
def test_function(self):
|
||||
def f(x: int, y: doesntexist):
|
||||
pass
|
||||
|
||||
anno = annotationlib.get_annotations(f, format=annotationlib.Format.SOURCE)
|
||||
anno = annotationlib.get_annotations(f, format=Format.STRING)
|
||||
self.assertEqual(anno, {"x": "int", "y": "doesntexist"})
|
||||
|
||||
def test_expressions(self):
|
||||
@ -133,7 +129,7 @@ class TestSourceFormat(unittest.TestCase):
|
||||
):
|
||||
pass
|
||||
|
||||
anno = annotationlib.get_annotations(f, format=annotationlib.Format.SOURCE)
|
||||
anno = annotationlib.get_annotations(f, format=Format.STRING)
|
||||
self.assertEqual(
|
||||
anno,
|
||||
{
|
||||
@ -184,7 +180,7 @@ class TestSourceFormat(unittest.TestCase):
|
||||
):
|
||||
pass
|
||||
|
||||
anno = annotationlib.get_annotations(f, format=annotationlib.Format.SOURCE)
|
||||
anno = annotationlib.get_annotations(f, format=Format.STRING)
|
||||
self.assertEqual(
|
||||
anno,
|
||||
{
|
||||
@ -218,7 +214,7 @@ class TestSourceFormat(unittest.TestCase):
|
||||
):
|
||||
pass
|
||||
|
||||
anno = annotationlib.get_annotations(f, format=annotationlib.Format.SOURCE)
|
||||
anno = annotationlib.get_annotations(f, format=Format.STRING)
|
||||
self.assertEqual(
|
||||
anno,
|
||||
{
|
||||
@ -241,13 +237,13 @@ class TestSourceFormat(unittest.TestCase):
|
||||
pass
|
||||
|
||||
with self.assertRaisesRegex(TypeError, format_msg):
|
||||
annotationlib.get_annotations(f, format=annotationlib.Format.SOURCE)
|
||||
annotationlib.get_annotations(f, format=Format.STRING)
|
||||
|
||||
def f(fstring_format: f"{a:02d}"):
|
||||
pass
|
||||
|
||||
with self.assertRaisesRegex(TypeError, format_msg):
|
||||
annotationlib.get_annotations(f, format=annotationlib.Format.SOURCE)
|
||||
annotationlib.get_annotations(f, format=Format.STRING)
|
||||
|
||||
|
||||
class TestForwardRefClass(unittest.TestCase):
|
||||
@ -276,7 +272,7 @@ class TestForwardRefClass(unittest.TestCase):
|
||||
with self.assertRaises(NameError):
|
||||
ForwardRef("T").evaluate(owner=int)
|
||||
|
||||
T, = Gen.__type_params__
|
||||
(T,) = Gen.__type_params__
|
||||
self.assertIs(ForwardRef("T").evaluate(type_params=Gen.__type_params__), T)
|
||||
self.assertIs(ForwardRef("T").evaluate(owner=Gen), T)
|
||||
|
||||
@ -294,8 +290,7 @@ class TestForwardRefClass(unittest.TestCase):
|
||||
def test_fwdref_with_module(self):
|
||||
self.assertIs(ForwardRef("Format", module="annotationlib").evaluate(), Format)
|
||||
self.assertIs(
|
||||
ForwardRef("Counter", module="collections").evaluate(),
|
||||
collections.Counter
|
||||
ForwardRef("Counter", module="collections").evaluate(), collections.Counter
|
||||
)
|
||||
self.assertEqual(
|
||||
ForwardRef("Counter[int]", module="collections").evaluate(),
|
||||
@ -383,22 +378,20 @@ class TestGetAnnotations(unittest.TestCase):
|
||||
|
||||
self.assertEqual(annotationlib.get_annotations(C1), {"a": int})
|
||||
self.assertEqual(
|
||||
annotationlib.get_annotations(C1, format=annotationlib.Format.FORWARDREF),
|
||||
annotationlib.get_annotations(C1, format=Format.FORWARDREF),
|
||||
{"a": int},
|
||||
)
|
||||
self.assertEqual(
|
||||
annotationlib.get_annotations(C1, format=annotationlib.Format.SOURCE),
|
||||
annotationlib.get_annotations(C1, format=Format.STRING),
|
||||
{"a": "int"},
|
||||
)
|
||||
self.assertEqual(annotationlib.get_annotations(NoDict), {"b": str})
|
||||
self.assertEqual(
|
||||
annotationlib.get_annotations(
|
||||
NoDict, format=annotationlib.Format.FORWARDREF
|
||||
),
|
||||
annotationlib.get_annotations(NoDict, format=Format.FORWARDREF),
|
||||
{"b": str},
|
||||
)
|
||||
self.assertEqual(
|
||||
annotationlib.get_annotations(NoDict, format=annotationlib.Format.SOURCE),
|
||||
annotationlib.get_annotations(NoDict, format=Format.STRING),
|
||||
{"b": "str"},
|
||||
)
|
||||
|
||||
@ -410,20 +403,20 @@ class TestGetAnnotations(unittest.TestCase):
|
||||
pass
|
||||
|
||||
self.assertEqual(
|
||||
annotationlib.get_annotations(f1, format=annotationlib.Format.VALUE),
|
||||
annotationlib.get_annotations(f1, format=Format.VALUE),
|
||||
{"a": int},
|
||||
)
|
||||
self.assertEqual(annotationlib.get_annotations(f1, format=1), {"a": int})
|
||||
|
||||
fwd = annotationlib.ForwardRef("undefined")
|
||||
self.assertEqual(
|
||||
annotationlib.get_annotations(f2, format=annotationlib.Format.FORWARDREF),
|
||||
annotationlib.get_annotations(f2, format=Format.FORWARDREF),
|
||||
{"a": fwd},
|
||||
)
|
||||
self.assertEqual(annotationlib.get_annotations(f2, format=2), {"a": fwd})
|
||||
|
||||
self.assertEqual(
|
||||
annotationlib.get_annotations(f1, format=annotationlib.Format.SOURCE),
|
||||
annotationlib.get_annotations(f1, format=Format.STRING),
|
||||
{"a": "int"},
|
||||
)
|
||||
self.assertEqual(annotationlib.get_annotations(f1, format=3), {"a": "int"})
|
||||
@ -446,30 +439,26 @@ class TestGetAnnotations(unittest.TestCase):
|
||||
pass
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
annotationlib.get_annotations(
|
||||
foo, format=annotationlib.Format.FORWARDREF, eval_str=True
|
||||
)
|
||||
annotationlib.get_annotations(
|
||||
foo, format=annotationlib.Format.SOURCE, eval_str=True
|
||||
)
|
||||
annotationlib.get_annotations(foo, format=Format.FORWARDREF, eval_str=True)
|
||||
annotationlib.get_annotations(foo, format=Format.STRING, eval_str=True)
|
||||
|
||||
def test_stock_annotations(self):
|
||||
def foo(a: int, b: str):
|
||||
pass
|
||||
|
||||
for format in (annotationlib.Format.VALUE, annotationlib.Format.FORWARDREF):
|
||||
for format in (Format.VALUE, Format.FORWARDREF):
|
||||
with self.subTest(format=format):
|
||||
self.assertEqual(
|
||||
annotationlib.get_annotations(foo, format=format),
|
||||
{"a": int, "b": str},
|
||||
)
|
||||
self.assertEqual(
|
||||
annotationlib.get_annotations(foo, format=annotationlib.Format.SOURCE),
|
||||
annotationlib.get_annotations(foo, format=Format.STRING),
|
||||
{"a": "int", "b": "str"},
|
||||
)
|
||||
|
||||
foo.__annotations__ = {"a": "foo", "b": "str"}
|
||||
for format in annotationlib.Format:
|
||||
for format in Format:
|
||||
with self.subTest(format=format):
|
||||
self.assertEqual(
|
||||
annotationlib.get_annotations(foo, format=format),
|
||||
@ -491,10 +480,10 @@ class TestGetAnnotations(unittest.TestCase):
|
||||
for kwargs in [
|
||||
{},
|
||||
{"eval_str": False},
|
||||
{"format": annotationlib.Format.VALUE},
|
||||
{"format": annotationlib.Format.FORWARDREF},
|
||||
{"format": annotationlib.Format.VALUE, "eval_str": False},
|
||||
{"format": annotationlib.Format.FORWARDREF, "eval_str": False},
|
||||
{"format": Format.VALUE},
|
||||
{"format": Format.FORWARDREF},
|
||||
{"format": Format.VALUE, "eval_str": False},
|
||||
{"format": Format.FORWARDREF, "eval_str": False},
|
||||
]:
|
||||
with self.subTest(**kwargs):
|
||||
self.assertEqual(
|
||||
@ -529,7 +518,7 @@ class TestGetAnnotations(unittest.TestCase):
|
||||
|
||||
for kwargs in [
|
||||
{"eval_str": True},
|
||||
{"format": annotationlib.Format.VALUE, "eval_str": True},
|
||||
{"format": Format.VALUE, "eval_str": True},
|
||||
]:
|
||||
with self.subTest(**kwargs):
|
||||
self.assertEqual(
|
||||
@ -563,48 +552,36 @@ class TestGetAnnotations(unittest.TestCase):
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
annotationlib.get_annotations(isa, format=annotationlib.Format.SOURCE),
|
||||
annotationlib.get_annotations(isa, format=Format.STRING),
|
||||
{"a": "int", "b": "str"},
|
||||
)
|
||||
self.assertEqual(
|
||||
annotationlib.get_annotations(
|
||||
isa.MyClass, format=annotationlib.Format.SOURCE
|
||||
),
|
||||
annotationlib.get_annotations(isa.MyClass, format=Format.STRING),
|
||||
{"a": "int", "b": "str"},
|
||||
)
|
||||
self.assertEqual(
|
||||
annotationlib.get_annotations(
|
||||
isa.function, format=annotationlib.Format.SOURCE
|
||||
),
|
||||
annotationlib.get_annotations(isa.function, format=Format.STRING),
|
||||
{"a": "int", "b": "str", "return": "MyClass"},
|
||||
)
|
||||
self.assertEqual(
|
||||
annotationlib.get_annotations(
|
||||
isa.function2, format=annotationlib.Format.SOURCE
|
||||
),
|
||||
annotationlib.get_annotations(isa.function2, format=Format.STRING),
|
||||
{"a": "int", "b": "str", "c": "MyClass", "return": "MyClass"},
|
||||
)
|
||||
self.assertEqual(
|
||||
annotationlib.get_annotations(
|
||||
isa.function3, format=annotationlib.Format.SOURCE
|
||||
),
|
||||
annotationlib.get_annotations(isa.function3, format=Format.STRING),
|
||||
{"a": "int", "b": "str", "c": "MyClass"},
|
||||
)
|
||||
self.assertEqual(
|
||||
annotationlib.get_annotations(
|
||||
annotationlib, format=annotationlib.Format.SOURCE
|
||||
),
|
||||
annotationlib.get_annotations(annotationlib, format=Format.STRING),
|
||||
{},
|
||||
)
|
||||
self.assertEqual(
|
||||
annotationlib.get_annotations(isa.UnannotatedClass, format=Format.STRING),
|
||||
{},
|
||||
)
|
||||
self.assertEqual(
|
||||
annotationlib.get_annotations(
|
||||
isa.UnannotatedClass, format=annotationlib.Format.SOURCE
|
||||
),
|
||||
{},
|
||||
)
|
||||
self.assertEqual(
|
||||
annotationlib.get_annotations(
|
||||
isa.unannotated_function, format=annotationlib.Format.SOURCE
|
||||
isa.unannotated_function, format=Format.STRING
|
||||
),
|
||||
{},
|
||||
)
|
||||
@ -620,13 +597,11 @@ class TestGetAnnotations(unittest.TestCase):
|
||||
{"a": int, "b": str, "return": isa.MyClass},
|
||||
)
|
||||
self.assertEqual(
|
||||
annotationlib.get_annotations(
|
||||
wrapped, format=annotationlib.Format.FORWARDREF
|
||||
),
|
||||
annotationlib.get_annotations(wrapped, format=Format.FORWARDREF),
|
||||
{"a": int, "b": str, "return": isa.MyClass},
|
||||
)
|
||||
self.assertEqual(
|
||||
annotationlib.get_annotations(wrapped, format=annotationlib.Format.SOURCE),
|
||||
annotationlib.get_annotations(wrapped, format=Format.STRING),
|
||||
{"a": "int", "b": "str", "return": "MyClass"},
|
||||
)
|
||||
self.assertEqual(
|
||||
@ -643,12 +618,12 @@ class TestGetAnnotations(unittest.TestCase):
|
||||
for kwargs in [
|
||||
{},
|
||||
{"eval_str": False},
|
||||
{"format": annotationlib.Format.VALUE},
|
||||
{"format": annotationlib.Format.FORWARDREF},
|
||||
{"format": annotationlib.Format.SOURCE},
|
||||
{"format": annotationlib.Format.VALUE, "eval_str": False},
|
||||
{"format": annotationlib.Format.FORWARDREF, "eval_str": False},
|
||||
{"format": annotationlib.Format.SOURCE, "eval_str": False},
|
||||
{"format": Format.VALUE},
|
||||
{"format": Format.FORWARDREF},
|
||||
{"format": Format.STRING},
|
||||
{"format": Format.VALUE, "eval_str": False},
|
||||
{"format": Format.FORWARDREF, "eval_str": False},
|
||||
{"format": Format.STRING, "eval_str": False},
|
||||
]:
|
||||
with self.subTest(**kwargs):
|
||||
self.assertEqual(
|
||||
@ -681,7 +656,7 @@ class TestGetAnnotations(unittest.TestCase):
|
||||
|
||||
for kwargs in [
|
||||
{"eval_str": True},
|
||||
{"format": annotationlib.Format.VALUE, "eval_str": True},
|
||||
{"format": Format.VALUE, "eval_str": True},
|
||||
]:
|
||||
with self.subTest(**kwargs):
|
||||
self.assertEqual(
|
||||
@ -767,9 +742,9 @@ class TestGetAnnotations(unittest.TestCase):
|
||||
annotationlib.get_annotations(f, format=Format.FORWARDREF),
|
||||
{"x": str},
|
||||
)
|
||||
# ... but not in SOURCE which always uses __annotate__
|
||||
# ... but not in STRING which always uses __annotate__
|
||||
self.assertEqual(
|
||||
annotationlib.get_annotations(f, format=Format.SOURCE),
|
||||
annotationlib.get_annotations(f, format=Format.STRING),
|
||||
{"x": "int"},
|
||||
)
|
||||
|
||||
@ -804,7 +779,7 @@ class TestGetAnnotations(unittest.TestCase):
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
annotationlib.get_annotations(ha, format=Format.SOURCE), {"x": "int"}
|
||||
annotationlib.get_annotations(ha, format=Format.STRING), {"x": "int"}
|
||||
)
|
||||
|
||||
def test_raising_annotations_on_custom_object(self):
|
||||
@ -844,7 +819,7 @@ class TestGetAnnotations(unittest.TestCase):
|
||||
annotationlib.get_annotations(hb, format=Format.FORWARDREF), {"x": int}
|
||||
)
|
||||
self.assertEqual(
|
||||
annotationlib.get_annotations(hb, format=Format.SOURCE), {"x": str}
|
||||
annotationlib.get_annotations(hb, format=Format.STRING), {"x": str}
|
||||
)
|
||||
|
||||
def test_pep695_generic_class_with_future_annotations(self):
|
||||
@ -974,15 +949,13 @@ class TestCallEvaluateFunction(unittest.TestCase):
|
||||
return undefined
|
||||
|
||||
with self.assertRaises(NameError):
|
||||
annotationlib.call_evaluate_function(evaluate, annotationlib.Format.VALUE)
|
||||
annotationlib.call_evaluate_function(evaluate, Format.VALUE)
|
||||
self.assertEqual(
|
||||
annotationlib.call_evaluate_function(
|
||||
evaluate, annotationlib.Format.FORWARDREF
|
||||
),
|
||||
annotationlib.call_evaluate_function(evaluate, Format.FORWARDREF),
|
||||
annotationlib.ForwardRef("undefined"),
|
||||
)
|
||||
self.assertEqual(
|
||||
annotationlib.call_evaluate_function(evaluate, annotationlib.Format.SOURCE),
|
||||
annotationlib.call_evaluate_function(evaluate, Format.STRING),
|
||||
"undefined",
|
||||
)
|
||||
|
||||
@ -1093,25 +1066,25 @@ class TestGetAnnotateFunction(unittest.TestCase):
|
||||
|
||||
|
||||
class TestToSource(unittest.TestCase):
|
||||
def test_value_to_source(self):
|
||||
self.assertEqual(value_to_source(int), "int")
|
||||
self.assertEqual(value_to_source(MyClass), "test.test_annotationlib.MyClass")
|
||||
self.assertEqual(value_to_source(len), "len")
|
||||
self.assertEqual(value_to_source(value_to_source), "value_to_source")
|
||||
self.assertEqual(value_to_source(times_three), "times_three")
|
||||
self.assertEqual(value_to_source(...), "...")
|
||||
self.assertEqual(value_to_source(None), "None")
|
||||
self.assertEqual(value_to_source(1), "1")
|
||||
self.assertEqual(value_to_source("1"), "'1'")
|
||||
self.assertEqual(value_to_source(Format.VALUE), repr(Format.VALUE))
|
||||
self.assertEqual(value_to_source(MyClass()), "my repr")
|
||||
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_annotations_to_source(self):
|
||||
self.assertEqual(annotations_to_source({}), {})
|
||||
self.assertEqual(annotations_to_source({"x": int}), {"x": "int"})
|
||||
self.assertEqual(annotations_to_source({"x": "int"}), {"x": "int"})
|
||||
def test_annotations_to_string(self):
|
||||
self.assertEqual(annotations_to_string({}), {})
|
||||
self.assertEqual(annotations_to_string({"x": int}), {"x": "int"})
|
||||
self.assertEqual(annotations_to_string({"x": "int"}), {"x": "int"})
|
||||
self.assertEqual(
|
||||
annotations_to_source({"x": int, "y": str}), {"x": "int", "y": "str"}
|
||||
annotations_to_string({"x": int, "y": str}), {"x": "int", "y": "str"}
|
||||
)
|
||||
|
||||
|
||||
|
@ -375,7 +375,7 @@ class DeferredEvaluationTests(unittest.TestCase):
|
||||
with self.assertRaises(NotImplementedError):
|
||||
annotate(annotationlib.Format.FORWARDREF)
|
||||
with self.assertRaises(NotImplementedError):
|
||||
annotate(annotationlib.Format.SOURCE)
|
||||
annotate(annotationlib.Format.STRING)
|
||||
with self.assertRaises(NotImplementedError):
|
||||
annotate(None)
|
||||
self.assertEqual(annotate(annotationlib.Format.VALUE), {"x": int})
|
||||
|
@ -1440,7 +1440,7 @@ class TestEvaluateFunctions(unittest.TestCase):
|
||||
self.assertIs(case(1), int)
|
||||
self.assertIs(annotationlib.call_evaluate_function(case, annotationlib.Format.VALUE), int)
|
||||
self.assertIs(annotationlib.call_evaluate_function(case, annotationlib.Format.FORWARDREF), int)
|
||||
self.assertEqual(annotationlib.call_evaluate_function(case, annotationlib.Format.SOURCE), 'int')
|
||||
self.assertEqual(annotationlib.call_evaluate_function(case, annotationlib.Format.STRING), 'int')
|
||||
|
||||
def test_constraints(self):
|
||||
def f[T: (int, str)](): pass
|
||||
@ -1451,7 +1451,7 @@ class TestEvaluateFunctions(unittest.TestCase):
|
||||
self.assertEqual(case.evaluate_constraints(1), (int, str))
|
||||
self.assertEqual(annotationlib.call_evaluate_function(case.evaluate_constraints, annotationlib.Format.VALUE), (int, str))
|
||||
self.assertEqual(annotationlib.call_evaluate_function(case.evaluate_constraints, annotationlib.Format.FORWARDREF), (int, str))
|
||||
self.assertEqual(annotationlib.call_evaluate_function(case.evaluate_constraints, annotationlib.Format.SOURCE), '(int, str)')
|
||||
self.assertEqual(annotationlib.call_evaluate_function(case.evaluate_constraints, annotationlib.Format.STRING), '(int, str)')
|
||||
|
||||
def test_const_evaluator(self):
|
||||
T = TypeVar("T", bound=int)
|
||||
|
@ -7059,7 +7059,7 @@ class GetTypeHintTests(BaseTestCase):
|
||||
self.assertIsInstance(annos['x'], annotationlib.ForwardRef)
|
||||
self.assertEqual(annos['x'].__arg__, 'undefined')
|
||||
|
||||
self.assertEqual(get_type_hints(C, format=annotationlib.Format.SOURCE),
|
||||
self.assertEqual(get_type_hints(C, format=annotationlib.Format.STRING),
|
||||
{'x': 'undefined'})
|
||||
|
||||
|
||||
@ -7898,7 +7898,7 @@ class NamedTupleTests(BaseTestCase):
|
||||
self.assertEqual(Z.__annotations__, annos)
|
||||
self.assertEqual(Z.__annotate__(annotationlib.Format.VALUE), annos)
|
||||
self.assertEqual(Z.__annotate__(annotationlib.Format.FORWARDREF), annos)
|
||||
self.assertEqual(Z.__annotate__(annotationlib.Format.SOURCE), {"a": "None", "b": "str"})
|
||||
self.assertEqual(Z.__annotate__(annotationlib.Format.STRING), {"a": "None", "b": "str"})
|
||||
|
||||
def test_future_annotations(self):
|
||||
code = """
|
||||
@ -8241,7 +8241,7 @@ class TypedDictTests(BaseTestCase):
|
||||
self.assertEqual(Emp.__annotations__, annos)
|
||||
self.assertEqual(Emp.__annotate__(annotationlib.Format.VALUE), annos)
|
||||
self.assertEqual(Emp.__annotate__(annotationlib.Format.FORWARDREF), annos)
|
||||
self.assertEqual(Emp.__annotate__(annotationlib.Format.SOURCE), {'name': 'str', 'id': 'int'})
|
||||
self.assertEqual(Emp.__annotate__(annotationlib.Format.STRING), {'name': 'str', 'id': 'int'})
|
||||
self.assertEqual(Emp.__total__, True)
|
||||
self.assertEqual(Emp.__required_keys__, {'name', 'id'})
|
||||
self.assertIsInstance(Emp.__required_keys__, frozenset)
|
||||
@ -8603,7 +8603,7 @@ class TypedDictTests(BaseTestCase):
|
||||
self.assertEqual(A.__orig_bases__, (TypedDict, Generic[T]))
|
||||
self.assertEqual(A.__mro__, (A, Generic, dict, object))
|
||||
self.assertEqual(A.__annotations__, {'a': T})
|
||||
self.assertEqual(A.__annotate__(annotationlib.Format.SOURCE), {'a': 'T'})
|
||||
self.assertEqual(A.__annotate__(annotationlib.Format.STRING), {'a': 'T'})
|
||||
self.assertEqual(A.__parameters__, (T,))
|
||||
self.assertEqual(A[str].__parameters__, ())
|
||||
self.assertEqual(A[str].__args__, (str,))
|
||||
@ -8616,7 +8616,7 @@ class TypedDictTests(BaseTestCase):
|
||||
self.assertEqual(A.__orig_bases__, (TypedDict, Generic[T]))
|
||||
self.assertEqual(A.__mro__, (A, Generic, dict, object))
|
||||
self.assertEqual(A.__annotations__, {'a': T})
|
||||
self.assertEqual(A.__annotate__(annotationlib.Format.SOURCE), {'a': 'T'})
|
||||
self.assertEqual(A.__annotate__(annotationlib.Format.STRING), {'a': 'T'})
|
||||
self.assertEqual(A.__parameters__, (T,))
|
||||
self.assertEqual(A[str].__parameters__, ())
|
||||
self.assertEqual(A[str].__args__, (str,))
|
||||
@ -8628,7 +8628,7 @@ class TypedDictTests(BaseTestCase):
|
||||
self.assertEqual(A2.__orig_bases__, (Generic[T], TypedDict))
|
||||
self.assertEqual(A2.__mro__, (A2, Generic, dict, object))
|
||||
self.assertEqual(A2.__annotations__, {'a': T})
|
||||
self.assertEqual(A2.__annotate__(annotationlib.Format.SOURCE), {'a': 'T'})
|
||||
self.assertEqual(A2.__annotate__(annotationlib.Format.STRING), {'a': 'T'})
|
||||
self.assertEqual(A2.__parameters__, (T,))
|
||||
self.assertEqual(A2[str].__parameters__, ())
|
||||
self.assertEqual(A2[str].__args__, (str,))
|
||||
@ -8640,7 +8640,7 @@ class TypedDictTests(BaseTestCase):
|
||||
self.assertEqual(B.__orig_bases__, (A[KT],))
|
||||
self.assertEqual(B.__mro__, (B, Generic, dict, object))
|
||||
self.assertEqual(B.__annotations__, {'a': T, 'b': KT})
|
||||
self.assertEqual(B.__annotate__(annotationlib.Format.SOURCE), {'a': 'T', 'b': 'KT'})
|
||||
self.assertEqual(B.__annotate__(annotationlib.Format.STRING), {'a': 'T', 'b': 'KT'})
|
||||
self.assertEqual(B.__parameters__, (KT,))
|
||||
self.assertEqual(B.__total__, False)
|
||||
self.assertEqual(B.__optional_keys__, frozenset(['b']))
|
||||
@ -8665,7 +8665,7 @@ class TypedDictTests(BaseTestCase):
|
||||
'b': KT,
|
||||
'c': int,
|
||||
})
|
||||
self.assertEqual(C.__annotate__(annotationlib.Format.SOURCE), {
|
||||
self.assertEqual(C.__annotate__(annotationlib.Format.STRING), {
|
||||
'a': 'T',
|
||||
'b': 'KT',
|
||||
'c': 'int',
|
||||
@ -8689,7 +8689,7 @@ class TypedDictTests(BaseTestCase):
|
||||
'b': T,
|
||||
'c': KT,
|
||||
})
|
||||
self.assertEqual(Point3D.__annotate__(annotationlib.Format.SOURCE), {
|
||||
self.assertEqual(Point3D.__annotate__(annotationlib.Format.STRING), {
|
||||
'a': 'T',
|
||||
'b': 'T',
|
||||
'c': 'KT',
|
||||
@ -8725,7 +8725,7 @@ class TypedDictTests(BaseTestCase):
|
||||
'b': KT,
|
||||
'c': int,
|
||||
})
|
||||
self.assertEqual(WithImplicitAny.__annotate__(annotationlib.Format.SOURCE), {
|
||||
self.assertEqual(WithImplicitAny.__annotate__(annotationlib.Format.STRING), {
|
||||
'a': 'T',
|
||||
'b': 'KT',
|
||||
'c': 'int',
|
||||
@ -8929,7 +8929,7 @@ class TypedDictTests(BaseTestCase):
|
||||
A.__annotations__
|
||||
|
||||
self.assertEqual(
|
||||
A.__annotate__(annotationlib.Format.SOURCE),
|
||||
A.__annotate__(annotationlib.Format.STRING),
|
||||
{'x': 'NotRequired[undefined]', 'y': 'ReadOnly[undefined]',
|
||||
'z': 'Required[undefined]'},
|
||||
)
|
||||
|
@ -245,7 +245,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 annotationlib.value_to_source(obj)
|
||||
return annotationlib.value_to_string(obj)
|
||||
|
||||
|
||||
def _collect_type_parameters(args, *, enforce_default_ordering: bool = True):
|
||||
@ -1036,7 +1036,7 @@ def evaluate_forward_ref(
|
||||
* Recursively evaluates forward references nested within the type hint.
|
||||
* Rejects certain objects that are not valid type hints.
|
||||
* Replaces type hints that evaluate to None with types.NoneType.
|
||||
* Supports the *FORWARDREF* and *SOURCE* formats.
|
||||
* Supports the *FORWARDREF* and *STRING* formats.
|
||||
|
||||
*forward_ref* must be an instance of ForwardRef. *owner*, if given,
|
||||
should be the object that holds the annotations that the forward reference
|
||||
@ -1053,7 +1053,7 @@ def evaluate_forward_ref(
|
||||
if type_params is _sentinel:
|
||||
_deprecation_warning_for_no_type_params_passed("typing.evaluate_forward_ref")
|
||||
type_params = ()
|
||||
if format == annotationlib.Format.SOURCE:
|
||||
if format == annotationlib.Format.STRING:
|
||||
return forward_ref.__forward_arg__
|
||||
if forward_ref.__forward_arg__ in _recursive_guard:
|
||||
return forward_ref
|
||||
@ -2380,7 +2380,7 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False,
|
||||
hints = {}
|
||||
for base in reversed(obj.__mro__):
|
||||
ann = annotationlib.get_annotations(base, format=format)
|
||||
if format is annotationlib.Format.SOURCE:
|
||||
if format is annotationlib.Format.STRING:
|
||||
hints.update(ann)
|
||||
continue
|
||||
if globalns is None:
|
||||
@ -2404,7 +2404,7 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False,
|
||||
value = _eval_type(value, base_globals, base_locals, base.__type_params__,
|
||||
format=format, owner=obj)
|
||||
hints[name] = value
|
||||
if include_extras or format is annotationlib.Format.SOURCE:
|
||||
if include_extras or format is annotationlib.Format.STRING:
|
||||
return hints
|
||||
else:
|
||||
return {k: _strip_annotations(t) for k, t in hints.items()}
|
||||
@ -2418,7 +2418,7 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False,
|
||||
and not hasattr(obj, '__annotate__')
|
||||
):
|
||||
raise TypeError(f"{obj!r} is not a module, class, or callable.")
|
||||
if format is annotationlib.Format.SOURCE:
|
||||
if format is annotationlib.Format.STRING:
|
||||
return hints
|
||||
|
||||
if globalns is None:
|
||||
@ -2937,7 +2937,7 @@ def _make_eager_annotate(types):
|
||||
if format in (annotationlib.Format.VALUE, annotationlib.Format.FORWARDREF):
|
||||
return checked_types
|
||||
else:
|
||||
return annotationlib.annotations_to_source(types)
|
||||
return annotationlib.annotations_to_string(types)
|
||||
return annotate
|
||||
|
||||
|
||||
@ -2972,7 +2972,7 @@ class NamedTupleMeta(type):
|
||||
|
||||
def annotate(format):
|
||||
annos = annotationlib.call_annotate_function(original_annotate, format)
|
||||
if format != annotationlib.Format.SOURCE:
|
||||
if format != annotationlib.Format.STRING:
|
||||
return {key: _type_check(val, f"field {key} annotation must be a type")
|
||||
for key, val in annos.items()}
|
||||
return annos
|
||||
@ -3220,13 +3220,13 @@ class _TypedDictMeta(type):
|
||||
annos.update(base_annos)
|
||||
if own_annotate is not None:
|
||||
own = annotationlib.call_annotate_function(own_annotate, format, owner=tp_dict)
|
||||
if format != annotationlib.Format.SOURCE:
|
||||
if format != annotationlib.Format.STRING:
|
||||
own = {
|
||||
n: _type_check(tp, msg, module=tp_dict.__module__)
|
||||
for n, tp in own.items()
|
||||
}
|
||||
elif format == annotationlib.Format.SOURCE:
|
||||
own = annotationlib.annotations_to_source(own_annotations)
|
||||
elif format == annotationlib.Format.STRING:
|
||||
own = annotationlib.annotations_to_string(own_annotations)
|
||||
else:
|
||||
own = own_checked_annotations
|
||||
annos.update(own)
|
||||
|
@ -168,7 +168,7 @@ constevaluator_call(PyObject *self, PyObject *args, PyObject *kwargs)
|
||||
return NULL;
|
||||
}
|
||||
PyObject *value = ((constevaluatorobject *)self)->value;
|
||||
if (format == 3) { // SOURCE
|
||||
if (format == 3) { // STRING
|
||||
PyUnicodeWriter *writer = PyUnicodeWriter_Create(5); // cannot be <5
|
||||
if (writer == NULL) {
|
||||
return NULL;
|
||||
|
Loading…
Reference in New Issue
Block a user