summaryrefslogtreecommitdiff
path: root/checkers/utils.py
diff options
context:
space:
mode:
authorIonel Cristian Maries <contact@ionelmc.ro>2015-02-14 18:13:20 +0200
committerIonel Cristian Maries <contact@ionelmc.ro>2015-02-14 18:13:20 +0200
commit6d8412476a296b3a3691af1ffabcb672d9a4920f (patch)
treee358c7e886ff4d67d0efc6263f0472655efddfff /checkers/utils.py
parent0369bd6a914af3ad92ce53eac3786bf8de785f7f (diff)
downloadpylint-6d8412476a296b3a3691af1ffabcb672d9a4920f.tar.gz
Move all package files to a pylint package.
Diffstat (limited to 'checkers/utils.py')
-rw-r--r--checkers/utils.py564
1 files changed, 0 insertions, 564 deletions
diff --git a/checkers/utils.py b/checkers/utils.py
deleted file mode 100644
index 2cb01d5..0000000
--- a/checkers/utils.py
+++ /dev/null
@@ -1,564 +0,0 @@
-# pylint: disable=W0611
-#
-# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
-# http://www.logilab.fr/ -- mailto:contact@logilab.fr
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of the GNU General Public License as published by the Free Software
-# Foundation; either version 2 of the License, or (at your option) any later
-# version.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-"""some functions that may be useful for various checkers
-"""
-
-import re
-import sys
-import string
-
-import astroid
-from astroid import scoped_nodes
-from logilab.common.compat import builtins
-
-BUILTINS_NAME = builtins.__name__
-COMP_NODE_TYPES = astroid.ListComp, astroid.SetComp, astroid.DictComp, astroid.GenExpr
-PY3K = sys.version_info[0] == 3
-
-if not PY3K:
- EXCEPTIONS_MODULE = "exceptions"
-else:
- EXCEPTIONS_MODULE = "builtins"
-ABC_METHODS = set(('abc.abstractproperty', 'abc.abstractmethod',
- 'abc.abstractclassmethod', 'abc.abstractstaticmethod'))
-
-
-class NoSuchArgumentError(Exception):
- pass
-
-def is_inside_except(node):
- """Returns true if node is inside the name of an except handler."""
- current = node
- while current and not isinstance(current.parent, astroid.ExceptHandler):
- current = current.parent
-
- return current and current is current.parent.name
-
-
-def get_all_elements(node):
- """Recursively returns all atoms in nested lists and tuples."""
- if isinstance(node, (astroid.Tuple, astroid.List)):
- for child in node.elts:
- for e in get_all_elements(child):
- yield e
- else:
- yield node
-
-
-def clobber_in_except(node):
- """Checks if an assignment node in an except handler clobbers an existing
- variable.
-
- Returns (True, args for W0623) if assignment clobbers an existing variable,
- (False, None) otherwise.
- """
- if isinstance(node, astroid.AssAttr):
- return (True, (node.attrname, 'object %r' % (node.expr.as_string(),)))
- elif isinstance(node, astroid.AssName):
- name = node.name
- if is_builtin(name):
- return (True, (name, 'builtins'))
- else:
- stmts = node.lookup(name)[1]
- if (stmts and not isinstance(stmts[0].ass_type(),
- (astroid.Assign, astroid.AugAssign,
- astroid.ExceptHandler))):
- return (True, (name, 'outer scope (line %s)' % stmts[0].fromlineno))
- return (False, None)
-
-
-def safe_infer(node):
- """return the inferred value for the given node.
- Return None if inference failed or if there is some ambiguity (more than
- one node has been inferred)
- """
- try:
- inferit = node.infer()
- value = next(inferit)
- except astroid.InferenceError:
- return
- try:
- next(inferit)
- return # None if there is ambiguity on the inferred node
- except astroid.InferenceError:
- return # there is some kind of ambiguity
- except StopIteration:
- return value
-
-def is_super(node):
- """return True if the node is referencing the "super" builtin function
- """
- if getattr(node, 'name', None) == 'super' and \
- node.root().name == BUILTINS_NAME:
- return True
- return False
-
-def is_error(node):
- """return true if the function does nothing but raising an exception"""
- for child_node in node.get_children():
- if isinstance(child_node, astroid.Raise):
- return True
- return False
-
-def is_raising(body):
- """return true if the given statement node raise an exception"""
- for node in body:
- if isinstance(node, astroid.Raise):
- return True
- return False
-
-def is_empty(body):
- """return true if the given node does nothing but 'pass'"""
- return len(body) == 1 and isinstance(body[0], astroid.Pass)
-
-builtins = builtins.__dict__.copy()
-SPECIAL_BUILTINS = ('__builtins__',) # '__path__', '__file__')
-
-def is_builtin_object(node):
- """Returns True if the given node is an object from the __builtin__ module."""
- return node and node.root().name == BUILTINS_NAME
-
-def is_builtin(name): # was is_native_builtin
- """return true if <name> could be considered as a builtin defined by python
- """
- if name in builtins:
- return True
- if name in SPECIAL_BUILTINS:
- return True
- return False
-
-def is_defined_before(var_node):
- """return True if the variable node is defined by a parent node (list,
- set, dict, or generator comprehension, lambda) or in a previous sibling
- node on the same line (statement_defining ; statement_using)
- """
- varname = var_node.name
- _node = var_node.parent
- while _node:
- if isinstance(_node, COMP_NODE_TYPES):
- for ass_node in _node.nodes_of_class(astroid.AssName):
- if ass_node.name == varname:
- return True
- elif isinstance(_node, astroid.For):
- for ass_node in _node.target.nodes_of_class(astroid.AssName):
- if ass_node.name == varname:
- return True
- elif isinstance(_node, astroid.With):
- for expr, ids in _node.items:
- if expr.parent_of(var_node):
- break
- if (ids and
- isinstance(ids, astroid.AssName) and
- ids.name == varname):
- return True
- elif isinstance(_node, (astroid.Lambda, astroid.Function)):
- if _node.args.is_argument(varname):
- return True
- if getattr(_node, 'name', None) == varname:
- return True
- break
- elif isinstance(_node, astroid.ExceptHandler):
- if isinstance(_node.name, astroid.AssName):
- ass_node = _node.name
- if ass_node.name == varname:
- return True
- _node = _node.parent
- # possibly multiple statements on the same line using semi colon separator
- stmt = var_node.statement()
- _node = stmt.previous_sibling()
- lineno = stmt.fromlineno
- while _node and _node.fromlineno == lineno:
- for ass_node in _node.nodes_of_class(astroid.AssName):
- if ass_node.name == varname:
- return True
- for imp_node in _node.nodes_of_class((astroid.From, astroid.Import)):
- if varname in [name[1] or name[0] for name in imp_node.names]:
- return True
- _node = _node.previous_sibling()
- return False
-
-def is_func_default(node):
- """return true if the given Name node is used in function default argument's
- value
- """
- parent = node.scope()
- if isinstance(parent, astroid.Function):
- for default_node in parent.args.defaults:
- for default_name_node in default_node.nodes_of_class(astroid.Name):
- if default_name_node is node:
- return True
- return False
-
-def is_func_decorator(node):
- """return true if the name is used in function decorator"""
- parent = node.parent
- while parent is not None:
- if isinstance(parent, astroid.Decorators):
- return True
- if (parent.is_statement or
- isinstance(parent, astroid.Lambda) or
- isinstance(parent, (scoped_nodes.ComprehensionScope,
- scoped_nodes.ListComp))):
- break
- parent = parent.parent
- return False
-
-def is_ancestor_name(frame, node):
- """return True if `frame` is a astroid.Class node with `node` in the
- subtree of its bases attribute
- """
- try:
- bases = frame.bases
- except AttributeError:
- return False
- for base in bases:
- if node in base.nodes_of_class(astroid.Name):
- return True
- return False
-
-def assign_parent(node):
- """return the higher parent which is not an AssName, Tuple or List node
- """
- while node and isinstance(node, (astroid.AssName,
- astroid.Tuple,
- astroid.List)):
- node = node.parent
- return node
-
-def overrides_an_abstract_method(class_node, name):
- """return True if pnode is a parent of node"""
- for ancestor in class_node.ancestors():
- if name in ancestor and isinstance(ancestor[name], astroid.Function) and \
- ancestor[name].is_abstract(pass_is_abstract=False):
- return True
- return False
-
-def overrides_a_method(class_node, name):
- """return True if <name> is a method overridden from an ancestor"""
- for ancestor in class_node.ancestors():
- if name in ancestor and isinstance(ancestor[name], astroid.Function):
- return True
- return False
-
-PYMETHODS = set(('__new__', '__init__', '__del__', '__hash__',
- '__str__', '__repr__',
- '__len__', '__iter__',
- '__delete__', '__get__', '__set__',
- '__getitem__', '__setitem__', '__delitem__', '__contains__',
- '__getattribute__', '__getattr__', '__setattr__', '__delattr__',
- '__call__',
- '__enter__', '__exit__',
- '__cmp__', '__ge__', '__gt__', '__le__', '__lt__', '__eq__',
- '__nonzero__', '__neg__', '__invert__',
- '__mul__', '__imul__', '__rmul__',
- '__div__', '__idiv__', '__rdiv__',
- '__add__', '__iadd__', '__radd__',
- '__sub__', '__isub__', '__rsub__',
- '__pow__', '__ipow__', '__rpow__',
- '__mod__', '__imod__', '__rmod__',
- '__and__', '__iand__', '__rand__',
- '__or__', '__ior__', '__ror__',
- '__xor__', '__ixor__', '__rxor__',
- # XXX To be continued
- ))
-
-def check_messages(*messages):
- """decorator to store messages that are handled by a checker method"""
-
- def store_messages(func):
- func.checks_msgs = messages
- return func
- return store_messages
-
-class IncompleteFormatString(Exception):
- """A format string ended in the middle of a format specifier."""
- pass
-
-class UnsupportedFormatCharacter(Exception):
- """A format character in a format string is not one of the supported
- format characters."""
- def __init__(self, index):
- Exception.__init__(self, index)
- self.index = index
-
-def parse_format_string(format_string):
- """Parses a format string, returning a tuple of (keys, num_args), where keys
- is the set of mapping keys in the format string, and num_args is the number
- of arguments required by the format string. Raises
- IncompleteFormatString or UnsupportedFormatCharacter if a
- parse error occurs."""
- keys = set()
- num_args = 0
- def next_char(i):
- i += 1
- if i == len(format_string):
- raise IncompleteFormatString
- return (i, format_string[i])
- i = 0
- while i < len(format_string):
- char = format_string[i]
- if char == '%':
- i, char = next_char(i)
- # Parse the mapping key (optional).
- key = None
- if char == '(':
- depth = 1
- i, char = next_char(i)
- key_start = i
- while depth != 0:
- if char == '(':
- depth += 1
- elif char == ')':
- depth -= 1
- i, char = next_char(i)
- key_end = i - 1
- key = format_string[key_start:key_end]
-
- # Parse the conversion flags (optional).
- while char in '#0- +':
- i, char = next_char(i)
- # Parse the minimum field width (optional).
- if char == '*':
- num_args += 1
- i, char = next_char(i)
- else:
- while char in string.digits:
- i, char = next_char(i)
- # Parse the precision (optional).
- if char == '.':
- i, char = next_char(i)
- if char == '*':
- num_args += 1
- i, char = next_char(i)
- else:
- while char in string.digits:
- i, char = next_char(i)
- # Parse the length modifier (optional).
- if char in 'hlL':
- i, char = next_char(i)
- # Parse the conversion type (mandatory).
- if PY3K:
- flags = 'diouxXeEfFgGcrs%a'
- else:
- flags = 'diouxXeEfFgGcrs%'
- if char not in flags:
- raise UnsupportedFormatCharacter(i)
- if key:
- keys.add(key)
- elif char != '%':
- num_args += 1
- i += 1
- return keys, num_args
-
-
-def is_attr_protected(attrname):
- """return True if attribute name is protected (start with _ and some other
- details), False otherwise.
- """
- return attrname[0] == '_' and not attrname == '_' and not (
- attrname.startswith('__') and attrname.endswith('__'))
-
-def node_frame_class(node):
- """return klass node for a method node (or a staticmethod or a
- classmethod), return null otherwise
- """
- klass = node.frame()
-
- while klass is not None and not isinstance(klass, astroid.Class):
- if klass.parent is None:
- klass = None
- else:
- klass = klass.parent.frame()
-
- return klass
-
-def is_super_call(expr):
- """return True if expression node is a function call and if function name
- is super. Check before that you're in a method.
- """
- return (isinstance(expr, astroid.CallFunc) and
- isinstance(expr.func, astroid.Name) and
- expr.func.name == 'super')
-
-def is_attr_private(attrname):
- """Check that attribute name is private (at least two leading underscores,
- at most one trailing underscore)
- """
- regex = re.compile('^_{2,}.*[^_]+_?$')
- return regex.match(attrname)
-
-def get_argument_from_call(callfunc_node, position=None, keyword=None):
- """Returns the specified argument from a function call.
-
- :param callfunc_node: Node representing a function call to check.
- :param int position: position of the argument.
- :param str keyword: the keyword of the argument.
-
- :returns: The node representing the argument, None if the argument is not found.
- :raises ValueError: if both position and keyword are None.
- :raises NoSuchArgumentError: if no argument at the provided position or with
- the provided keyword.
- """
- if position is None and keyword is None:
- raise ValueError('Must specify at least one of: position or keyword.')
- try:
- if position is not None and not isinstance(callfunc_node.args[position], astroid.Keyword):
- return callfunc_node.args[position]
- except IndexError as error:
- raise NoSuchArgumentError(error)
- if keyword:
- for arg in callfunc_node.args:
- if isinstance(arg, astroid.Keyword) and arg.arg == keyword:
- return arg.value
- raise NoSuchArgumentError
-
-def inherit_from_std_ex(node):
- """
- Return true if the given class node is subclass of
- exceptions.Exception.
- """
- if node.name in ('Exception', 'BaseException') \
- and node.root().name == EXCEPTIONS_MODULE:
- return True
- return any(inherit_from_std_ex(parent)
- for parent in node.ancestors(recurs=False))
-
-def is_import_error(handler):
- """
- Check if the given exception handler catches
- ImportError.
-
- :param handler: A node, representing an ExceptHandler node.
- :returns: True if the handler catches ImportError, False otherwise.
- """
- names = None
- if isinstance(handler.type, astroid.Tuple):
- names = [name for name in handler.type.elts
- if isinstance(name, astroid.Name)]
- elif isinstance(handler.type, astroid.Name):
- names = [handler.type]
- else:
- # Don't try to infer that.
- return
- for name in names:
- try:
- for infered in name.infer():
- if (isinstance(infered, astroid.Class) and
- inherit_from_std_ex(infered) and
- infered.name == 'ImportError'):
- return True
- except astroid.InferenceError:
- continue
-
-def has_known_bases(klass):
- """Returns true if all base classes of a class could be inferred."""
- try:
- return klass._all_bases_known
- except AttributeError:
- pass
- for base in klass.bases:
- result = safe_infer(base)
- # TODO: check for A->B->A->B pattern in class structure too?
- if (not isinstance(result, astroid.Class) or
- result is klass or
- not has_known_bases(result)):
- klass._all_bases_known = False
- return False
- klass._all_bases_known = True
- return True
-
-def decorated_with_property(node):
- """ Detect if the given function node is decorated with a property. """
- if not node.decorators:
- return False
- for decorator in node.decorators.nodes:
- if not isinstance(decorator, astroid.Name):
- continue
- try:
- for infered in decorator.infer():
- if isinstance(infered, astroid.Class):
- if (infered.root().name == BUILTINS_NAME and
- infered.name == 'property'):
- return True
- for ancestor in infered.ancestors():
- if (ancestor.name == 'property' and
- ancestor.root().name == BUILTINS_NAME):
- return True
- except astroid.InferenceError:
- pass
-
-
-def decorated_with_abc(func):
- """Determine if the `func` node is decorated with `abc` decorators."""
- if func.decorators:
- for node in func.decorators.nodes:
- try:
- infered = next(node.infer())
- except astroid.InferenceError:
- continue
- if infered and infered.qname() in ABC_METHODS:
- return True
-
-
-def unimplemented_abstract_methods(node, is_abstract_cb=decorated_with_abc):
- """
- Get the unimplemented abstract methods for the given *node*.
-
- A method can be considered abstract if the callback *is_abstract_cb*
- returns a ``True`` value. The check defaults to verifying that
- a method is decorated with abstract methods.
- The function will work only for new-style classes. For old-style
- classes, it will simply return an empty dictionary.
- For the rest of them, it will return a dictionary of abstract method
- names and their inferred objects.
- """
- visited = {}
- try:
- mro = reversed(node.mro())
- except NotImplementedError:
- # Old style class, it will not have a mro.
- return {}
- except astroid.ResolveError:
- # Probably inconsistent hierarchy, don'try
- # to figure this out here.
- return {}
- for ancestor in mro:
- for obj in ancestor.values():
- infered = obj
- if isinstance(obj, astroid.AssName):
- infered = safe_infer(obj)
- if not infered:
- continue
- if not isinstance(infered, astroid.Function):
- if obj.name in visited:
- del visited[obj.name]
- if isinstance(infered, astroid.Function):
- # It's critical to use the original name,
- # since after inferring, an object can be something
- # else than expected, as in the case of the
- # following assignment.
- #
- # class A:
- # def keys(self): pass
- # __iter__ = keys
- abstract = is_abstract_cb(infered)
- if abstract:
- visited[obj.name] = infered
- elif not abstract and obj.name in visited:
- del visited[obj.name]
- return visited