Skip to content

Commit 938d373

Browse files
authored
Collapse unions of Literal types in stringify_annotation (#12325)
1 parent 7953798 commit 938d373

File tree

4 files changed

+21
-1
lines changed

4 files changed

+21
-1
lines changed

CHANGES.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ Features added
1818
Patch by James Addison and Adam Turner
1919

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

2225
Bugs fixed
2326
----------

sphinx/util/typing.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,15 @@ def stringify_annotation(
439439
# They must be a list or a tuple, otherwise they are considered 'broken'.
440440
annotation_args = getattr(annotation, '__args__', ())
441441
if annotation_args and isinstance(annotation_args, (list, tuple)):
442+
if (
443+
qualname in {'Union', 'types.UnionType'}
444+
and all(getattr(a, '__origin__', ...) is typing.Literal for a in annotation_args)
445+
):
446+
# special case to flatten a Union of Literals into a literal
447+
flattened_args = typing.Literal[annotation_args].__args__ # type: ignore[attr-defined]
448+
args = ', '.join(_format_literal_arg_stringify(a, mode=mode)
449+
for a in flattened_args)
450+
return f'{module_prefix}Literal[{args}]'
442451
if qualname in {'Optional', 'Union', 'types.UnionType'}:
443452
return ' | '.join(stringify_annotation(a, mode) for a in annotation_args)
444453
elif qualname == 'Callable':

tests/test_util/test_util_inspect.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,10 @@ def test_signature_annotations():
359359
sig = inspect.signature(mod.f25)
360360
assert stringify_signature(sig) == '(a, b, /)'
361361

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

363367
def test_signature_from_str_basic():
364368
signature = '(a, b, *args, c=0, d="blah", **kwargs)'

tests/test_util/typing_test_data.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from inspect import Signature
22
from numbers import Integral
3-
from typing import Any, Callable, Dict, List, Optional, Tuple, TypeVar, Union
3+
from typing import Any, Callable, Dict, List, Literal, Optional, Tuple, TypeVar, Union
44

55

66
def f0(x: int, y: Integral) -> None:
@@ -121,6 +121,10 @@ def f25(a, b, /):
121121
pass
122122

123123

124+
def f26(x: Literal[1, 2, 3] = 1, y: Union[Literal["a"], Literal["b"]] = "a") -> None:
125+
pass
126+
127+
124128
class Node:
125129
def __init__(self, parent: Optional['Node']) -> None:
126130
pass

0 commit comments

Comments
 (0)