gvsig-scripting / org.gvsig.scripting / trunk / org.gvsig.scripting / org.gvsig.scripting.app / org.gvsig.scripting.app.mainplugin / src / main / resources-plugin / scripting / lib / pylint / checkers / utils.py @ 745
History | View | Annotate | Download (26 KB)
1 |
# pylint: disable=W0611
|
---|---|
2 |
#
|
3 |
# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
|
4 |
# http://www.logilab.fr/ -- mailto:contact@logilab.fr
|
5 |
#
|
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
|
8 |
# Foundation; either version 2 of the License, or (at your option) any later
|
9 |
# version.
|
10 |
#
|
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
|
13 |
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
14 |
#
|
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.,
|
17 |
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
18 |
"""some functions that may be useful for various checkers
|
19 |
"""
|
20 |
import functools |
21 |
import re |
22 |
import sys |
23 |
import string |
24 |
import warnings |
25 |
|
26 |
import six |
27 |
from six.moves import map, builtins # pylint: disable=redefined-builtin |
28 |
|
29 |
import astroid |
30 |
from astroid import scoped_nodes |
31 |
|
32 |
BUILTINS_NAME = builtins.__name__ |
33 |
COMP_NODE_TYPES = (astroid.ListComp, astroid.SetComp, |
34 |
astroid.DictComp, astroid.GeneratorExp) |
35 |
PY3K = sys.version_info[0] == 3 |
36 |
|
37 |
if not PY3K: |
38 |
EXCEPTIONS_MODULE = "exceptions"
|
39 |
else:
|
40 |
EXCEPTIONS_MODULE = "builtins"
|
41 |
ABC_METHODS = set(('abc.abstractproperty', 'abc.abstractmethod', |
42 |
'abc.abstractclassmethod', 'abc.abstractstaticmethod')) |
43 |
ITER_METHOD = '__iter__'
|
44 |
NEXT_METHOD = 'next' if six.PY2 else '__next__' |
45 |
GETITEM_METHOD = '__getitem__'
|
46 |
CONTAINS_METHOD = '__contains__'
|
47 |
KEYS_METHOD = 'keys'
|
48 |
|
49 |
# Dictionary which maps the number of expected parameters a
|
50 |
# special method can have to a set of special methods.
|
51 |
# The following keys are used to denote the parameters restrictions:
|
52 |
#
|
53 |
# * None: variable number of parameters
|
54 |
# * number: exactly that number of parameters
|
55 |
# * tuple: this are the odd ones. Basically it means that the function
|
56 |
# can work with any number of arguments from that tuple,
|
57 |
# although it's best to implement it in order to accept
|
58 |
# all of them.
|
59 |
_SPECIAL_METHODS_PARAMS = { |
60 |
None: ('__new__', '__init__', '__call__'), |
61 |
|
62 |
0: ('__del__', '__repr__', '__str__', '__bytes__', '__hash__', '__bool__', |
63 |
'__dir__', '__len__', '__length_hint__', '__iter__', '__reversed__', |
64 |
'__neg__', '__pos__', '__abs__', '__invert__', '__complex__', '__int__', |
65 |
'__float__', '__neg__', '__pos__', '__abs__', '__complex__', '__int__', |
66 |
'__float__', '__index__', '__enter__', '__aenter__', '__getnewargs_ex__', |
67 |
'__getnewargs__', '__getstate__', '__reduce__', '__copy__', |
68 |
'__unicode__', '__nonzero__', '__await__', '__aiter__', '__anext__'), |
69 |
|
70 |
1: ('__format__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', |
71 |
'__ge__', '__getattr__', '__getattribute__', '__delattr__', |
72 |
'__delete__', '__instancecheck__', '__subclasscheck__', |
73 |
'__getitem__', '__missing__', '__delitem__', '__contains__', |
74 |
'__add__', '__sub__', '__mul__', '__truediv__', '__floordiv__', |
75 |
'__mod__', '__divmod__', '__lshift__', '__rshift__', '__and__', |
76 |
'__xor__', '__or__', '__radd__', '__rsub__', '__rmul__', '__rtruediv__', |
77 |
'__rmod__', '__rdivmod__', '__rpow__', '__rlshift__', '__rrshift__', |
78 |
'__rand__', '__rxor__', '__ror__', '__iadd__', '__isub__', '__imul__', |
79 |
'__itruediv__', '__ifloordiv__', '__imod__', '__ilshift__', |
80 |
'__irshift__', '__iand__', '__ixor__', '__ior__', '__ipow__', |
81 |
'__setstate__', '__reduce_ex__', '__deepcopy__', '__cmp__', |
82 |
'__matmul__', '__rmatmul__'), |
83 |
|
84 |
2: ('__setattr__', '__get__', '__set__', '__setitem__'), |
85 |
|
86 |
3: ('__exit__', '__aexit__'), |
87 |
|
88 |
(0, 1): ('__round__', ), |
89 |
} |
90 |
|
91 |
SPECIAL_METHODS_PARAMS = { |
92 |
name: params |
93 |
for params, methods in _SPECIAL_METHODS_PARAMS.items() |
94 |
for name in methods |
95 |
} |
96 |
PYMETHODS = set(SPECIAL_METHODS_PARAMS)
|
97 |
|
98 |
|
99 |
class NoSuchArgumentError(Exception): |
100 |
pass
|
101 |
|
102 |
def is_inside_except(node): |
103 |
"""Returns true if node is inside the name of an except handler."""
|
104 |
current = node |
105 |
while current and not isinstance(current.parent, astroid.ExceptHandler): |
106 |
current = current.parent |
107 |
|
108 |
return current and current is current.parent.name |
109 |
|
110 |
|
111 |
def get_all_elements(node): |
112 |
"""Recursively returns all atoms in nested lists and tuples."""
|
113 |
if isinstance(node, (astroid.Tuple, astroid.List)): |
114 |
for child in node.elts: |
115 |
for e in get_all_elements(child): |
116 |
yield e
|
117 |
else:
|
118 |
yield node
|
119 |
|
120 |
|
121 |
def clobber_in_except(node): |
122 |
"""Checks if an assignment node in an except handler clobbers an existing
|
123 |
variable.
|
124 |
|
125 |
Returns (True, args for W0623) if assignment clobbers an existing variable,
|
126 |
(False, None) otherwise.
|
127 |
"""
|
128 |
if isinstance(node, astroid.AssignAttr): |
129 |
return (True, (node.attrname, 'object %r' % (node.expr.as_string(),))) |
130 |
elif isinstance(node, astroid.AssignName): |
131 |
name = node.name |
132 |
if is_builtin(name):
|
133 |
return (True, (name, 'builtins')) |
134 |
else:
|
135 |
stmts = node.lookup(name)[1]
|
136 |
if (stmts and not isinstance(stmts[0].assign_type(), |
137 |
(astroid.Assign, astroid.AugAssign, |
138 |
astroid.ExceptHandler))): |
139 |
return (True, (name, 'outer scope (line %s)' % stmts[0].fromlineno)) |
140 |
return (False, None) |
141 |
|
142 |
|
143 |
def is_super(node): |
144 |
"""return True if the node is referencing the "super" builtin function
|
145 |
"""
|
146 |
if getattr(node, 'name', None) == 'super' and \ |
147 |
node.root().name == BUILTINS_NAME: |
148 |
return True |
149 |
return False |
150 |
|
151 |
def is_error(node): |
152 |
"""return true if the function does nothing but raising an exception"""
|
153 |
for child_node in node.get_children(): |
154 |
if isinstance(child_node, astroid.Raise): |
155 |
return True |
156 |
return False |
157 |
|
158 |
def is_raising(body): |
159 |
"""return true if the given statement node raise an exception"""
|
160 |
for node in body: |
161 |
if isinstance(node, astroid.Raise): |
162 |
return True |
163 |
return False |
164 |
|
165 |
builtins = builtins.__dict__.copy() |
166 |
SPECIAL_BUILTINS = ('__builtins__',) # '__path__', '__file__') |
167 |
|
168 |
def is_builtin_object(node): |
169 |
"""Returns True if the given node is an object from the __builtin__ module."""
|
170 |
return node and node.root().name == BUILTINS_NAME |
171 |
|
172 |
def is_builtin(name): |
173 |
"""return true if <name> could be considered as a builtin defined by python
|
174 |
"""
|
175 |
return name in builtins or name in SPECIAL_BUILTINS |
176 |
|
177 |
def is_defined_before(var_node): |
178 |
"""return True if the variable node is defined by a parent node (list,
|
179 |
set, dict, or generator comprehension, lambda) or in a previous sibling
|
180 |
node on the same line (statement_defining ; statement_using)
|
181 |
"""
|
182 |
varname = var_node.name |
183 |
_node = var_node.parent |
184 |
while _node:
|
185 |
if isinstance(_node, COMP_NODE_TYPES): |
186 |
for ass_node in _node.nodes_of_class(astroid.AssignName): |
187 |
if ass_node.name == varname:
|
188 |
return True |
189 |
elif isinstance(_node, astroid.For): |
190 |
for ass_node in _node.target.nodes_of_class(astroid.AssignName): |
191 |
if ass_node.name == varname:
|
192 |
return True |
193 |
elif isinstance(_node, astroid.With): |
194 |
for expr, ids in _node.items: |
195 |
if expr.parent_of(var_node):
|
196 |
break
|
197 |
if (ids and |
198 |
isinstance(ids, astroid.AssignName) and |
199 |
ids.name == varname): |
200 |
return True |
201 |
elif isinstance(_node, (astroid.Lambda, astroid.FunctionDef)): |
202 |
if _node.args.is_argument(varname):
|
203 |
# If the name is found inside a default value
|
204 |
# of a function, then let the search continue
|
205 |
# in the parent's tree.
|
206 |
if _node.args.parent_of(var_node):
|
207 |
try:
|
208 |
_node.args.default_value(varname) |
209 |
_node = _node.parent |
210 |
continue
|
211 |
except astroid.NoDefault:
|
212 |
pass
|
213 |
return True |
214 |
if getattr(_node, 'name', None) == varname: |
215 |
return True |
216 |
break
|
217 |
elif isinstance(_node, astroid.ExceptHandler): |
218 |
if isinstance(_node.name, astroid.AssignName): |
219 |
ass_node = _node.name |
220 |
if ass_node.name == varname:
|
221 |
return True |
222 |
_node = _node.parent |
223 |
# possibly multiple statements on the same line using semi colon separator
|
224 |
stmt = var_node.statement() |
225 |
_node = stmt.previous_sibling() |
226 |
lineno = stmt.fromlineno |
227 |
while _node and _node.fromlineno == lineno: |
228 |
for ass_node in _node.nodes_of_class(astroid.AssignName): |
229 |
if ass_node.name == varname:
|
230 |
return True |
231 |
for imp_node in _node.nodes_of_class((astroid.ImportFrom, astroid.Import)): |
232 |
if varname in [name[1] or name[0] for name in imp_node.names]: |
233 |
return True |
234 |
_node = _node.previous_sibling() |
235 |
return False |
236 |
|
237 |
def is_func_default(node): |
238 |
"""return true if the given Name node is used in function default argument's
|
239 |
value
|
240 |
"""
|
241 |
parent = node.scope() |
242 |
if isinstance(parent, astroid.FunctionDef): |
243 |
for default_node in parent.args.defaults: |
244 |
for default_name_node in default_node.nodes_of_class(astroid.Name): |
245 |
if default_name_node is node: |
246 |
return True |
247 |
return False |
248 |
|
249 |
def is_func_decorator(node): |
250 |
"""return true if the name is used in function decorator"""
|
251 |
parent = node.parent |
252 |
while parent is not None: |
253 |
if isinstance(parent, astroid.Decorators): |
254 |
return True |
255 |
if (parent.is_statement or |
256 |
isinstance(parent, astroid.Lambda) or |
257 |
isinstance(parent, (scoped_nodes.ComprehensionScope,
|
258 |
scoped_nodes.ListComp))): |
259 |
break
|
260 |
parent = parent.parent |
261 |
return False |
262 |
|
263 |
def is_ancestor_name(frame, node): |
264 |
"""return True if `frame` is a astroid.Class node with `node` in the
|
265 |
subtree of its bases attribute
|
266 |
"""
|
267 |
try:
|
268 |
bases = frame.bases |
269 |
except AttributeError: |
270 |
return False |
271 |
for base in bases: |
272 |
if node in base.nodes_of_class(astroid.Name): |
273 |
return True |
274 |
return False |
275 |
|
276 |
def assign_parent(node): |
277 |
"""return the higher parent which is not an AssName, Tuple or List node
|
278 |
"""
|
279 |
while node and isinstance(node, (astroid.AssignName, |
280 |
astroid.Tuple, |
281 |
astroid.List)): |
282 |
node = node.parent |
283 |
return node
|
284 |
|
285 |
|
286 |
def overrides_a_method(class_node, name): |
287 |
"""return True if <name> is a method overridden from an ancestor"""
|
288 |
for ancestor in class_node.ancestors(): |
289 |
if name in ancestor and isinstance(ancestor[name], astroid.FunctionDef): |
290 |
return True |
291 |
return False |
292 |
|
293 |
def check_messages(*messages): |
294 |
"""decorator to store messages that are handled by a checker method"""
|
295 |
|
296 |
def store_messages(func): |
297 |
func.checks_msgs = messages |
298 |
return func
|
299 |
return store_messages
|
300 |
|
301 |
class IncompleteFormatString(Exception): |
302 |
"""A format string ended in the middle of a format specifier."""
|
303 |
pass
|
304 |
|
305 |
class UnsupportedFormatCharacter(Exception): |
306 |
"""A format character in a format string is not one of the supported
|
307 |
format characters."""
|
308 |
def __init__(self, index): |
309 |
Exception.__init__(self, index) |
310 |
self.index = index
|
311 |
|
312 |
def parse_format_string(format_string): |
313 |
"""Parses a format string, returning a tuple of (keys, num_args), where keys
|
314 |
is the set of mapping keys in the format string, and num_args is the number
|
315 |
of arguments required by the format string. Raises
|
316 |
IncompleteFormatString or UnsupportedFormatCharacter if a
|
317 |
parse error occurs."""
|
318 |
keys = set()
|
319 |
num_args = 0
|
320 |
def next_char(i): |
321 |
i += 1
|
322 |
if i == len(format_string): |
323 |
raise IncompleteFormatString
|
324 |
return (i, format_string[i])
|
325 |
i = 0
|
326 |
while i < len(format_string): |
327 |
char = format_string[i] |
328 |
if char == '%': |
329 |
i, char = next_char(i) |
330 |
# Parse the mapping key (optional).
|
331 |
key = None
|
332 |
if char == '(': |
333 |
depth = 1
|
334 |
i, char = next_char(i) |
335 |
key_start = i |
336 |
while depth != 0: |
337 |
if char == '(': |
338 |
depth += 1
|
339 |
elif char == ')': |
340 |
depth -= 1
|
341 |
i, char = next_char(i) |
342 |
key_end = i - 1
|
343 |
key = format_string[key_start:key_end] |
344 |
|
345 |
# Parse the conversion flags (optional).
|
346 |
while char in '#0- +': |
347 |
i, char = next_char(i) |
348 |
# Parse the minimum field width (optional).
|
349 |
if char == '*': |
350 |
num_args += 1
|
351 |
i, char = next_char(i) |
352 |
else:
|
353 |
while char in string.digits: |
354 |
i, char = next_char(i) |
355 |
# Parse the precision (optional).
|
356 |
if char == '.': |
357 |
i, char = next_char(i) |
358 |
if char == '*': |
359 |
num_args += 1
|
360 |
i, char = next_char(i) |
361 |
else:
|
362 |
while char in string.digits: |
363 |
i, char = next_char(i) |
364 |
# Parse the length modifier (optional).
|
365 |
if char in 'hlL': |
366 |
i, char = next_char(i) |
367 |
# Parse the conversion type (mandatory).
|
368 |
if PY3K:
|
369 |
flags = 'diouxXeEfFgGcrs%a'
|
370 |
else:
|
371 |
flags = 'diouxXeEfFgGcrs%'
|
372 |
if char not in flags: |
373 |
raise UnsupportedFormatCharacter(i)
|
374 |
if key:
|
375 |
keys.add(key) |
376 |
elif char != '%': |
377 |
num_args += 1
|
378 |
i += 1
|
379 |
return keys, num_args
|
380 |
|
381 |
|
382 |
def is_attr_protected(attrname): |
383 |
"""return True if attribute name is protected (start with _ and some other
|
384 |
details), False otherwise.
|
385 |
"""
|
386 |
return attrname[0] == '_' and attrname != '_' and not ( |
387 |
attrname.startswith('__') and attrname.endswith('__')) |
388 |
|
389 |
def node_frame_class(node): |
390 |
"""return klass node for a method node (or a staticmethod or a
|
391 |
classmethod), return null otherwise
|
392 |
"""
|
393 |
klass = node.frame() |
394 |
|
395 |
while klass is not None and not isinstance(klass, astroid.ClassDef): |
396 |
if klass.parent is None: |
397 |
klass = None
|
398 |
else:
|
399 |
klass = klass.parent.frame() |
400 |
|
401 |
return klass
|
402 |
|
403 |
|
404 |
def is_attr_private(attrname): |
405 |
"""Check that attribute name is private (at least two leading underscores,
|
406 |
at most one trailing underscore)
|
407 |
"""
|
408 |
regex = re.compile('^_{2,}.*[^_]+_?$')
|
409 |
return regex.match(attrname)
|
410 |
|
411 |
def get_argument_from_call(callfunc_node, position=None, keyword=None): |
412 |
"""Returns the specified argument from a function call.
|
413 |
|
414 |
:param callfunc_node: Node representing a function call to check.
|
415 |
:param int position: position of the argument.
|
416 |
:param str keyword: the keyword of the argument.
|
417 |
|
418 |
:returns: The node representing the argument, None if the argument is not found.
|
419 |
:raises ValueError: if both position and keyword are None.
|
420 |
:raises NoSuchArgumentError: if no argument at the provided position or with
|
421 |
the provided keyword.
|
422 |
"""
|
423 |
if position is None and keyword is None: |
424 |
raise ValueError('Must specify at least one of: position or keyword.') |
425 |
if position is not None: |
426 |
try:
|
427 |
return callfunc_node.args[position]
|
428 |
except IndexError: |
429 |
pass
|
430 |
if keyword and callfunc_node.keywords: |
431 |
for arg in callfunc_node.keywords: |
432 |
if arg.arg == keyword:
|
433 |
return arg.value
|
434 |
|
435 |
raise NoSuchArgumentError
|
436 |
|
437 |
def inherit_from_std_ex(node): |
438 |
"""
|
439 |
Return true if the given class node is subclass of
|
440 |
exceptions.Exception.
|
441 |
"""
|
442 |
if node.name in ('Exception', 'BaseException') \ |
443 |
and node.root().name == EXCEPTIONS_MODULE:
|
444 |
return True |
445 |
return any(inherit_from_std_ex(parent) |
446 |
for parent in node.ancestors(recurs=False)) |
447 |
|
448 |
def error_of_type(handler, error_type): |
449 |
"""
|
450 |
Check if the given exception handler catches
|
451 |
the given error_type.
|
452 |
|
453 |
The *handler* parameter is a node, representing an ExceptHandler node.
|
454 |
The *error_type* can be an exception, such as AttributeError, or it
|
455 |
can be a tuple of errors.
|
456 |
The function will return True if the handler catches any of the
|
457 |
given errors.
|
458 |
"""
|
459 |
if not isinstance(error_type, tuple): |
460 |
error_type = (error_type, ) |
461 |
expected_errors = {error.__name__ for error in error_type} |
462 |
return handler.catch(expected_errors)
|
463 |
|
464 |
|
465 |
def is_import_error(handler): |
466 |
warnings.warn("This function is deprecated in the favour of "
|
467 |
"error_of_type. It will be removed in Pylint 1.6.",
|
468 |
DeprecationWarning, stacklevel=2) |
469 |
return error_of_type(handler, ImportError) |
470 |
|
471 |
|
472 |
def decorated_with_property(node): |
473 |
""" Detect if the given function node is decorated with a property. """
|
474 |
if not node.decorators: |
475 |
return False |
476 |
for decorator in node.decorators.nodes: |
477 |
if not isinstance(decorator, astroid.Name): |
478 |
continue
|
479 |
try:
|
480 |
for infered in decorator.infer(): |
481 |
if isinstance(infered, astroid.ClassDef): |
482 |
if (infered.root().name == BUILTINS_NAME and |
483 |
infered.name == 'property'):
|
484 |
return True |
485 |
for ancestor in infered.ancestors(): |
486 |
if (ancestor.name == 'property' and |
487 |
ancestor.root().name == BUILTINS_NAME): |
488 |
return True |
489 |
except astroid.InferenceError:
|
490 |
pass
|
491 |
|
492 |
|
493 |
def decorated_with(func, qnames): |
494 |
"""Determine if the `func` node has a decorator with the qualified name `qname`."""
|
495 |
decorators = func.decorators.nodes if func.decorators else [] |
496 |
for decorator_node in decorators: |
497 |
dec = safe_infer(decorator_node) |
498 |
if dec and dec.qname() in qnames: |
499 |
return True |
500 |
|
501 |
|
502 |
def unimplemented_abstract_methods(node, is_abstract_cb=None): |
503 |
"""
|
504 |
Get the unimplemented abstract methods for the given *node*.
|
505 |
|
506 |
A method can be considered abstract if the callback *is_abstract_cb*
|
507 |
returns a ``True`` value. The check defaults to verifying that
|
508 |
a method is decorated with abstract methods.
|
509 |
The function will work only for new-style classes. For old-style
|
510 |
classes, it will simply return an empty dictionary.
|
511 |
For the rest of them, it will return a dictionary of abstract method
|
512 |
names and their inferred objects.
|
513 |
"""
|
514 |
if is_abstract_cb is None: |
515 |
is_abstract_cb = functools.partial( |
516 |
decorated_with, qnames=ABC_METHODS) |
517 |
visited = {} |
518 |
try:
|
519 |
mro = reversed(node.mro())
|
520 |
except NotImplementedError: |
521 |
# Old style class, it will not have a mro.
|
522 |
return {}
|
523 |
except astroid.ResolveError:
|
524 |
# Probably inconsistent hierarchy, don'try
|
525 |
# to figure this out here.
|
526 |
return {}
|
527 |
for ancestor in mro: |
528 |
for obj in ancestor.values(): |
529 |
infered = obj |
530 |
if isinstance(obj, astroid.AssignName): |
531 |
infered = safe_infer(obj) |
532 |
if not infered: |
533 |
# Might be an abstract function,
|
534 |
# but since we don't have enough information
|
535 |
# in order to take this decision, we're taking
|
536 |
# the *safe* decision instead.
|
537 |
if obj.name in visited: |
538 |
del visited[obj.name]
|
539 |
continue
|
540 |
if not isinstance(infered, astroid.FunctionDef): |
541 |
if obj.name in visited: |
542 |
del visited[obj.name]
|
543 |
if isinstance(infered, astroid.FunctionDef): |
544 |
# It's critical to use the original name,
|
545 |
# since after inferring, an object can be something
|
546 |
# else than expected, as in the case of the
|
547 |
# following assignment.
|
548 |
#
|
549 |
# class A:
|
550 |
# def keys(self): pass
|
551 |
# __iter__ = keys
|
552 |
abstract = is_abstract_cb(infered) |
553 |
if abstract:
|
554 |
visited[obj.name] = infered |
555 |
elif not abstract and obj.name in visited: |
556 |
del visited[obj.name]
|
557 |
return visited
|
558 |
|
559 |
|
560 |
def node_ignores_exception(node, exception): |
561 |
"""Check if the node is in a TryExcept which handles the given exception.
|
562 |
|
563 |
This will also return ``True`` if the node is protected by an `except Exception`
|
564 |
or by a bare except clause.
|
565 |
"""
|
566 |
current = node |
567 |
ignores = (astroid.ExceptHandler, astroid.TryExcept) |
568 |
while current and not isinstance(current.parent, ignores): |
569 |
current = current.parent |
570 |
|
571 |
func = functools.partial(error_of_type, |
572 |
error_type=(Exception, exception))
|
573 |
if current and isinstance(current.parent, astroid.TryExcept): |
574 |
handles_errors = any(map(func, current.parent.handlers)) |
575 |
empty_handlers = any(handler.type is None |
576 |
for handler in current.parent.handlers) |
577 |
if handles_errors or empty_handlers: |
578 |
return True |
579 |
return False |
580 |
|
581 |
|
582 |
def class_is_abstract(node): |
583 |
"""return true if the given class node should be considered as an abstract
|
584 |
class
|
585 |
"""
|
586 |
for method in node.methods(): |
587 |
if method.parent.frame() is node: |
588 |
if method.is_abstract(pass_is_abstract=False): |
589 |
return True |
590 |
return False |
591 |
|
592 |
|
593 |
def _hasattr(value, attr): |
594 |
try:
|
595 |
value.getattr(attr) |
596 |
return True |
597 |
except astroid.NotFoundError:
|
598 |
return False |
599 |
|
600 |
|
601 |
def is_comprehension(node): |
602 |
comprehensions = (astroid.ListComp, |
603 |
astroid.SetComp, |
604 |
astroid.DictComp, |
605 |
astroid.GeneratorExp) |
606 |
return isinstance(node, comprehensions) |
607 |
|
608 |
|
609 |
def _supports_mapping_protocol(value): |
610 |
return _hasattr(value, GETITEM_METHOD) and _hasattr(value, KEYS_METHOD) |
611 |
|
612 |
|
613 |
def _supports_membership_test_protocol(value): |
614 |
return _hasattr(value, CONTAINS_METHOD)
|
615 |
|
616 |
|
617 |
def _supports_iteration_protocol(value): |
618 |
return _hasattr(value, ITER_METHOD) or _hasattr(value, GETITEM_METHOD) |
619 |
|
620 |
|
621 |
def _supports_subscript_protocol(value): |
622 |
return _hasattr(value, GETITEM_METHOD)
|
623 |
|
624 |
|
625 |
def _is_abstract_class_name(name): |
626 |
lname = name.lower() |
627 |
is_mixin = lname.endswith('mixin')
|
628 |
is_abstract = lname.startswith('abstract')
|
629 |
is_base = lname.startswith('base') or lname.endswith('base') |
630 |
return is_mixin or is_abstract or is_base |
631 |
|
632 |
|
633 |
def is_inside_abstract_class(node): |
634 |
while node is not None: |
635 |
if isinstance(node, astroid.ClassDef): |
636 |
if class_is_abstract(node):
|
637 |
return True |
638 |
name = getattr(node, 'name', None) |
639 |
if name is not None and _is_abstract_class_name(name): |
640 |
return True |
641 |
node = node.parent |
642 |
return False |
643 |
|
644 |
|
645 |
def is_iterable(value): |
646 |
if isinstance(value, astroid.ClassDef): |
647 |
if not has_known_bases(value): |
648 |
return True |
649 |
# classobj can only be iterable if it has an iterable metaclass
|
650 |
meta = value.metaclass() |
651 |
if meta is not None: |
652 |
if _supports_iteration_protocol(meta):
|
653 |
return True |
654 |
if isinstance(value, astroid.Instance): |
655 |
if not has_known_bases(value): |
656 |
return True |
657 |
if _supports_iteration_protocol(value):
|
658 |
return True |
659 |
return False |
660 |
|
661 |
|
662 |
def is_mapping(value): |
663 |
if isinstance(value, astroid.ClassDef): |
664 |
if not has_known_bases(value): |
665 |
return True |
666 |
# classobj can only be a mapping if it has a metaclass is mapping
|
667 |
meta = value.metaclass() |
668 |
if meta is not None: |
669 |
if _supports_mapping_protocol(meta):
|
670 |
return True |
671 |
if isinstance(value, astroid.Instance): |
672 |
if not has_known_bases(value): |
673 |
return True |
674 |
if _supports_mapping_protocol(value):
|
675 |
return True |
676 |
return False |
677 |
|
678 |
|
679 |
def supports_membership_test(value): |
680 |
if isinstance(value, astroid.ClassDef): |
681 |
if not has_known_bases(value): |
682 |
return True |
683 |
meta = value.metaclass() |
684 |
if meta is not None and _supports_membership_test_protocol(meta): |
685 |
return True |
686 |
if isinstance(value, astroid.Instance): |
687 |
if not has_known_bases(value): |
688 |
return True |
689 |
if _supports_membership_test_protocol(value):
|
690 |
return True |
691 |
return is_iterable(value)
|
692 |
|
693 |
|
694 |
def supports_subscript(value): |
695 |
if isinstance(value, astroid.ClassDef): |
696 |
if not has_known_bases(value): |
697 |
return False |
698 |
meta = value.metaclass() |
699 |
if meta is not None and _supports_subscript_protocol(meta): |
700 |
return True |
701 |
if isinstance(value, astroid.Instance): |
702 |
if not has_known_bases(value): |
703 |
return True |
704 |
if _supports_subscript_protocol(value):
|
705 |
return True |
706 |
return False |
707 |
|
708 |
# TODO(cpopa): deprecate these or leave them as aliases?
|
709 |
def safe_infer(node, context=None): |
710 |
"""Return the inferred value for the given node.
|
711 |
|
712 |
Return None if inference failed or if there is some ambiguity (more than
|
713 |
one node has been inferred).
|
714 |
"""
|
715 |
try:
|
716 |
inferit = node.infer(context=context) |
717 |
value = next(inferit)
|
718 |
except astroid.InferenceError:
|
719 |
return
|
720 |
try:
|
721 |
next(inferit)
|
722 |
return # None if there is ambiguity on the inferred node |
723 |
except astroid.InferenceError:
|
724 |
return # there is some kind of ambiguity |
725 |
except StopIteration: |
726 |
return value
|
727 |
|
728 |
|
729 |
def has_known_bases(klass, context=None): |
730 |
"""Return true if all base classes of a class could be inferred."""
|
731 |
try:
|
732 |
return klass._all_bases_known
|
733 |
except AttributeError: |
734 |
pass
|
735 |
for base in klass.bases: |
736 |
result = safe_infer(base, context=context) |
737 |
# TODO: check for A->B->A->B pattern in class structure too?
|
738 |
if (not isinstance(result, astroid.ClassDef) or |
739 |
result is klass or |
740 |
not has_known_bases(result, context=context)):
|
741 |
klass._all_bases_known = False
|
742 |
return False |
743 |
klass._all_bases_known = True
|
744 |
return True |