summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Turner <9087854+AA-Turner@users.noreply.github.com>2023-04-05 13:07:25 +0100
committerGitHub <noreply@github.com>2023-04-05 13:07:25 +0100
commit4a3febb69a124300f1481fc33b44a5672f24e0fa (patch)
tree10defdc4bcc3d606604cab9dae3ca445ba74685b
parent609b2f2650dfea191e688ce6238ba567da551052 (diff)
downloadsphinx-git-4a3febb69a124300f1481fc33b44a5672f24e0fa.tar.gz
Add an option for displaying short ``Literal`` types (#11109)
The new ``python_display_short_literal_types`` configuration option for the ``py`` domain controls display of PEP 586 ``Literal`` types. The 'short' format is inspired by PEP 604, using the bitwise OR operator to distinguish the possible legal values for the argument.
-rw-r--r--CHANGES2
-rw-r--r--doc/usage/configuration.rst30
-rw-r--r--sphinx/domains/python.py5
-rw-r--r--tests/test_domain_py.py67
4 files changed, 104 insertions, 0 deletions
diff --git a/CHANGES b/CHANGES
index d90ba2f0d..fe7a0a8c4 100644
--- a/CHANGES
+++ b/CHANGES
@@ -53,6 +53,8 @@ Features added
Patch by Jeremy Maitin-Shepard
* #11281: Support for :confval:`imgmath_latex` ``= 'tectonic'`` or
``= 'xelatex'``. Patch by Dimitar Dimitrov
+* #11109, #9643: Add :confval:`python_display_short_literal_types` option for
+ condensed rendering of ``Literal`` types.
Bugs fixed
----------
diff --git a/doc/usage/configuration.rst b/doc/usage/configuration.rst
index fb5a7615c..c92e468f1 100644
--- a/doc/usage/configuration.rst
+++ b/doc/usage/configuration.rst
@@ -2936,6 +2936,36 @@ Options for the C++ domain
Options for the Python domain
-----------------------------
+.. confval:: python_display_short_literal_types
+
+ This value controls how :py:data:`~typing.Literal` types are displayed.
+ The setting is a boolean, default ``False``.
+
+ Examples
+ ~~~~~~~~
+
+ The examples below use the following :rst:dir:`py:function` directive:
+
+ .. code:: reStructuredText
+
+ .. py:function:: serve_food(item: Literal["egg", "spam", "lobster thermidor"]) -> None
+
+ When ``False``, :py:data:`~typing.Literal` types display as per standard
+ Python syntax, i.e.:
+
+ .. code:: python
+
+ serve_food(item: Literal["egg", "spam", "lobster thermidor"]) -> None
+
+ When ``True``, :py:data:`~typing.Literal` types display with a short,
+ :PEP:`604`-inspired syntax, i.e.:
+
+ .. code:: python
+
+ serve_food(item: "egg" | "spam" | "lobster thermidor") -> None
+
+ .. versionadded:: 6.2
+
.. confval:: python_use_unqualified_type_names
If true, suppress the module name of the python reference if it can be
diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py
index 99bc1bcae..edd4e829f 100644
--- a/sphinx/domains/python.py
+++ b/sphinx/domains/python.py
@@ -130,6 +130,8 @@ def type_to_xref(target: str, env: BuildEnvironment | None = None,
def _parse_annotation(annotation: str, env: BuildEnvironment | None) -> list[Node]:
"""Parse type annotation."""
+ short_literals = env.config.python_display_short_literal_types
+
def unparse(node: ast.AST) -> list[Node]:
if isinstance(node, ast.Attribute):
return [nodes.Text(f"{unparse(node.value)[0]}.{node.attr}")]
@@ -182,6 +184,8 @@ def _parse_annotation(annotation: str, env: BuildEnvironment | None) -> list[Nod
if isinstance(node, ast.Subscript):
if getattr(node.value, 'id', '') in {'Optional', 'Union'}:
return _unparse_pep_604_annotation(node)
+ if short_literals and getattr(node.value, 'id', '') == 'Literal':
+ return _unparse_pep_604_annotation(node)
result = unparse(node.value)
result.append(addnodes.desc_sig_punctuation('', '['))
result.extend(unparse(node.slice))
@@ -1511,6 +1515,7 @@ def setup(app: Sphinx) -> dict[str, Any]:
app.add_domain(PythonDomain)
app.add_config_value('python_use_unqualified_type_names', False, 'env')
+ app.add_config_value('python_display_short_literal_types', False, 'env')
app.connect('object-description-transform', filter_meta_fields)
app.connect('missing-reference', builtin_resolver, priority=900)
diff --git a/tests/test_domain_py.py b/tests/test_domain_py.py
index 66204e69e..6cac6cba1 100644
--- a/tests/test_domain_py.py
+++ b/tests/test_domain_py.py
@@ -1470,3 +1470,70 @@ def test_module_content_line_number(app):
source, line = docutils.utils.get_source_line(xrefs[0])
assert 'index.rst' in source
assert line == 3
+
+
+@pytest.mark.sphinx(freshenv=True, confoverrides={'python_display_short_literal_types': True})
+def test_short_literal_types(app):
+ text = """\
+.. py:function:: literal_ints(x: Literal[1, 2, 3] = 1) -> None
+.. py:function:: literal_union(x: Union[Literal["a"], Literal["b"], Literal["c"]]) -> None
+"""
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree, (
+ addnodes.index,
+ [desc, (
+ [desc_signature, (
+ [desc_name, 'literal_ints'],
+ [desc_parameterlist, (
+ [desc_parameter, (
+ [desc_sig_name, 'x'],
+ [desc_sig_punctuation, ':'],
+ desc_sig_space,
+ [desc_sig_name, (
+ [desc_sig_literal_number, '1'],
+ desc_sig_space,
+ [desc_sig_punctuation, '|'],
+ desc_sig_space,
+ [desc_sig_literal_number, '2'],
+ desc_sig_space,
+ [desc_sig_punctuation, '|'],
+ desc_sig_space,
+ [desc_sig_literal_number, '3'],
+ )],
+ desc_sig_space,
+ [desc_sig_operator, '='],
+ desc_sig_space,
+ [nodes.inline, '1'],
+ )],
+ )],
+ [desc_returns, pending_xref, 'None'],
+ )],
+ [desc_content, ()],
+ )],
+ addnodes.index,
+ [desc, (
+ [desc_signature, (
+ [desc_name, 'literal_union'],
+ [desc_parameterlist, (
+ [desc_parameter, (
+ [desc_sig_name, 'x'],
+ [desc_sig_punctuation, ':'],
+ desc_sig_space,
+ [desc_sig_name, (
+ [desc_sig_literal_string, "'a'"],
+ desc_sig_space,
+ [desc_sig_punctuation, '|'],
+ desc_sig_space,
+ [desc_sig_literal_string, "'b'"],
+ desc_sig_space,
+ [desc_sig_punctuation, '|'],
+ desc_sig_space,
+ [desc_sig_literal_string, "'c'"],
+ )],
+ )],
+ )],
+ [desc_returns, pending_xref, 'None'],
+ )],
+ [desc_content, ()],
+ )],
+ ))