Skip to content
1 change: 1 addition & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Features added
Patch by James Addison and Adam Turner

.. _officially recommended: https://jinja.palletsprojects.com/en/latest/templates/#template-file-extension
* #11773: Added metadata information to rendering of `~typing.Annotated` types.

Bugs fixed
----------
Expand Down
4 changes: 3 additions & 1 deletion sphinx/util/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,9 @@ def stringify_annotation(
for a in annotation_args)
return f'{module_prefix}Literal[{args}]'
elif _is_annotated_form(annotation): # for py39+
return stringify_annotation(annotation_args[0], mode)
args = stringify_annotation(annotation_args[0], mode)
meta = ', '.join(map(repr, annotation.__metadata__))
return f'{module_prefix}Annotated[{args}, {meta}]'
elif all(is_system_TypeVar(a) for a in annotation_args):
# Suppress arguments if all system defined TypeVars (ex. Dict[KT, VT])
return module_prefix + qualname
Expand Down
18 changes: 10 additions & 8 deletions tests/test_util/test_util_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,12 @@
WrapperDescriptorType,
)
from typing import (
Annotated,
Any,
Dict,
ForwardRef,
List,
Literal,
NewType,
Optional,
Tuple,
Expand Down Expand Up @@ -184,6 +187,11 @@ def test_restify_type_hints_containers():
"[:py:obj:`None`]")


def test_restify_Annotated():
assert restify(Annotated[str, "foo", "bar"]) == ':py:class:`~typing.Annotated`\\ [:py:class:`str`]'
assert restify(Annotated[str, "foo", "bar"], 'smart') == ':py:class:`~typing.Annotated`\\ [:py:class:`str`]'


def test_restify_type_hints_Callable():
assert restify(t.Callable) == ":py:class:`~typing.Callable`"
assert restify(t.Callable[[str], int]) == (":py:class:`~typing.Callable`\\ "
Expand Down Expand Up @@ -284,7 +292,6 @@ def test_restify_type_hints_alias():


def test_restify_type_ForwardRef():
from typing import ForwardRef # type: ignore[attr-defined]
assert restify(ForwardRef("MyInt")) == ":py:class:`MyInt`"

assert restify(list[ForwardRef("MyInt")]) == ":py:class:`list`\\ [:py:class:`MyInt`]"
Expand All @@ -293,7 +300,6 @@ def test_restify_type_ForwardRef():


def test_restify_type_Literal():
from typing import Literal # type: ignore[attr-defined]
assert restify(Literal[1, "2", "\r"]) == ":py:obj:`~typing.Literal`\\ [1, '2', '\\r']"

assert restify(Literal[MyEnum.a], 'fully-qualified-except-typing') == ':py:obj:`~typing.Literal`\\ [:py:attr:`tests.test_util.test_util_typing.MyEnum.a`]'
Expand Down Expand Up @@ -469,9 +475,8 @@ def test_stringify_type_hints_pep_585():


def test_stringify_Annotated():
from typing import Annotated # type: ignore[attr-defined]
assert stringify_annotation(Annotated[str, "foo", "bar"], 'fully-qualified-except-typing') == "str"
assert stringify_annotation(Annotated[str, "foo", "bar"], "smart") == "str"
assert stringify_annotation(Annotated[str, "foo", "bar"], 'fully-qualified-except-typing') == "Annotated[str, 'foo', 'bar']"
assert stringify_annotation(Annotated[str, "foo", "bar"], "smart") == "~typing.Annotated[str, 'foo', 'bar']"


def test_stringify_type_hints_string():
Expand Down Expand Up @@ -606,7 +611,6 @@ def test_stringify_type_hints_alias():


def test_stringify_type_Literal():
from typing import Literal # type: ignore[attr-defined]
assert stringify_annotation(Literal[1, "2", "\r"], 'fully-qualified-except-typing') == "Literal[1, '2', '\\r']"
assert stringify_annotation(Literal[1, "2", "\r"], "fully-qualified") == "typing.Literal[1, '2', '\\r']"
assert stringify_annotation(Literal[1, "2", "\r"], "smart") == "~typing.Literal[1, '2', '\\r']"
Expand Down Expand Up @@ -648,8 +652,6 @@ def test_stringify_mock():


def test_stringify_type_ForwardRef():
from typing import ForwardRef # type: ignore[attr-defined]

assert stringify_annotation(ForwardRef("MyInt")) == "MyInt"
assert stringify_annotation(ForwardRef("MyInt"), 'smart') == "MyInt"

Expand Down