mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Display `typing.Annotated` metadata in the Python domain (#11785)
Co-authored-by: Adam Turner <9087854+aa-turner@users.noreply.github.com>
This commit is contained in:
@@ -71,7 +71,6 @@ Features added
|
|||||||
parses the provided text into inline elements and text nodes.
|
parses the provided text into inline elements and text nodes.
|
||||||
|
|
||||||
Patch by Adam Turner.
|
Patch by Adam Turner.
|
||||||
|
|
||||||
* #12258: Support ``typing_extensions.Unpack``
|
* #12258: Support ``typing_extensions.Unpack``
|
||||||
Patch by Bénédikt Tran and Adam Turner.
|
Patch by Bénédikt Tran and Adam Turner.
|
||||||
* #12524: Add a ``class`` option to the :rst:dir:`toctree` directive.
|
* #12524: Add a ``class`` option to the :rst:dir:`toctree` directive.
|
||||||
@@ -100,6 +99,9 @@ Features added
|
|||||||
* #12508: LaTeX: Revamped styling of all admonitions, with addition of a
|
* #12508: LaTeX: Revamped styling of all admonitions, with addition of a
|
||||||
title row with icon.
|
title row with icon.
|
||||||
Patch by Jean-François B.
|
Patch by Jean-François B.
|
||||||
|
* #11773: Display :py:class:`~typing.Annotated` annotations
|
||||||
|
with their metadata in the Python domain.
|
||||||
|
Patch by Adam Turner and David Stansby.
|
||||||
|
|
||||||
Bugs fixed
|
Bugs fixed
|
||||||
----------
|
----------
|
||||||
|
|||||||
@@ -261,6 +261,14 @@ def restify(cls: Any, mode: _RestifyMode = 'fully-qualified-except-typing') -> s
|
|||||||
# evaluated before determining whether *cls* is a mocked object
|
# evaluated before determining whether *cls* is a mocked object
|
||||||
# or not; instead of two try-except blocks, we keep it here.
|
# or not; instead of two try-except blocks, we keep it here.
|
||||||
return f':py:class:`{module_prefix}{_INVALID_BUILTIN_CLASSES[cls]}`'
|
return f':py:class:`{module_prefix}{_INVALID_BUILTIN_CLASSES[cls]}`'
|
||||||
|
elif _is_annotated_form(cls):
|
||||||
|
args = restify(cls.__args__[0], mode)
|
||||||
|
meta = ', '.join(map(repr, cls.__metadata__))
|
||||||
|
if sys.version_info[:2] <= (3, 11):
|
||||||
|
# Hardcoded to fix errors on Python 3.11 and earlier.
|
||||||
|
return fr':py:class:`~typing.Annotated`\ [{args}, {meta}]'
|
||||||
|
return (f':py:class:`{module_prefix}{cls.__module__}.{cls.__name__}`'
|
||||||
|
fr'\ [{args}, {meta}]')
|
||||||
elif inspect.isNewType(cls):
|
elif inspect.isNewType(cls):
|
||||||
if sys.version_info[:2] >= (3, 10):
|
if sys.version_info[:2] >= (3, 10):
|
||||||
# newtypes have correct module info since Python 3.10+
|
# newtypes have correct module info since Python 3.10+
|
||||||
@@ -497,7 +505,14 @@ def stringify_annotation(
|
|||||||
for a in annotation_args)
|
for a in annotation_args)
|
||||||
return f'{module_prefix}Literal[{args}]'
|
return f'{module_prefix}Literal[{args}]'
|
||||||
elif _is_annotated_form(annotation): # for py39+
|
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__))
|
||||||
|
if sys.version_info[:2] <= (3, 11):
|
||||||
|
if mode == 'fully-qualified-except-typing':
|
||||||
|
return f'Annotated[{args}, {meta}]'
|
||||||
|
module_prefix = module_prefix.replace('builtins', 'typing')
|
||||||
|
return f'{module_prefix}Annotated[{args}, {meta}]'
|
||||||
|
return f'{module_prefix}Annotated[{args}, {meta}]'
|
||||||
elif all(is_system_TypeVar(a) for a in annotation_args):
|
elif all(is_system_TypeVar(a) for a in annotation_args):
|
||||||
# Suppress arguments if all system defined TypeVars (ex. Dict[KT, VT])
|
# Suppress arguments if all system defined TypeVars (ex. Dict[KT, VT])
|
||||||
return module_prefix + qualname
|
return module_prefix + qualname
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
"""Tests util.typing functions."""
|
"""Tests util.typing functions."""
|
||||||
|
|
||||||
|
import dataclasses
|
||||||
import sys
|
import sys
|
||||||
import typing as t
|
import typing as t
|
||||||
from collections import abc
|
from collections import abc
|
||||||
@@ -73,6 +74,11 @@ class BrokenType:
|
|||||||
__args__ = int
|
__args__ = int
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass(frozen=True)
|
||||||
|
class Gt:
|
||||||
|
gt: float
|
||||||
|
|
||||||
|
|
||||||
def test_restify():
|
def test_restify():
|
||||||
assert restify(int) == ":py:class:`int`"
|
assert restify(int) == ":py:class:`int`"
|
||||||
assert restify(int, "smart") == ":py:class:`int`"
|
assert restify(int, "smart") == ":py:class:`int`"
|
||||||
@@ -187,10 +193,11 @@ def test_restify_type_hints_containers():
|
|||||||
"[:py:obj:`None`]")
|
"[:py:obj:`None`]")
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.xfail(sys.version_info[:2] <= (3, 11), reason='Needs fixing.')
|
|
||||||
def test_restify_Annotated():
|
def test_restify_Annotated():
|
||||||
assert restify(Annotated[str, "foo", "bar"]) == ':py:class:`~typing.Annotated`\\ [:py:class:`str`]'
|
assert restify(Annotated[str, "foo", "bar"]) == ":py:class:`~typing.Annotated`\\ [:py:class:`str`, 'foo', 'bar']"
|
||||||
assert restify(Annotated[str, "foo", "bar"], 'smart') == ':py:class:`~typing.Annotated`\\ [:py:class:`str`]'
|
assert restify(Annotated[str, "foo", "bar"], 'smart') == ":py:class:`~typing.Annotated`\\ [:py:class:`str`, 'foo', 'bar']"
|
||||||
|
assert restify(Annotated[float, Gt(-10.0)]) == ':py:class:`~typing.Annotated`\\ [:py:class:`float`, Gt(gt=-10.0)]'
|
||||||
|
assert restify(Annotated[float, Gt(-10.0)], 'smart') == ':py:class:`~typing.Annotated`\\ [:py:class:`float`, Gt(gt=-10.0)]'
|
||||||
|
|
||||||
|
|
||||||
def test_restify_type_hints_Callable():
|
def test_restify_type_hints_Callable():
|
||||||
@@ -499,9 +506,12 @@ def test_stringify_type_hints_pep_585():
|
|||||||
assert stringify_annotation(tuple[List[dict[int, str]], str, ...], "smart") == "tuple[~typing.List[dict[int, str]], str, ...]"
|
assert stringify_annotation(tuple[List[dict[int, str]], str, ...], "smart") == "tuple[~typing.List[dict[int, str]], str, ...]"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail(sys.version_info[:2] <= (3, 9), reason='Needs fixing.')
|
||||||
def test_stringify_Annotated():
|
def test_stringify_Annotated():
|
||||||
assert stringify_annotation(Annotated[str, "foo", "bar"], 'fully-qualified-except-typing') == "str"
|
assert stringify_annotation(Annotated[str, "foo", "bar"], 'fully-qualified-except-typing') == "Annotated[str, 'foo', 'bar']"
|
||||||
assert stringify_annotation(Annotated[str, "foo", "bar"], "smart") == "str"
|
assert stringify_annotation(Annotated[str, "foo", "bar"], 'smart') == "~typing.Annotated[str, 'foo', 'bar']"
|
||||||
|
assert stringify_annotation(Annotated[float, Gt(-10.0)], 'fully-qualified-except-typing') == "Annotated[float, Gt(gt=-10.0)]"
|
||||||
|
assert stringify_annotation(Annotated[float, Gt(-10.0)], 'smart') == "~typing.Annotated[float, Gt(gt=-10.0)]"
|
||||||
|
|
||||||
|
|
||||||
def test_stringify_Unpack():
|
def test_stringify_Unpack():
|
||||||
@@ -662,7 +672,6 @@ def test_stringify_type_hints_alias():
|
|||||||
|
|
||||||
|
|
||||||
def test_stringify_type_Literal():
|
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-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"], "fully-qualified") == "typing.Literal[1, '2', '\\r']"
|
||||||
assert stringify_annotation(Literal[1, "2", "\r"], "smart") == "~typing.Literal[1, '2', '\\r']"
|
assert stringify_annotation(Literal[1, "2", "\r"], "smart") == "~typing.Literal[1, '2', '\\r']"
|
||||||
@@ -704,8 +713,6 @@ def test_stringify_mock():
|
|||||||
|
|
||||||
|
|
||||||
def test_stringify_type_ForwardRef():
|
def test_stringify_type_ForwardRef():
|
||||||
from typing import ForwardRef # type: ignore[attr-defined]
|
|
||||||
|
|
||||||
assert stringify_annotation(ForwardRef("MyInt")) == "MyInt"
|
assert stringify_annotation(ForwardRef("MyInt")) == "MyInt"
|
||||||
assert stringify_annotation(ForwardRef("MyInt"), 'smart') == "MyInt"
|
assert stringify_annotation(ForwardRef("MyInt"), 'smart') == "MyInt"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user