py domain: Allow "~" prefix as a prefix of typehints in signature

Now python domain supports the "~" prefix at the beginning of the
typehints of the function signatures:

  .. py:function:: func(x: ~typing.Dict)

It's rescognized as the same as python reference roles do
(ex. :py:class:`~typing.Dict`).
This commit is contained in:
Takeshi KOMIYA 2021-11-30 01:54:19 +09:00
parent a3edc982c1
commit 5eaed2bed7
2 changed files with 31 additions and 5 deletions

View File

@ -80,7 +80,8 @@ class ModuleEntry(NamedTuple):
deprecated: bool
def type_to_xref(target: str, env: BuildEnvironment = None) -> addnodes.pending_xref:
def type_to_xref(target: str, env: BuildEnvironment = None, suppress_prefix: bool = False
) -> addnodes.pending_xref:
"""Convert a type string to a cross reference node."""
if target == 'None':
reftype = 'obj'
@ -101,6 +102,8 @@ def type_to_xref(target: str, env: BuildEnvironment = None) -> addnodes.pending_
elif target.startswith('~'):
target = target[1:]
text = target.split('.')[-1]
elif suppress_prefix:
text = target.split('.')[-1]
else:
text = target
@ -150,6 +153,8 @@ def _parse_annotation(annotation: str, env: BuildEnvironment = None) -> List[Nod
return unparse(node.value)
elif isinstance(node, ast.Index):
return unparse(node.value)
elif isinstance(node, ast.Invert):
return [addnodes.desc_sig_punctuation('', '~')]
elif isinstance(node, ast.List):
result = [addnodes.desc_sig_punctuation('', '[')]
if node.elts:
@ -180,6 +185,8 @@ def _parse_annotation(annotation: str, env: BuildEnvironment = None) -> List[Nod
if isinstance(subnode, nodes.Text):
result[i] = nodes.literal('', '', subnode)
return result
elif isinstance(node, ast.UnaryOp):
return unparse(node.op) + unparse(node.operand)
elif isinstance(node, ast.Tuple):
if node.elts:
result = []
@ -209,12 +216,19 @@ def _parse_annotation(annotation: str, env: BuildEnvironment = None) -> List[Nod
try:
tree = ast_parse(annotation)
result = unparse(tree)
for i, node in enumerate(result):
result: List[Node] = []
for node in unparse(tree):
if isinstance(node, nodes.literal):
result[i] = node[0]
result.append(node[0])
elif isinstance(node, nodes.Text) and node.strip():
result[i] = type_to_xref(str(node), env)
if (result and isinstance(result[-1], addnodes.desc_sig_punctuation) and
result[-1].astext() == '~'):
result.pop()
result.append(type_to_xref(str(node), env, suppress_prefix=True))
else:
result.append(type_to_xref(str(node), env))
else:
result.append(node)
return result
except SyntaxError:
return [type_to_xref(annotation, env)]

View File

@ -350,6 +350,18 @@ def test_parse_annotation(app):
assert_node(doctree[0], pending_xref, refdomain="py", reftype="obj", reftarget="None")
def test_parse_annotation_suppress(app):
doctree = _parse_annotation("~typing.Dict[str, str]", app.env)
assert_node(doctree, ([pending_xref, "Dict"],
[desc_sig_punctuation, "["],
[pending_xref, "str"],
[desc_sig_punctuation, ","],
desc_sig_space,
[pending_xref, "str"],
[desc_sig_punctuation, "]"]))
assert_node(doctree[0], pending_xref, refdomain="py", reftype="class", reftarget="typing.Dict")
@pytest.mark.skipif(sys.version_info < (3, 8), reason='python 3.8+ is required.')
def test_parse_annotation_Literal(app):
doctree = _parse_annotation("Literal[True, False]", app.env)