mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Fix #7928: py domain: failed to resolve a type annotation for the attribute
This commit is contained in:
parent
2feb0b43b6
commit
4410668776
1
CHANGES
1
CHANGES
@ -33,6 +33,7 @@ Bugs fixed
|
||||
* #7715: LaTeX: ``numfig_secnum_depth > 1`` leads to wrong figure links
|
||||
* #7846: html theme: XML-invalid files were generated
|
||||
* #7894: gettext: Wrong source info is shown when using rst_epilog
|
||||
* #7928: py domain: failed to resolve a type annotation for the attribute
|
||||
* #7869: :rst:role:`abbr` role without an explanation will show the explanation
|
||||
from the previous abbr role
|
||||
* C and C++, removed ``noindex`` directive option as it did
|
||||
|
@ -77,18 +77,24 @@ ModuleEntry = NamedTuple('ModuleEntry', [('docname', str),
|
||||
('deprecated', bool)])
|
||||
|
||||
|
||||
def type_to_xref(text: str) -> addnodes.pending_xref:
|
||||
def type_to_xref(text: str, env: BuildEnvironment = None) -> addnodes.pending_xref:
|
||||
"""Convert a type string to a cross reference node."""
|
||||
if text == 'None':
|
||||
reftype = 'obj'
|
||||
else:
|
||||
reftype = 'class'
|
||||
|
||||
if env:
|
||||
kwargs = {'py:module': env.ref_context.get('py:module'),
|
||||
'py:class': env.ref_context.get('py:class')}
|
||||
else:
|
||||
kwargs = {}
|
||||
|
||||
return pending_xref('', nodes.Text(text),
|
||||
refdomain='py', reftype=reftype, reftarget=text)
|
||||
refdomain='py', reftype=reftype, reftarget=text, **kwargs)
|
||||
|
||||
|
||||
def _parse_annotation(annotation: str) -> List[Node]:
|
||||
def _parse_annotation(annotation: str, env: BuildEnvironment = None) -> List[Node]:
|
||||
"""Parse type annotation."""
|
||||
def unparse(node: ast.AST) -> List[Node]:
|
||||
if isinstance(node, ast.Attribute):
|
||||
@ -130,18 +136,22 @@ def _parse_annotation(annotation: str) -> List[Node]:
|
||||
else:
|
||||
raise SyntaxError # unsupported syntax
|
||||
|
||||
if env is None:
|
||||
warnings.warn("The env parameter for _parse_annotation becomes required now.",
|
||||
RemovedInSphinx50Warning, stacklevel=2)
|
||||
|
||||
try:
|
||||
tree = ast_parse(annotation)
|
||||
result = unparse(tree)
|
||||
for i, node in enumerate(result):
|
||||
if isinstance(node, nodes.Text):
|
||||
result[i] = type_to_xref(str(node))
|
||||
result[i] = type_to_xref(str(node), env)
|
||||
return result
|
||||
except SyntaxError:
|
||||
return [type_to_xref(annotation)]
|
||||
return [type_to_xref(annotation, env)]
|
||||
|
||||
|
||||
def _parse_arglist(arglist: str) -> addnodes.desc_parameterlist:
|
||||
def _parse_arglist(arglist: str, env: BuildEnvironment = None) -> addnodes.desc_parameterlist:
|
||||
"""Parse a list of arguments using AST parser"""
|
||||
params = addnodes.desc_parameterlist(arglist)
|
||||
sig = signature_from_str('(%s)' % arglist)
|
||||
@ -167,7 +177,7 @@ def _parse_arglist(arglist: str) -> addnodes.desc_parameterlist:
|
||||
node += addnodes.desc_sig_name('', param.name)
|
||||
|
||||
if param.annotation is not param.empty:
|
||||
children = _parse_annotation(param.annotation)
|
||||
children = _parse_annotation(param.annotation, env)
|
||||
node += addnodes.desc_sig_punctuation('', ':')
|
||||
node += nodes.Text(' ')
|
||||
node += addnodes.desc_sig_name('', '', *children) # type: ignore
|
||||
@ -415,7 +425,7 @@ class PyObject(ObjectDescription):
|
||||
signode += addnodes.desc_name(name, name)
|
||||
if arglist:
|
||||
try:
|
||||
signode += _parse_arglist(arglist)
|
||||
signode += _parse_arglist(arglist, self.env)
|
||||
except SyntaxError:
|
||||
# fallback to parse arglist original parser.
|
||||
# it supports to represent optional arguments (ex. "func(foo [, bar])")
|
||||
@ -430,7 +440,7 @@ class PyObject(ObjectDescription):
|
||||
signode += addnodes.desc_parameterlist()
|
||||
|
||||
if retann:
|
||||
children = _parse_annotation(retann)
|
||||
children = _parse_annotation(retann, self.env)
|
||||
signode += addnodes.desc_returns(retann, '', *children)
|
||||
|
||||
anno = self.options.get('annotation')
|
||||
@ -626,7 +636,7 @@ class PyVariable(PyObject):
|
||||
|
||||
typ = self.options.get('type')
|
||||
if typ:
|
||||
annotations = _parse_annotation(typ)
|
||||
annotations = _parse_annotation(typ, self.env)
|
||||
signode += addnodes.desc_annotation(typ, '', nodes.Text(': '), *annotations)
|
||||
|
||||
value = self.options.get('value')
|
||||
@ -872,7 +882,7 @@ class PyAttribute(PyObject):
|
||||
|
||||
typ = self.options.get('type')
|
||||
if typ:
|
||||
annotations = _parse_annotation(typ)
|
||||
annotations = _parse_annotation(typ, self.env)
|
||||
signode += addnodes.desc_annotation(typ, '', nodes.Text(': '), *annotations)
|
||||
|
||||
value = self.options.get('value')
|
||||
|
@ -236,18 +236,18 @@ def test_get_full_qualified_name():
|
||||
assert domain.get_full_qualified_name(node) == 'module1.Class.func'
|
||||
|
||||
|
||||
def test_parse_annotation():
|
||||
doctree = _parse_annotation("int")
|
||||
def test_parse_annotation(app):
|
||||
doctree = _parse_annotation("int", app.env)
|
||||
assert_node(doctree, ([pending_xref, "int"],))
|
||||
assert_node(doctree[0], pending_xref, refdomain="py", reftype="class", reftarget="int")
|
||||
|
||||
doctree = _parse_annotation("List[int]")
|
||||
doctree = _parse_annotation("List[int]", app.env)
|
||||
assert_node(doctree, ([pending_xref, "List"],
|
||||
[desc_sig_punctuation, "["],
|
||||
[pending_xref, "int"],
|
||||
[desc_sig_punctuation, "]"]))
|
||||
|
||||
doctree = _parse_annotation("Tuple[int, int]")
|
||||
doctree = _parse_annotation("Tuple[int, int]", app.env)
|
||||
assert_node(doctree, ([pending_xref, "Tuple"],
|
||||
[desc_sig_punctuation, "["],
|
||||
[pending_xref, "int"],
|
||||
@ -255,14 +255,14 @@ def test_parse_annotation():
|
||||
[pending_xref, "int"],
|
||||
[desc_sig_punctuation, "]"]))
|
||||
|
||||
doctree = _parse_annotation("Tuple[()]")
|
||||
doctree = _parse_annotation("Tuple[()]", app.env)
|
||||
assert_node(doctree, ([pending_xref, "Tuple"],
|
||||
[desc_sig_punctuation, "["],
|
||||
[desc_sig_punctuation, "("],
|
||||
[desc_sig_punctuation, ")"],
|
||||
[desc_sig_punctuation, "]"]))
|
||||
|
||||
doctree = _parse_annotation("Callable[[int, int], int]")
|
||||
doctree = _parse_annotation("Callable[[int, int], int]", app.env)
|
||||
assert_node(doctree, ([pending_xref, "Callable"],
|
||||
[desc_sig_punctuation, "["],
|
||||
[desc_sig_punctuation, "["],
|
||||
@ -275,12 +275,11 @@ def test_parse_annotation():
|
||||
[desc_sig_punctuation, "]"]))
|
||||
|
||||
# None type makes an object-reference (not a class reference)
|
||||
doctree = _parse_annotation("None")
|
||||
doctree = _parse_annotation("None", app.env)
|
||||
assert_node(doctree, ([pending_xref, "None"],))
|
||||
assert_node(doctree[0], pending_xref, refdomain="py", reftype="obj", reftarget="None")
|
||||
|
||||
|
||||
|
||||
def test_pyfunction_signature(app):
|
||||
text = ".. py:function:: hello(name: str) -> str"
|
||||
doctree = restructuredtext.parse(app, text)
|
||||
@ -458,14 +457,22 @@ def test_pyobject_prefix(app):
|
||||
|
||||
|
||||
def test_pydata(app):
|
||||
text = ".. py:data:: var\n"
|
||||
text = (".. py:module:: example\n"
|
||||
".. py:data:: var\n"
|
||||
" :type: int\n")
|
||||
domain = app.env.get_domain('py')
|
||||
doctree = restructuredtext.parse(app, text)
|
||||
assert_node(doctree, (addnodes.index,
|
||||
[desc, ([desc_signature, desc_name, "var"],
|
||||
assert_node(doctree, (nodes.target,
|
||||
addnodes.index,
|
||||
addnodes.index,
|
||||
[desc, ([desc_signature, ([desc_addname, "example."],
|
||||
[desc_name, "var"],
|
||||
[desc_annotation, (": ",
|
||||
[pending_xref, "int"])])],
|
||||
[desc_content, ()])]))
|
||||
assert 'var' in domain.objects
|
||||
assert domain.objects['var'] == ('index', 'var', 'data')
|
||||
assert_node(doctree[3][0][2][1], pending_xref, **{"py:module": "example"})
|
||||
assert 'example.var' in domain.objects
|
||||
assert domain.objects['example.var'] == ('index', 'example.var', 'data')
|
||||
|
||||
|
||||
def test_pyfunction(app):
|
||||
@ -698,6 +705,8 @@ def test_pyattribute(app):
|
||||
[desc_sig_punctuation, "]"])],
|
||||
[desc_annotation, " = ''"])],
|
||||
[desc_content, ()]))
|
||||
assert_node(doctree[1][1][1][0][1][1], pending_xref, **{"py:class": "Class"})
|
||||
assert_node(doctree[1][1][1][0][1][3], pending_xref, **{"py:class": "Class"})
|
||||
assert 'Class.attr' in domain.objects
|
||||
assert domain.objects['Class.attr'] == ('index', 'Class.attr', 'attribute')
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user