OLD | NEW |
1 # pylint: disable=W0611 | 1 # pylint: disable=W0611 |
2 # | 2 # |
3 # Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE). | 3 # Copyright (c) 2003-2010 LOGILAB S.A. (Paris, FRANCE). |
4 # http://www.logilab.fr/ -- mailto:contact@logilab.fr | 4 # http://www.logilab.fr/ -- mailto:contact@logilab.fr |
5 # | 5 # |
6 # This program is free software; you can redistribute it and/or modify it under | 6 # This program is free software; you can redistribute it and/or modify it under |
7 # the terms of the GNU General Public License as published by the Free Software | 7 # the terms of the GNU General Public License as published by the Free Software |
8 # Foundation; either version 2 of the License, or (at your option) any later | 8 # Foundation; either version 2 of the License, or (at your option) any later |
9 # version. | 9 # version. |
10 # | 10 # |
11 # This program is distributed in the hope that it will be useful, but WITHOUT | 11 # This program is distributed in the hope that it will be useful, but WITHOUT |
12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | 12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
13 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. | 13 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. |
14 # | 14 # |
15 # You should have received a copy of the GNU General Public License along with | 15 # You should have received a copy of the GNU General Public License along with |
16 # this program; if not, write to the Free Software Foundation, Inc., | 16 # this program; if not, write to the Free Software Foundation, Inc., |
17 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | 17 # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
18 """some functions that may be useful for various checkers | 18 """some functions that may be useful for various checkers |
19 """ | 19 """ |
20 | 20 |
21 import re | |
22 import sys | |
23 import string | 21 import string |
| 22 from logilab import astng |
| 23 from logilab.common.compat import builtins |
| 24 BUILTINS_NAME = builtins.__name__ |
24 | 25 |
25 import astroid | 26 COMP_NODE_TYPES = astng.ListComp, astng.SetComp, astng.DictComp, astng.GenExpr |
26 from astroid import scoped_nodes | |
27 from logilab.common.compat import builtins | |
28 | |
29 BUILTINS_NAME = builtins.__name__ | |
30 COMP_NODE_TYPES = astroid.ListComp, astroid.SetComp, astroid.DictComp, astroid.G
enExpr | |
31 PY3K = sys.version_info[0] == 3 | |
32 | |
33 | |
34 class NoSuchArgumentError(Exception): | |
35 pass | |
36 | 27 |
37 def is_inside_except(node): | 28 def is_inside_except(node): |
38 """Returns true if node is inside the name of an except handler.""" | 29 """Returns true if node is directly inside an exception handler""" |
39 current = node | 30 return isinstance(node.parent, astng.ExceptHandler) |
40 while current and not isinstance(current.parent, astroid.ExceptHandler): | |
41 current = current.parent | |
42 | |
43 return current and current is current.parent.name | |
44 | |
45 | |
46 def get_all_elements(node): | |
47 """Recursively returns all atoms in nested lists and tuples.""" | |
48 if isinstance(node, (astroid.Tuple, astroid.List)): | |
49 for child in node.elts: | |
50 for e in get_all_elements(child): | |
51 yield e | |
52 else: | |
53 yield node | |
54 | 31 |
55 | 32 |
56 def clobber_in_except(node): | 33 def clobber_in_except(node): |
57 """Checks if an assignment node in an except handler clobbers an existing | 34 """Checks if an assignment node in an except handler clobbers an existing |
58 variable. | 35 variable. |
59 | 36 |
60 Returns (True, args for W0623) if assignment clobbers an existing variable, | 37 Returns (True, args for W0623) if assignment clobbers an existing variable, |
61 (False, None) otherwise. | 38 (False, None) otherwise. |
62 """ | 39 """ |
63 if isinstance(node, astroid.AssAttr): | 40 if isinstance(node, astng.AssAttr): |
64 return (True, (node.attrname, 'object %r' % (node.expr.as_string(),))) | 41 return (True, (node.attrname, 'object %r' % (node.expr.name,))) |
65 elif isinstance(node, astroid.AssName): | 42 elif node is not None: |
66 name = node.name | 43 name = node.name |
67 if is_builtin(name): | 44 if is_builtin(name): |
68 return (True, (name, 'builtins')) | 45 return (True, (name, 'builtins')) |
69 else: | 46 else: |
70 stmts = node.lookup(name)[1] | 47 scope, stmts = node.lookup(name) |
71 if (stmts and not isinstance(stmts[0].ass_type(), | 48 if (stmts and |
72 (astroid.Assign, astroid.AugAssign, | 49 not isinstance(stmts[0].ass_type(), |
73 astroid.ExceptHandler))): | 50 (astng.Assign, astng.AugAssign, astng.ExceptHandl
er))): |
74 return (True, (name, 'outer scope (line %s)' % stmts[0].fromline
no)) | 51 return (True, (name, 'outer scope (line %i)' % (stmts[0].lineno,
))) |
75 return (False, None) | 52 return (False, None) |
76 | 53 |
77 | 54 |
78 def safe_infer(node): | 55 def safe_infer(node): |
79 """return the inferred value for the given node. | 56 """return the inferred value for the given node. |
80 Return None if inference failed or if there is some ambiguity (more than | 57 Return None if inference failed or if there is some ambiguity (more than |
81 one node has been inferred) | 58 one node has been inferred) |
82 """ | 59 """ |
83 try: | 60 try: |
84 inferit = node.infer() | 61 inferit = node.infer() |
85 value = inferit.next() | 62 value = inferit.next() |
86 except astroid.InferenceError: | 63 except astng.InferenceError: |
87 return | 64 return |
88 try: | 65 try: |
89 inferit.next() | 66 inferit.next() |
90 return # None if there is ambiguity on the inferred node | 67 return # None if there is ambiguity on the inferred node |
91 except astroid.InferenceError: | |
92 return # there is some kind of ambiguity | |
93 except StopIteration: | 68 except StopIteration: |
94 return value | 69 return value |
95 | 70 |
96 def is_super(node): | 71 def is_super(node): |
97 """return True if the node is referencing the "super" builtin function | 72 """return True if the node is referencing the "super" builtin function |
98 """ | 73 """ |
99 if getattr(node, 'name', None) == 'super' and \ | 74 if getattr(node, 'name', None) == 'super' and \ |
100 node.root().name == BUILTINS_NAME: | 75 node.root().name == BUILTINS_NAME: |
101 return True | 76 return True |
102 return False | 77 return False |
103 | 78 |
104 def is_error(node): | 79 def is_error(node): |
105 """return true if the function does nothing but raising an exception""" | 80 """return true if the function does nothing but raising an exception""" |
106 for child_node in node.get_children(): | 81 for child_node in node.get_children(): |
107 if isinstance(child_node, astroid.Raise): | 82 if isinstance(child_node, astng.Raise): |
108 return True | 83 return True |
109 return False | 84 return False |
110 | 85 |
111 def is_raising(body): | 86 def is_raising(body): |
112 """return true if the given statement node raise an exception""" | 87 """return true if the given statement node raise an exception""" |
113 for node in body: | 88 for node in body: |
114 if isinstance(node, astroid.Raise): | 89 if isinstance(node, astng.Raise): |
115 return True | 90 return True |
116 return False | 91 return False |
117 | 92 |
118 def is_empty(body): | 93 def is_empty(body): |
119 """return true if the given node does nothing but 'pass'""" | 94 """return true if the given node does nothing but 'pass'""" |
120 return len(body) == 1 and isinstance(body[0], astroid.Pass) | 95 return len(body) == 1 and isinstance(body[0], astng.Pass) |
121 | 96 |
122 builtins = builtins.__dict__.copy() | 97 builtins = __builtins__.copy() |
123 SPECIAL_BUILTINS = ('__builtins__',) # '__path__', '__file__') | 98 SPECIAL_BUILTINS = ('__builtins__',) # '__path__', '__file__') |
124 | 99 |
125 def is_builtin_object(node): | |
126 """Returns True if the given node is an object from the __builtin__ module."
"" | |
127 return node and node.root().name == BUILTINS_NAME | |
128 | |
129 def is_builtin(name): # was is_native_builtin | 100 def is_builtin(name): # was is_native_builtin |
130 """return true if <name> could be considered as a builtin defined by python | 101 """return true if <name> could be considered as a builtin defined by python |
131 """ | 102 """ |
132 if name in builtins: | 103 if name in builtins: |
133 return True | 104 return True |
134 if name in SPECIAL_BUILTINS: | 105 if name in SPECIAL_BUILTINS: |
135 return True | 106 return True |
136 return False | 107 return False |
137 | 108 |
138 def is_defined_before(var_node): | 109 def is_defined_before(var_node): |
139 """return True if the variable node is defined by a parent node (list, | 110 """return True if the variable node is defined by a parent node (list, |
140 set, dict, or generator comprehension, lambda) or in a previous sibling | 111 set, dict, or generator comprehension, lambda) or in a previous sibling |
141 node on the same line (statement_defining ; statement_using) | 112 node on the same line (statement_defining ; statement_using) |
142 """ | 113 """ |
143 varname = var_node.name | 114 varname = var_node.name |
144 _node = var_node.parent | 115 _node = var_node.parent |
145 while _node: | 116 while _node: |
146 if isinstance(_node, COMP_NODE_TYPES): | 117 if isinstance(_node, COMP_NODE_TYPES): |
147 for ass_node in _node.nodes_of_class(astroid.AssName): | 118 for ass_node in _node.nodes_of_class(astng.AssName): |
148 if ass_node.name == varname: | 119 if ass_node.name == varname: |
149 return True | 120 return True |
150 elif isinstance(_node, astroid.For): | 121 elif isinstance(_node, astng.For): |
151 for ass_node in _node.target.nodes_of_class(astroid.AssName): | 122 for ass_node in _node.target.nodes_of_class(astng.AssName): |
152 if ass_node.name == varname: | 123 if ass_node.name == varname: |
153 return True | 124 return True |
154 elif isinstance(_node, astroid.With): | 125 elif isinstance(_node, astng.With): |
155 for expr, vars in _node.items: | 126 if _node.vars is None: |
156 if expr.parent_of(var_node): | 127 # quickfix : case in which 'with' is used without 'as' |
157 break | 128 return False |
158 if (vars and | 129 if _node.vars.name == varname: |
159 isinstance(vars, astroid.AssName) and | 130 return True |
160 vars.name == varname): | 131 elif isinstance(_node, (astng.Lambda, astng.Function)): |
161 return True | |
162 elif isinstance(_node, (astroid.Lambda, astroid.Function)): | |
163 if _node.args.is_argument(varname): | 132 if _node.args.is_argument(varname): |
164 return True | 133 return True |
165 if getattr(_node, 'name', None) == varname: | 134 if getattr(_node, 'name', None) == varname: |
166 return True | 135 return True |
167 break | 136 break |
168 elif isinstance(_node, astroid.ExceptHandler): | |
169 if isinstance(_node.name, astroid.AssName): | |
170 ass_node = _node.name | |
171 if ass_node.name == varname: | |
172 return True | |
173 _node = _node.parent | 137 _node = _node.parent |
174 # possibly multiple statements on the same line using semi colon separator | 138 # possibly multiple statements on the same line using semi colon separator |
175 stmt = var_node.statement() | 139 stmt = var_node.statement() |
176 _node = stmt.previous_sibling() | 140 _node = stmt.previous_sibling() |
177 lineno = stmt.fromlineno | 141 lineno = stmt.fromlineno |
178 while _node and _node.fromlineno == lineno: | 142 while _node and _node.fromlineno == lineno: |
179 for ass_node in _node.nodes_of_class(astroid.AssName): | 143 for ass_node in _node.nodes_of_class(astng.AssName): |
180 if ass_node.name == varname: | 144 if ass_node.name == varname: |
181 return True | 145 return True |
182 for imp_node in _node.nodes_of_class((astroid.From, astroid.Import)): | 146 for imp_node in _node.nodes_of_class( (astng.From, astng.Import)): |
183 if varname in [name[1] or name[0] for name in imp_node.names]: | 147 if varname in [name[1] or name[0] for name in imp_node.names]: |
184 return True | 148 return True |
185 _node = _node.previous_sibling() | 149 _node = _node.previous_sibling() |
186 return False | 150 return False |
187 | 151 |
188 def is_func_default(node): | 152 def is_func_default(node): |
189 """return true if the given Name node is used in function default argument's | 153 """return true if the given Name node is used in function default argument's |
190 value | 154 value |
191 """ | 155 """ |
192 parent = node.scope() | 156 parent = node.scope() |
193 if isinstance(parent, astroid.Function): | 157 if isinstance(parent, astng.Function): |
194 for default_node in parent.args.defaults: | 158 for default_node in parent.args.defaults: |
195 for default_name_node in default_node.nodes_of_class(astroid.Name): | 159 for default_name_node in default_node.nodes_of_class(astng.Name): |
196 if default_name_node is node: | 160 if default_name_node is node: |
197 return True | 161 return True |
198 return False | 162 return False |
199 | 163 |
200 def is_func_decorator(node): | 164 def is_func_decorator(node): |
201 """return true if the name is used in function decorator""" | 165 """return true if the name is used in function decorator""" |
202 parent = node.parent | 166 parent = node.parent |
203 while parent is not None: | 167 while parent is not None: |
204 if isinstance(parent, astroid.Decorators): | 168 if isinstance(parent, astng.Decorators): |
205 return True | 169 return True |
206 if (parent.is_statement or | 170 if parent.is_statement or isinstance(parent, astng.Lambda): |
207 isinstance(parent, astroid.Lambda) or | |
208 isinstance(parent, (scoped_nodes.ComprehensionScope, | |
209 scoped_nodes.ListComp))): | |
210 break | 171 break |
211 parent = parent.parent | 172 parent = parent.parent |
212 return False | 173 return False |
213 | 174 |
214 def is_ancestor_name(frame, node): | 175 def is_ancestor_name(frame, node): |
215 """return True if `frame` is a astroid.Class node with `node` in the | 176 """return True if `frame` is a astng.Class node with `node` in the |
216 subtree of its bases attribute | 177 subtree of its bases attribute |
217 """ | 178 """ |
218 try: | 179 try: |
219 bases = frame.bases | 180 bases = frame.bases |
220 except AttributeError: | 181 except AttributeError: |
221 return False | 182 return False |
222 for base in bases: | 183 for base in bases: |
223 if node in base.nodes_of_class(astroid.Name): | 184 if node in base.nodes_of_class(astng.Name): |
224 return True | 185 return True |
225 return False | 186 return False |
226 | 187 |
227 def assign_parent(node): | 188 def assign_parent(node): |
228 """return the higher parent which is not an AssName, Tuple or List node | 189 """return the higher parent which is not an AssName, Tuple or List node |
229 """ | 190 """ |
230 while node and isinstance(node, (astroid.AssName, | 191 while node and isinstance(node, (astng.AssName, |
231 astroid.Tuple, | 192 astng.Tuple, |
232 astroid.List)): | 193 astng.List)): |
233 node = node.parent | 194 node = node.parent |
234 return node | 195 return node |
235 | 196 |
236 def overrides_an_abstract_method(class_node, name): | 197 def overrides_an_abstract_method(class_node, name): |
237 """return True if pnode is a parent of node""" | 198 """return True if pnode is a parent of node""" |
238 for ancestor in class_node.ancestors(): | 199 for ancestor in class_node.ancestors(): |
239 if name in ancestor and isinstance(ancestor[name], astroid.Function) and
\ | 200 if name in ancestor and isinstance(ancestor[name], astng.Function) and \ |
240 ancestor[name].is_abstract(pass_is_abstract=False): | 201 ancestor[name].is_abstract(pass_is_abstract=False): |
241 return True | 202 return True |
242 return False | 203 return False |
243 | 204 |
244 def overrides_a_method(class_node, name): | 205 def overrides_a_method(class_node, name): |
245 """return True if <name> is a method overridden from an ancestor""" | 206 """return True if <name> is a method overridden from an ancestor""" |
246 for ancestor in class_node.ancestors(): | 207 for ancestor in class_node.ancestors(): |
247 if name in ancestor and isinstance(ancestor[name], astroid.Function): | 208 if name in ancestor and isinstance(ancestor[name], astng.Function): |
248 return True | 209 return True |
249 return False | 210 return False |
250 | 211 |
251 PYMETHODS = set(('__new__', '__init__', '__del__', '__hash__', | 212 PYMETHODS = set(('__new__', '__init__', '__del__', '__hash__', |
252 '__str__', '__repr__', | 213 '__str__', '__repr__', |
253 '__len__', '__iter__', | 214 '__len__', '__iter__', |
254 '__delete__', '__get__', '__set__', | 215 '__delete__', '__get__', '__set__', |
255 '__getitem__', '__setitem__', '__delitem__', '__contains__', | 216 '__getitem__', '__setitem__', '__delitem__', '__contains__', |
256 '__getattribute__', '__getattr__', '__setattr__', '__delattr__'
, | 217 '__getattribute__', '__getattr__', '__setattr__', '__delattr__'
, |
257 '__call__', | 218 '__call__', |
258 '__enter__', '__exit__', | 219 '__enter__', '__exit__', |
259 '__cmp__', '__ge__', '__gt__', '__le__', '__lt__', '__eq__', | 220 '__cmp__', '__ge__', '__gt__', '__le__', '__lt__', '__eq__', |
260 '__nonzero__', '__neg__', '__invert__', | 221 '__nonzero__', '__neg__', '__invert__', |
261 '__mul__', '__imul__', '__rmul__', | 222 '__mul__', '__imul__', '__rmul__', |
262 '__div__', '__idiv__', '__rdiv__', | 223 '__div__', '__idiv__', '__rdiv__', |
263 '__add__', '__iadd__', '__radd__', | 224 '__add__', '__iadd__', '__radd__', |
264 '__sub__', '__isub__', '__rsub__', | 225 '__sub__', '__isub__', '__rsub__', |
265 '__pow__', '__ipow__', '__rpow__', | 226 '__pow__', '__ipow__', '__rpow__', |
266 '__mod__', '__imod__', '__rmod__', | 227 '__mod__', '__imod__', '__rmod__', |
267 '__and__', '__iand__', '__rand__', | 228 '__and__', '__iand__', '__rand__', |
268 '__or__', '__ior__', '__ror__', | 229 '__or__', '__ior__', '__ror__', |
269 '__xor__', '__ixor__', '__rxor__', | 230 '__xor__', '__ixor__', '__rxor__', |
270 # XXX To be continued | 231 # XXX To be continued |
271 )) | 232 )) |
272 | 233 |
273 def check_messages(*messages): | 234 def check_messages(*messages): |
274 """decorator to store messages that are handled by a checker method""" | 235 """decorator to store messages that are handled by a checker method""" |
275 | 236 |
276 def store_messages(func): | 237 def store_messages(func): |
277 func.checks_msgs = messages | 238 func.checks_msgs = messages |
278 return func | 239 return func |
279 return store_messages | 240 return store_messages |
280 | 241 |
281 class IncompleteFormatString(Exception): | 242 class IncompleteFormatString(Exception): |
(...skipping 15 matching lines...) Expand all Loading... |
297 parse error occurs.""" | 258 parse error occurs.""" |
298 keys = set() | 259 keys = set() |
299 num_args = 0 | 260 num_args = 0 |
300 def next_char(i): | 261 def next_char(i): |
301 i += 1 | 262 i += 1 |
302 if i == len(format_string): | 263 if i == len(format_string): |
303 raise IncompleteFormatString | 264 raise IncompleteFormatString |
304 return (i, format_string[i]) | 265 return (i, format_string[i]) |
305 i = 0 | 266 i = 0 |
306 while i < len(format_string): | 267 while i < len(format_string): |
307 char = format_string[i] | 268 c = format_string[i] |
308 if char == '%': | 269 if c == '%': |
309 i, char = next_char(i) | 270 i, c = next_char(i) |
310 # Parse the mapping key (optional). | 271 # Parse the mapping key (optional). |
311 key = None | 272 key = None |
312 if char == '(': | 273 if c == '(': |
313 depth = 1 | 274 depth = 1 |
314 i, char = next_char(i) | 275 i, c = next_char(i) |
315 key_start = i | 276 key_start = i |
316 while depth != 0: | 277 while depth != 0: |
317 if char == '(': | 278 if c == '(': |
318 depth += 1 | 279 depth += 1 |
319 elif char == ')': | 280 elif c == ')': |
320 depth -= 1 | 281 depth -= 1 |
321 i, char = next_char(i) | 282 i, c = next_char(i) |
322 key_end = i - 1 | 283 key_end = i - 1 |
323 key = format_string[key_start:key_end] | 284 key = format_string[key_start:key_end] |
324 | 285 |
325 # Parse the conversion flags (optional). | 286 # Parse the conversion flags (optional). |
326 while char in '#0- +': | 287 while c in '#0- +': |
327 i, char = next_char(i) | 288 i, c = next_char(i) |
328 # Parse the minimum field width (optional). | 289 # Parse the minimum field width (optional). |
329 if char == '*': | 290 if c == '*': |
330 num_args += 1 | 291 num_args += 1 |
331 i, char = next_char(i) | 292 i, c = next_char(i) |
332 else: | 293 else: |
333 while char in string.digits: | 294 while c in string.digits: |
334 i, char = next_char(i) | 295 i, c = next_char(i) |
335 # Parse the precision (optional). | 296 # Parse the precision (optional). |
336 if char == '.': | 297 if c == '.': |
337 i, char = next_char(i) | 298 i, c = next_char(i) |
338 if char == '*': | 299 if c == '*': |
339 num_args += 1 | 300 num_args += 1 |
340 i, char = next_char(i) | 301 i, c = next_char(i) |
341 else: | 302 else: |
342 while char in string.digits: | 303 while c in string.digits: |
343 i, char = next_char(i) | 304 i, c = next_char(i) |
344 # Parse the length modifier (optional). | 305 # Parse the length modifier (optional). |
345 if char in 'hlL': | 306 if c in 'hlL': |
346 i, char = next_char(i) | 307 i, c = next_char(i) |
347 # Parse the conversion type (mandatory). | 308 # Parse the conversion type (mandatory). |
348 if PY3K: | 309 if c not in 'diouxXeEfFgGcrs%': |
349 flags = 'diouxXeEfFgGcrs%a' | |
350 else: | |
351 flags = 'diouxXeEfFgGcrs%' | |
352 if char not in flags: | |
353 raise UnsupportedFormatCharacter(i) | 310 raise UnsupportedFormatCharacter(i) |
354 if key: | 311 if key: |
355 keys.add(key) | 312 keys.add(key) |
356 elif char != '%': | 313 elif c != '%': |
357 num_args += 1 | 314 num_args += 1 |
358 i += 1 | 315 i += 1 |
359 return keys, num_args | 316 return keys, num_args |
360 | |
361 | |
362 def is_attr_protected(attrname): | |
363 """return True if attribute name is protected (start with _ and some other | |
364 details), False otherwise. | |
365 """ | |
366 return attrname[0] == '_' and not attrname == '_' and not ( | |
367 attrname.startswith('__') and attrname.endswith('__')) | |
368 | |
369 def node_frame_class(node): | |
370 """return klass node for a method node (or a staticmethod or a | |
371 classmethod), return null otherwise | |
372 """ | |
373 klass = node.frame() | |
374 | |
375 while klass is not None and not isinstance(klass, astroid.Class): | |
376 if klass.parent is None: | |
377 klass = None | |
378 else: | |
379 klass = klass.parent.frame() | |
380 | |
381 return klass | |
382 | |
383 def is_super_call(expr): | |
384 """return True if expression node is a function call and if function name | |
385 is super. Check before that you're in a method. | |
386 """ | |
387 return (isinstance(expr, astroid.CallFunc) and | |
388 isinstance(expr.func, astroid.Name) and | |
389 expr.func.name == 'super') | |
390 | |
391 def is_attr_private(attrname): | |
392 """Check that attribute name is private (at least two leading underscores, | |
393 at most one trailing underscore) | |
394 """ | |
395 regex = re.compile('^_{2,}.*[^_]+_?$') | |
396 return regex.match(attrname) | |
397 | |
398 def get_argument_from_call(callfunc_node, position=None, keyword=None): | |
399 """Returns the specified argument from a function call. | |
400 | |
401 :param callfunc_node: Node representing a function call to check. | |
402 :param int position: position of the argument. | |
403 :param str keyword: the keyword of the argument. | |
404 | |
405 :returns: The node representing the argument, None if the argument is not fo
und. | |
406 :raises ValueError: if both position and keyword are None. | |
407 :raises NoSuchArgumentError: if no argument at the provided position or with | |
408 the provided keyword. | |
409 """ | |
410 if position is None and keyword is None: | |
411 raise ValueError('Must specify at least one of: position or keyword.') | |
412 try: | |
413 if position is not None and not isinstance(callfunc_node.args[position],
astroid.Keyword): | |
414 return callfunc_node.args[position] | |
415 except IndexError, error: | |
416 raise NoSuchArgumentError(error) | |
417 if keyword: | |
418 for arg in callfunc_node.args: | |
419 if isinstance(arg, astroid.Keyword) and arg.arg == keyword: | |
420 return arg.value | |
421 raise NoSuchArgumentError | |
OLD | NEW |