diff --git a/CHANGES b/CHANGES index 4848e29e72..1a58b9dc98 100644 --- a/CHANGES +++ b/CHANGES @@ -78,6 +78,8 @@ Bugs fixed * #9678: linkcheck: file extension was shown twice in warnings * #9697: py domain: An index entry with parens was registered for ``py:method`` directive with ``:property:`` option +* #9775: py domain: Literal typehint was converted to a cross reference when + :confval:`autodoc_typehints='description'` * #9708: needs_extension failed to check double-digit version correctly * #9688: Fix :rst:dir:`code`` does not recognize ``:class:`` option * #9733: Fix for logging handler flushing warnings in the middle of the docs diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index 9bb9a1e723..fd6a788921 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -353,17 +353,21 @@ def make_xrefs(self, rolename: str, domain: str, target: str, split_contnode = bool(contnode and contnode.astext() == target) + in_literal = False results = [] for sub_target in filter(None, sub_targets): if split_contnode: contnode = nodes.Text(sub_target) - if delims_re.match(sub_target): + if in_literal or delims_re.match(sub_target): results.append(contnode or innernode(sub_target, sub_target)) else: results.append(self.make_xref(rolename, domain, sub_target, innernode, contnode, env, inliner, location)) + if sub_target in ('Literal', 'typing.Literal'): + in_literal = True + return results diff --git a/tests/test_domain_py.py b/tests/test_domain_py.py index 5ba63d0e35..e34218dfa4 100644 --- a/tests/test_domain_py.py +++ b/tests/test_domain_py.py @@ -1110,6 +1110,42 @@ def test_info_field_list_piped_type(app): **{"py:module": "example", "py:class": "Class"}) +def test_info_field_list_Literal(app): + text = (".. py:module:: example\n" + ".. py:class:: Class\n" + "\n" + " :param age: blah blah\n" + " :type age: Literal['foo', 'bar', 'baz']\n") + doctree = restructuredtext.parse(app, text) + + assert_node(doctree, + (nodes.target, + addnodes.index, + addnodes.index, + [desc, ([desc_signature, ([desc_annotation, ("class", desc_sig_space)], + [desc_addname, "example."], + [desc_name, "Class"])], + [desc_content, nodes.field_list, nodes.field, (nodes.field_name, + nodes.field_body)])])) + assert_node(doctree[3][1][0][0][1], + ([nodes.paragraph, ([addnodes.literal_strong, "age"], + " (", + [pending_xref, addnodes.literal_emphasis, "Literal"], + [addnodes.literal_emphasis, "["], + [addnodes.literal_emphasis, "'foo'"], + [addnodes.literal_emphasis, ", "], + [addnodes.literal_emphasis, "'bar'"], + [addnodes.literal_emphasis, ", "], + [addnodes.literal_emphasis, "'baz'"], + [addnodes.literal_emphasis, "]"], + ")", + " -- ", + "blah blah")],)) + assert_node(doctree[3][1][0][0][1][0][2], pending_xref, + refdomain="py", reftype="class", reftarget="Literal", + **{"py:module": "example", "py:class": "Class"}) + + def test_info_field_list_var(app): text = (".. py:class:: Class\n" "\n"