diff options
author | Ionel Cristian Maries <contact@ionelmc.ro> | 2015-02-14 18:13:20 +0200 |
---|---|---|
committer | Ionel Cristian Maries <contact@ionelmc.ro> | 2015-02-14 18:13:20 +0200 |
commit | 6d8412476a296b3a3691af1ffabcb672d9a4920f (patch) | |
tree | e358c7e886ff4d67d0efc6263f0472655efddfff /checkers/utils.py | |
parent | 0369bd6a914af3ad92ce53eac3786bf8de785f7f (diff) | |
download | pylint-6d8412476a296b3a3691af1ffabcb672d9a4920f.tar.gz |
Move all package files to a pylint package.
Diffstat (limited to 'checkers/utils.py')
-rw-r--r-- | checkers/utils.py | 564 |
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 |