| OLD | NEW |
| 1 # pylint: disable=W0611 | 1 # pylint: disable=W0611 |
| 2 # | 2 # |
| 3 # Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE). | 3 # Copyright (c) 2003-2010 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 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | 17 # 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 | |
| 23 import string | 21 import string |
| 22 from logilab import astng |
| 23 from logilab.common.compat import builtins |
| 24 BUILTINS_NAME = builtins.__name__ |
| 24 | 25 |
| 25 import astroid | 26 COMP_NODE_TYPES = astng.ListComp, astng.SetComp, astng.DictComp, astng.GenExpr |
| 26 from astroid import scoped_nodes | |
| 27 from logilab.common.compat import builtins | |
| 28 | |
| 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 | |
| 32 | |
| 33 | |
| 34 class NoSuchArgumentError(Exception): | |
| 35 pass | |
| 36 | 27 |
| 37 def is_inside_except(node): | 28 def is_inside_except(node): |
| 38 """Returns true if node is inside the name of an except handler.""" | 29 """Returns true if node is directly inside an exception handler""" |
| 39 current = node | 30 return isinstance(node.parent, astng.ExceptHandler) |
| 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 | |
| 54 | 31 |
| 55 | 32 |
| 56 def clobber_in_except(node): | 33 def clobber_in_except(node): |
| 57 """Checks if an assignment node in an except handler clobbers an existing | 34 """Checks if an assignment node in an except handler clobbers an existing |
| 58 variable. | 35 variable. |
| 59 | 36 |
| 60 Returns (True, args for W0623) if assignment clobbers an existing variable, | 37 Returns (True, args for W0623) if assignment clobbers an existing variable, |
| 61 (False, None) otherwise. | 38 (False, None) otherwise. |
| 62 """ | 39 """ |
| 63 if isinstance(node, astroid.AssAttr): | 40 if isinstance(node, astng.AssAttr): |
| 64 return (True, (node.attrname, 'object %r' % (node.expr.as_string(),))) | 41 return (True, (node.attrname, 'object %r' % (node.expr.name,))) |
| 65 elif isinstance(node, astroid.AssName): | 42 elif node is not None: |
| 66 name = node.name | 43 name = node.name |
| 67 if is_builtin(name): | 44 if is_builtin(name): |
| 68 return (True, (name, 'builtins')) | 45 return (True, (name, 'builtins')) |
| 69 else: | 46 else: |
| 70 stmts = node.lookup(name)[1] | 47 scope, stmts = node.lookup(name) |
| 71 if (stmts and not isinstance(stmts[0].ass_type(), | 48 if (stmts and |
| 72 (astroid.Assign, astroid.AugAssign, | 49 not isinstance(stmts[0].ass_type(), |
| 73 astroid.ExceptHandler))): | 50 (astng.Assign, astng.AugAssign, astng.ExceptHandl
er))): |
| 74 return (True, (name, 'outer scope (line %s)' % stmts[0].fromline
no)) | 51 return (True, (name, 'outer scope (line %i)' % (stmts[0].lineno,
))) |
| 75 return (False, None) | 52 return (False, None) |
| 76 | 53 |
| 77 | 54 |
| 78 def safe_infer(node): | 55 def safe_infer(node): |
| 79 """return the inferred value for the given node. | 56 """return the inferred value for the given node. |
| 80 Return None if inference failed or if there is some ambiguity (more than | 57 Return None if inference failed or if there is some ambiguity (more than |
| 81 one node has been inferred) | 58 one node has been inferred) |
| 82 """ | 59 """ |
| 83 try: | 60 try: |
| 84 inferit = node.infer() | 61 inferit = node.infer() |
| 85 value = inferit.next() | 62 value = inferit.next() |
| 86 except astroid.InferenceError: | 63 except astng.InferenceError: |
| 87 return | 64 return |
| 88 try: | 65 try: |
| 89 inferit.next() | 66 inferit.next() |
| 90 return # None if there is ambiguity on the inferred node | 67 return # None if there is ambiguity on the inferred node |
| 91 except astroid.InferenceError: | |
| 92 return # there is some kind of ambiguity | |
| 93 except StopIteration: | 68 except StopIteration: |
| 94 return value | 69 return value |
| 95 | 70 |
| 96 def is_super(node): | 71 def is_super(node): |
| 97 """return True if the node is referencing the "super" builtin function | 72 """return True if the node is referencing the "super" builtin function |
| 98 """ | 73 """ |
| 99 if getattr(node, 'name', None) == 'super' and \ | 74 if getattr(node, 'name', None) == 'super' and \ |
| 100 node.root().name == BUILTINS_NAME: | 75 node.root().name == BUILTINS_NAME: |
| 101 return True | 76 return True |
| 102 return False | 77 return False |
| 103 | 78 |
| 104 def is_error(node): | 79 def is_error(node): |
| 105 """return true if the function does nothing but raising an exception""" | 80 """return true if the function does nothing but raising an exception""" |
| 106 for child_node in node.get_children(): | 81 for child_node in node.get_children(): |
| 107 if isinstance(child_node, astroid.Raise): | 82 if isinstance(child_node, astng.Raise): |
| 108 return True | 83 return True |
| 109 return False | 84 return False |
| 110 | 85 |
| 111 def is_raising(body): | 86 def is_raising(body): |
| 112 """return true if the given statement node raise an exception""" | 87 """return true if the given statement node raise an exception""" |
| 113 for node in body: | 88 for node in body: |
| 114 if isinstance(node, astroid.Raise): | 89 if isinstance(node, astng.Raise): |
| 115 return True | 90 return True |
| 116 return False | 91 return False |
| 117 | 92 |
| 118 def is_empty(body): | 93 def is_empty(body): |
| 119 """return true if the given node does nothing but 'pass'""" | 94 """return true if the given node does nothing but 'pass'""" |
| 120 return len(body) == 1 and isinstance(body[0], astroid.Pass) | 95 return len(body) == 1 and isinstance(body[0], astng.Pass) |
| 121 | 96 |
| 122 builtins = builtins.__dict__.copy() | 97 builtins = __builtins__.copy() |
| 123 SPECIAL_BUILTINS = ('__builtins__',) # '__path__', '__file__') | 98 SPECIAL_BUILTINS = ('__builtins__',) # '__path__', '__file__') |
| 124 | 99 |
| 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 | |
| 129 def is_builtin(name): # was is_native_builtin | 100 def is_builtin(name): # was is_native_builtin |
| 130 """return true if <name> could be considered as a builtin defined by python | 101 """return true if <name> could be considered as a builtin defined by python |
| 131 """ | 102 """ |
| 132 if name in builtins: | 103 if name in builtins: |
| 133 return True | 104 return True |
| 134 if name in SPECIAL_BUILTINS: | 105 if name in SPECIAL_BUILTINS: |
| 135 return True | 106 return True |
| 136 return False | 107 return False |
| 137 | 108 |
| 138 def is_defined_before(var_node): | 109 def is_defined_before(var_node): |
| 139 """return True if the variable node is defined by a parent node (list, | 110 """return True if the variable node is defined by a parent node (list, |
| 140 set, dict, or generator comprehension, lambda) or in a previous sibling | 111 set, dict, or generator comprehension, lambda) or in a previous sibling |
| 141 node on the same line (statement_defining ; statement_using) | 112 node on the same line (statement_defining ; statement_using) |
| 142 """ | 113 """ |
| 143 varname = var_node.name | 114 varname = var_node.name |
| 144 _node = var_node.parent | 115 _node = var_node.parent |
| 145 while _node: | 116 while _node: |
| 146 if isinstance(_node, COMP_NODE_TYPES): | 117 if isinstance(_node, COMP_NODE_TYPES): |
| 147 for ass_node in _node.nodes_of_class(astroid.AssName): | 118 for ass_node in _node.nodes_of_class(astng.AssName): |
| 148 if ass_node.name == varname: | 119 if ass_node.name == varname: |
| 149 return True | 120 return True |
| 150 elif isinstance(_node, astroid.For): | 121 elif isinstance(_node, astng.For): |
| 151 for ass_node in _node.target.nodes_of_class(astroid.AssName): | 122 for ass_node in _node.target.nodes_of_class(astng.AssName): |
| 152 if ass_node.name == varname: | 123 if ass_node.name == varname: |
| 153 return True | 124 return True |
| 154 elif isinstance(_node, astroid.With): | 125 elif isinstance(_node, astng.With): |
| 155 for expr, vars in _node.items: | 126 if _node.vars is None: |
| 156 if expr.parent_of(var_node): | 127 # quickfix : case in which 'with' is used without 'as' |
| 157 break | 128 return False |
| 158 if (vars and | 129 if _node.vars.name == varname: |
| 159 isinstance(vars, astroid.AssName) and | 130 return True |
| 160 vars.name == varname): | 131 elif isinstance(_node, (astng.Lambda, astng.Function)): |
| 161 return True | |
| 162 elif isinstance(_node, (astroid.Lambda, astroid.Function)): | |
| 163 if _node.args.is_argument(varname): | 132 if _node.args.is_argument(varname): |
| 164 return True | 133 return True |
| 165 if getattr(_node, 'name', None) == varname: | 134 if getattr(_node, 'name', None) == varname: |
| 166 return True | 135 return True |
| 167 break | 136 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 | |
| 173 _node = _node.parent | 137 _node = _node.parent |
| 174 # possibly multiple statements on the same line using semi colon separator | 138 # possibly multiple statements on the same line using semi colon separator |
| 175 stmt = var_node.statement() | 139 stmt = var_node.statement() |
| 176 _node = stmt.previous_sibling() | 140 _node = stmt.previous_sibling() |
| 177 lineno = stmt.fromlineno | 141 lineno = stmt.fromlineno |
| 178 while _node and _node.fromlineno == lineno: | 142 while _node and _node.fromlineno == lineno: |
| 179 for ass_node in _node.nodes_of_class(astroid.AssName): | 143 for ass_node in _node.nodes_of_class(astng.AssName): |
| 180 if ass_node.name == varname: | 144 if ass_node.name == varname: |
| 181 return True | 145 return True |
| 182 for imp_node in _node.nodes_of_class((astroid.From, astroid.Import)): | 146 for imp_node in _node.nodes_of_class( (astng.From, astng.Import)): |
| 183 if varname in [name[1] or name[0] for name in imp_node.names]: | 147 if varname in [name[1] or name[0] for name in imp_node.names]: |
| 184 return True | 148 return True |
| 185 _node = _node.previous_sibling() | 149 _node = _node.previous_sibling() |
| 186 return False | 150 return False |
| 187 | 151 |
| 188 def is_func_default(node): | 152 def is_func_default(node): |
| 189 """return true if the given Name node is used in function default argument's | 153 """return true if the given Name node is used in function default argument's |
| 190 value | 154 value |
| 191 """ | 155 """ |
| 192 parent = node.scope() | 156 parent = node.scope() |
| 193 if isinstance(parent, astroid.Function): | 157 if isinstance(parent, astng.Function): |
| 194 for default_node in parent.args.defaults: | 158 for default_node in parent.args.defaults: |
| 195 for default_name_node in default_node.nodes_of_class(astroid.Name): | 159 for default_name_node in default_node.nodes_of_class(astng.Name): |
| 196 if default_name_node is node: | 160 if default_name_node is node: |
| 197 return True | 161 return True |
| 198 return False | 162 return False |
| 199 | 163 |
| 200 def is_func_decorator(node): | 164 def is_func_decorator(node): |
| 201 """return true if the name is used in function decorator""" | 165 """return true if the name is used in function decorator""" |
| 202 parent = node.parent | 166 parent = node.parent |
| 203 while parent is not None: | 167 while parent is not None: |
| 204 if isinstance(parent, astroid.Decorators): | 168 if isinstance(parent, astng.Decorators): |
| 205 return True | 169 return True |
| 206 if (parent.is_statement or | 170 if parent.is_statement or isinstance(parent, astng.Lambda): |
| 207 isinstance(parent, astroid.Lambda) or | |
| 208 isinstance(parent, (scoped_nodes.ComprehensionScope, | |
| 209 scoped_nodes.ListComp))): | |
| 210 break | 171 break |
| 211 parent = parent.parent | 172 parent = parent.parent |
| 212 return False | 173 return False |
| 213 | 174 |
| 214 def is_ancestor_name(frame, node): | 175 def is_ancestor_name(frame, node): |
| 215 """return True if `frame` is a astroid.Class node with `node` in the | 176 """return True if `frame` is a astng.Class node with `node` in the |
| 216 subtree of its bases attribute | 177 subtree of its bases attribute |
| 217 """ | 178 """ |
| 218 try: | 179 try: |
| 219 bases = frame.bases | 180 bases = frame.bases |
| 220 except AttributeError: | 181 except AttributeError: |
| 221 return False | 182 return False |
| 222 for base in bases: | 183 for base in bases: |
| 223 if node in base.nodes_of_class(astroid.Name): | 184 if node in base.nodes_of_class(astng.Name): |
| 224 return True | 185 return True |
| 225 return False | 186 return False |
| 226 | 187 |
| 227 def assign_parent(node): | 188 def assign_parent(node): |
| 228 """return the higher parent which is not an AssName, Tuple or List node | 189 """return the higher parent which is not an AssName, Tuple or List node |
| 229 """ | 190 """ |
| 230 while node and isinstance(node, (astroid.AssName, | 191 while node and isinstance(node, (astng.AssName, |
| 231 astroid.Tuple, | 192 astng.Tuple, |
| 232 astroid.List)): | 193 astng.List)): |
| 233 node = node.parent | 194 node = node.parent |
| 234 return node | 195 return node |
| 235 | 196 |
| 236 def overrides_an_abstract_method(class_node, name): | 197 def overrides_an_abstract_method(class_node, name): |
| 237 """return True if pnode is a parent of node""" | 198 """return True if pnode is a parent of node""" |
| 238 for ancestor in class_node.ancestors(): | 199 for ancestor in class_node.ancestors(): |
| 239 if name in ancestor and isinstance(ancestor[name], astroid.Function) and
\ | 200 if name in ancestor and isinstance(ancestor[name], astng.Function) and \ |
| 240 ancestor[name].is_abstract(pass_is_abstract=False): | 201 ancestor[name].is_abstract(pass_is_abstract=False): |
| 241 return True | 202 return True |
| 242 return False | 203 return False |
| 243 | 204 |
| 244 def overrides_a_method(class_node, name): | 205 def overrides_a_method(class_node, name): |
| 245 """return True if <name> is a method overridden from an ancestor""" | 206 """return True if <name> is a method overridden from an ancestor""" |
| 246 for ancestor in class_node.ancestors(): | 207 for ancestor in class_node.ancestors(): |
| 247 if name in ancestor and isinstance(ancestor[name], astroid.Function): | 208 if name in ancestor and isinstance(ancestor[name], astng.Function): |
| 248 return True | 209 return True |
| 249 return False | 210 return False |
| 250 | 211 |
| 251 PYMETHODS = set(('__new__', '__init__', '__del__', '__hash__', | 212 PYMETHODS = set(('__new__', '__init__', '__del__', '__hash__', |
| 252 '__str__', '__repr__', | 213 '__str__', '__repr__', |
| 253 '__len__', '__iter__', | 214 '__len__', '__iter__', |
| 254 '__delete__', '__get__', '__set__', | 215 '__delete__', '__get__', '__set__', |
| 255 '__getitem__', '__setitem__', '__delitem__', '__contains__', | 216 '__getitem__', '__setitem__', '__delitem__', '__contains__', |
| 256 '__getattribute__', '__getattr__', '__setattr__', '__delattr__'
, | 217 '__getattribute__', '__getattr__', '__setattr__', '__delattr__'
, |
| 257 '__call__', | 218 '__call__', |
| 258 '__enter__', '__exit__', | 219 '__enter__', '__exit__', |
| 259 '__cmp__', '__ge__', '__gt__', '__le__', '__lt__', '__eq__', | 220 '__cmp__', '__ge__', '__gt__', '__le__', '__lt__', '__eq__', |
| 260 '__nonzero__', '__neg__', '__invert__', | 221 '__nonzero__', '__neg__', '__invert__', |
| 261 '__mul__', '__imul__', '__rmul__', | 222 '__mul__', '__imul__', '__rmul__', |
| 262 '__div__', '__idiv__', '__rdiv__', | 223 '__div__', '__idiv__', '__rdiv__', |
| 263 '__add__', '__iadd__', '__radd__', | 224 '__add__', '__iadd__', '__radd__', |
| 264 '__sub__', '__isub__', '__rsub__', | 225 '__sub__', '__isub__', '__rsub__', |
| 265 '__pow__', '__ipow__', '__rpow__', | 226 '__pow__', '__ipow__', '__rpow__', |
| 266 '__mod__', '__imod__', '__rmod__', | 227 '__mod__', '__imod__', '__rmod__', |
| 267 '__and__', '__iand__', '__rand__', | 228 '__and__', '__iand__', '__rand__', |
| 268 '__or__', '__ior__', '__ror__', | 229 '__or__', '__ior__', '__ror__', |
| 269 '__xor__', '__ixor__', '__rxor__', | 230 '__xor__', '__ixor__', '__rxor__', |
| 270 # XXX To be continued | 231 # XXX To be continued |
| 271 )) | 232 )) |
| 272 | 233 |
| 273 def check_messages(*messages): | 234 def check_messages(*messages): |
| 274 """decorator to store messages that are handled by a checker method""" | 235 """decorator to store messages that are handled by a checker method""" |
| 275 | 236 |
| 276 def store_messages(func): | 237 def store_messages(func): |
| 277 func.checks_msgs = messages | 238 func.checks_msgs = messages |
| 278 return func | 239 return func |
| 279 return store_messages | 240 return store_messages |
| 280 | 241 |
| 281 class IncompleteFormatString(Exception): | 242 class IncompleteFormatString(Exception): |
| (...skipping 15 matching lines...) Expand all Loading... |
| 297 parse error occurs.""" | 258 parse error occurs.""" |
| 298 keys = set() | 259 keys = set() |
| 299 num_args = 0 | 260 num_args = 0 |
| 300 def next_char(i): | 261 def next_char(i): |
| 301 i += 1 | 262 i += 1 |
| 302 if i == len(format_string): | 263 if i == len(format_string): |
| 303 raise IncompleteFormatString | 264 raise IncompleteFormatString |
| 304 return (i, format_string[i]) | 265 return (i, format_string[i]) |
| 305 i = 0 | 266 i = 0 |
| 306 while i < len(format_string): | 267 while i < len(format_string): |
| 307 char = format_string[i] | 268 c = format_string[i] |
| 308 if char == '%': | 269 if c == '%': |
| 309 i, char = next_char(i) | 270 i, c = next_char(i) |
| 310 # Parse the mapping key (optional). | 271 # Parse the mapping key (optional). |
| 311 key = None | 272 key = None |
| 312 if char == '(': | 273 if c == '(': |
| 313 depth = 1 | 274 depth = 1 |
| 314 i, char = next_char(i) | 275 i, c = next_char(i) |
| 315 key_start = i | 276 key_start = i |
| 316 while depth != 0: | 277 while depth != 0: |
| 317 if char == '(': | 278 if c == '(': |
| 318 depth += 1 | 279 depth += 1 |
| 319 elif char == ')': | 280 elif c == ')': |
| 320 depth -= 1 | 281 depth -= 1 |
| 321 i, char = next_char(i) | 282 i, c = next_char(i) |
| 322 key_end = i - 1 | 283 key_end = i - 1 |
| 323 key = format_string[key_start:key_end] | 284 key = format_string[key_start:key_end] |
| 324 | 285 |
| 325 # Parse the conversion flags (optional). | 286 # Parse the conversion flags (optional). |
| 326 while char in '#0- +': | 287 while c in '#0- +': |
| 327 i, char = next_char(i) | 288 i, c = next_char(i) |
| 328 # Parse the minimum field width (optional). | 289 # Parse the minimum field width (optional). |
| 329 if char == '*': | 290 if c == '*': |
| 330 num_args += 1 | 291 num_args += 1 |
| 331 i, char = next_char(i) | 292 i, c = next_char(i) |
| 332 else: | 293 else: |
| 333 while char in string.digits: | 294 while c in string.digits: |
| 334 i, char = next_char(i) | 295 i, c = next_char(i) |
| 335 # Parse the precision (optional). | 296 # Parse the precision (optional). |
| 336 if char == '.': | 297 if c == '.': |
| 337 i, char = next_char(i) | 298 i, c = next_char(i) |
| 338 if char == '*': | 299 if c == '*': |
| 339 num_args += 1 | 300 num_args += 1 |
| 340 i, char = next_char(i) | 301 i, c = next_char(i) |
| 341 else: | 302 else: |
| 342 while char in string.digits: | 303 while c in string.digits: |
| 343 i, char = next_char(i) | 304 i, c = next_char(i) |
| 344 # Parse the length modifier (optional). | 305 # Parse the length modifier (optional). |
| 345 if char in 'hlL': | 306 if c in 'hlL': |
| 346 i, char = next_char(i) | 307 i, c = next_char(i) |
| 347 # Parse the conversion type (mandatory). | 308 # Parse the conversion type (mandatory). |
| 348 if PY3K: | 309 if c not in 'diouxXeEfFgGcrs%': |
| 349 flags = 'diouxXeEfFgGcrs%a' | |
| 350 else: | |
| 351 flags = 'diouxXeEfFgGcrs%' | |
| 352 if char not in flags: | |
| 353 raise UnsupportedFormatCharacter(i) | 310 raise UnsupportedFormatCharacter(i) |
| 354 if key: | 311 if key: |
| 355 keys.add(key) | 312 keys.add(key) |
| 356 elif char != '%': | 313 elif c != '%': |
| 357 num_args += 1 | 314 num_args += 1 |
| 358 i += 1 | 315 i += 1 |
| 359 return keys, num_args | 316 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 |