Adding new stuff
This commit is contained in:
parent
131691e143
commit
6c0a9f5b29
718 changed files with 0 additions and 0 deletions
564
vim-plugins/python-mode/pymode/libs/pylint/checkers/utils.py
Normal file
564
vim-plugins/python-mode/pymode/libs/pylint/checkers/utils.py
Normal file
|
|
@ -0,0 +1,564 @@
|
|||
# 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue