Properly format `collections.abc.Callable` (#12314)

This commit is contained in:
Adam Turner
2024-04-23 09:01:09 +01:00
committed by GitHub
parent 1ff9adfb62
commit 5a55856dd1
2 changed files with 65 additions and 34 deletions

View File

@@ -178,7 +178,7 @@ def _is_annotated_form(obj: Any) -> TypeGuard[Annotated[Any, ...]]:
return typing.get_origin(obj) is Annotated or str(obj).startswith('typing.Annotated')
def _get_typing_internal_name(obj: Any) -> str | None:
def _typing_internal_name(obj: Any) -> str | None:
if sys.version_info[:2] >= (3, 10):
return obj.__name__
return getattr(obj, '_name', None)
@@ -252,7 +252,7 @@ def restify(cls: Any, mode: _RestifyMode = 'fully-qualified-except-typing') -> s
# *cls* is defined in ``typing``, and thus ``__args__`` must exist
return ' | '.join(restify(a, mode) for a in cls.__args__)
elif inspect.isgenericalias(cls):
cls_name = _get_typing_internal_name(cls)
cls_name = _typing_internal_name(cls)
if isinstance(cls.__origin__, typing._SpecialForm):
# ClassVar; Concatenate; Final; Literal; Unpack; TypeGuard
@@ -271,12 +271,15 @@ def restify(cls: Any, mode: _RestifyMode = 'fully-qualified-except-typing') -> s
return text
# Callable has special formatting
if cls_module_is_typing and _get_typing_internal_name(cls) == 'Callable':
if (
(cls_module_is_typing and _typing_internal_name(cls) == 'Callable')
or (cls.__module__ == 'collections.abc' and cls.__name__ == 'Callable')
):
args = ', '.join(restify(a, mode) for a in __args__[:-1])
returns = restify(__args__[-1], mode)
return fr'{text}\ [[{args}], {returns}]'
if cls_module_is_typing and _get_typing_internal_name(cls.__origin__) == 'Literal':
if cls_module_is_typing and _typing_internal_name(cls.__origin__) == 'Literal':
args = ', '.join(_format_literal_arg_restify(a, mode=mode)
for a in cls.__args__)
return fr'{text}\ [{args}]'
@@ -285,7 +288,7 @@ def restify(cls: Any, mode: _RestifyMode = 'fully-qualified-except-typing') -> s
args = ', '.join(restify(a, mode) for a in __args__)
return fr'{text}\ [{args}]'
elif isinstance(cls, typing._SpecialForm):
cls_name = _get_typing_internal_name(cls)
cls_name = _typing_internal_name(cls)
return f':py:obj:`~{cls.__module__}.{cls_name}`'
elif sys.version_info[:2] >= (3, 11) and cls is typing.Any:
# handle bpo-46998

View File

@@ -1,6 +1,8 @@
"""Tests util.typing functions."""
import sys
import typing as t
from collections import abc
from contextvars import Context, ContextVar, Token
from enum import Enum
from numbers import Integral
@@ -29,10 +31,7 @@ from types import (
)
from typing import (
Any,
Callable,
Dict,
Generator,
Iterator,
List,
NewType,
Optional,
@@ -173,20 +172,29 @@ def test_restify_type_hints_containers():
assert restify(MyList[Tuple[int, int]]) == (":py:class:`tests.test_util.test_util_typing.MyList`\\ "
"[:py:class:`~typing.Tuple`\\ "
"[:py:class:`int`, :py:class:`int`]]")
assert restify(Generator[None, None, None]) == (":py:class:`~typing.Generator`\\ "
"[:py:obj:`None`, :py:obj:`None`, "
":py:obj:`None`]")
assert restify(Iterator[None]) == (":py:class:`~typing.Iterator`\\ "
"[:py:obj:`None`]")
assert restify(t.Generator[None, None, None]) == (":py:class:`~typing.Generator`\\ "
"[:py:obj:`None`, :py:obj:`None`, "
":py:obj:`None`]")
assert restify(abc.Generator[None, None, None]) == (":py:class:`collections.abc.Generator`\\ "
"[:py:obj:`None`, :py:obj:`None`, "
":py:obj:`None`]")
assert restify(t.Iterator[None]) == (":py:class:`~typing.Iterator`\\ "
"[:py:obj:`None`]")
assert restify(abc.Iterator[None]) == (":py:class:`collections.abc.Iterator`\\ "
"[:py:obj:`None`]")
def test_restify_type_hints_Callable():
assert restify(Callable) == ":py:class:`~typing.Callable`"
assert restify(Callable[[str], int]) == (":py:class:`~typing.Callable`\\ "
"[[:py:class:`str`], :py:class:`int`]")
assert restify(Callable[..., int]) == (":py:class:`~typing.Callable`\\ "
"[[...], :py:class:`int`]")
assert restify(t.Callable) == ":py:class:`~typing.Callable`"
assert restify(t.Callable[[str], int]) == (":py:class:`~typing.Callable`\\ "
"[[:py:class:`str`], :py:class:`int`]")
assert restify(t.Callable[..., int]) == (":py:class:`~typing.Callable`\\ "
"[[...], :py:class:`int`]")
assert restify(abc.Callable) == ":py:class:`collections.abc.Callable`"
assert restify(abc.Callable[[str], int]) == (":py:class:`collections.abc.Callable`\\ "
"[[:py:class:`str`], :py:class:`int`]")
assert restify(abc.Callable[..., int]) == (":py:class:`collections.abc.Callable`\\ "
"[[...], :py:class:`int`]")
def test_restify_type_hints_Union():
@@ -409,13 +417,21 @@ def test_stringify_type_hints_containers():
assert stringify_annotation(MyList[Tuple[int, int]], "fully-qualified") == "tests.test_util.test_util_typing.MyList[typing.Tuple[int, int]]"
assert stringify_annotation(MyList[Tuple[int, int]], "smart") == "~tests.test_util.test_util_typing.MyList[~typing.Tuple[int, int]]"
assert stringify_annotation(Generator[None, None, None], 'fully-qualified-except-typing') == "Generator[None, None, None]"
assert stringify_annotation(Generator[None, None, None], "fully-qualified") == "typing.Generator[None, None, None]"
assert stringify_annotation(Generator[None, None, None], "smart") == "~typing.Generator[None, None, None]"
assert stringify_annotation(t.Generator[None, None, None], 'fully-qualified-except-typing') == "Generator[None, None, None]"
assert stringify_annotation(t.Generator[None, None, None], "fully-qualified") == "typing.Generator[None, None, None]"
assert stringify_annotation(t.Generator[None, None, None], "smart") == "~typing.Generator[None, None, None]"
assert stringify_annotation(Iterator[None], 'fully-qualified-except-typing') == "Iterator[None]"
assert stringify_annotation(Iterator[None], "fully-qualified") == "typing.Iterator[None]"
assert stringify_annotation(Iterator[None], "smart") == "~typing.Iterator[None]"
assert stringify_annotation(abc.Generator[None, None, None], 'fully-qualified-except-typing') == "collections.abc.Generator[None, None, None]"
assert stringify_annotation(abc.Generator[None, None, None], "fully-qualified") == "collections.abc.Generator[None, None, None]"
assert stringify_annotation(abc.Generator[None, None, None], "smart") == "~collections.abc.Generator[None, None, None]"
assert stringify_annotation(t.Iterator[None], 'fully-qualified-except-typing') == "Iterator[None]"
assert stringify_annotation(t.Iterator[None], "fully-qualified") == "typing.Iterator[None]"
assert stringify_annotation(t.Iterator[None], "smart") == "~typing.Iterator[None]"
assert stringify_annotation(abc.Iterator[None], 'fully-qualified-except-typing') == "collections.abc.Iterator[None]"
assert stringify_annotation(abc.Iterator[None], "fully-qualified") == "collections.abc.Iterator[None]"
assert stringify_annotation(abc.Iterator[None], "smart") == "~collections.abc.Iterator[None]"
def test_stringify_type_hints_pep_585():
@@ -489,17 +505,29 @@ def test_stringify_type_hints_string():
def test_stringify_type_hints_Callable():
assert stringify_annotation(Callable, 'fully-qualified-except-typing') == "Callable"
assert stringify_annotation(Callable, "fully-qualified") == "typing.Callable"
assert stringify_annotation(Callable, "smart") == "~typing.Callable"
assert stringify_annotation(t.Callable, 'fully-qualified-except-typing') == "Callable"
assert stringify_annotation(t.Callable, "fully-qualified") == "typing.Callable"
assert stringify_annotation(t.Callable, "smart") == "~typing.Callable"
assert stringify_annotation(Callable[[str], int], 'fully-qualified-except-typing') == "Callable[[str], int]"
assert stringify_annotation(Callable[[str], int], "fully-qualified") == "typing.Callable[[str], int]"
assert stringify_annotation(Callable[[str], int], "smart") == "~typing.Callable[[str], int]"
assert stringify_annotation(t.Callable[[str], int], 'fully-qualified-except-typing') == "Callable[[str], int]"
assert stringify_annotation(t.Callable[[str], int], "fully-qualified") == "typing.Callable[[str], int]"
assert stringify_annotation(t.Callable[[str], int], "smart") == "~typing.Callable[[str], int]"
assert stringify_annotation(Callable[..., int], 'fully-qualified-except-typing') == "Callable[[...], int]"
assert stringify_annotation(Callable[..., int], "fully-qualified") == "typing.Callable[[...], int]"
assert stringify_annotation(Callable[..., int], "smart") == "~typing.Callable[[...], int]"
assert stringify_annotation(t.Callable[..., int], 'fully-qualified-except-typing') == "Callable[[...], int]"
assert stringify_annotation(t.Callable[..., int], "fully-qualified") == "typing.Callable[[...], int]"
assert stringify_annotation(t.Callable[..., int], "smart") == "~typing.Callable[[...], int]"
assert stringify_annotation(abc.Callable, 'fully-qualified-except-typing') == "collections.abc.Callable"
assert stringify_annotation(abc.Callable, "fully-qualified") == "collections.abc.Callable"
assert stringify_annotation(abc.Callable, "smart") == "~collections.abc.Callable"
assert stringify_annotation(abc.Callable[[str], int], 'fully-qualified-except-typing') == "collections.abc.Callable[[str], int]"
assert stringify_annotation(abc.Callable[[str], int], "fully-qualified") == "collections.abc.Callable[[str], int]"
assert stringify_annotation(abc.Callable[[str], int], "smart") == "~collections.abc.Callable[[str], int]"
assert stringify_annotation(abc.Callable[..., int], 'fully-qualified-except-typing') == "collections.abc.Callable[[...], int]"
assert stringify_annotation(abc.Callable[..., int], "fully-qualified") == "collections.abc.Callable[[...], int]"
assert stringify_annotation(abc.Callable[..., int], "smart") == "~collections.abc.Callable[[...], int]"
def test_stringify_type_hints_Union():