OLD | NEW |
1 # pylint: disable=W0611 | 1 # pylint: disable=W0611 |
2 # | 2 # |
3 # Copyright (c) 2003-2010 LOGILAB S.A. (Paris, FRANCE). | 3 # Copyright (c) 2003-2013 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 # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | 17 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 |
21 import string | 23 import string |
22 from logilab import astng | 24 |
| 25 import astroid |
| 26 from astroid import scoped_nodes |
23 from logilab.common.compat import builtins | 27 from logilab.common.compat import builtins |
| 28 |
24 BUILTINS_NAME = builtins.__name__ | 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 |
25 | 32 |
26 COMP_NODE_TYPES = astng.ListComp, astng.SetComp, astng.DictComp, astng.GenExpr | 33 |
| 34 class NoSuchArgumentError(Exception): |
| 35 pass |
27 | 36 |
28 def is_inside_except(node): | 37 def is_inside_except(node): |
29 """Returns true if node is directly inside an exception handler""" | 38 """Returns true if node is inside the name of an except handler.""" |
30 return isinstance(node.parent, astng.ExceptHandler) | 39 current = node |
| 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 |
31 | 54 |
32 | 55 |
33 def clobber_in_except(node): | 56 def clobber_in_except(node): |
34 """Checks if an assignment node in an except handler clobbers an existing | 57 """Checks if an assignment node in an except handler clobbers an existing |
35 variable. | 58 variable. |
36 | 59 |
37 Returns (True, args for W0623) if assignment clobbers an existing variable, | 60 Returns (True, args for W0623) if assignment clobbers an existing variable, |
38 (False, None) otherwise. | 61 (False, None) otherwise. |
39 """ | 62 """ |
40 if isinstance(node, astng.AssAttr): | 63 if isinstance(node, astroid.AssAttr): |
41 return (True, (node.attrname, 'object %r' % (node.expr.name,))) | 64 return (True, (node.attrname, 'object %r' % (node.expr.as_string(),))) |
42 elif node is not None: | 65 elif isinstance(node, astroid.AssName): |
43 name = node.name | 66 name = node.name |
44 if is_builtin(name): | 67 if is_builtin(name): |
45 return (True, (name, 'builtins')) | 68 return (True, (name, 'builtins')) |
46 else: | 69 else: |
47 scope, stmts = node.lookup(name) | 70 stmts = node.lookup(name)[1] |
48 if (stmts and | 71 if (stmts and not isinstance(stmts[0].ass_type(), |
49 not isinstance(stmts[0].ass_type(), | 72 (astroid.Assign, astroid.AugAssign, |
50 (astng.Assign, astng.AugAssign, astng.ExceptHandl
er))): | 73 astroid.ExceptHandler))): |
51 return (True, (name, 'outer scope (line %i)' % (stmts[0].lineno,
))) | 74 return (True, (name, 'outer scope (line %s)' % stmts[0].fromline
no)) |
52 return (False, None) | 75 return (False, None) |
53 | 76 |
54 | 77 |
55 def safe_infer(node): | 78 def safe_infer(node): |
56 """return the inferred value for the given node. | 79 """return the inferred value for the given node. |
57 Return None if inference failed or if there is some ambiguity (more than | 80 Return None if inference failed or if there is some ambiguity (more than |
58 one node has been inferred) | 81 one node has been inferred) |
59 """ | 82 """ |
60 try: | 83 try: |
61 inferit = node.infer() | 84 inferit = node.infer() |
62 value = inferit.next() | 85 value = inferit.next() |
63 except astng.InferenceError: | 86 except astroid.InferenceError: |
64 return | 87 return |
65 try: | 88 try: |
66 inferit.next() | 89 inferit.next() |
67 return # None if there is ambiguity on the inferred node | 90 return # None if there is ambiguity on the inferred node |
| 91 except astroid.InferenceError: |
| 92 return # there is some kind of ambiguity |
68 except StopIteration: | 93 except StopIteration: |
69 return value | 94 return value |
70 | 95 |
71 def is_super(node): | 96 def is_super(node): |
72 """return True if the node is referencing the "super" builtin function | 97 """return True if the node is referencing the "super" builtin function |
73 """ | 98 """ |
74 if getattr(node, 'name', None) == 'super' and \ | 99 if getattr(node, 'name', None) == 'super' and \ |
75 node.root().name == BUILTINS_NAME: | 100 node.root().name == BUILTINS_NAME: |
76 return True | 101 return True |
77 return False | 102 return False |
78 | 103 |
79 def is_error(node): | 104 def is_error(node): |
80 """return true if the function does nothing but raising an exception""" | 105 """return true if the function does nothing but raising an exception""" |
81 for child_node in node.get_children(): | 106 for child_node in node.get_children(): |
82 if isinstance(child_node, astng.Raise): | 107 if isinstance(child_node, astroid.Raise): |
83 return True | 108 return True |
84 return False | 109 return False |
85 | 110 |
86 def is_raising(body): | 111 def is_raising(body): |
87 """return true if the given statement node raise an exception""" | 112 """return true if the given statement node raise an exception""" |
88 for node in body: | 113 for node in body: |
89 if isinstance(node, astng.Raise): | 114 if isinstance(node, astroid.Raise): |
90 return True | 115 return True |
91 return False | 116 return False |
92 | 117 |
93 def is_empty(body): | 118 def is_empty(body): |
94 """return true if the given node does nothing but 'pass'""" | 119 """return true if the given node does nothing but 'pass'""" |
95 return len(body) == 1 and isinstance(body[0], astng.Pass) | 120 return len(body) == 1 and isinstance(body[0], astroid.Pass) |
96 | 121 |
97 builtins = __builtins__.copy() | 122 builtins = builtins.__dict__.copy() |
98 SPECIAL_BUILTINS = ('__builtins__',) # '__path__', '__file__') | 123 SPECIAL_BUILTINS = ('__builtins__',) # '__path__', '__file__') |
99 | 124 |
| 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 |
100 def is_builtin(name): # was is_native_builtin | 129 def is_builtin(name): # was is_native_builtin |
101 """return true if <name> could be considered as a builtin defined by python | 130 """return true if <name> could be considered as a builtin defined by python |
102 """ | 131 """ |
103 if name in builtins: | 132 if name in builtins: |
104 return True | 133 return True |
105 if name in SPECIAL_BUILTINS: | 134 if name in SPECIAL_BUILTINS: |
106 return True | 135 return True |
107 return False | 136 return False |
108 | 137 |
109 def is_defined_before(var_node): | 138 def is_defined_before(var_node): |
110 """return True if the variable node is defined by a parent node (list, | 139 """return True if the variable node is defined by a parent node (list, |
111 set, dict, or generator comprehension, lambda) or in a previous sibling | 140 set, dict, or generator comprehension, lambda) or in a previous sibling |
112 node on the same line (statement_defining ; statement_using) | 141 node on the same line (statement_defining ; statement_using) |
113 """ | 142 """ |
114 varname = var_node.name | 143 varname = var_node.name |
115 _node = var_node.parent | 144 _node = var_node.parent |
116 while _node: | 145 while _node: |
117 if isinstance(_node, COMP_NODE_TYPES): | 146 if isinstance(_node, COMP_NODE_TYPES): |
118 for ass_node in _node.nodes_of_class(astng.AssName): | 147 for ass_node in _node.nodes_of_class(astroid.AssName): |
119 if ass_node.name == varname: | 148 if ass_node.name == varname: |
120 return True | 149 return True |
121 elif isinstance(_node, astng.For): | 150 elif isinstance(_node, astroid.For): |
122 for ass_node in _node.target.nodes_of_class(astng.AssName): | 151 for ass_node in _node.target.nodes_of_class(astroid.AssName): |
123 if ass_node.name == varname: | 152 if ass_node.name == varname: |
124 return True | 153 return True |
125 elif isinstance(_node, astng.With): | 154 elif isinstance(_node, astroid.With): |
126 if _node.vars is None: | 155 for expr, vars in _node.items: |
127 # quickfix : case in which 'with' is used without 'as' | 156 if expr.parent_of(var_node): |
128 return False | 157 break |
129 if _node.vars.name == varname: | 158 if (vars and |
130 return True | 159 isinstance(vars, astroid.AssName) and |
131 elif isinstance(_node, (astng.Lambda, astng.Function)): | 160 vars.name == varname): |
| 161 return True |
| 162 elif isinstance(_node, (astroid.Lambda, astroid.Function)): |
132 if _node.args.is_argument(varname): | 163 if _node.args.is_argument(varname): |
133 return True | 164 return True |
134 if getattr(_node, 'name', None) == varname: | 165 if getattr(_node, 'name', None) == varname: |
135 return True | 166 return True |
136 break | 167 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 |
137 _node = _node.parent | 173 _node = _node.parent |
138 # possibly multiple statements on the same line using semi colon separator | 174 # possibly multiple statements on the same line using semi colon separator |
139 stmt = var_node.statement() | 175 stmt = var_node.statement() |
140 _node = stmt.previous_sibling() | 176 _node = stmt.previous_sibling() |
141 lineno = stmt.fromlineno | 177 lineno = stmt.fromlineno |
142 while _node and _node.fromlineno == lineno: | 178 while _node and _node.fromlineno == lineno: |
143 for ass_node in _node.nodes_of_class(astng.AssName): | 179 for ass_node in _node.nodes_of_class(astroid.AssName): |
144 if ass_node.name == varname: | 180 if ass_node.name == varname: |
145 return True | 181 return True |
146 for imp_node in _node.nodes_of_class( (astng.From, astng.Import)): | 182 for imp_node in _node.nodes_of_class((astroid.From, astroid.Import)): |
147 if varname in [name[1] or name[0] for name in imp_node.names]: | 183 if varname in [name[1] or name[0] for name in imp_node.names]: |
148 return True | 184 return True |
149 _node = _node.previous_sibling() | 185 _node = _node.previous_sibling() |
150 return False | 186 return False |
151 | 187 |
152 def is_func_default(node): | 188 def is_func_default(node): |
153 """return true if the given Name node is used in function default argument's | 189 """return true if the given Name node is used in function default argument's |
154 value | 190 value |
155 """ | 191 """ |
156 parent = node.scope() | 192 parent = node.scope() |
157 if isinstance(parent, astng.Function): | 193 if isinstance(parent, astroid.Function): |
158 for default_node in parent.args.defaults: | 194 for default_node in parent.args.defaults: |
159 for default_name_node in default_node.nodes_of_class(astng.Name): | 195 for default_name_node in default_node.nodes_of_class(astroid.Name): |
160 if default_name_node is node: | 196 if default_name_node is node: |
161 return True | 197 return True |
162 return False | 198 return False |
163 | 199 |
164 def is_func_decorator(node): | 200 def is_func_decorator(node): |
165 """return true if the name is used in function decorator""" | 201 """return true if the name is used in function decorator""" |
166 parent = node.parent | 202 parent = node.parent |
167 while parent is not None: | 203 while parent is not None: |
168 if isinstance(parent, astng.Decorators): | 204 if isinstance(parent, astroid.Decorators): |
169 return True | 205 return True |
170 if parent.is_statement or isinstance(parent, astng.Lambda): | 206 if (parent.is_statement or |
| 207 isinstance(parent, astroid.Lambda) or |
| 208 isinstance(parent, (scoped_nodes.ComprehensionScope, |
| 209 scoped_nodes.ListComp))): |
171 break | 210 break |
172 parent = parent.parent | 211 parent = parent.parent |
173 return False | 212 return False |
174 | 213 |
175 def is_ancestor_name(frame, node): | 214 def is_ancestor_name(frame, node): |
176 """return True if `frame` is a astng.Class node with `node` in the | 215 """return True if `frame` is a astroid.Class node with `node` in the |
177 subtree of its bases attribute | 216 subtree of its bases attribute |
178 """ | 217 """ |
179 try: | 218 try: |
180 bases = frame.bases | 219 bases = frame.bases |
181 except AttributeError: | 220 except AttributeError: |
182 return False | 221 return False |
183 for base in bases: | 222 for base in bases: |
184 if node in base.nodes_of_class(astng.Name): | 223 if node in base.nodes_of_class(astroid.Name): |
185 return True | 224 return True |
186 return False | 225 return False |
187 | 226 |
188 def assign_parent(node): | 227 def assign_parent(node): |
189 """return the higher parent which is not an AssName, Tuple or List node | 228 """return the higher parent which is not an AssName, Tuple or List node |
190 """ | 229 """ |
191 while node and isinstance(node, (astng.AssName, | 230 while node and isinstance(node, (astroid.AssName, |
192 astng.Tuple, | 231 astroid.Tuple, |
193 astng.List)): | 232 astroid.List)): |
194 node = node.parent | 233 node = node.parent |
195 return node | 234 return node |
196 | 235 |
197 def overrides_an_abstract_method(class_node, name): | 236 def overrides_an_abstract_method(class_node, name): |
198 """return True if pnode is a parent of node""" | 237 """return True if pnode is a parent of node""" |
199 for ancestor in class_node.ancestors(): | 238 for ancestor in class_node.ancestors(): |
200 if name in ancestor and isinstance(ancestor[name], astng.Function) and \ | 239 if name in ancestor and isinstance(ancestor[name], astroid.Function) and
\ |
201 ancestor[name].is_abstract(pass_is_abstract=False): | 240 ancestor[name].is_abstract(pass_is_abstract=False): |
202 return True | 241 return True |
203 return False | 242 return False |
204 | 243 |
205 def overrides_a_method(class_node, name): | 244 def overrides_a_method(class_node, name): |
206 """return True if <name> is a method overridden from an ancestor""" | 245 """return True if <name> is a method overridden from an ancestor""" |
207 for ancestor in class_node.ancestors(): | 246 for ancestor in class_node.ancestors(): |
208 if name in ancestor and isinstance(ancestor[name], astng.Function): | 247 if name in ancestor and isinstance(ancestor[name], astroid.Function): |
209 return True | 248 return True |
210 return False | 249 return False |
211 | 250 |
212 PYMETHODS = set(('__new__', '__init__', '__del__', '__hash__', | 251 PYMETHODS = set(('__new__', '__init__', '__del__', '__hash__', |
213 '__str__', '__repr__', | 252 '__str__', '__repr__', |
214 '__len__', '__iter__', | 253 '__len__', '__iter__', |
215 '__delete__', '__get__', '__set__', | 254 '__delete__', '__get__', '__set__', |
216 '__getitem__', '__setitem__', '__delitem__', '__contains__', | 255 '__getitem__', '__setitem__', '__delitem__', '__contains__', |
217 '__getattribute__', '__getattr__', '__setattr__', '__delattr__'
, | 256 '__getattribute__', '__getattr__', '__setattr__', '__delattr__'
, |
218 '__call__', | 257 '__call__', |
219 '__enter__', '__exit__', | 258 '__enter__', '__exit__', |
220 '__cmp__', '__ge__', '__gt__', '__le__', '__lt__', '__eq__', | 259 '__cmp__', '__ge__', '__gt__', '__le__', '__lt__', '__eq__', |
221 '__nonzero__', '__neg__', '__invert__', | 260 '__nonzero__', '__neg__', '__invert__', |
222 '__mul__', '__imul__', '__rmul__', | 261 '__mul__', '__imul__', '__rmul__', |
223 '__div__', '__idiv__', '__rdiv__', | 262 '__div__', '__idiv__', '__rdiv__', |
224 '__add__', '__iadd__', '__radd__', | 263 '__add__', '__iadd__', '__radd__', |
225 '__sub__', '__isub__', '__rsub__', | 264 '__sub__', '__isub__', '__rsub__', |
226 '__pow__', '__ipow__', '__rpow__', | 265 '__pow__', '__ipow__', '__rpow__', |
227 '__mod__', '__imod__', '__rmod__', | 266 '__mod__', '__imod__', '__rmod__', |
228 '__and__', '__iand__', '__rand__', | 267 '__and__', '__iand__', '__rand__', |
229 '__or__', '__ior__', '__ror__', | 268 '__or__', '__ior__', '__ror__', |
230 '__xor__', '__ixor__', '__rxor__', | 269 '__xor__', '__ixor__', '__rxor__', |
231 # XXX To be continued | 270 # XXX To be continued |
232 )) | 271 )) |
233 | 272 |
234 def check_messages(*messages): | 273 def check_messages(*messages): |
235 """decorator to store messages that are handled by a checker method""" | 274 """decorator to store messages that are handled by a checker method""" |
236 | 275 |
237 def store_messages(func): | 276 def store_messages(func): |
238 func.checks_msgs = messages | 277 func.checks_msgs = messages |
239 return func | 278 return func |
240 return store_messages | 279 return store_messages |
241 | 280 |
242 class IncompleteFormatString(Exception): | 281 class IncompleteFormatString(Exception): |
(...skipping 15 matching lines...) Expand all Loading... |
258 parse error occurs.""" | 297 parse error occurs.""" |
259 keys = set() | 298 keys = set() |
260 num_args = 0 | 299 num_args = 0 |
261 def next_char(i): | 300 def next_char(i): |
262 i += 1 | 301 i += 1 |
263 if i == len(format_string): | 302 if i == len(format_string): |
264 raise IncompleteFormatString | 303 raise IncompleteFormatString |
265 return (i, format_string[i]) | 304 return (i, format_string[i]) |
266 i = 0 | 305 i = 0 |
267 while i < len(format_string): | 306 while i < len(format_string): |
268 c = format_string[i] | 307 char = format_string[i] |
269 if c == '%': | 308 if char == '%': |
270 i, c = next_char(i) | 309 i, char = next_char(i) |
271 # Parse the mapping key (optional). | 310 # Parse the mapping key (optional). |
272 key = None | 311 key = None |
273 if c == '(': | 312 if char == '(': |
274 depth = 1 | 313 depth = 1 |
275 i, c = next_char(i) | 314 i, char = next_char(i) |
276 key_start = i | 315 key_start = i |
277 while depth != 0: | 316 while depth != 0: |
278 if c == '(': | 317 if char == '(': |
279 depth += 1 | 318 depth += 1 |
280 elif c == ')': | 319 elif char == ')': |
281 depth -= 1 | 320 depth -= 1 |
282 i, c = next_char(i) | 321 i, char = next_char(i) |
283 key_end = i - 1 | 322 key_end = i - 1 |
284 key = format_string[key_start:key_end] | 323 key = format_string[key_start:key_end] |
285 | 324 |
286 # Parse the conversion flags (optional). | 325 # Parse the conversion flags (optional). |
287 while c in '#0- +': | 326 while char in '#0- +': |
288 i, c = next_char(i) | 327 i, char = next_char(i) |
289 # Parse the minimum field width (optional). | 328 # Parse the minimum field width (optional). |
290 if c == '*': | 329 if char == '*': |
291 num_args += 1 | 330 num_args += 1 |
292 i, c = next_char(i) | 331 i, char = next_char(i) |
293 else: | 332 else: |
294 while c in string.digits: | 333 while char in string.digits: |
295 i, c = next_char(i) | 334 i, char = next_char(i) |
296 # Parse the precision (optional). | 335 # Parse the precision (optional). |
297 if c == '.': | 336 if char == '.': |
298 i, c = next_char(i) | 337 i, char = next_char(i) |
299 if c == '*': | 338 if char == '*': |
300 num_args += 1 | 339 num_args += 1 |
301 i, c = next_char(i) | 340 i, char = next_char(i) |
302 else: | 341 else: |
303 while c in string.digits: | 342 while char in string.digits: |
304 i, c = next_char(i) | 343 i, char = next_char(i) |
305 # Parse the length modifier (optional). | 344 # Parse the length modifier (optional). |
306 if c in 'hlL': | 345 if char in 'hlL': |
307 i, c = next_char(i) | 346 i, char = next_char(i) |
308 # Parse the conversion type (mandatory). | 347 # Parse the conversion type (mandatory). |
309 if c not in 'diouxXeEfFgGcrs%': | 348 if PY3K: |
| 349 flags = 'diouxXeEfFgGcrs%a' |
| 350 else: |
| 351 flags = 'diouxXeEfFgGcrs%' |
| 352 if char not in flags: |
310 raise UnsupportedFormatCharacter(i) | 353 raise UnsupportedFormatCharacter(i) |
311 if key: | 354 if key: |
312 keys.add(key) | 355 keys.add(key) |
313 elif c != '%': | 356 elif char != '%': |
314 num_args += 1 | 357 num_args += 1 |
315 i += 1 | 358 i += 1 |
316 return keys, num_args | 359 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 |