Skip to content

Commit a9e0dce

Browse files
committed
Fix Arguments.arguments so it actually returns all arguments
Closes #2213. Arguments.arguments() has been modified so that it returns all arguments as it should (according to its own doc). A test case was also added to verify this.
1 parent a7ab088 commit a9e0dce

File tree

4 files changed

+54
-19
lines changed

4 files changed

+54
-19
lines changed

astroid/nodes/node_classes.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -780,7 +780,18 @@ def fromlineno(self) -> int:
780780
@cached_property
781781
def arguments(self):
782782
"""Get all the arguments for this node, including positional only and positional and keyword"""
783-
return list(itertools.chain((self.posonlyargs or ()), self.args or ()))
783+
retval = list(itertools.chain((self.posonlyargs or ()), (self.args or ())))
784+
if self.vararg:
785+
retval.append(
786+
Name(self.vararg, -1, -1, self, end_lineno=None, end_col_offset=None)
787+
)
788+
retval += self.kwonlyargs or ()
789+
if self.kwarg:
790+
retval.append(
791+
Name(self.kwarg, -1, -1, self, end_lineno=None, end_col_offset=None)
792+
)
793+
794+
return retval
784795

785796
def format_args(self, *, skippable_names: set[str] | None = None) -> str:
786797
"""Get the arguments formatted as string.
@@ -910,15 +921,16 @@ def default_value(self, argname):
910921
:raises NoDefault: If there is no default value defined for the
911922
given argument.
912923
"""
913-
args = self.arguments
924+
# Ignore *args and **kwargs
925+
args = list(filter(lambda x: not isinstance(x, Name), self.arguments))
926+
index = _find_arg(argname, self.kwonlyargs)[0]
927+
if index is not None and self.kw_defaults[index] is not None:
928+
return self.kw_defaults[index]
914929
index = _find_arg(argname, args)[0]
915930
if index is not None:
916931
idx = index - (len(args) - len(self.defaults))
917932
if idx >= 0:
918933
return self.defaults[idx]
919-
index = _find_arg(argname, self.kwonlyargs)[0]
920-
if index is not None and self.kw_defaults[index] is not None:
921-
return self.kw_defaults[index]
922934
raise NoDefault(func=self.parent, name=argname)
923935

924936
def is_argument(self, name) -> bool:
@@ -955,7 +967,9 @@ def find_argname(self, argname, rec=DEPRECATED_ARGUMENT_DEFAULT):
955967
stacklevel=2,
956968
)
957969
if self.arguments:
958-
return _find_arg(argname, self.arguments)
970+
index, argument = _find_arg(argname, self.arguments)
971+
if not isinstance(argument, Name):
972+
return index, argument
959973
return None, None
960974

961975
def get_children(self):

astroid/nodes/scoped_nodes/scoped_nodes.py

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -963,11 +963,7 @@ def argnames(self) -> list[str]:
963963
names = [elt.name for elt in self.args.arguments]
964964
else:
965965
names = []
966-
if self.args.vararg:
967-
names.append(self.args.vararg)
968-
names += [elt.name for elt in self.args.kwonlyargs]
969-
if self.args.kwarg:
970-
names.append(self.args.kwarg)
966+
971967
return names
972968

973969
def infer_call_result(
@@ -1280,11 +1276,7 @@ def argnames(self) -> list[str]:
12801276
names = [elt.name for elt in self.args.arguments]
12811277
else:
12821278
names = []
1283-
if self.args.vararg:
1284-
names.append(self.args.vararg)
1285-
names += [elt.name for elt in self.args.kwonlyargs]
1286-
if self.args.kwarg:
1287-
names.append(self.args.kwarg)
1279+
12881280
return names
12891281

12901282
def getattr(

astroid/protocols.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -352,14 +352,17 @@ def _arguments_infer_argname(
352352
# more
353353
from astroid import arguments # pylint: disable=import-outside-toplevel
354354

355-
if not (self.arguments or self.vararg or self.kwarg):
355+
if not self.arguments:
356356
yield util.Uninferable
357357
return
358358

359+
filtered_args = list(
360+
filter(lambda arg: not isinstance(arg, nodes.Name), self.arguments)
361+
)
359362
functype = self.parent.type
360363
# first argument of instance/class method
361364
if (
362-
self.arguments
365+
filtered_args
363366
and getattr(self.arguments[0], "name", None) == name
364367
and functype != "staticmethod"
365368
):
@@ -388,7 +391,7 @@ def _arguments_infer_argname(
388391
if name == self.vararg:
389392
vararg = nodes.const_factory(())
390393
vararg.parent = self
391-
if not self.arguments and self.parent.name == "__init__":
394+
if not filtered_args and self.parent.name == "__init__":
392395
cls = self.parent.parent.scope()
393396
vararg.elts = [cls.instantiate_class()]
394397
yield vararg

tests/test_nodes.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
Uninferable,
2323
bases,
2424
builder,
25+
extract_node,
2526
nodes,
2627
parse,
2728
test_utils,
@@ -1943,3 +1944,28 @@ def test_str_repr_no_warnings(node):
19431944
test_node = node(**args)
19441945
str(test_node)
19451946
repr(test_node)
1947+
1948+
1949+
def test_arguments_contains_all():
1950+
"""Ensure Arguments.arguments actually returns all available arguments"""
1951+
1952+
def manually_get_args(arg_node) -> set:
1953+
names = set()
1954+
if arg_node.args.vararg:
1955+
names.add(arg_node.args.vararg)
1956+
if arg_node.args.kwarg:
1957+
names.add(arg_node.args.kwarg)
1958+
1959+
names.update([x.name for x in arg_node.args.args])
1960+
names.update([x.name for x in arg_node.args.kwonlyargs])
1961+
1962+
return names
1963+
1964+
node = extract_node("""def a(fruit: str, *args, b=None, c=None, **kwargs): ...""")
1965+
assert manually_get_args(node) == {x.name for x in node.args.arguments}
1966+
1967+
node = extract_node("""def a(mango: int, b="banana", c=None, **kwargs): ...""")
1968+
assert manually_get_args(node) == {x.name for x in node.args.arguments}
1969+
1970+
node = extract_node("""def a(self, num = 10, *args): ...""")
1971+
assert manually_get_args(node) == {x.name for x in node.args.arguments}

0 commit comments

Comments
 (0)