Custom argument formatting

Closes #162
pull/171/head
Ashley Whetter 5 years ago
parent 46c577eda1
commit 34d02faa2f

@ -3,6 +3,20 @@ Changelog
Versions follow `Semantic Versioning <https://semver.org/>`_ (``<major>.<minor>.<patch>``).
v1.2.0 (TBC)
------------
Features
^^^^^^^^
* (Python) Can read per argument type comments with astroid > 2.2.5.
Bug Fixes
^^^^^^^^^
* (Python) Forward reference annotations are no longer rendered as strings.
v1.1.0 (2019-06-23)
-------------------

@ -2,6 +2,7 @@ try:
import builtins
except ImportError:
import __builtin__ as builtins
import itertools
import re
import sys
@ -13,9 +14,11 @@ if sys.version_info < (3,):
_EXCEPTIONS_MODULE = "exceptions"
# getattr to keep linter happy
_STRING_TYPES = getattr(builtins, "basestring")
_zip_longest = itertools.izip_longest
else:
_EXCEPTIONS_MODULE = "builtins"
_STRING_TYPES = str
_zip_longest = itertools.zip_longest
def resolve_import_alias(name, import_names):
@ -359,3 +362,106 @@ def get_module_all(node):
all_.append(elt_name.value)
return all_
def merge_annotations(annotations, comment_annotations):
for ann, comment_ann in _zip_longest(annotations, comment_annotations):
if not ann or isinstance(ann, astroid.Ellipsis):
yield comment_ann
else:
yield ann
def _format_args(args, defaults=None, annotations=None):
values = []
if args is None:
return ""
if annotations is None:
annotations = []
if defaults is not None:
default_offset = len(args) - len(defaults)
packed = _zip_longest(args, annotations)
for i, (arg, annotation) in enumerate(packed):
if isinstance(arg, astroid.Tuple):
values.append("({})".format(_format_args(arg.elts)))
else:
argname = arg.name
default_sep = "="
if annotation is not None:
ann_str = annotation.as_string()
if isinstance(annotation, astroid.Const):
ann_str = annotation.value
argname = "{}: {}".format(argname, ann_str)
default_sep = " = "
values.append(argname)
if defaults is not None and i >= default_offset:
if defaults[i - default_offset] is not None:
values[-1] += default_sep + defaults[i - default_offset].as_string()
return ", ".join(values)
def format_args(args_node):
result = []
positional_only_defaults = []
positional_or_keyword_defaults = args_node.defaults
if args_node.defaults:
args = args_node.args or []
positional_or_keyword_defaults = args_node.defaults[-len(args) :]
positional_only_defaults = args_node.defaults[
: len(args_node.defaults) - len(args)
]
plain_annotations = getattr(args_node, "annotations", ()) or ()
func_comment_annotations = getattr(args_node.parent, "type_comment_args", ()) or ()
comment_annotations = getattr(args_node, "type_comment_args", ()) or ()
annotations = list(
merge_annotations(
plain_annotations,
merge_annotations(func_comment_annotations, comment_annotations),
)
)
if getattr(args_node, "posonlyargs", None):
result.append(_format_args(args_node.posonlyargs, positional_only_defaults))
result.append("/")
if args_node.args:
result.append(
_format_args(args_node.args, positional_or_keyword_defaults, annotations)
)
if args_node.vararg:
vararg_result = "*{}".format(args_node.vararg)
if getattr(args_node, "varargannotation", None):
vararg_result = "{}: {}".format(
vararg_result, args_node.varargannotation.as_string()
)
result.append(vararg_result)
if getattr(args_node, "kwonlyargs", None):
if not args_node.vararg:
result.append("*")
result.append(
_format_args(
args_node.kwonlyargs,
args_node.kw_defaults,
args_node.kwonlyargs_annotations,
)
)
if args_node.kwarg:
kwarg_result = "**{}".format(args_node.kwarg)
if getattr(args_node, "kwargannotation", None):
kwarg_result = "{}: {}".format(
kwarg_result, args_node.kwargannotation.as_string()
)
result.append(kwarg_result)
return ", ".join(result)

@ -157,11 +157,13 @@ class Parser(object):
elif getattr(node, "type_comment_returns", None):
return_annotation = node.type_comment_returns.as_string()
arg_string = astroid_utils.format_args(node.args)
data = {
"type": type_,
"name": node.name,
"full_name": self._get_full_name(node.name),
"args": node.args.as_string(),
"args": arg_string,
"doc": self._encode(node.doc or ""),
"from_line_no": node.fromlineno,
"to_line_no": node.tolineno,

@ -11,7 +11,7 @@ ratings = [0, 1, 2, 3, 4, 5] # type: List[int]
rating_names = {0: "zero", 1: "one"} # type: Dict[int, str]
# TODO: Currently unsupported by astroid (#665)
def f(
start, # type: int
end, # type: int

@ -132,14 +132,12 @@ class TestPy3Module(object):
assert "Dict[int, str]" in example_file
assert "start:int" in example_file
assert "start: int" in example_file
assert "Iterable[int]" in example_file
assert "List[Union[str, int]]" in example_file
# TODO: This should not display as a string
# after we do proper formatting
assert "not_yet_a:'A'" in example_file
assert "not_yet_a: A" in example_file
assert "is_an_a" in example_file
assert "ClassVar" in example_file
@ -180,14 +178,14 @@ class TestAnnotationCommentsModule(object):
assert "Dict[int, str]" in example_file
# TODO: Type is currently unsupported by astroid (#665)
assert "start" in example_file
# When astroid>2.2.5
# assert "start: int" in example_file
# assert "end: int" in example_file
assert "Iterable[int]" in example_file
assert "List[Union[str, int]]" in example_file
# TODO: This should not display the type after we do proper formatting
assert "not_yet_a" in example_file
assert "not_yet_a: A" in example_file
assert "is_an_a" in example_file
assert "ClassVar" in example_file

Loading…
Cancel
Save