summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaudiu Popa <pcmanticore@gmail.com>2015-10-09 00:35:34 +0300
committerClaudiu Popa <pcmanticore@gmail.com>2015-10-09 00:35:34 +0300
commit86b7ed8432640c34c8bec27bf739056e25507024 (patch)
tree592deb689e17e177ef218d335045714d06ae7768
parentbb6863c2aae937fa97f98fa927dd27148f952e40 (diff)
downloadpylint-86b7ed8432640c34c8bec27bf739056e25507024.tar.gz
Add a new error, 'repeated-keyword', when a keyword argument is passed multiple times into a function call.
This is similar with redundant-keyword-arg, but it's mildly different that it needs to be a separate error. This change also uses a CallSite for understanding the arguments that were passed into a function call, since with this we can make sense about multiple starred arguments passed into (PEP 448).
-rw-r--r--ChangeLog6
-rw-r--r--pylint/checkers/typecheck.py23
-rw-r--r--pylint/test/functional/arguments.py14
-rw-r--r--pylint/test/functional/repeated_keyword.py13
-rw-r--r--pylint/test/functional/repeated_keyword.txt1
-rw-r--r--pylint/test/functional/unpacking_generalizations.py29
-rw-r--r--pylint/test/functional/unpacking_generalizations.rc2
-rw-r--r--pylint/test/functional/unpacking_generalizations.txt6
8 files changed, 88 insertions, 6 deletions
diff --git a/ChangeLog b/ChangeLog
index e1e71c0..8d227a0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -327,6 +327,12 @@ ChangeLog for Pylint
* Don't emit 'assigning-non-slot' for descriptors. Closes issue #652.
+ * Add a new error, 'repeated-keyword', when a keyword argument is passed
+ multiple times into a function call.
+
+ This is similar with redundant-keyword-arg, but it's mildly different
+ that it needs to be a separate error.
+
2015-03-14 -- 1.4.3
diff --git a/pylint/checkers/typecheck.py b/pylint/checkers/typecheck.py
index b6ba698..865508e 100644
--- a/pylint/checkers/typecheck.py
+++ b/pylint/checkers/typecheck.py
@@ -24,6 +24,7 @@ import sys
import astroid
import astroid.context
+import astroid.arguments
from astroid import bases
from astroid import exceptions
from astroid import objects
@@ -143,6 +144,9 @@ MSGS = {
'unsupported-binary-operation',
'Emitted when a binary arithmetic operation between two '
'operands is not supported.'),
+ 'E1132': ('Got multiple values for keyword argument %r in function call',
+ 'repeated-keyword',
+ 'Emitted when a function call got multiple values for a keyword.'),
}
# builtin sequence types in Python 2 and 3.
@@ -458,9 +462,9 @@ accessed. Python regular expressions are accepted.'}
"""
# Build the set of keyword arguments, checking for duplicate keywords,
# and count the positional arguments.
- keyword_args = {keyword.arg for keyword in node.keywords or []
- if keyword.arg is not None}
- num_positional_args = len(node.args)
+ call_site = astroid.arguments.CallSite.from_call(node)
+ num_positional_args = len(call_site.positional_arguments)
+ keyword_args = list(call_site.keyword_arguments.keys())
called = helpers.safe_infer(node.func)
# only function, generator and object defining __call__ are allowed
@@ -482,8 +486,17 @@ accessed. Python regular expressions are accepted.'}
return
if len(called.argnames()) != len(set(called.argnames())):
- # Duplicate parameter name (see E9801). We can't really make sense
- # of the function call in this case, so just return.
+ # Duplicate parameter name (see duplicate-argument). We can't really
+ # make sense of the function call in this case, so just return.
+ return
+
+ # Warn about duplicated keyword arguments, such as `f=24, **{'f': 24}`
+ for keyword in call_site.duplicated_keywords:
+ self.add_message('repeated-keyword',
+ node=node, args=(keyword, ))
+
+ if call_site.has_invalid_arguments() or call_site.has_invalid_keywords():
+ # Can't make sense of this.
return
# Analyze the list of formal parameters.
diff --git a/pylint/test/functional/arguments.py b/pylint/test/functional/arguments.py
index f8db22c..9f0dc63 100644
--- a/pylint/test/functional/arguments.py
+++ b/pylint/test/functional/arguments.py
@@ -1,4 +1,4 @@
-# pylint: disable=too-few-public-methods, no-absolute-import,missing-docstring
+# pylint: disable=too-few-public-methods, no-absolute-import,missing-docstring,import-error
"""Test function argument checker"""
def decorator(fun):
@@ -142,3 +142,15 @@ class Issue642(object):
attr = 0
def __str__(self):
return "{self.attr}".format(self=self)
+
+# These should not emit anything regarding the number of arguments,
+# since they have something invalid.
+from ala_bala_portocola import unknown
+
+function_1_arg(*unknown)
+function_1_arg(1, *2)
+function_1_arg(1, 2, 3, **unknown)
+function_1_arg(4, 5, **1)
+function_1_arg(5, 6, **{unknown: 1})
+function_1_arg(**{object: 1})
+function_1_arg(**{1: 2})
diff --git a/pylint/test/functional/repeated_keyword.py b/pylint/test/functional/repeated_keyword.py
new file mode 100644
index 0000000..786c53b
--- /dev/null
+++ b/pylint/test/functional/repeated_keyword.py
@@ -0,0 +1,13 @@
+"""Check that a keyword is not repeated in a function call
+
+This is somehow related to redundant-keyword, but it's not the same.
+"""
+
+# pylint: disable=missing-docstring, invalid-name
+
+def test(a, b):
+ return a, b
+
+test(1, 24)
+test(1, b=24, **{})
+test(1, b=24, **{'b': 24}) # [repeated-keyword]
diff --git a/pylint/test/functional/repeated_keyword.txt b/pylint/test/functional/repeated_keyword.txt
new file mode 100644
index 0000000..1344b15
--- /dev/null
+++ b/pylint/test/functional/repeated_keyword.txt
@@ -0,0 +1 @@
+repeated-keyword:13::"Got multiple values for keyword argument 'b' in function call" \ No newline at end of file
diff --git a/pylint/test/functional/unpacking_generalizations.py b/pylint/test/functional/unpacking_generalizations.py
new file mode 100644
index 0000000..1c5fb16
--- /dev/null
+++ b/pylint/test/functional/unpacking_generalizations.py
@@ -0,0 +1,29 @@
+"""Various tests for unpacking generalizations added in Python 3.5"""
+
+# pylint: disable=missing-docstring, invalid-name
+
+def func_variadic_args(*args):
+ return args
+
+
+def func_variadic_positional_args(a, b, *args):
+ return a, b, args
+
+def func_positional_args(a, b, c, d):
+ return a, b, c, d
+
+
+func_variadic_args(*(2, 3), *(3, 4), *(4, 5))
+func_variadic_args(1, 2, *(2, 3), 2, 3, *(4, 5))
+func_variadic_positional_args(1, 2, *(4, 5), *(5, 6))
+func_variadic_positional_args(*(2, 3), *(4, 5), *(5, 6))
+func_variadic_positional_args(*(2, 3))
+func_variadic_positional_args(*(2, 3, 4))
+func_variadic_positional_args(1, 2, 3, *(3, 4))
+
+func_positional_args(*(2, 3, 4), *(2, 3)) # [too-many-function-args]
+func_positional_args(*(1, 2), 3) # [no-value-for-parameter]
+func_positional_args(1, *(2, ), 3, *(4, 5)) # [too-many-function-args]
+func_positional_args(1, 2, c=24, d=32, **{'d': 32}) # [repeated-keyword]
+# +1: [repeated-keyword,repeated-keyword]
+func_positional_args(1, 2, c=24, **{'c': 34, 'd': 33}, **{'d': 24})
diff --git a/pylint/test/functional/unpacking_generalizations.rc b/pylint/test/functional/unpacking_generalizations.rc
new file mode 100644
index 0000000..03004f2
--- /dev/null
+++ b/pylint/test/functional/unpacking_generalizations.rc
@@ -0,0 +1,2 @@
+[testoptions]
+min_pyver=3.5 \ No newline at end of file
diff --git a/pylint/test/functional/unpacking_generalizations.txt b/pylint/test/functional/unpacking_generalizations.txt
new file mode 100644
index 0000000..d92fcc9
--- /dev/null
+++ b/pylint/test/functional/unpacking_generalizations.txt
@@ -0,0 +1,6 @@
+too-many-function-args:24::Too many positional arguments for function call
+no-value-for-parameter:25::"No value for argument 'd' in function call"
+too-many-function-args:26::Too many positional arguments for function call
+repeated-keyword:27::Got multiple values for keyword argument 'd' in function call
+repeated-keyword:29::Got multiple values for keyword argument 'c' in function call
+repeated-keyword:29::Got multiple values for keyword argument 'd' in function call \ No newline at end of file