Skip to content

Commit c6e267d

Browse files
committed
1 parent a9cd0eb commit c6e267d

File tree

3 files changed

+75
-12
lines changed

3 files changed

+75
-12
lines changed

src/sphinx_autodoc_typehints/__init__.py

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -172,11 +172,11 @@ def get_annotation_args(annotation: Any, module: str, class_name: str) -> tuple[
172172
return () if len(result) == 1 and result[0] == () else result # type: ignore[misc]
173173

174174

175-
def format_internal_tuple(t: tuple[Any, ...], config: Config) -> str:
175+
def format_internal_tuple(t: tuple[Any, ...], config: Config, *, short_literals: bool = False) -> str:
176176
# An annotation can be a tuple, e.g., for numpy.typing:
177177
# In this case, format_annotation receives:
178178
# This solution should hopefully be general for *any* type that allows tuples in annotations
179-
fmt = [format_annotation(a, config) for a in t]
179+
fmt = [format_annotation(a, config, short_literals=short_literals) for a in t]
180180
if len(fmt) == 0:
181181
return "()"
182182
if len(fmt) == 1:
@@ -196,12 +196,13 @@ def fixup_module_name(config: Config, module: str) -> str:
196196
return module
197197

198198

199-
def format_annotation(annotation: Any, config: Config) -> str: # noqa: C901, PLR0911, PLR0912, PLR0915, PLR0914
199+
def format_annotation(annotation: Any, config: Config, *, short_literals: bool = False) -> str: # noqa: C901, PLR0911, PLR0912, PLR0915, PLR0914
200200
"""
201201
Format the annotation.
202202
203203
:param annotation:
204204
:param config:
205+
:param short_literals: Render :py:class:`Literals` in PEP 604 style (``|``).
205206
:return:
206207
"""
207208
typehints_formatter: Callable[..., str] | None = getattr(config, "typehints_formatter", None)
@@ -254,7 +255,7 @@ def format_annotation(annotation: Any, config: Config) -> str: # noqa: C901, PL
254255
params = {k: getattr(annotation, f"__{k}__") for k in ("bound", "covariant", "contravariant")}
255256
params = {k: v for k, v in params.items() if v}
256257
if "bound" in params:
257-
params["bound"] = f" {format_annotation(params['bound'], config)}"
258+
params["bound"] = f" {format_annotation(params['bound'], config, short_literals=short_literals)}"
258259
args_format = f"\\(``{annotation.__name__}``{', {}' if args else ''}"
259260
if params:
260261
args_format += "".join(f", {k}={v}" for k, v in params.items())
@@ -275,20 +276,22 @@ def format_annotation(annotation: Any, config: Config) -> str: # noqa: C901, PL
275276
args_format = f"\\[:py:data:`{prefix}typing.Union`\\[{{}}]]"
276277
args = tuple(x for x in args if x is not type(None))
277278
elif full_name in {"typing.Callable", "collections.abc.Callable"} and args and args[0] is not ...:
278-
fmt = [format_annotation(arg, config) for arg in args]
279+
fmt = [format_annotation(arg, config, short_literals=short_literals) for arg in args]
279280
formatted_args = f"\\[\\[{', '.join(fmt[:-1])}], {fmt[-1]}]"
280281
elif full_name == "typing.Literal":
282+
if short_literals:
283+
return f"\\{' | '.join(f'``{arg!r}``' for arg in args)}"
281284
formatted_args = f"\\[{', '.join(f'``{arg!r}``' for arg in args)}]"
282285
elif is_bars_union:
283-
return " | ".join([format_annotation(arg, config) for arg in args])
286+
return " | ".join([format_annotation(arg, config, short_literals=short_literals) for arg in args])
284287

285288
if args and not formatted_args:
286289
try:
287290
iter(args)
288291
except TypeError:
289-
fmt = [format_annotation(args, config)]
292+
fmt = [format_annotation(args, config, short_literals=short_literals)]
290293
else:
291-
fmt = [format_annotation(arg, config) for arg in args]
294+
fmt = [format_annotation(arg, config, short_literals=short_literals) for arg in args]
292295
formatted_args = args_format.format(", ".join(fmt))
293296

294297
escape = "\\ " if formatted_args else ""
@@ -783,7 +786,10 @@ def _inject_signature(
783786
if annotation is None:
784787
type_annotation = f":type {arg_name}: "
785788
else:
786-
formatted_annotation = add_type_css_class(format_annotation(annotation, app.config))
789+
short_literals = app.config.python_display_short_literal_types
790+
formatted_annotation = add_type_css_class(
791+
format_annotation(annotation, app.config, short_literals=short_literals)
792+
)
787793
type_annotation = f":type {arg_name}: {formatted_annotation}"
788794

789795
if app.config.typehints_defaults:
@@ -923,7 +929,10 @@ def _inject_rtype( # noqa: PLR0913, PLR0917
923929
if not app.config.typehints_use_rtype and r.found_return and " -- " in lines[insert_index]:
924930
return
925931

926-
formatted_annotation = add_type_css_class(format_annotation(type_hints["return"], app.config))
932+
short_literals = app.config.python_display_short_literal_types
933+
formatted_annotation = add_type_css_class(
934+
format_annotation(type_hints["return"], app.config, short_literals=short_literals)
935+
)
927936

928937
if r.found_param and insert_index < len(lines) and lines[insert_index].strip():
929938
insert_index -= 1

src/sphinx_autodoc_typehints/attributes_patch.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,12 @@
4242
orig_handle_signature = PyAttribute.handle_signature
4343

4444

45-
def _stringify_annotation(app: Sphinx, annotation: Any, *args: Any, **kwargs: Any) -> str: # noqa: ARG001
45+
def _stringify_annotation(app: Sphinx, annotation: Any, *args: Any, short_literals: bool = False, **kwargs: Any) -> str: # noqa: ARG001
4646
# Format the annotation with sphinx-autodoc-typehints and inject our magic prefix to tell our patched
4747
# PyAttribute.handle_signature to treat it as rst.
4848
from . import format_annotation # noqa: PLC0415
4949

50-
return TYPE_IS_RST_LABEL + format_annotation(annotation, app.config)
50+
return TYPE_IS_RST_LABEL + format_annotation(annotation, app.config, short_literals=short_literals)
5151

5252

5353
def patch_attribute_documenter(app: Sphinx) -> None:

tests/test_integration.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from typing import ( # no type comments
1010
TYPE_CHECKING,
1111
Any,
12+
Literal,
1213
NewType,
1314
Optional,
1415
TypeVar,
@@ -661,6 +662,59 @@ def func_with_overload(a: Union[int, str], b: Union[int, str]) -> None:
661662
"""
662663

663664

665+
@expected(
666+
"""\
667+
mod.func_literals_long_format(a, b)
668+
669+
A docstring.
670+
671+
Parameters:
672+
* **a** ("Literal"["'arg1'", "'arg2'"]) -- Argument that can
673+
take either of two literal values.
674+
675+
* **b** ("Literal"["'arg1'", "'arg2'"]) -- Argument that can
676+
take either of two literal values.
677+
678+
Return type:
679+
"None"
680+
""",
681+
)
682+
def func_literals_long_format(a: Literal["arg1", "arg2"], b: Literal["arg1", "arg2"]) -> None:
683+
"""
684+
A docstring.
685+
686+
:param a: Argument that can take either of two literal values.
687+
:param b: Argument that can take either of two literal values.
688+
"""
689+
690+
691+
@expected(
692+
"""\
693+
mod.func_literals_short_format(a, b)
694+
695+
A docstring.
696+
697+
Parameters:
698+
* **a** ("'arg1'" | "'arg2'") -- Argument that can take either
699+
of two literal values.
700+
701+
* **b** ("'arg1'" | "'arg2'") -- Argument that can take either
702+
of two literal values.
703+
704+
Return type:
705+
"None"
706+
""",
707+
python_display_short_literal_types=True,
708+
)
709+
def func_literals_short_format(a: Literal["arg1", "arg2"], b: Literal["arg1", "arg2"]) -> None:
710+
"""
711+
A docstring.
712+
713+
:param a: Argument that can take either of two literal values.
714+
:param b: Argument that can take either of two literal values.
715+
"""
716+
717+
664718
@expected(
665719
"""\
666720
class mod.TestClassAttributeDocs

0 commit comments

Comments
 (0)