8
8
from pyflakes import messages
9
9
10
10
11
+
11
12
class Binding (object ):
12
13
"""
14
+ Represents the binding of a value to a name.
15
+
16
+ The checker uses this to keep track of which names have been bound and
17
+ which names have not. See L{Assignment} for a special type of binding that
18
+ is checked with stricter rules.
19
+
13
20
@ivar used: pair of (L{Scope}, line-number) indicating the scope and
14
21
line number that this binding was last used
15
22
"""
23
+
16
24
def __init__ (self , name , source ):
17
25
self .name = name
18
26
self .source = source
19
27
self .used = False
20
28
29
+
21
30
def __str__ (self ):
22
31
return self .name
23
32
33
+
24
34
def __repr__ (self ):
25
35
return '<%s object %r from line %r at 0x%x>' % (self .__class__ .__name__ ,
26
36
self .name ,
27
37
self .source .lineno ,
28
38
id (self ))
29
39
40
+
41
+
30
42
class UnBinding (Binding ):
31
43
'''Created by the 'del' operator.'''
32
44
@@ -35,22 +47,44 @@ def __init__(self, name, source):
35
47
name = name .split ('.' )[0 ]
36
48
super (Importation , self ).__init__ (name , source )
37
49
50
+
51
+
52
+ class Argument (Binding ):
53
+ """
54
+ Represents binding a name as an argument.
55
+ """
56
+
57
+
58
+
38
59
class Assignment (Binding ):
39
- pass
60
+ """
61
+ Represents binding a name with an explicit assignment.
62
+
63
+ The checker will raise warnings for any Assignment that isn't used. Also,
64
+ the checker does not consider assignments in tuple/list unpacking to be
65
+ Assignments, rather it treats them as simple Bindings.
66
+ """
67
+
68
+
40
69
41
70
class FunctionDefinition (Binding ):
42
71
pass
43
72
44
73
74
+
45
75
class Scope (dict ):
46
76
importStarred = False # set to True when import * is found
47
77
78
+
48
79
def __repr__ (self ):
49
80
return '<%s at 0x%x %s>' % (self .__class__ .__name__ , id (self ), dict .__repr__ (self ))
50
81
82
+
51
83
def __init__ (self ):
52
84
super (Scope , self ).__init__ ()
53
85
86
+
87
+
54
88
class ClassScope (Scope ):
55
89
pass
56
90
@@ -72,41 +106,77 @@ class ModuleScope(Scope):
72
106
pass
73
107
74
108
75
-
76
109
# Globally defined names which are not attributes of the __builtin__ module.
77
110
_MAGIC_GLOBALS = ['__file__' , '__builtins__' ]
78
111
79
112
80
113
81
114
class Checker (object ):
115
+ """
116
+ I check the cleanliness and sanity of Python code.
117
+
118
+ @ivar _deferredFunctions: Tracking list used by L{deferFunction}. Elements
119
+ of the list are two-tuples. The first element is the callable passed
120
+ to L{deferFunction}. The second element is a copy of the scope stack
121
+ at the time L{deferFunction} was called.
122
+
123
+ @ivar _deferredAssignments: Similar to C{_deferredFunctions}, but for
124
+ callables which are deferred assignment checks.
125
+ """
126
+
82
127
nodeDepth = 0
83
128
traceTree = False
84
129
85
130
def __init__ (self , tree , filename = '(none)' ):
86
- self .deferred = []
131
+ self ._deferredFunctions = []
132
+ self ._deferredAssignments = []
87
133
self .dead_scopes = []
88
134
self .messages = []
89
135
self .filename = filename
90
136
self .scopeStack = [ModuleScope ()]
91
137
self .futuresAllowed = True
92
-
93
138
self .handleChildren (tree )
94
- for handler , scope in self .deferred :
95
- self .scopeStack = scope
96
- handler ()
139
+ self ._runDeferred (self ._deferredFunctions )
140
+ # Set _deferredFunctions to None so that deferFunction will fail
141
+ # noisily if called after we've run through the deferred functions.
142
+ self ._deferredFunctions = None
143
+ self ._runDeferred (self ._deferredAssignments )
144
+ # Set _deferredAssignments to None so that deferAssignment will fail
145
+ # noisly if called after we've run through the deferred assignments.
146
+ self ._deferredAssignments = None
97
147
del self .scopeStack [1 :]
98
148
self .popScope ()
99
149
self .check_dead_scopes ()
100
150
101
- def defer (self , callable ):
102
- '''Schedule something to be called after just before completion.
151
+
152
+ def deferFunction (self , callable ):
153
+ '''
154
+ Schedule a function handler to be called just before completion.
103
155
104
156
This is used for handling function bodies, which must be deferred
105
157
because code later in the file might modify the global scope. When
106
158
`callable` is called, the scope at the time this is called will be
107
159
restored, however it will contain any new bindings added to it.
108
160
'''
109
- self .deferred .append ( (callable , self .scopeStack [:]) )
161
+ self ._deferredFunctions .append ((callable , self .scopeStack [:]))
162
+
163
+
164
+ def deferAssignment (self , callable ):
165
+ """
166
+ Schedule an assignment handler to be called just after deferred
167
+ function handlers.
168
+ """
169
+ self ._deferredAssignments .append ((callable , self .scopeStack [:]))
170
+
171
+
172
+ def _runDeferred (self , deferred ):
173
+ """
174
+ Run the callables in C{deferred} using their associated scope stack.
175
+ """
176
+ for handler , scope in deferred :
177
+ self .scopeStack = scope
178
+ handler ()
179
+
110
180
111
181
def scope (self ):
112
182
return self .scopeStack [- 1 ]
@@ -132,9 +202,10 @@ def report(self, messageClass, *args, **kwargs):
132
202
133
203
def handleChildren (self , tree ):
134
204
for node in tree .getChildNodes ():
135
- self .handleNode (node )
205
+ self .handleNode (node , tree )
136
206
137
- def handleNode (self , node ):
207
+ def handleNode (self , node , parent ):
208
+ node .parent = parent
138
209
if self .traceTree :
139
210
print ' ' * self .nodeDepth + node .__class__ .__name__
140
211
self .nodeDepth += 1
@@ -208,7 +279,7 @@ def WITH(self, node):
208
279
# Of course these are assignments, not references, so we have to
209
280
# handle them as a special case here.
210
281
211
- self .handleNode (node .expr )
282
+ self .handleNode (node .expr , node )
212
283
213
284
if isinstance (node .vars , ast .AssTuple ):
214
285
varNodes = node .vars .nodes
@@ -232,8 +303,8 @@ def GLOBAL(self, node):
232
303
233
304
def LISTCOMP (self , node ):
234
305
for qual in node .quals :
235
- self .handleNode (qual )
236
- self .handleNode (node .expr )
306
+ self .handleNode (qual , node )
307
+ self .handleNode (node .expr , node )
237
308
238
309
GENEXPRINNER = LISTCOMP
239
310
@@ -305,7 +376,7 @@ def FUNCTION(self, node):
305
376
306
377
def LAMBDA (self , node ):
307
378
for default in node .defaults :
308
- self .handleNode (default )
379
+ self .handleNode (default , node )
309
380
310
381
def runFunction ():
311
382
args = []
@@ -322,11 +393,21 @@ def addArgs(arglist):
322
393
self .pushFunctionScope ()
323
394
addArgs (node .argnames )
324
395
for name in args :
325
- self .addBinding (node .lineno , Assignment (name , node ), reportRedef = False )
326
- self .handleNode (node .code )
396
+ self .addBinding (node .lineno , Argument (name , node ), reportRedef = False )
397
+ self .handleNode (node .code , node )
398
+ def checkUnusedAssignments ():
399
+ """
400
+ Check to see if any assignments have not been used.
401
+ """
402
+ for name , binding in self .scope .iteritems ():
403
+ if (not binding .used and not name in self .scope .globals
404
+ and isinstance (binding , Assignment )):
405
+ self .report (messages .UnusedVariable ,
406
+ binding .source .lineno , name )
407
+ self .deferAssignment (checkUnusedAssignments )
327
408
self .popScope ()
328
409
329
- self .defer (runFunction )
410
+ self .deferFunction (runFunction )
330
411
331
412
332
413
def CLASS (self , node ):
@@ -337,9 +418,9 @@ def CLASS(self, node):
337
418
"""
338
419
if getattr (node , "decorators" , None ) is not None :
339
420
self .handleChildren (node .decorators )
340
- self .addBinding (node .lineno , Assignment (node .name , node ))
421
+ self .addBinding (node .lineno , Binding (node .name , node ))
341
422
for baseNode in node .bases :
342
- self .handleNode (baseNode )
423
+ self .handleNode (baseNode , node )
343
424
self .pushClassScope ()
344
425
self .handleChildren (node .code )
345
426
self .popScope ()
@@ -372,12 +453,20 @@ def ASSNAME(self, node):
372
453
scope [node .name ].source .lineno )
373
454
break
374
455
375
- self .addBinding (node .lineno , Assignment (node .name , node ))
456
+ if isinstance (node .parent ,
457
+ (ast .For , ast .ListCompFor , ast .GenExprFor ,
458
+ ast .AssTuple , ast .AssList )):
459
+ binding = Binding (node .name , node )
460
+ else :
461
+ binding = Assignment (node .name , node )
462
+ if node .name in self .scope :
463
+ binding .used = self .scope [node .name ].used
464
+ self .addBinding (node .lineno , binding )
376
465
377
466
def ASSIGN (self , node ):
378
- self .handleNode (node .expr )
467
+ self .handleNode (node .expr , node )
379
468
for subnode in node .nodes [::- 1 ]:
380
- self .handleNode (subnode )
469
+ self .handleNode (subnode , node )
381
470
382
471
def IMPORT (self , node ):
383
472
for name , alias in node .names :
0 commit comments