UtilitiesPython2/py2flakes/checker.py

branch
5_4_x
changeset 3551
d608f3f42d71
parent 3160
209a07d7e401
equal deleted inserted replaced
3548:509de9ee6cb8 3551:d608f3f42d71
5 # Original (c) 2005-2010 Divmod, Inc. 5 # Original (c) 2005-2010 Divmod, Inc.
6 # 6 #
7 # This module is based on pyflakes for Python2 but was heavily hacked to 7 # This module is based on pyflakes for Python2 but was heavily hacked to
8 # work within eric5 8 # work within eric5
9 9
10 import __builtin__ 10 """
11 import os.path 11 Main module.
12 import _ast 12
13 13 Implement the central Checker class.
14 from py2flakes import messages 14 Also, it models the Bindings and Scopes.
15 15 """
16 16 import doctest
17 # utility function to iterate over an AST node's children, adapted 17 import os
18 # from Python 2.6's standard ast module 18 import sys
19
20 PY2 = sys.version_info < (3, 0)
21 PY32 = sys.version_info < (3, 3) # Python 2.5 to 3.2
22 PY33 = sys.version_info < (3, 4) # Python 2.5 to 3.3
23 builtin_vars = dir(__import__('__builtin__' if PY2 else 'builtins'))
24
19 try: 25 try:
20 import ast 26 import ast
21 iter_child_nodes = ast.iter_child_nodes 27 except ImportError: # Python 2.5
22 except (ImportError, AttributeError): 28 import _ast as ast
23 def iter_child_nodes(node, astcls=_ast.AST): 29
24 """ 30 if 'decorator_list' not in ast.ClassDef._fields:
25 Yield all direct child nodes of *node*, that is, all fields that are 31 # Patch the missing attribute 'decorator_list'
26 nodes and all items of fields that are lists of nodes. 32 ast.ClassDef.decorator_list = ()
27 """ 33 ast.FunctionDef.decorator_list = property(lambda s: s.decorators)
28 for name in node._fields: 34
29 field = getattr(node, name, None) 35 from py2flakes import messages
30 if isinstance(field, astcls): 36
31 yield field 37
32 elif isinstance(field, list): 38 if PY2:
33 for item in field: 39 def getNodeType(node_class):
34 yield item 40 # workaround str.upper() which is locale-dependent
41 return str(unicode(node_class.__name__).upper()) # __IGNORE_WARNING__
42 else:
43 def getNodeType(node_class):
44 return node_class.__name__.upper()
45
46 # Python >= 3.3 uses ast.Try instead of (ast.TryExcept + ast.TryFinally)
47 if PY32:
48 def getAlternatives(n):
49 if isinstance(n, (ast.If, ast.TryFinally)):
50 return [n.body]
51 if isinstance(n, ast.TryExcept):
52 return [n.body + n.orelse] + [[hdl] for hdl in n.handlers]
53 else:
54 def getAlternatives(n):
55 if isinstance(n, ast.If):
56 return [n.body]
57 if isinstance(n, ast.Try):
58 return [n.body + n.orelse] + [[hdl] for hdl in n.handlers]
59
60
61 class _FieldsOrder(dict):
62 """Fix order of AST node fields."""
63
64 def _get_fields(self, node_class):
65 # handle iter before target, and generators before element
66 fields = node_class._fields
67 if 'iter' in fields:
68 key_first = 'iter'.find
69 elif 'generators' in fields:
70 key_first = 'generators'.find
71 else:
72 key_first = 'value'.find
73 return tuple(sorted(fields, key=key_first, reverse=True))
74
75 def __missing__(self, node_class):
76 self[node_class] = fields = self._get_fields(node_class)
77 return fields
78
79
80 def iter_child_nodes(node, omit=None, _fields_order=_FieldsOrder()):
81 """
82 Yield all direct child nodes of *node*, that is, all fields that
83 are nodes and all items of fields that are lists of nodes.
84 """
85 for name in _fields_order[node.__class__]:
86 if name == omit:
87 continue
88 field = getattr(node, name, None)
89 if isinstance(field, ast.AST):
90 yield field
91 elif isinstance(field, list):
92 for item in field:
93 yield item
35 94
36 95
37 class Binding(object): 96 class Binding(object):
38 """ 97 """
39 Represents the binding of a value to a name. 98 Represents the binding of a value to a name.
40 99
41 The checker uses this to keep track of which names have been bound and 100 The checker uses this to keep track of which names have been bound and
42 which names have not. See L{Assignment} for a special type of binding that 101 which names have not. See L{Assignment} for a special type of binding that
43 is checked with stricter rules. 102 is checked with stricter rules.
44 """ 103
104 @ivar used: pair of (L{Scope}, line-number) indicating the scope and
105 line number that this binding was last used
106 """
107
45 def __init__(self, name, source): 108 def __init__(self, name, source):
46 self.name = name 109 self.name = name
47 self.source = source 110 self.source = source
48 self.used = False 111 self.used = False
49 112
50 def __str__(self): 113 def __str__(self):
51 return self.name 114 return self.name
52 115
53 def __repr__(self): 116 def __repr__(self):
54 return '<%s object %r from line %r at 0x%x>' % ( 117 return '<%s object %r from line %r at 0x%x>' % (self.__class__.__name__,
55 self.__class__.__name__, 118 self.name,
56 self.name, 119 self.source.lineno,
57 self.source.lineno, 120 id(self))
58 id(self)) 121
59 122 def redefines(self, other):
60 123 return isinstance(other, Definition) and self.name == other.name
61 class UnBinding(Binding): 124
62 ''' 125
63 Created by the 'del' operator. 126 class Definition(Binding):
64 ''' 127 """
65 128 A binding that defines a function or a class.
66 129 """
67 class Importation(Binding): 130
131
132 class Importation(Definition):
68 """ 133 """
69 A binding created by an import statement. 134 A binding created by an import statement.
70 """ 135
136 @ivar fullName: The complete name given to the import statement,
137 possibly including multiple dotted components.
138 @type fullName: C{str}
139 """
140
71 def __init__(self, name, source): 141 def __init__(self, name, source):
72 self.fullName = name 142 self.fullName = name
143 self.redefined = []
73 name = name.split('.')[0] 144 name = name.split('.')[0]
74 super(Importation, self).__init__(name, source) 145 super(Importation, self).__init__(name, source)
146
147 def redefines(self, other):
148 if isinstance(other, Importation):
149 return self.fullName == other.fullName
150 return isinstance(other, Definition) and self.name == other.name
75 151
76 152
77 class Argument(Binding): 153 class Argument(Binding):
78 """ 154 """
79 Represents binding a name as an argument. 155 Represents binding a name as an argument.
88 the checker does not consider assignments in tuple/list unpacking to be 164 the checker does not consider assignments in tuple/list unpacking to be
89 Assignments, rather it treats them as simple Bindings. 165 Assignments, rather it treats them as simple Bindings.
90 """ 166 """
91 167
92 168
93 class FunctionDefinition(Binding): 169 class FunctionDefinition(Definition):
94 """
95 Represents a function definition.
96 """
97 pass 170 pass
98 171
99 172
173 class ClassDefinition(Definition):
174 pass
175
176
100 class ExportBinding(Binding): 177 class ExportBinding(Binding):
101 """ 178 """
102 A binding created by an __all__ assignment. If the names in the list 179 A binding created by an C{__all__} assignment. If the names in the list
103 can be determined statically, they will be treated as names for export and 180 can be determined statically, they will be treated as names for export and
104 additional checking applied to them. 181 additional checking applied to them.
105 182
106 The only __all__ assignment that can be recognized is one which takes 183 The only C{__all__} assignment that can be recognized is one which takes
107 the value of a literal list containing literal strings. For example:: 184 the value of a literal list containing literal strings. For example::
108 185
109 __all__ = ["foo", "bar"] 186 __all__ = ["foo", "bar"]
110 187
111 Names which are imported and not otherwise used but appear in the value of 188 Names which are imported and not otherwise used but appear in the value of
112 __all__ will not have an unused import warning reported for them. 189 C{__all__} will not have an unused import warning reported for them.
113 """ 190 """
114 def names(self): 191
115 """ 192 def __init__(self, name, source, scope):
116 Return a list of the names referenced by this binding. 193 if '__all__' in scope and isinstance(source, ast.AugAssign):
117 """ 194 self.names = list(scope['__all__'].names)
118 names = [] 195 else:
119 if isinstance(self.source, _ast.List): 196 self.names = []
120 for node in self.source.elts: 197 if isinstance(source.value, (ast.List, ast.Tuple)):
121 if isinstance(node, _ast.Str): 198 for node in source.value.elts:
122 names.append(node.s) 199 if isinstance(node, ast.Str):
123 return names 200 self.names.append(node.s)
201 super(ExportBinding, self).__init__(name, source)
124 202
125 203
126 class Scope(dict): 204 class Scope(dict):
127 """
128 Class defining the scope base class.
129 """
130 importStarred = False # set to True when import * is found 205 importStarred = False # set to True when import * is found
131 206
132 def __repr__(self): 207 def __repr__(self):
133 return '<%s at 0x%x %s>' % ( 208 scope_cls = self.__class__.__name__
134 self.__class__.__name__, id(self), dict.__repr__(self)) 209 return '<%s at 0x%x %s>' % (scope_cls, id(self), dict.__repr__(self))
135
136 def __init__(self):
137 super(Scope, self).__init__()
138 210
139 211
140 class ClassScope(Scope): 212 class ClassScope(Scope):
141 """
142 Class representing a name scope for a class.
143 """
144 pass 213 pass
145 214
146 215
147 class FunctionScope(Scope): 216 class FunctionScope(Scope):
148 """ 217 """
149 Class representing a name scope for a function. 218 I represent a name scope for a function.
150 """ 219
220 @ivar globals: Names declared 'global' in this function.
221 """
222 usesLocals = False
223 alwaysUsed = set(['__tracebackhide__',
224 '__traceback_info__', '__traceback_supplement__'])
225
151 def __init__(self): 226 def __init__(self):
152 super(FunctionScope, self).__init__() 227 super(FunctionScope, self).__init__()
153 self.globals = {} 228 # Simplify: manage the special locals as globals
229 self.globals = self.alwaysUsed.copy()
230 self.returnValue = None # First non-empty return
231 self.isGenerator = False # Detect a generator
232
233 def unusedAssignments(self):
234 """
235 Return a generator for the assignments which have not been used.
236 """
237 for name, binding in self.items():
238 if (not binding.used and name not in self.globals
239 and not self.usesLocals
240 and isinstance(binding, Assignment)):
241 yield name, binding
242
243
244 class GeneratorScope(Scope):
245 pass
154 246
155 247
156 class ModuleScope(Scope): 248 class ModuleScope(Scope):
157 """
158 Class representing a name scope for a module.
159 """
160 pass 249 pass
161 250
162 # Globally defined names which are not attributes of the __builtin__ module. 251
163 _MAGIC_GLOBALS = ['__file__', '__builtins__'] 252 # Globally defined names which are not attributes of the builtins module, or
253 # are only present on some platforms.
254 _MAGIC_GLOBALS = ['__file__', '__builtins__', 'WindowsError']
255
256
257 def getNodeName(node):
258 # Returns node.id, or node.name, or None
259 if hasattr(node, 'id'): # One of the many nodes with an id
260 return node.id
261 if hasattr(node, 'name'): # a ExceptHandler node
262 return node.name
164 263
165 264
166 class Checker(object): 265 class Checker(object):
167 """ 266 """
168 Class to check the cleanliness and sanity of Python code. 267 I check the cleanliness and sanity of Python code.
169 """ 268
269 @ivar _deferredFunctions: Tracking list used by L{deferFunction}. Elements
270 of the list are two-tuples. The first element is the callable passed
271 to L{deferFunction}. The second element is a copy of the scope stack
272 at the time L{deferFunction} was called.
273
274 @ivar _deferredAssignments: Similar to C{_deferredFunctions}, but for
275 callables which are deferred assignment checks.
276 """
277
170 nodeDepth = 0 278 nodeDepth = 0
279 offset = None
171 traceTree = False 280 traceTree = False
172 281
173 def __init__(self, module, filename='(none)'): 282 builtIns = set(builtin_vars).union(_MAGIC_GLOBALS)
174 """ 283 _customBuiltIns = os.environ.get('PYFLAKES_BUILTINS')
175 Constructor 284 if _customBuiltIns:
176 285 builtIns.update(_customBuiltIns.split(','))
177 @param module parsed module tree or module source code 286 del _customBuiltIns
178 @param filename name of the module file (string) 287
179 """ 288 def __init__(self, tree, filename='(none)', builtins=None,
289 withDoctest='PYFLAKES_NODOCTEST' not in os.environ):
290 self._nodeHandlers = {}
180 self._deferredFunctions = [] 291 self._deferredFunctions = []
181 self._deferredAssignments = [] 292 self._deferredAssignments = []
182 self.dead_scopes = [] 293 self.deadScopes = []
183 self.messages = [] 294 self.messages = []
184 self.filename = filename 295 self.filename = filename
296 if builtins:
297 self.builtIns = self.builtIns.union(builtins)
298 self.withDoctest = withDoctest
185 self.scopeStack = [ModuleScope()] 299 self.scopeStack = [ModuleScope()]
300 self.exceptHandlers = [()]
186 self.futuresAllowed = True 301 self.futuresAllowed = True
187 302
188 if isinstance(module, str): 303 ## added for eric5
189 module = ast.parse(module, filename, "exec") 304 if isinstance(tree, basestring):
190 self.handleChildren(module) 305 tree = compile(tree, filename, "exec", ast.PyCF_ONLY_AST)
191 self._runDeferred(self._deferredFunctions) 306 ## end added for eric5
307 self.root = tree
308 self.handleChildren(tree)
309 self.runDeferred(self._deferredFunctions)
192 # Set _deferredFunctions to None so that deferFunction will fail 310 # Set _deferredFunctions to None so that deferFunction will fail
193 # noisily if called after we've run through the deferred functions. 311 # noisily if called after we've run through the deferred functions.
194 self._deferredFunctions = None 312 self._deferredFunctions = None
195 self._runDeferred(self._deferredAssignments) 313 self.runDeferred(self._deferredAssignments)
196 # Set _deferredAssignments to None so that deferAssignment will fail 314 # Set _deferredAssignments to None so that deferAssignment will fail
197 # noisly if called after we've run through the deferred assignments. 315 # noisily if called after we've run through the deferred assignments.
198 self._deferredAssignments = None 316 self._deferredAssignments = None
199 del self.scopeStack[1:] 317 del self.scopeStack[1:]
200 self.popScope() 318 self.popScope()
201 self.check_dead_scopes() 319 self.checkDeadScopes()
202 320
203 def deferFunction(self, callable): 321 def deferFunction(self, callable):
204 ''' 322 """
205 Schedule a function handler to be called just before completion. 323 Schedule a function handler to be called just before completion.
206 324
207 This is used for handling function bodies, which must be deferred 325 This is used for handling function bodies, which must be deferred
208 because code later in the file might modify the global scope. When 326 because code later in the file might modify the global scope. When
209 `callable` is called, the scope at the time this is called will be 327 `callable` is called, the scope at the time this is called will be
210 restored, however it will contain any new bindings added to it. 328 restored, however it will contain any new bindings added to it.
211 ''' 329 """
212 self._deferredFunctions.append((callable, self.scopeStack[:])) 330 self._deferredFunctions.append((callable, self.scopeStack[:], self.offset))
213 331
214 def deferAssignment(self, callable): 332 def deferAssignment(self, callable):
215 """ 333 """
216 Schedule an assignment handler to be called just after deferred 334 Schedule an assignment handler to be called just after deferred
217 function handlers. 335 function handlers.
218 """ 336 """
219 self._deferredAssignments.append((callable, self.scopeStack[:])) 337 self._deferredAssignments.append((callable, self.scopeStack[:], self.offset))
220 338
221 def _runDeferred(self, deferred): 339 def runDeferred(self, deferred):
222 """ 340 """
223 Run the callables in deferred using their associated scope stack. 341 Run the callables in C{deferred} using their associated scope stack.
224 """ 342 """
225 for handler, scope in deferred: 343 for handler, scope, offset in deferred:
226 self.scopeStack = scope 344 self.scopeStack = scope
345 self.offset = offset
227 handler() 346 handler()
228 347
348 @property
229 def scope(self): 349 def scope(self):
230 return self.scopeStack[-1] 350 return self.scopeStack[-1]
231 scope = property(scope)
232 351
233 def popScope(self): 352 def popScope(self):
234 self.dead_scopes.append(self.scopeStack.pop()) 353 self.deadScopes.append(self.scopeStack.pop())
235 354
236 def check_dead_scopes(self): 355 def checkDeadScopes(self):
237 """ 356 """
238 Look at scopes which have been fully examined and report names in them 357 Look at scopes which have been fully examined and report names in them
239 which were imported but unused. 358 which were imported but unused.
240 """ 359 """
241 for scope in self.dead_scopes: 360 for scope in self.deadScopes:
242 export = isinstance(scope.get('__all__'), ExportBinding) 361 if isinstance(scope.get('__all__'), ExportBinding):
243 if export: 362 all_names = set(scope['__all__'].names)
244 all = scope['__all__'].names() 363 if not scope.importStarred and \
245 if os.path.split(self.filename)[1] != '__init__.py': 364 os.path.basename(self.filename) != '__init__.py':
246 # Look for possible mistakes in the export list 365 # Look for possible mistakes in the export list
247 undefined = set(all) - set(scope) 366 undefined = all_names.difference(scope)
248 for name in undefined: 367 for name in undefined:
249 self.report( 368 self.report(messages.UndefinedExport,
250 messages.UndefinedExport, 369 scope['__all__'].source, name)
251 scope['__all__'].source.lineno,
252 name)
253 else: 370 else:
254 all = [] 371 all_names = []
255 372
256 # Look for imported names that aren't used. 373 # Look for imported names that aren't used.
257 for importation in scope.itervalues(): 374 for value in scope.values():
258 if isinstance(importation, Importation): 375 if isinstance(value, Importation):
259 if not importation.used and importation.name not in all: 376 used = value.used or value.name in all_names
260 self.report( 377 if not used:
261 messages.UnusedImport, 378 messg = messages.UnusedImport
262 importation.source.lineno, 379 self.report(messg, value.source, value.name)
263 importation.name) 380 for node in value.redefined:
264 381 if isinstance(self.getParent(node), ast.For):
265 def pushFunctionScope(self): 382 messg = messages.ImportShadowedByLoopVar
266 self.scopeStack.append(FunctionScope()) 383 elif used:
267 384 continue
268 def pushClassScope(self): 385 else:
269 self.scopeStack.append(ClassScope()) 386 messg = messages.RedefinedWhileUnused
387 self.report(messg, node, value.name, value.source)
388
389 def pushScope(self, scopeClass=FunctionScope):
390 self.scopeStack.append(scopeClass())
270 391
271 def report(self, messageClass, *args, **kwargs): 392 def report(self, messageClass, *args, **kwargs):
272 self.messages.append(messageClass(self.filename, *args, **kwargs)) 393 self.messages.append(messageClass(self.filename, *args, **kwargs))
273 394
274 def handleChildren(self, tree): 395 def getParent(self, node):
275 for node in iter_child_nodes(tree): 396 # Lookup the first parent which is not Tuple, List or Starred
276 self.handleNode(node, tree) 397 while True:
277 398 node = node.parent
278 def isDocstring(self, node): 399 if not hasattr(node, 'elts') and not hasattr(node, 'ctx'):
279 """ 400 return node
280 Determine if the given node is a docstring, as long as it is at the 401
281 correct place in the node tree. 402 def getCommonAncestor(self, lnode, rnode, stop):
282 """ 403 if stop in (lnode, rnode) or not (hasattr(lnode, 'parent') and
283 return isinstance(node, _ast.Str) or \ 404 hasattr(rnode, 'parent')):
284 (isinstance(node, _ast.Expr) and 405 return None
285 isinstance(node.value, _ast.Str)) 406 if lnode is rnode:
286 407 return lnode
287 def handleNode(self, node, parent): 408
288 if node: 409 if (lnode.depth > rnode.depth):
289 node.parent = parent 410 return self.getCommonAncestor(lnode.parent, rnode, stop)
290 if self.traceTree: 411 if (lnode.depth < rnode.depth):
291 print ' ' * self.nodeDepth + node.__class__.__name__ 412 return self.getCommonAncestor(lnode, rnode.parent, stop)
292 self.nodeDepth += 1 413 return self.getCommonAncestor(lnode.parent, rnode.parent, stop)
293 if self.futuresAllowed and not \ 414
294 (isinstance(node, _ast.ImportFrom) or 415 def descendantOf(self, node, ancestors, stop):
295 self.isDocstring(node)): 416 for a in ancestors:
296 self.futuresAllowed = False 417 if self.getCommonAncestor(node, a, stop):
297 nodeType = node.__class__.__name__.upper() 418 return True
419 return False
420
421 def differentForks(self, lnode, rnode):
422 """True, if lnode and rnode are located on different forks of IF/TRY"""
423 ancestor = self.getCommonAncestor(lnode, rnode, self.root)
424 parts = getAlternatives(ancestor)
425 if parts:
426 for items in parts:
427 if self.descendantOf(lnode, items, ancestor) ^ \
428 self.descendantOf(rnode, items, ancestor):
429 return True
430 return False
431
432 def addBinding(self, node, value):
433 """
434 Called when a binding is altered.
435
436 - `node` is the statement responsible for the change
437 - `value` is the new value, a Binding instance
438 """
439 # assert value.source in (node, node.parent):
440 for scope in self.scopeStack[::-1]:
441 if value.name in scope:
442 break
443 existing = scope.get(value.name)
444
445 if existing and not self.differentForks(node, existing.source):
446
447 parent_stmt = self.getParent(value.source)
448 if isinstance(existing, Importation) and isinstance(parent_stmt, ast.For):
449 self.report(messages.ImportShadowedByLoopVar,
450 node, value.name, existing.source)
451
452 elif scope is self.scope:
453 if (isinstance(parent_stmt, ast.comprehension) and
454 not isinstance(self.getParent(existing.source),
455 (ast.For, ast.comprehension))):
456 self.report(messages.RedefinedInListComp,
457 node, value.name, existing.source)
458 elif not existing.used and value.redefines(existing):
459 self.report(messages.RedefinedWhileUnused,
460 node, value.name, existing.source)
461
462 elif isinstance(existing, Importation) and value.redefines(existing):
463 existing.redefined.append(node)
464
465 self.scope[value.name] = value
466
467 def getNodeHandler(self, node_class):
468 try:
469 return self._nodeHandlers[node_class]
470 except KeyError:
471 nodeType = getNodeType(node_class)
472 self._nodeHandlers[node_class] = handler = getattr(self, nodeType)
473 return handler
474
475 def handleNodeLoad(self, node):
476 name = getNodeName(node)
477 if not name:
478 return
479 # try local scope
480 try:
481 self.scope[name].used = (self.scope, node)
482 except KeyError:
483 pass
484 else:
485 return
486
487 scopes = [scope for scope in self.scopeStack[:-1]
488 if isinstance(scope, (FunctionScope, ModuleScope))]
489 if isinstance(self.scope, GeneratorScope) and scopes[-1] != self.scopeStack[-2]:
490 scopes.append(self.scopeStack[-2])
491
492 # try enclosing function scopes and global scope
493 importStarred = self.scope.importStarred
494 for scope in reversed(scopes):
495 importStarred = importStarred or scope.importStarred
298 try: 496 try:
299 handler = getattr(self, nodeType) 497 scope[name].used = (self.scope, node)
300 handler(node)
301 except AttributeError:
302 print nodeType, "not supported yet. Please report this."
303 finally:
304 self.nodeDepth -= 1
305 if self.traceTree:
306 print ' ' * self.nodeDepth + 'end ' + node.__class__.__name__
307
308 def ignore(self, node):
309 pass
310
311 # "stmt" type nodes
312 RETURN = DELETE = PRINT = WHILE = IF = WITH = RAISE = TRYEXCEPT = \
313 TRYFINALLY = ASSERT = EXEC = EXPR = handleChildren
314
315 CONTINUE = BREAK = PASS = ignore
316
317 # "expr" type nodes
318 BOOLOP = BINOP = UNARYOP = IFEXP = DICT = SET = YIELD = COMPARE = \
319 CALL = REPR = ATTRIBUTE = SUBSCRIPT = LIST = TUPLE = handleChildren
320
321 NUM = STR = ELLIPSIS = ignore
322
323 # "slice" type nodes
324 SLICE = EXTSLICE = INDEX = handleChildren
325
326 # expression contexts are node instances too, though being constants
327 LOAD = STORE = DEL = AUGLOAD = AUGSTORE = PARAM = ignore
328
329 # same for operators
330 AND = OR = ADD = SUB = MULT = DIV = MOD = POW = LSHIFT = RSHIFT = \
331 BITOR = BITXOR = BITAND = FLOORDIV = INVERT = NOT = UADD = USUB = \
332 EQ = NOTEQ = LT = LTE = GT = GTE = IS = ISNOT = IN = NOTIN = ignore
333
334 # additional node types
335 COMPREHENSION = EXCEPTHANDLER = KEYWORD = handleChildren
336
337 def addBinding(self, lineno, value, reportRedef=True):
338 '''
339 Called when a binding is altered.
340
341 @param lineno line of the statement responsible for the change
342 (integer)
343 @param value the optional new value, a Binding instance, associated
344 with the binding; if None, the binding is deleted if it exists
345 @param reportRedef flag indicating if rebinding while unused will be
346 reported (boolean)
347 '''
348 if (isinstance(self.scope.get(value.name), FunctionDefinition)
349 and isinstance(value, FunctionDefinition)):
350 self.report(messages.RedefinedFunction,
351 lineno, value.name,
352 self.scope[value.name].source.lineno)
353
354 if not isinstance(self.scope, ClassScope):
355 for scope in self.scopeStack[::-1]:
356 existing = scope.get(value.name)
357 if (isinstance(existing, Importation)
358 and not existing.used
359 and (not isinstance(value, Importation) or
360 value.fullName == existing.fullName)
361 and reportRedef):
362
363 self.report(messages.RedefinedWhileUnused,
364 lineno, value.name,
365 scope[value.name].source.lineno)
366
367 if isinstance(value, UnBinding):
368 try:
369 del self.scope[value.name]
370 except KeyError:
371 self.report(messages.UndefinedName, lineno, value.name)
372 else:
373 self.scope[value.name] = value
374
375 def GLOBAL(self, node):
376 """
377 Keep track of globals declarations.
378 """
379 if isinstance(self.scope, FunctionScope):
380 self.scope.globals.update(dict.fromkeys(node.names))
381
382 def LISTCOMP(self, node):
383 # handle generators before element
384 for gen in node.generators:
385 self.handleNode(gen, node)
386 self.handleNode(node.elt, node)
387
388 GENERATOREXP = SETCOMP = LISTCOMP
389
390 # dictionary comprehensions; introduced in Python 2.7
391 def DICTCOMP(self, node):
392 for gen in node.generators:
393 self.handleNode(gen, node)
394 self.handleNode(node.key, node)
395 self.handleNode(node.value, node)
396
397 def FOR(self, node):
398 """
399 Process bindings for loop variables.
400 """
401 vars = []
402
403 def collectLoopVars(n):
404 if isinstance(n, _ast.Name):
405 vars.append(n.id)
406 elif isinstance(n, _ast.expr_context):
407 return
408 else:
409 for c in iter_child_nodes(n):
410 collectLoopVars(c)
411
412 collectLoopVars(node.target)
413 for varn in vars:
414 if (isinstance(self.scope.get(varn), Importation)
415 # unused ones will get an unused import warning
416 and self.scope[varn].used):
417 self.report(messages.ImportShadowedByLoopVar,
418 node.lineno, varn, self.scope[varn].source.lineno)
419
420 self.handleChildren(node)
421
422 def NAME(self, node):
423 """
424 Locate the name in locals / function / globals scopes.
425 """
426 if isinstance(node.ctx, (_ast.Load, _ast.AugLoad)):
427 # try local scope
428 importStarred = self.scope.importStarred
429 try:
430 self.scope[node.id].used = (self.scope, node.lineno)
431 except KeyError: 498 except KeyError:
432 pass 499 pass
433 else: 500 else:
434 return 501 return
435 502
436 # try enclosing function scopes 503 # look in the built-ins
437 504 if importStarred or name in self.builtIns:
438 for scope in self.scopeStack[-2:0:-1]: 505 return
439 importStarred = importStarred or scope.importStarred 506 if name == '__path__' and os.path.basename(self.filename) == '__init__.py':
440 if not isinstance(scope, FunctionScope): 507 # the special name __path__ is valid only in packages
508 return
509
510 # protected with a NameError handler?
511 if 'NameError' not in self.exceptHandlers[-1]:
512 self.report(messages.UndefinedName, node, name)
513
514 def handleNodeStore(self, node):
515 name = getNodeName(node)
516 if not name:
517 return
518 # if the name hasn't already been defined in the current scope
519 if isinstance(self.scope, FunctionScope) and name not in self.scope:
520 # for each function or module scope above us
521 for scope in self.scopeStack[:-1]:
522 if not isinstance(scope, (FunctionScope, ModuleScope)):
441 continue 523 continue
442 try: 524 # if the name was defined in that scope, and the name has
443 scope[node.id].used = (self.scope, node.lineno) 525 # been accessed already in the current scope, and hasn't
444 except KeyError: 526 # been declared global
445 pass 527 used = name in scope and scope[name].used
446 else: 528 if used and used[0] is self.scope and name not in self.scope.globals:
447 return 529 # then it's probably a mistake
448 530 self.report(messages.UndefinedLocal,
449 # try global scope 531 scope[name].used[1], name, scope[name].source)
450 532 break
451 importStarred = importStarred or self.scopeStack[0].importStarred 533
534 parent_stmt = self.getParent(node)
535 if isinstance(parent_stmt, (ast.For, ast.comprehension)) or (
536 parent_stmt != node.parent and
537 not self.isLiteralTupleUnpacking(parent_stmt)):
538 binding = Binding(name, node)
539 elif name == '__all__' and isinstance(self.scope, ModuleScope):
540 binding = ExportBinding(name, node.parent, self.scope)
541 else:
542 binding = Assignment(name, node)
543 if name in self.scope:
544 binding.used = self.scope[name].used
545 self.addBinding(node, binding)
546
547 def handleNodeDelete(self, node):
548 name = getNodeName(node)
549 if not name:
550 return
551 if isinstance(self.scope, FunctionScope) and name in self.scope.globals:
552 self.scope.globals.remove(name)
553 else:
452 try: 554 try:
453 self.scopeStack[0][node.id].used = (self.scope, node.lineno) 555 del self.scope[name]
454 except KeyError: 556 except KeyError:
455 if ((not hasattr(__builtin__, node.id)) 557 self.report(messages.UndefinedName, node, name)
456 and node.id not in _MAGIC_GLOBALS 558
457 and not importStarred): 559 def handleChildren(self, tree, omit=None):
458 if (os.path.basename(self.filename) == '__init__.py' and 560 for node in iter_child_nodes(tree, omit=omit):
459 node.id == '__path__'): 561 self.handleNode(node, tree)
460 # the special name __path__ is valid only in packages 562
461 pass 563 def isLiteralTupleUnpacking(self, node):
462 else: 564 if isinstance(node, ast.Assign):
463 self.report(messages.UndefinedName, 565 for child in node.targets + [node.value]:
464 node.lineno, node.id) 566 if not hasattr(child, 'elts'):
465 elif isinstance(node.ctx, (_ast.Store, _ast.AugStore)): 567 return False
466 # if the name hasn't already been defined in the current scope 568 return True
467 if isinstance(self.scope, FunctionScope) and \ 569
468 node.id not in self.scope: 570 def isDocstring(self, node):
469 # for each function or module scope above us 571 """
470 for scope in self.scopeStack[:-1]: 572 Determine if the given node is a docstring, as long as it is at the
471 if not isinstance(scope, (FunctionScope, ModuleScope)): 573 correct place in the node tree.
472 continue 574 """
473 # if the name was defined in that scope, and the name has 575 return isinstance(node, ast.Str) or (isinstance(node, ast.Expr) and
474 # been accessed already in the current scope, and hasn't 576 isinstance(node.value, ast.Str))
475 # been declared global 577
476 if (node.id in scope 578 def getDocstring(self, node):
477 and scope[node.id].used 579 if isinstance(node, ast.Expr):
478 and scope[node.id].used[0] is self.scope 580 node = node.value
479 and node.id not in self.scope.globals): 581 if not isinstance(node, ast.Str):
480 # then it's probably a mistake 582 return (None, None)
481 self.report(messages.UndefinedLocal, 583 # Computed incorrectly if the docstring has backslash
482 scope[node.id].used[1], 584 doctest_lineno = node.lineno - node.s.count('\n') - 1
483 node.id, 585 return (node.s, doctest_lineno)
484 scope[node.id].source.lineno) 586
485 break 587 def handleNode(self, node, parent):
486 588 if node is None:
487 if isinstance(node.parent, 589 return
488 (_ast.For, _ast.comprehension, _ast.Tuple, 590 if self.offset and getattr(node, 'lineno', None) is not None:
489 _ast.List)): 591 node.lineno += self.offset[0]
490 binding = Binding(node.id, node) 592 node.col_offset += self.offset[1]
491 elif (node.id == '__all__' and 593 if self.traceTree:
492 isinstance(self.scope, ModuleScope)): 594 print(' ' * self.nodeDepth + node.__class__.__name__)
493 binding = ExportBinding(node.id, node.parent.value) 595 if self.futuresAllowed and not (isinstance(node, ast.ImportFrom) or
596 self.isDocstring(node)):
597 self.futuresAllowed = False
598 self.nodeDepth += 1
599 node.depth = self.nodeDepth
600 node.parent = parent
601 try:
602 handler = self.getNodeHandler(node.__class__)
603 handler(node)
604 finally:
605 self.nodeDepth -= 1
606 if self.traceTree:
607 print(' ' * self.nodeDepth + 'end ' + node.__class__.__name__)
608
609 _getDoctestExamples = doctest.DocTestParser().get_examples
610
611 def handleDoctests(self, node):
612 try:
613 (docstring, node_lineno) = self.getDocstring(node.body[0])
614 examples = docstring and self._getDoctestExamples(docstring)
615 except (ValueError, IndexError):
616 # e.g. line 6 of the docstring for <string> has inconsistent
617 # leading whitespace: ...
618 return
619 if not examples:
620 return
621 node_offset = self.offset or (0, 0)
622 self.pushScope()
623 underscore_in_builtins = '_' in self.builtIns
624 if not underscore_in_builtins:
625 self.builtIns.add('_')
626 for example in examples:
627 try:
628 tree = compile(example.source, "<doctest>", "exec", ast.PyCF_ONLY_AST)
629 except SyntaxError:
630 e = sys.exc_info()[1]
631 position = (node_lineno + example.lineno + e.lineno,
632 example.indent + 4 + (e.offset or 0))
633 self.report(messages.DoctestSyntaxError, node, position)
494 else: 634 else:
495 binding = Assignment(node.id, node) 635 self.offset = (node_offset[0] + node_lineno + example.lineno,
496 if node.id in self.scope: 636 node_offset[1] + example.indent + 4)
497 binding.used = self.scope[node.id].used 637 self.handleChildren(tree)
498 self.addBinding(node.lineno, binding) 638 self.offset = node_offset
499 elif isinstance(node.ctx, _ast.Del): 639 if not underscore_in_builtins:
500 if isinstance(self.scope, FunctionScope) and \ 640 self.builtIns.remove('_')
501 node.id in self.scope.globals: 641 self.popScope()
502 del self.scope.globals[node.id] 642
503 else: 643 def ignore(self, node):
504 self.addBinding(node.lineno, UnBinding(node.id, node)) 644 pass
645
646 # "stmt" type nodes
647 DELETE = PRINT = FOR = WHILE = IF = WITH = WITHITEM = RAISE = \
648 TRYFINALLY = ASSERT = EXEC = EXPR = ASSIGN = handleChildren
649
650 CONTINUE = BREAK = PASS = ignore
651
652 # "expr" type nodes
653 BOOLOP = BINOP = UNARYOP = IFEXP = DICT = SET = \
654 COMPARE = CALL = REPR = ATTRIBUTE = SUBSCRIPT = LIST = TUPLE = \
655 STARRED = NAMECONSTANT = handleChildren
656
657 NUM = STR = BYTES = ELLIPSIS = ignore
658
659 # "slice" type nodes
660 SLICE = EXTSLICE = INDEX = handleChildren
661
662 # expression contexts are node instances too, though being constants
663 LOAD = STORE = DEL = AUGLOAD = AUGSTORE = PARAM = ignore
664
665 # same for operators
666 AND = OR = ADD = SUB = MULT = DIV = MOD = POW = LSHIFT = RSHIFT = \
667 BITOR = BITXOR = BITAND = FLOORDIV = INVERT = NOT = UADD = USUB = \
668 EQ = NOTEQ = LT = LTE = GT = GTE = IS = ISNOT = IN = NOTIN = ignore
669
670 # additional node types
671 LISTCOMP = COMPREHENSION = KEYWORD = handleChildren
672
673 def GLOBAL(self, node):
674 """
675 Keep track of globals declarations.
676 """
677 if isinstance(self.scope, FunctionScope):
678 self.scope.globals.update(node.names)
679
680 NONLOCAL = GLOBAL
681
682 def GENERATOREXP(self, node):
683 self.pushScope(GeneratorScope)
684 self.handleChildren(node)
685 self.popScope()
686
687 DICTCOMP = SETCOMP = GENERATOREXP
688
689 def NAME(self, node):
690 """
691 Handle occurrence of Name (which can be a load/store/delete access.)
692 """
693 # Locate the name in locals / function / globals scopes.
694 if isinstance(node.ctx, (ast.Load, ast.AugLoad)):
695 self.handleNodeLoad(node)
696 if (node.id == 'locals' and isinstance(self.scope, FunctionScope)
697 and isinstance(node.parent, ast.Call)):
698 # we are doing locals() call in current scope
699 self.scope.usesLocals = True
700 elif isinstance(node.ctx, (ast.Store, ast.AugStore)):
701 self.handleNodeStore(node)
702 elif isinstance(node.ctx, ast.Del):
703 self.handleNodeDelete(node)
505 else: 704 else:
506 # must be a Param context -- this only happens for names in 705 # must be a Param context -- this only happens for names in function
507 # function arguments, but these aren't dispatched through here 706 # arguments, but these aren't dispatched through here
508 raise RuntimeError( 707 raise RuntimeError("Got impossible expression context: %r" % (node.ctx,))
509 "Got impossible expression context: %r" % (node.ctx,)) 708
709 def RETURN(self, node):
710 if node.value and not self.scope.returnValue:
711 self.scope.returnValue = node.value
712 self.handleNode(node.value, node)
713
714 def YIELD(self, node):
715 self.scope.isGenerator = True
716 self.handleNode(node.value, node)
717
718 YIELDFROM = YIELD
510 719
511 def FUNCTIONDEF(self, node): 720 def FUNCTIONDEF(self, node):
512 if hasattr(node, 'decorators'): 721 for deco in node.decorator_list:
513 for deco in node.decorators: 722 self.handleNode(deco, node)
514 self.handleNode(deco, node)
515 else:
516 for deco in node.decorator_list:
517 self.handleNode(deco, node)
518 self.addBinding(node.lineno, FunctionDefinition(node.name, node))
519 self.LAMBDA(node) 723 self.LAMBDA(node)
724 self.addBinding(node, FunctionDefinition(node.name, node))
725 if self.withDoctest:
726 self.deferFunction(lambda: self.handleDoctests(node))
520 727
521 def LAMBDA(self, node): 728 def LAMBDA(self, node):
522 for default in node.args.defaults: 729 args = []
523 self.handleNode(default, node) 730 annotations = []
524 731
525 def runFunction(): 732 if PY2:
526 args = []
527
528 def addArgs(arglist): 733 def addArgs(arglist):
529 for arg in arglist: 734 for arg in arglist:
530 if isinstance(arg, _ast.Tuple): 735 if isinstance(arg, ast.Tuple):
531 addArgs(arg.elts) 736 addArgs(arg.elts)
532 else: 737 else:
533 if arg.id in args:
534 self.report(messages.DuplicateArgument,
535 node.lineno, arg.id)
536 args.append(arg.id) 738 args.append(arg.id)
537
538 self.pushFunctionScope()
539 addArgs(node.args.args) 739 addArgs(node.args.args)
540 # vararg/kwarg identifiers are not Name nodes 740 defaults = node.args.defaults
541 if node.args.vararg: 741 else:
542 args.append(node.args.vararg) 742 for arg in node.args.args + node.args.kwonlyargs:
543 if node.args.kwarg: 743 args.append(arg.arg)
544 args.append(node.args.kwarg) 744 annotations.append(arg.annotation)
745 defaults = node.args.defaults + node.args.kw_defaults
746
747 # Only for Python3 FunctionDefs
748 is_py3_func = hasattr(node, 'returns')
749
750 for arg_name in ('vararg', 'kwarg'):
751 wildcard = getattr(node.args, arg_name)
752 if not wildcard:
753 continue
754 args.append(wildcard if PY33 else wildcard.arg)
755 if is_py3_func:
756 if PY33: # Python 2.5 to 3.3
757 argannotation = arg_name + 'annotation'
758 annotations.append(getattr(node.args, argannotation))
759 else: # Python >= 3.4
760 annotations.append(wildcard.annotation)
761
762 if is_py3_func:
763 annotations.append(node.returns)
764
765 if len(set(args)) < len(args):
766 for (idx, arg) in enumerate(args):
767 if arg in args[:idx]:
768 self.report(messages.DuplicateArgument, node, arg)
769
770 for child in annotations + defaults:
771 if child:
772 self.handleNode(child, node)
773
774 def runFunction():
775
776 self.pushScope()
545 for name in args: 777 for name in args:
546 self.addBinding(node.lineno, Argument(name, node), 778 self.addBinding(node, Argument(name, node))
547 reportRedef=False)
548 if isinstance(node.body, list): 779 if isinstance(node.body, list):
549 # case for FunctionDefs 780 # case for FunctionDefs
550 for stmt in node.body: 781 for stmt in node.body:
551 self.handleNode(stmt, node) 782 self.handleNode(stmt, node)
552 else: 783 else:
555 786
556 def checkUnusedAssignments(): 787 def checkUnusedAssignments():
557 """ 788 """
558 Check to see if any assignments have not been used. 789 Check to see if any assignments have not been used.
559 """ 790 """
560 for name, binding in self.scope.iteritems(): 791 for name, binding in self.scope.unusedAssignments():
561 if (not binding.used and not name in self.scope.globals 792 self.report(messages.UnusedVariable, binding.source, name)
562 and isinstance(binding, Assignment)):
563 self.report(messages.UnusedVariable,
564 binding.source.lineno, name)
565 self.deferAssignment(checkUnusedAssignments) 793 self.deferAssignment(checkUnusedAssignments)
794
795 if PY32:
796 def checkReturnWithArgumentInsideGenerator():
797 """
798 Check to see if there is any return statement with
799 arguments but the function is a generator.
800 """
801 if self.scope.isGenerator and self.scope.returnValue:
802 self.report(messages.ReturnWithArgsInsideGenerator,
803 self.scope.returnValue)
804 self.deferAssignment(checkReturnWithArgumentInsideGenerator)
566 self.popScope() 805 self.popScope()
567 806
568 self.deferFunction(runFunction) 807 self.deferFunction(runFunction)
569 808
570 def CLASSDEF(self, node): 809 def CLASSDEF(self, node):
571 """ 810 """
572 Check names used in a class definition, including its decorators, base 811 Check names used in a class definition, including its decorators, base
573 classes, and the body of its definition. Additionally, add its name to 812 classes, and the body of its definition. Additionally, add its name to
574 the current scope. 813 the current scope.
575 """ 814 """
576 # decorator_list is present as of Python 2.6 815 for deco in node.decorator_list:
577 for deco in getattr(node, 'decorator_list', []):
578 self.handleNode(deco, node) 816 self.handleNode(deco, node)
579 for baseNode in node.bases: 817 for baseNode in node.bases:
580 self.handleNode(baseNode, node) 818 self.handleNode(baseNode, node)
581 self.pushClassScope() 819 if not PY2:
820 for keywordNode in node.keywords:
821 self.handleNode(keywordNode, node)
822 self.pushScope(ClassScope)
823 if self.withDoctest:
824 self.deferFunction(lambda: self.handleDoctests(node))
582 for stmt in node.body: 825 for stmt in node.body:
583 self.handleNode(stmt, node) 826 self.handleNode(stmt, node)
584 self.popScope() 827 self.popScope()
585 self.addBinding(node.lineno, Binding(node.name, node)) 828 self.addBinding(node, ClassDefinition(node.name, node))
586 829
587 def ASSIGN(self, node): 830 def AUGASSIGN(self, node):
831 self.handleNodeLoad(node.target)
588 self.handleNode(node.value, node) 832 self.handleNode(node.value, node)
589 for target in node.targets:
590 self.handleNode(target, node)
591
592 def AUGASSIGN(self, node):
593 # AugAssign is awkward: must set the context explicitly and visit
594 # twice, once with AugLoad context, once with AugStore context
595 node.target.ctx = _ast.AugLoad()
596 self.handleNode(node.target, node)
597 self.handleNode(node.value, node)
598 node.target.ctx = _ast.AugStore()
599 self.handleNode(node.target, node) 833 self.handleNode(node.target, node)
600 834
601 def IMPORT(self, node): 835 def IMPORT(self, node):
602 for alias in node.names: 836 for alias in node.names:
603 name = alias.asname or alias.name 837 name = alias.asname or alias.name
604 importation = Importation(name, node) 838 importation = Importation(name, node)
605 self.addBinding(node.lineno, importation) 839 self.addBinding(node, importation)
606 840
607 def IMPORTFROM(self, node): 841 def IMPORTFROM(self, node):
608 if node.module == '__future__': 842 if node.module == '__future__':
609 if not self.futuresAllowed: 843 if not self.futuresAllowed:
610 self.report(messages.LateFutureImport, node.lineno, 844 self.report(messages.LateFutureImport,
611 [n.name for n in node.names]) 845 node, [n.name for n in node.names])
612 else: 846 else:
613 self.futuresAllowed = False 847 self.futuresAllowed = False
614 848
615 for alias in node.names: 849 for alias in node.names:
616 if alias.name == '*': 850 if alias.name == '*':
617 self.scope.importStarred = True 851 self.scope.importStarred = True
618 self.report(messages.ImportStarUsed, node.lineno, node.module) 852 self.report(messages.ImportStarUsed, node, node.module)
619 continue 853 continue
620 name = alias.asname or alias.name 854 name = alias.asname or alias.name
621 importation = Importation(name, node) 855 importation = Importation(name, node)
622 if node.module == '__future__': 856 if node.module == '__future__':
623 importation.used = (self.scope, node.lineno) 857 importation.used = (self.scope, node)
624 self.addBinding(node.lineno, importation) 858 self.addBinding(node, importation)
859
860 def TRY(self, node):
861 handler_names = []
862 # List the exception handlers
863 for handler in node.handlers:
864 if isinstance(handler.type, ast.Tuple):
865 for exc_type in handler.type.elts:
866 handler_names.append(getNodeName(exc_type))
867 elif handler.type:
868 handler_names.append(getNodeName(handler.type))
869 # Memorize the except handlers and process the body
870 self.exceptHandlers.append(handler_names)
871 for child in node.body:
872 self.handleNode(child, node)
873 self.exceptHandlers.pop()
874 # Process the other nodes: "except:", "else:", "finally:"
875 self.handleChildren(node, omit='body')
876
877 TRYEXCEPT = TRY
878
879 def EXCEPTHANDLER(self, node):
880 # 3.x: in addition to handling children, we must handle the name of
881 # the exception, which is not a Name node, but a simple string.
882 if isinstance(node.name, str):
883 self.handleNodeStore(node)
884 self.handleChildren(node)
625 885
626 # 886 #
627 # eflag: FileType = Python2 887 # eflag: FileType = Python2

eric ide

mercurial