UtilitiesPython2/py2flakes/checker.py

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

eric ide

mercurial