Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-119180: Rename SOURCE format to STRING #124620

Merged
merged 2 commits into from
Sep 26, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
22 changes: 11 additions & 11 deletions Doc/library/annotationlib.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion Doc/library/typing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions Doc/whatsnew/3.14.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand All @@ -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
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_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__
Expand Down
52 changes: 32 additions & 20 deletions Lib/annotationlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@
"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
Expand Down Expand Up @@ -291,9 +291,21 @@ def __convert_to_ast(self, other):
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)
Expand Down Expand Up @@ -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.
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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}")

Expand Down Expand Up @@ -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.

Expand All @@ -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()
}

Expand Down
Loading
Loading