| OLD | NEW | 
|    1 # pylint: disable=W0611 |    1 # pylint: disable=W0611 | 
|    2 # |    2 # | 
|    3 # Copyright (c) 2003-2010 LOGILAB S.A. (Paris, FRANCE). |    3 # Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE). | 
|    4 # http://www.logilab.fr/ -- mailto:contact@logilab.fr |    4 # http://www.logilab.fr/ -- mailto:contact@logilab.fr | 
|    5 # |    5 # | 
|    6 # This program is free software; you can redistribute it and/or modify it under |    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 |    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 |    8 # Foundation; either version 2 of the License, or (at your option) any later | 
|    9 # version. |    9 # version. | 
|   10 # |   10 # | 
|   11 # This program is distributed in the hope that it will be useful, but WITHOUT |   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 |   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. |   13 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. | 
|   14 # |   14 # | 
|   15 # You should have received a copy of the GNU General Public License along with |   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., |   16 # this program; if not, write to the Free Software Foundation, Inc., | 
|   17 # 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. |   17 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | 
|   18 """some functions that may be useful for various checkers |   18 """some functions that may be useful for various checkers | 
|   19 """ |   19 """ | 
|   20  |   20  | 
 |   21 import re | 
 |   22 import sys | 
|   21 import string |   23 import string | 
|   22 from logilab import astng |   24  | 
 |   25 import astroid | 
 |   26 from astroid import scoped_nodes | 
|   23 from logilab.common.compat import builtins |   27 from logilab.common.compat import builtins | 
 |   28  | 
|   24 BUILTINS_NAME = builtins.__name__ |   29 BUILTINS_NAME = builtins.__name__ | 
 |   30 COMP_NODE_TYPES = astroid.ListComp, astroid.SetComp, astroid.DictComp, astroid.G
     enExpr | 
 |   31 PY3K = sys.version_info[0] == 3 | 
|   25  |   32  | 
|   26 COMP_NODE_TYPES = astng.ListComp, astng.SetComp, astng.DictComp, astng.GenExpr |   33  | 
 |   34 class NoSuchArgumentError(Exception): | 
 |   35     pass | 
|   27  |   36  | 
|   28 def is_inside_except(node): |   37 def is_inside_except(node): | 
|   29     """Returns true if node is directly inside an exception handler""" |   38     """Returns true if node is inside the name of an except handler.""" | 
|   30     return isinstance(node.parent, astng.ExceptHandler) |   39     current = node | 
 |   40     while current and not isinstance(current.parent, astroid.ExceptHandler): | 
 |   41         current = current.parent | 
 |   42  | 
 |   43     return current and current is current.parent.name | 
 |   44  | 
 |   45  | 
 |   46 def get_all_elements(node): | 
 |   47     """Recursively returns all atoms in nested lists and tuples.""" | 
 |   48     if isinstance(node, (astroid.Tuple, astroid.List)): | 
 |   49         for child in node.elts: | 
 |   50             for e in get_all_elements(child): | 
 |   51                 yield e | 
 |   52     else: | 
 |   53         yield node | 
|   31  |   54  | 
|   32  |   55  | 
|   33 def clobber_in_except(node): |   56 def clobber_in_except(node): | 
|   34     """Checks if an assignment node in an except handler clobbers an existing |   57     """Checks if an assignment node in an except handler clobbers an existing | 
|   35     variable. |   58     variable. | 
|   36  |   59  | 
|   37     Returns (True, args for W0623) if assignment clobbers an existing variable, |   60     Returns (True, args for W0623) if assignment clobbers an existing variable, | 
|   38     (False, None) otherwise. |   61     (False, None) otherwise. | 
|   39     """ |   62     """ | 
|   40     if isinstance(node, astng.AssAttr): |   63     if isinstance(node, astroid.AssAttr): | 
|   41         return (True, (node.attrname, 'object %r' % (node.expr.name,))) |   64         return (True, (node.attrname, 'object %r' % (node.expr.as_string(),))) | 
|   42     elif node is not None: |   65     elif isinstance(node, astroid.AssName): | 
|   43         name = node.name |   66         name = node.name | 
|   44         if is_builtin(name): |   67         if is_builtin(name): | 
|   45             return (True, (name, 'builtins')) |   68             return (True, (name, 'builtins')) | 
|   46         else: |   69         else: | 
|   47             scope, stmts = node.lookup(name) |   70             stmts = node.lookup(name)[1] | 
|   48             if (stmts and  |   71             if (stmts and not isinstance(stmts[0].ass_type(), | 
|   49                 not isinstance(stmts[0].ass_type(),  |   72                                          (astroid.Assign, astroid.AugAssign, | 
|   50                                (astng.Assign, astng.AugAssign, astng.ExceptHandl
     er))): |   73                                           astroid.ExceptHandler))): | 
|   51                 return (True, (name, 'outer scope (line %i)' % (stmts[0].lineno,
     ))) |   74                 return (True, (name, 'outer scope (line %s)' % stmts[0].fromline
     no)) | 
|   52     return (False, None) |   75     return (False, None) | 
|   53  |   76  | 
|   54  |   77  | 
|   55 def safe_infer(node): |   78 def safe_infer(node): | 
|   56     """return the inferred value for the given node. |   79     """return the inferred value for the given node. | 
|   57     Return None if inference failed or if there is some ambiguity (more than |   80     Return None if inference failed or if there is some ambiguity (more than | 
|   58     one node has been inferred) |   81     one node has been inferred) | 
|   59     """ |   82     """ | 
|   60     try: |   83     try: | 
|   61         inferit = node.infer() |   84         inferit = node.infer() | 
|   62         value = inferit.next() |   85         value = inferit.next() | 
|   63     except astng.InferenceError: |   86     except astroid.InferenceError: | 
|   64         return |   87         return | 
|   65     try: |   88     try: | 
|   66         inferit.next() |   89         inferit.next() | 
|   67         return # None if there is ambiguity on the inferred node |   90         return # None if there is ambiguity on the inferred node | 
 |   91     except astroid.InferenceError: | 
 |   92         return # there is some kind of ambiguity | 
|   68     except StopIteration: |   93     except StopIteration: | 
|   69         return value |   94         return value | 
|   70  |   95  | 
|   71 def is_super(node): |   96 def is_super(node): | 
|   72     """return True if the node is referencing the "super" builtin function |   97     """return True if the node is referencing the "super" builtin function | 
|   73     """ |   98     """ | 
|   74     if getattr(node, 'name', None) == 'super' and \ |   99     if getattr(node, 'name', None) == 'super' and \ | 
|   75            node.root().name == BUILTINS_NAME: |  100            node.root().name == BUILTINS_NAME: | 
|   76         return True |  101         return True | 
|   77     return False |  102     return False | 
|   78  |  103  | 
|   79 def is_error(node): |  104 def is_error(node): | 
|   80     """return true if the function does nothing but raising an exception""" |  105     """return true if the function does nothing but raising an exception""" | 
|   81     for child_node in node.get_children(): |  106     for child_node in node.get_children(): | 
|   82         if isinstance(child_node, astng.Raise): |  107         if isinstance(child_node, astroid.Raise): | 
|   83             return True |  108             return True | 
|   84         return False |  109         return False | 
|   85  |  110  | 
|   86 def is_raising(body): |  111 def is_raising(body): | 
|   87     """return true if the given statement node raise an exception""" |  112     """return true if the given statement node raise an exception""" | 
|   88     for node in body: |  113     for node in body: | 
|   89         if isinstance(node, astng.Raise): |  114         if isinstance(node, astroid.Raise): | 
|   90             return True |  115             return True | 
|   91     return False |  116     return False | 
|   92  |  117  | 
|   93 def is_empty(body): |  118 def is_empty(body): | 
|   94     """return true if the given node does nothing but 'pass'""" |  119     """return true if the given node does nothing but 'pass'""" | 
|   95     return len(body) == 1 and isinstance(body[0], astng.Pass) |  120     return len(body) == 1 and isinstance(body[0], astroid.Pass) | 
|   96  |  121  | 
|   97 builtins =  __builtins__.copy() |  122 builtins = builtins.__dict__.copy() | 
|   98 SPECIAL_BUILTINS = ('__builtins__',) # '__path__', '__file__') |  123 SPECIAL_BUILTINS = ('__builtins__',) # '__path__', '__file__') | 
|   99  |  124  | 
 |  125 def is_builtin_object(node): | 
 |  126     """Returns True if the given node is an object from the __builtin__ module."
     "" | 
 |  127     return node and node.root().name == BUILTINS_NAME | 
 |  128  | 
|  100 def is_builtin(name): # was is_native_builtin |  129 def is_builtin(name): # was is_native_builtin | 
|  101     """return true if <name> could be considered as a builtin defined by python |  130     """return true if <name> could be considered as a builtin defined by python | 
|  102     """ |  131     """ | 
|  103     if name in builtins: |  132     if name in builtins: | 
|  104         return True |  133         return True | 
|  105     if name in SPECIAL_BUILTINS: |  134     if name in SPECIAL_BUILTINS: | 
|  106         return True |  135         return True | 
|  107     return False |  136     return False | 
|  108  |  137  | 
|  109 def is_defined_before(var_node): |  138 def is_defined_before(var_node): | 
|  110     """return True if the variable node is defined by a parent node (list, |  139     """return True if the variable node is defined by a parent node (list, | 
|  111     set, dict, or generator comprehension, lambda) or in a previous sibling |  140     set, dict, or generator comprehension, lambda) or in a previous sibling | 
|  112     node on the same line (statement_defining ; statement_using) |  141     node on the same line (statement_defining ; statement_using) | 
|  113     """ |  142     """ | 
|  114     varname = var_node.name |  143     varname = var_node.name | 
|  115     _node = var_node.parent |  144     _node = var_node.parent | 
|  116     while _node: |  145     while _node: | 
|  117         if isinstance(_node, COMP_NODE_TYPES): |  146         if isinstance(_node, COMP_NODE_TYPES): | 
|  118             for ass_node in _node.nodes_of_class(astng.AssName): |  147             for ass_node in _node.nodes_of_class(astroid.AssName): | 
|  119                 if ass_node.name == varname: |  148                 if ass_node.name == varname: | 
|  120                     return True |  149                     return True | 
|  121         elif isinstance(_node, astng.For): |  150         elif isinstance(_node, astroid.For): | 
|  122             for ass_node in _node.target.nodes_of_class(astng.AssName): |  151             for ass_node in _node.target.nodes_of_class(astroid.AssName): | 
|  123                 if ass_node.name == varname: |  152                 if ass_node.name == varname: | 
|  124                     return True |  153                     return True | 
|  125         elif isinstance(_node, astng.With): |  154         elif isinstance(_node, astroid.With): | 
|  126             if _node.vars is None: |  155             for expr, vars in _node.items: | 
|  127                 # quickfix : case in which 'with' is used without 'as' |  156                 if expr.parent_of(var_node): | 
|  128                 return False |  157                     break | 
|  129             if _node.vars.name == varname: |  158                 if (vars and | 
|  130                 return True |  159                         isinstance(vars, astroid.AssName) and | 
|  131         elif isinstance(_node, (astng.Lambda, astng.Function)): |  160                         vars.name == varname): | 
 |  161                     return True | 
 |  162         elif isinstance(_node, (astroid.Lambda, astroid.Function)): | 
|  132             if _node.args.is_argument(varname): |  163             if _node.args.is_argument(varname): | 
|  133                 return True |  164                 return True | 
|  134             if getattr(_node, 'name', None) == varname: |  165             if getattr(_node, 'name', None) == varname: | 
|  135                 return True |  166                 return True | 
|  136             break |  167             break | 
 |  168         elif isinstance(_node, astroid.ExceptHandler): | 
 |  169             if isinstance(_node.name, astroid.AssName): | 
 |  170                 ass_node = _node.name | 
 |  171                 if ass_node.name == varname: | 
 |  172                     return True | 
|  137         _node = _node.parent |  173         _node = _node.parent | 
|  138     # possibly multiple statements on the same line using semi colon separator |  174     # possibly multiple statements on the same line using semi colon separator | 
|  139     stmt = var_node.statement() |  175     stmt = var_node.statement() | 
|  140     _node = stmt.previous_sibling() |  176     _node = stmt.previous_sibling() | 
|  141     lineno = stmt.fromlineno |  177     lineno = stmt.fromlineno | 
|  142     while _node and _node.fromlineno == lineno: |  178     while _node and _node.fromlineno == lineno: | 
|  143         for ass_node in _node.nodes_of_class(astng.AssName): |  179         for ass_node in _node.nodes_of_class(astroid.AssName): | 
|  144             if ass_node.name == varname: |  180             if ass_node.name == varname: | 
|  145                 return True |  181                 return True | 
|  146         for imp_node in _node.nodes_of_class( (astng.From, astng.Import)): |  182         for imp_node in _node.nodes_of_class((astroid.From, astroid.Import)): | 
|  147             if varname in [name[1] or name[0] for name in imp_node.names]: |  183             if varname in [name[1] or name[0] for name in imp_node.names]: | 
|  148                 return True |  184                 return True | 
|  149         _node = _node.previous_sibling() |  185         _node = _node.previous_sibling() | 
|  150     return False |  186     return False | 
|  151  |  187  | 
|  152 def is_func_default(node): |  188 def is_func_default(node): | 
|  153     """return true if the given Name node is used in function default argument's |  189     """return true if the given Name node is used in function default argument's | 
|  154     value |  190     value | 
|  155     """ |  191     """ | 
|  156     parent = node.scope() |  192     parent = node.scope() | 
|  157     if isinstance(parent, astng.Function): |  193     if isinstance(parent, astroid.Function): | 
|  158         for default_node in parent.args.defaults: |  194         for default_node in parent.args.defaults: | 
|  159             for default_name_node in default_node.nodes_of_class(astng.Name): |  195             for default_name_node in default_node.nodes_of_class(astroid.Name): | 
|  160                 if default_name_node is node: |  196                 if default_name_node is node: | 
|  161                     return True |  197                     return True | 
|  162     return False |  198     return False | 
|  163  |  199  | 
|  164 def is_func_decorator(node): |  200 def is_func_decorator(node): | 
|  165     """return true if the name is used in function decorator""" |  201     """return true if the name is used in function decorator""" | 
|  166     parent = node.parent |  202     parent = node.parent | 
|  167     while parent is not None: |  203     while parent is not None: | 
|  168         if isinstance(parent, astng.Decorators): |  204         if isinstance(parent, astroid.Decorators): | 
|  169             return True |  205             return True | 
|  170         if parent.is_statement or isinstance(parent, astng.Lambda): |  206         if (parent.is_statement or | 
 |  207                 isinstance(parent, astroid.Lambda) or | 
 |  208                 isinstance(parent, (scoped_nodes.ComprehensionScope, | 
 |  209                                     scoped_nodes.ListComp))): | 
|  171             break |  210             break | 
|  172         parent = parent.parent |  211         parent = parent.parent | 
|  173     return False |  212     return False | 
|  174  |  213  | 
|  175 def is_ancestor_name(frame, node): |  214 def is_ancestor_name(frame, node): | 
|  176     """return True if `frame` is a astng.Class node with `node` in the |  215     """return True if `frame` is a astroid.Class node with `node` in the | 
|  177     subtree of its bases attribute |  216     subtree of its bases attribute | 
|  178     """ |  217     """ | 
|  179     try: |  218     try: | 
|  180         bases = frame.bases |  219         bases = frame.bases | 
|  181     except AttributeError: |  220     except AttributeError: | 
|  182         return False |  221         return False | 
|  183     for base in bases: |  222     for base in bases: | 
|  184         if node in base.nodes_of_class(astng.Name): |  223         if node in base.nodes_of_class(astroid.Name): | 
|  185             return True |  224             return True | 
|  186     return False |  225     return False | 
|  187  |  226  | 
|  188 def assign_parent(node): |  227 def assign_parent(node): | 
|  189     """return the higher parent which is not an AssName, Tuple or List node |  228     """return the higher parent which is not an AssName, Tuple or List node | 
|  190     """ |  229     """ | 
|  191     while node and isinstance(node, (astng.AssName, |  230     while node and isinstance(node, (astroid.AssName, | 
|  192                                      astng.Tuple, |  231                                      astroid.Tuple, | 
|  193                                      astng.List)): |  232                                      astroid.List)): | 
|  194         node = node.parent |  233         node = node.parent | 
|  195     return node |  234     return node | 
|  196  |  235  | 
|  197 def overrides_an_abstract_method(class_node, name): |  236 def overrides_an_abstract_method(class_node, name): | 
|  198     """return True if pnode is a parent of node""" |  237     """return True if pnode is a parent of node""" | 
|  199     for ancestor in class_node.ancestors(): |  238     for ancestor in class_node.ancestors(): | 
|  200         if name in ancestor and isinstance(ancestor[name], astng.Function) and \ |  239         if name in ancestor and isinstance(ancestor[name], astroid.Function) and
      \ | 
|  201                ancestor[name].is_abstract(pass_is_abstract=False): |  240                ancestor[name].is_abstract(pass_is_abstract=False): | 
|  202             return True |  241             return True | 
|  203     return False |  242     return False | 
|  204  |  243  | 
|  205 def overrides_a_method(class_node, name): |  244 def overrides_a_method(class_node, name): | 
|  206     """return True if <name> is a method overridden from an ancestor""" |  245     """return True if <name> is a method overridden from an ancestor""" | 
|  207     for ancestor in class_node.ancestors(): |  246     for ancestor in class_node.ancestors(): | 
|  208         if name in ancestor and isinstance(ancestor[name], astng.Function): |  247         if name in ancestor and isinstance(ancestor[name], astroid.Function): | 
|  209             return True |  248             return True | 
|  210     return False |  249     return False | 
|  211  |  250  | 
|  212 PYMETHODS = set(('__new__', '__init__', '__del__', '__hash__', |  251 PYMETHODS = set(('__new__', '__init__', '__del__', '__hash__', | 
|  213                  '__str__', '__repr__', |  252                  '__str__', '__repr__', | 
|  214                  '__len__', '__iter__', |  253                  '__len__', '__iter__', | 
|  215                  '__delete__', '__get__', '__set__', |  254                  '__delete__', '__get__', '__set__', | 
|  216                  '__getitem__', '__setitem__', '__delitem__', '__contains__', |  255                  '__getitem__', '__setitem__', '__delitem__', '__contains__', | 
|  217                  '__getattribute__', '__getattr__', '__setattr__', '__delattr__'
     , |  256                  '__getattribute__', '__getattr__', '__setattr__', '__delattr__'
     , | 
|  218                  '__call__', |  257                  '__call__', | 
|  219                  '__enter__', '__exit__', |  258                  '__enter__', '__exit__', | 
|  220                  '__cmp__', '__ge__', '__gt__', '__le__', '__lt__', '__eq__', |  259                  '__cmp__', '__ge__', '__gt__', '__le__', '__lt__', '__eq__', | 
|  221                  '__nonzero__', '__neg__', '__invert__', |  260                  '__nonzero__', '__neg__', '__invert__', | 
|  222                  '__mul__', '__imul__', '__rmul__', |  261                  '__mul__', '__imul__', '__rmul__', | 
|  223                  '__div__', '__idiv__', '__rdiv__', |  262                  '__div__', '__idiv__', '__rdiv__', | 
|  224                  '__add__', '__iadd__', '__radd__', |  263                  '__add__', '__iadd__', '__radd__', | 
|  225                  '__sub__', '__isub__', '__rsub__', |  264                  '__sub__', '__isub__', '__rsub__', | 
|  226                  '__pow__', '__ipow__', '__rpow__', |  265                  '__pow__', '__ipow__', '__rpow__', | 
|  227                  '__mod__', '__imod__', '__rmod__', |  266                  '__mod__', '__imod__', '__rmod__', | 
|  228                  '__and__', '__iand__', '__rand__', |  267                  '__and__', '__iand__', '__rand__', | 
|  229                  '__or__', '__ior__', '__ror__', |  268                  '__or__', '__ior__', '__ror__', | 
|  230                  '__xor__', '__ixor__', '__rxor__', |  269                  '__xor__', '__ixor__', '__rxor__', | 
|  231                  # XXX To be continued |  270                  # XXX To be continued | 
|  232                  )) |  271                 )) | 
|  233  |  272  | 
|  234 def check_messages(*messages): |  273 def check_messages(*messages): | 
|  235     """decorator to store messages that are handled by a checker method""" |  274     """decorator to store messages that are handled by a checker method""" | 
|  236  |  275  | 
|  237     def store_messages(func): |  276     def store_messages(func): | 
|  238         func.checks_msgs = messages |  277         func.checks_msgs = messages | 
|  239         return func |  278         return func | 
|  240     return store_messages |  279     return store_messages | 
|  241  |  280  | 
|  242 class IncompleteFormatString(Exception): |  281 class IncompleteFormatString(Exception): | 
| (...skipping 15 matching lines...) Expand all  Loading... | 
|  258     parse error occurs.""" |  297     parse error occurs.""" | 
|  259     keys = set() |  298     keys = set() | 
|  260     num_args = 0 |  299     num_args = 0 | 
|  261     def next_char(i): |  300     def next_char(i): | 
|  262         i += 1 |  301         i += 1 | 
|  263         if i == len(format_string): |  302         if i == len(format_string): | 
|  264             raise IncompleteFormatString |  303             raise IncompleteFormatString | 
|  265         return (i, format_string[i]) |  304         return (i, format_string[i]) | 
|  266     i = 0 |  305     i = 0 | 
|  267     while i < len(format_string): |  306     while i < len(format_string): | 
|  268         c = format_string[i] |  307         char = format_string[i] | 
|  269         if c == '%': |  308         if char == '%': | 
|  270             i, c = next_char(i) |  309             i, char = next_char(i) | 
|  271             # Parse the mapping key (optional). |  310             # Parse the mapping key (optional). | 
|  272             key = None |  311             key = None | 
|  273             if c == '(': |  312             if char == '(': | 
|  274                 depth = 1 |  313                 depth = 1 | 
|  275                 i, c = next_char(i) |  314                 i, char = next_char(i) | 
|  276                 key_start = i |  315                 key_start = i | 
|  277                 while depth != 0: |  316                 while depth != 0: | 
|  278                     if c == '(': |  317                     if char == '(': | 
|  279                         depth += 1 |  318                         depth += 1 | 
|  280                     elif c == ')': |  319                     elif char == ')': | 
|  281                         depth -= 1 |  320                         depth -= 1 | 
|  282                     i, c = next_char(i) |  321                     i, char = next_char(i) | 
|  283                 key_end = i - 1 |  322                 key_end = i - 1 | 
|  284                 key = format_string[key_start:key_end] |  323                 key = format_string[key_start:key_end] | 
|  285  |  324  | 
|  286             # Parse the conversion flags (optional). |  325             # Parse the conversion flags (optional). | 
|  287             while c in '#0- +': |  326             while char in '#0- +': | 
|  288                 i, c = next_char(i) |  327                 i, char = next_char(i) | 
|  289             # Parse the minimum field width (optional). |  328             # Parse the minimum field width (optional). | 
|  290             if c == '*': |  329             if char == '*': | 
|  291                 num_args += 1 |  330                 num_args += 1 | 
|  292                 i, c = next_char(i) |  331                 i, char = next_char(i) | 
|  293             else: |  332             else: | 
|  294                 while c in string.digits: |  333                 while char in string.digits: | 
|  295                     i, c = next_char(i) |  334                     i, char = next_char(i) | 
|  296             # Parse the precision (optional). |  335             # Parse the precision (optional). | 
|  297             if c == '.': |  336             if char == '.': | 
|  298                 i, c = next_char(i) |  337                 i, char = next_char(i) | 
|  299                 if c == '*': |  338                 if char == '*': | 
|  300                     num_args += 1 |  339                     num_args += 1 | 
|  301                     i, c = next_char(i) |  340                     i, char = next_char(i) | 
|  302                 else: |  341                 else: | 
|  303                     while c in string.digits: |  342                     while char in string.digits: | 
|  304                         i, c = next_char(i) |  343                         i, char = next_char(i) | 
|  305             # Parse the length modifier (optional). |  344             # Parse the length modifier (optional). | 
|  306             if c in 'hlL': |  345             if char in 'hlL': | 
|  307                 i, c = next_char(i) |  346                 i, char = next_char(i) | 
|  308             # Parse the conversion type (mandatory). |  347             # Parse the conversion type (mandatory). | 
|  309             if c not in 'diouxXeEfFgGcrs%': |  348             if PY3K: | 
 |  349                 flags = 'diouxXeEfFgGcrs%a' | 
 |  350             else: | 
 |  351                 flags = 'diouxXeEfFgGcrs%' | 
 |  352             if char not in flags: | 
|  310                 raise UnsupportedFormatCharacter(i) |  353                 raise UnsupportedFormatCharacter(i) | 
|  311             if key: |  354             if key: | 
|  312                 keys.add(key) |  355                 keys.add(key) | 
|  313             elif c != '%': |  356             elif char != '%': | 
|  314                 num_args += 1 |  357                 num_args += 1 | 
|  315         i += 1 |  358         i += 1 | 
|  316     return keys, num_args |  359     return keys, num_args | 
 |  360  | 
 |  361  | 
 |  362 def is_attr_protected(attrname): | 
 |  363     """return True if attribute name is protected (start with _ and some other | 
 |  364     details), False otherwise. | 
 |  365     """ | 
 |  366     return attrname[0] == '_' and not attrname == '_' and not ( | 
 |  367         attrname.startswith('__') and attrname.endswith('__')) | 
 |  368  | 
 |  369 def node_frame_class(node): | 
 |  370     """return klass node for a method node (or a staticmethod or a | 
 |  371     classmethod), return null otherwise | 
 |  372     """ | 
 |  373     klass = node.frame() | 
 |  374  | 
 |  375     while klass is not None and not isinstance(klass, astroid.Class): | 
 |  376         if klass.parent is None: | 
 |  377             klass = None | 
 |  378         else: | 
 |  379             klass = klass.parent.frame() | 
 |  380  | 
 |  381     return klass | 
 |  382  | 
 |  383 def is_super_call(expr): | 
 |  384     """return True if expression node is a function call and if function name | 
 |  385     is super. Check before that you're in a method. | 
 |  386     """ | 
 |  387     return (isinstance(expr, astroid.CallFunc) and | 
 |  388             isinstance(expr.func, astroid.Name) and | 
 |  389             expr.func.name == 'super') | 
 |  390  | 
 |  391 def is_attr_private(attrname): | 
 |  392     """Check that attribute name is private (at least two leading underscores, | 
 |  393     at most one trailing underscore) | 
 |  394     """ | 
 |  395     regex = re.compile('^_{2,}.*[^_]+_?$') | 
 |  396     return regex.match(attrname) | 
 |  397  | 
 |  398 def get_argument_from_call(callfunc_node, position=None, keyword=None): | 
 |  399     """Returns the specified argument from a function call. | 
 |  400  | 
 |  401     :param callfunc_node: Node representing a function call to check. | 
 |  402     :param int position: position of the argument. | 
 |  403     :param str keyword: the keyword of the argument. | 
 |  404  | 
 |  405     :returns: The node representing the argument, None if the argument is not fo
     und. | 
 |  406     :raises ValueError: if both position and keyword are None. | 
 |  407     :raises NoSuchArgumentError: if no argument at the provided position or with | 
 |  408     the provided keyword. | 
 |  409     """ | 
 |  410     if position is None and keyword is None: | 
 |  411         raise ValueError('Must specify at least one of: position or keyword.') | 
 |  412     try: | 
 |  413         if position is not None and not isinstance(callfunc_node.args[position],
      astroid.Keyword): | 
 |  414             return callfunc_node.args[position] | 
 |  415     except IndexError, error: | 
 |  416         raise NoSuchArgumentError(error) | 
 |  417     if keyword: | 
 |  418         for arg in callfunc_node.args: | 
 |  419             if isinstance(arg, astroid.Keyword) and arg.arg == keyword: | 
 |  420                 return arg.value | 
 |  421     raise NoSuchArgumentError | 
| OLD | NEW |