Skip to content

Commit

Permalink
Collapse unions of Literal types in stringify_annotation
Browse files Browse the repository at this point in the history
  • Loading branch information
AA-Turner committed Apr 24, 2024
1 parent 6b70620 commit 091f9c0
Show file tree
Hide file tree
Showing 4 changed files with 21 additions and 1 deletion.
3 changes: 3 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ Features added
Patch by James Addison and Adam Turner

.. _officially recommended: https://jinja.palletsprojects.com/en/latest/templates/#template-file-extension
* Flatten ``Union[Literal[T], Literal[U], ...]`` to ``Literal[T, U, ...]``
when turning annotations into strings.
Patch by Adam Turner.

Bugs fixed
----------
Expand Down
9 changes: 9 additions & 0 deletions sphinx/util/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,15 @@ def stringify_annotation(
# They must be a list or a tuple, otherwise they are considered 'broken'.
annotation_args = getattr(annotation, '__args__', ())
if annotation_args and isinstance(annotation_args, (list, tuple)):
if (
qualname in {'Union', 'types.UnionType'}
and all(getattr(a, '__origin__', ...) is typing.Literal for a in annotation_args)
):
# special case to flatten a Union of Literals into a literal
flattened_args = typing.Literal[annotation_args].__args__ # type: ignore[valid-type]
args = ', '.join(_format_literal_arg_stringify(a, mode=mode)
for a in flattened_args)
return f'{module_prefix}Literal[{args}]'
if qualname in {'Optional', 'Union', 'types.UnionType'}:
return ' | '.join(stringify_annotation(a, mode) for a in annotation_args)
elif qualname == 'Callable':
Expand Down
4 changes: 4 additions & 0 deletions tests/test_util/test_util_inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,10 @@ def test_signature_annotations():
sig = inspect.signature(mod.f25)
assert stringify_signature(sig) == '(a, b, /)'

# collapse Literal types
sig = inspect.signature(mod.f26)
assert stringify_signature(sig) == "(x: typing.Literal[1, 2, 3] = 1, y: typing.Literal['a', 'b'] = 'a') -> None"


def test_signature_from_str_basic():
signature = '(a, b, *args, c=0, d="blah", **kwargs)'
Expand Down
6 changes: 5 additions & 1 deletion tests/test_util/typing_test_data.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from inspect import Signature
from numbers import Integral
from typing import Any, Callable, Dict, List, Optional, Tuple, TypeVar, Union
from typing import Any, Callable, Dict, List, Literal, Optional, Tuple, TypeVar, Union


def f0(x: int, y: Integral) -> None:
Expand Down Expand Up @@ -121,6 +121,10 @@ def f25(a, b, /):
pass


def f26(x: Literal[1, 2, 3] = 1, y: Union[Literal["a"], Literal["b"]] = "a") -> None:
pass


class Node:
def __init__(self, parent: Optional['Node']) -> None:
pass
Expand Down

0 comments on commit 091f9c0

Please sign in to comment.