Index: third_party/pylint/checkers/utils.py |
=================================================================== |
--- third_party/pylint/checkers/utils.py (revision 292986) |
+++ third_party/pylint/checkers/utils.py (working copy) |
@@ -1,6 +1,6 @@ |
# pylint: disable=W0611 |
# |
-# Copyright (c) 2003-2010 LOGILAB S.A. (Paris, FRANCE). |
+# 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 |
@@ -14,22 +14,45 @@ |
# |
# 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., |
-# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
+# 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 |
-from logilab import astng |
+ |
+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 |
-COMP_NODE_TYPES = astng.ListComp, astng.SetComp, astng.DictComp, astng.GenExpr |
+class NoSuchArgumentError(Exception): |
+ pass |
+ |
def is_inside_except(node): |
- """Returns true if node is directly inside an exception handler""" |
- return isinstance(node.parent, astng.ExceptHandler) |
+ """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. |
@@ -37,18 +60,18 @@ |
Returns (True, args for W0623) if assignment clobbers an existing variable, |
(False, None) otherwise. |
""" |
- if isinstance(node, astng.AssAttr): |
- return (True, (node.attrname, 'object %r' % (node.expr.name,))) |
- elif node is not None: |
+ 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: |
- scope, stmts = node.lookup(name) |
- if (stmts and |
- not isinstance(stmts[0].ass_type(), |
- (astng.Assign, astng.AugAssign, astng.ExceptHandler))): |
- return (True, (name, 'outer scope (line %i)' % (stmts[0].lineno,))) |
+ 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) |
@@ -60,11 +83,13 @@ |
try: |
inferit = node.infer() |
value = inferit.next() |
- except astng.InferenceError: |
+ except astroid.InferenceError: |
return |
try: |
inferit.next() |
return # None if there is ambiguity on the inferred node |
+ except astroid.InferenceError: |
+ return # there is some kind of ambiguity |
except StopIteration: |
return value |
@@ -79,7 +104,7 @@ |
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, astng.Raise): |
+ if isinstance(child_node, astroid.Raise): |
return True |
return False |
@@ -86,17 +111,21 @@ |
def is_raising(body): |
"""return true if the given statement node raise an exception""" |
for node in body: |
- if isinstance(node, astng.Raise): |
+ 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], astng.Pass) |
+ return len(body) == 1 and isinstance(body[0], astroid.Pass) |
-builtins = __builtins__.copy() |
+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 |
""" |
@@ -115,25 +144,32 @@ |
_node = var_node.parent |
while _node: |
if isinstance(_node, COMP_NODE_TYPES): |
- for ass_node in _node.nodes_of_class(astng.AssName): |
+ for ass_node in _node.nodes_of_class(astroid.AssName): |
if ass_node.name == varname: |
return True |
- elif isinstance(_node, astng.For): |
- for ass_node in _node.target.nodes_of_class(astng.AssName): |
+ 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, astng.With): |
- if _node.vars is None: |
- # quickfix : case in which 'with' is used without 'as' |
- return False |
- if _node.vars.name == varname: |
- return True |
- elif isinstance(_node, (astng.Lambda, astng.Function)): |
+ elif isinstance(_node, astroid.With): |
+ for expr, vars in _node.items: |
+ if expr.parent_of(var_node): |
+ break |
+ if (vars and |
+ isinstance(vars, astroid.AssName) and |
+ vars.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() |
@@ -140,10 +176,10 @@ |
_node = stmt.previous_sibling() |
lineno = stmt.fromlineno |
while _node and _node.fromlineno == lineno: |
- for ass_node in _node.nodes_of_class(astng.AssName): |
+ 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( (astng.From, astng.Import)): |
+ 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() |
@@ -154,9 +190,9 @@ |
value |
""" |
parent = node.scope() |
- if isinstance(parent, astng.Function): |
+ if isinstance(parent, astroid.Function): |
for default_node in parent.args.defaults: |
- for default_name_node in default_node.nodes_of_class(astng.Name): |
+ for default_name_node in default_node.nodes_of_class(astroid.Name): |
if default_name_node is node: |
return True |
return False |
@@ -165,15 +201,18 @@ |
"""return true if the name is used in function decorator""" |
parent = node.parent |
while parent is not None: |
- if isinstance(parent, astng.Decorators): |
+ if isinstance(parent, astroid.Decorators): |
return True |
- if parent.is_statement or isinstance(parent, astng.Lambda): |
+ 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 astng.Class node with `node` in the |
+ """return True if `frame` is a astroid.Class node with `node` in the |
subtree of its bases attribute |
""" |
try: |
@@ -181,7 +220,7 @@ |
except AttributeError: |
return False |
for base in bases: |
- if node in base.nodes_of_class(astng.Name): |
+ if node in base.nodes_of_class(astroid.Name): |
return True |
return False |
@@ -188,9 +227,9 @@ |
def assign_parent(node): |
"""return the higher parent which is not an AssName, Tuple or List node |
""" |
- while node and isinstance(node, (astng.AssName, |
- astng.Tuple, |
- astng.List)): |
+ while node and isinstance(node, (astroid.AssName, |
+ astroid.Tuple, |
+ astroid.List)): |
node = node.parent |
return node |
@@ -197,7 +236,7 @@ |
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], astng.Function) and \ |
+ if name in ancestor and isinstance(ancestor[name], astroid.Function) and \ |
ancestor[name].is_abstract(pass_is_abstract=False): |
return True |
return False |
@@ -205,7 +244,7 @@ |
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], astng.Function): |
+ if name in ancestor and isinstance(ancestor[name], astroid.Function): |
return True |
return False |
@@ -229,7 +268,7 @@ |
'__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""" |
@@ -265,52 +304,118 @@ |
return (i, format_string[i]) |
i = 0 |
while i < len(format_string): |
- c = format_string[i] |
- if c == '%': |
- i, c = next_char(i) |
+ char = format_string[i] |
+ if char == '%': |
+ i, char = next_char(i) |
# Parse the mapping key (optional). |
key = None |
- if c == '(': |
+ if char == '(': |
depth = 1 |
- i, c = next_char(i) |
+ i, char = next_char(i) |
key_start = i |
while depth != 0: |
- if c == '(': |
+ if char == '(': |
depth += 1 |
- elif c == ')': |
+ elif char == ')': |
depth -= 1 |
- i, c = next_char(i) |
+ i, char = next_char(i) |
key_end = i - 1 |
key = format_string[key_start:key_end] |
# Parse the conversion flags (optional). |
- while c in '#0- +': |
- i, c = next_char(i) |
+ while char in '#0- +': |
+ i, char = next_char(i) |
# Parse the minimum field width (optional). |
- if c == '*': |
+ if char == '*': |
num_args += 1 |
- i, c = next_char(i) |
+ i, char = next_char(i) |
else: |
- while c in string.digits: |
- i, c = next_char(i) |
+ while char in string.digits: |
+ i, char = next_char(i) |
# Parse the precision (optional). |
- if c == '.': |
- i, c = next_char(i) |
- if c == '*': |
+ if char == '.': |
+ i, char = next_char(i) |
+ if char == '*': |
num_args += 1 |
- i, c = next_char(i) |
+ i, char = next_char(i) |
else: |
- while c in string.digits: |
- i, c = next_char(i) |
+ while char in string.digits: |
+ i, char = next_char(i) |
# Parse the length modifier (optional). |
- if c in 'hlL': |
- i, c = next_char(i) |
+ if char in 'hlL': |
+ i, char = next_char(i) |
# Parse the conversion type (mandatory). |
- if c not in 'diouxXeEfFgGcrs%': |
+ if PY3K: |
+ flags = 'diouxXeEfFgGcrs%a' |
+ else: |
+ flags = 'diouxXeEfFgGcrs%' |
+ if char not in flags: |
raise UnsupportedFormatCharacter(i) |
if key: |
keys.add(key) |
- elif c != '%': |
+ 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, 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 |