# HG changeset patch
# User Julien Cristau <julien.cristau@logilab.fr>
# Date 1374181500 -7200
# Thu Jul 18 23:05:00 2013 +0200
# Node ID 4143143fcdfbba2519bb2fdcb7adcb397f730cd4
# Parent 2630f6a667472a4f8a4e0652759937b31ac4a5f1
Check py3k keyword-only arguments
Take them into account when matching call sites versus function
definitions. Closes #107788
# User Julien Cristau <julien.cristau@logilab.fr>
# Date 1374181500 -7200
# Thu Jul 18 23:05:00 2013 +0200
# Node ID 4143143fcdfbba2519bb2fdcb7adcb397f730cd4
# Parent 2630f6a667472a4f8a4e0652759937b31ac4a5f1
Check py3k keyword-only arguments
Take them into account when matching call sites versus function
definitions. Closes #107788
@@ -67,10 +67,15 @@
1 'E1124': ('Parameter %r passed as both positional and keyword argument', 2 'redundant-keyword-arg', 3 'Used when a function call would result in assigning multiple \ 4 values to a function parameter, one value from a positional \ 5 argument and one from a keyword argument.'), 6 + 'E1125': ('Missing mandatory keyword argument %r', 7 + 'missing-kwoa', 8 + 'Used when a function call doesn\'t pass a mandatory \ 9 + keyword-only argument.', 10 + {'minversion': (3, 0)}), 11 } 12 13 class TypeChecker(BaseChecker): 14 """try to find bugs in the code using type inference 15 """
@@ -314,10 +319,19 @@
16 defval = called.args.defaults[i - num_mandatory_parameters] 17 else: 18 defval = None 19 parameters.append([(name, defval), False]) 20 21 + kwparams = {} 22 + for i, arg in enumerate(called.args.kwonlyargs): 23 + if isinstance(arg, astroid.Keyword): 24 + name = arg.arg 25 + else: 26 + assert isinstance(arg, astroid.AssName) 27 + name = arg.name 28 + kwparams[name] = [called.args.kw_defaults[i], False] 29 + 30 # Match the supplied arguments against the function parameters. 31 32 # 1. Match the positional arguments. 33 for i in range(num_positional_args): 34 if i < len(parameters):
@@ -338,10 +352,16 @@
35 if parameters[i][1]: 36 # Duplicate definition of function parameter. 37 self.add_message('E1124', node=node, args=keyword) 38 else: 39 parameters[i][1] = True 40 + elif keyword in kwparams: 41 + if kwparams[keyword][1]: # XXX is that even possible? 42 + # Duplicate definition of function parameter. 43 + self.add_message('E1124', node=node, args=keyword) 44 + else: 45 + kwparams[keyword][1] = True 46 elif called.args.kwarg is not None: 47 # The keyword argument gets assigned to the **kwargs parameter. 48 pass 49 else: 50 # Unexpected keyword argument.
@@ -382,8 +402,14 @@
51 display_name = '<tuple>' 52 else: 53 display_name = repr(name) 54 self.add_message('E1120', node=node, args=display_name) 55 56 + for name in kwparams: 57 + defval, assigned = kwparams[name] 58 + if defval is None and not assigned: 59 + self.add_message('E1125', node=node, args=name) 60 + 61 + 62 def register(linter): 63 """required method to auto register this checker """ 64 linter.register_checker(TypeChecker(linter))
@@ -0,0 +1,12 @@
65 +# pylint: disable=C0121, C0102 66 +'''A little testscript for PEP 3102 and pylint''' 67 +def function(*, foo): 68 + '''A function for testing''' 69 + print(foo) 70 + 71 +function(foo=1) 72 + 73 +foo = 1 74 +function(foo) 75 + 76 +function(1)
@@ -0,0 +1,5 @@
77 +E: 10: Missing mandatory keyword argument 'foo' 78 +E: 10: Too many positional arguments for function call 79 +E: 12: Missing mandatory keyword argument 'foo' 80 +E: 12: Too many positional arguments for function call 81 +W: 3:function: Redefining name 'foo' from outer scope (line 9)