The fully qualified path of objects are included type annotations

pull/266/head
Ashley Whetter 4 years ago
parent 82eb508fe0
commit 73bb2d6105

@ -9,6 +9,8 @@ V1.6.1 (TBC)
Features Features
^^^^^^^^ ^^^^^^^^
* The fully qualified path of objects are included type annotations
so that Sphinx can link to them.
* Added support for Sphinx 3.3. and 3.4. * Added support for Sphinx 3.3. and 3.4.

@ -73,10 +73,11 @@ def get_full_basename(node, basename):
full_basename = basename full_basename = basename
top_level_name = re.sub(r"\(.*\)", "", basename).split(".", 1)[0] top_level_name = re.sub(r"\(.*\)", "", basename).split(".", 1)[0]
lookup_node = node lookup_node = (
while not hasattr(lookup_node, "lookup"): node if isinstance(node, astroid.node_classes.LookupMixIn) else node.scope()
lookup_node = lookup_node.parent )
assigns = lookup_node.lookup(top_level_name)[1] assigns = lookup_node.lookup(top_level_name)[1]
for assignment in assigns: for assignment in assigns:
if isinstance(assignment, astroid.nodes.ImportFrom): if isinstance(assignment, astroid.nodes.ImportFrom):
import_name = get_full_import_name(assignment, top_level_name) import_name = get_full_import_name(assignment, top_level_name)
@ -403,12 +404,45 @@ def merge_annotations(annotations, comment_annotations):
yield None yield None
def _format_annotation(annotation): def _resolve_annotation(annotation):
resolved = None
if isinstance(annotation, astroid.Const):
resolved = get_full_basename(annotation, str(annotation.value))
elif isinstance(annotation, astroid.Name):
resolved = get_full_basename(annotation, annotation.name)
elif isinstance(annotation, astroid.Attribute):
resolved = get_full_basename(annotation, annotation.as_string())
elif isinstance(annotation, astroid.Subscript):
value = _resolve_annotation(annotation.value)
slice_ = _resolve_annotation(annotation.slice)
resolved = f"{value}[{slice_}]"
elif isinstance(annotation, astroid.Tuple):
resolved = (
"(" + ", ".join(_resolve_annotation(elt) for elt in annotation.elts) + ")"
)
elif isinstance(annotation, astroid.List):
resolved = (
"[" + ", ".join(_resolve_annotation(elt) for elt in annotation.elts) + "]"
)
else:
resolved = annotation.as_string()
if resolved.startswith("typing."):
return resolved[len("typing.") :]
return resolved
def format_annotation(annotation, parent):
if annotation: if annotation:
if isinstance(annotation, astroid.Const): # Workaround https://github.com/PyCQA/astroid/issues/851
annotation = annotation.value if annotation.parent and not isinstance(
else: annotation.parent, astroid.node_classes.NodeNG
annotation = annotation.as_string() ):
annotation.parent = parent
return _resolve_annotation(annotation)
return annotation return annotation
@ -426,7 +460,7 @@ def _iter_args(args, annotations, defaults):
if isinstance(arg, astroid.Tuple): if isinstance(arg, astroid.Tuple):
name = "({})".format(", ".join(x.name for x in arg.elts)) name = "({})".format(", ".join(x.name for x in arg.elts))
yield (name, _format_annotation(annotation), default) yield (name, format_annotation(annotation, arg.parent), default)
def _get_args_info(args_node): # pylint: disable=too-many-branches,too-many-statements def _get_args_info(args_node): # pylint: disable=too-many-branches,too-many-statements
@ -485,9 +519,11 @@ def _get_args_info(args_node): # pylint: disable=too-many-branches,too-many-sta
if args_node.vararg: if args_node.vararg:
annotation = None annotation = None
if args_node.varargannotation: if args_node.varargannotation:
annotation = _format_annotation(args_node.varargannotation) annotation = format_annotation(args_node.varargannotation, args_node.parent)
elif len(annotations) > annotation_offset and annotations[annotation_offset]: elif len(annotations) > annotation_offset and annotations[annotation_offset]:
annotation = _format_annotation(annotations[annotation_offset]) annotation = format_annotation(
annotations[annotation_offset], args_node.parent
)
annotation_offset += 1 annotation_offset += 1
result.append(("*", args_node.vararg, annotation, None)) result.append(("*", args_node.vararg, annotation, None))
@ -515,9 +551,11 @@ def _get_args_info(args_node): # pylint: disable=too-many-branches,too-many-sta
if args_node.kwarg: if args_node.kwarg:
annotation = None annotation = None
if args_node.kwargannotation: if args_node.kwargannotation:
annotation = _format_annotation(args_node.kwargannotation) annotation = format_annotation(args_node.kwargannotation, args_node.parent)
elif len(annotations) > annotation_offset and annotations[annotation_offset]: elif len(annotations) > annotation_offset and annotations[annotation_offset]:
annotation = _format_annotation(annotations[annotation_offset]) annotation = format_annotation(
annotations[annotation_offset], args_node.parent
)
annotation_offset += 1 annotation_offset += 1
result.append(("**", args_node.kwarg, annotation, None)) result.append(("**", args_node.kwarg, annotation, None))

@ -173,10 +173,12 @@ class Parser(object):
annotations = astroid_utils.get_annotations_dict(node.args) annotations = astroid_utils.get_annotations_dict(node.args)
return_annotation = None return_annotation = None
if node.returns: if node.returns:
return_annotation = node.returns.as_string() return_annotation = astroid_utils.format_annotation(node.returns, node)
annotations["return"] = return_annotation annotations["return"] = return_annotation
elif node.type_comment_returns: elif node.type_comment_returns:
return_annotation = node.type_comment_returns.as_string() return_annotation = astroid_utils.format_annotation(
node.type_comment_returns, node
)
annotations["return"] = return_annotation annotations["return"] = return_annotation
arg_string = astroid_utils.format_args(node.args) arg_string = astroid_utils.format_args(node.args)

@ -7,6 +7,9 @@ import asyncio
import typing import typing
from typing import ClassVar, Dict, Iterable, List, Union, overload from typing import ClassVar, Dict, Iterable, List, Union, overload
from example2 import B
max_rating: int = 10 max_rating: int = 10
is_valid: bool is_valid: bool
@ -36,6 +39,10 @@ def f2(not_yet_a: "A") -> int:
... ...
def f3(imported: B) -> B:
...
@overload @overload
def overloaded_func(a: float) -> float: def overloaded_func(a: float) -> float:
... ...

@ -182,6 +182,9 @@ class TestPy3Module(object):
assert "List[Union[str, int]]" in example_file assert "List[Union[str, int]]" in example_file
assert "not_yet_a: A" in example_file assert "not_yet_a: A" in example_file
assert "imported: example2.B" in example_file
assert "-> example2.B" in example_file
assert "is_an_a" in example_file assert "is_an_a" in example_file
assert "ClassVar" in example_file assert "ClassVar" in example_file

Loading…
Cancel
Save