8
8
import os
9
9
import sys
10
10
11
+ from warnings import warn
12
+
11
13
PY2 = sys .version_info < (3 , 0 )
12
14
PY32 = sys .version_info < (3 , 3 ) # Python 2.5 to 3.2
13
15
PY33 = sys .version_info < (3 , 4 ) # Python 2.5 to 3.3
@@ -141,6 +143,18 @@ def redefines(self, other):
141
143
return isinstance (other , Definition ) and self .name == other .name
142
144
143
145
146
+ class FutureImportation (Importation ):
147
+ """
148
+ A binding created by a from `__future__` import statement.
149
+
150
+ `__future__` imports are implicitly used.
151
+ """
152
+
153
+ def __init__ (self , name , source , scope ):
154
+ super (FutureImportation , self ).__init__ (name , source )
155
+ self .used = (scope , source )
156
+
157
+
144
158
class Argument (Binding ):
145
159
"""
146
160
Represents binding a name as an argument.
@@ -237,11 +251,12 @@ class GeneratorScope(Scope):
237
251
238
252
239
253
class ModuleScope (Scope ):
240
- pass
254
+ """Scope for a module."""
255
+ _futures_allowed = True
241
256
242
257
243
258
class DoctestScope (ModuleScope ):
244
- pass
259
+ """Scope for a doctest."""
245
260
246
261
247
262
# Globally defined names which are not attributes of the builtins module, or
@@ -293,7 +308,6 @@ def __init__(self, tree, filename='(none)', builtins=None,
293
308
self .withDoctest = withDoctest
294
309
self .scopeStack = [ModuleScope ()]
295
310
self .exceptHandlers = [()]
296
- self .futuresAllowed = True
297
311
self .root = tree
298
312
self .handleChildren (tree )
299
313
self .runDeferred (self ._deferredFunctions )
@@ -308,6 +322,20 @@ def __init__(self, tree, filename='(none)', builtins=None,
308
322
self .popScope ()
309
323
self .checkDeadScopes ()
310
324
325
+ @property
326
+ def futuresAllowed (self ):
327
+ """Return whether `__future__` are permitted in the module context."""
328
+ warn ('Checker.futuresAllowed is deprecated' , DeprecationWarning , 2 )
329
+ if not isinstance (self .scope , ModuleScope ):
330
+ return False
331
+ return self .scopeStack [0 ]._futures_allowed
332
+
333
+ @futuresAllowed .setter
334
+ def futuresAllowed (self , value ):
335
+ """Disable permitting `__future__` in the module."""
336
+ warn ('Checker.futuresAllowed is deprecated' , DeprecationWarning , 2 )
337
+ self .scopeStack [0 ]._futures_allowed = value
338
+
311
339
def deferFunction (self , callable ):
312
340
"""
313
341
Schedule a function handler to be called just before completion.
@@ -606,9 +634,13 @@ def handleNode(self, node, parent):
606
634
node .col_offset += self .offset [1 ]
607
635
if self .traceTree :
608
636
print (' ' * self .nodeDepth + node .__class__ .__name__ )
609
- if self .futuresAllowed and not (isinstance (node , ast .ImportFrom ) or
610
- self .isDocstring (node )):
611
- self .futuresAllowed = False
637
+
638
+ if (isinstance (self .scope , ModuleScope ) and
639
+ self .scope ._futures_allowed and
640
+ not (isinstance (node , ast .ImportFrom ) or
641
+ self .isDocstring (node ))):
642
+ self .scope ._futures_allowed = False
643
+
612
644
self .nodeDepth += 1
613
645
node .depth = self .nodeDepth
614
646
node .parent = parent
@@ -952,21 +984,27 @@ def IMPORT(self, node):
952
984
953
985
def IMPORTFROM (self , node ):
954
986
if node .module == '__future__' :
955
- if not self .futuresAllowed :
987
+ # __future__ can only appear in module/doctest scope and
988
+ # should not have been disabled already in handleNode and
989
+ # the scope must not have any other type of binding.
990
+ if (not isinstance (self .scope , ModuleScope ) or
991
+ not self .scope ._futures_allowed or
992
+ any (not isinstance (binding , FutureImportation )
993
+ for binding in self .scope .values ())):
994
+ self .scope ._futures_allowed = False
956
995
self .report (messages .LateFutureImport ,
957
996
node , [n .name for n in node .names ])
958
- else :
959
- self .futuresAllowed = False
960
997
961
998
for alias in node .names :
962
- if alias .name == '*' :
999
+ name = alias .asname or alias .name
1000
+ if node .module == '__future__' :
1001
+ importation = FutureImportation (name , node , self .scope )
1002
+ elif alias .name == '*' :
963
1003
self .scope .importStarred = True
964
1004
self .report (messages .ImportStarUsed , node , node .module )
965
1005
continue
966
- name = alias .asname or alias .name
967
- importation = Importation (name , node )
968
- if node .module == '__future__' :
969
- importation .used = (self .scope , node )
1006
+ else :
1007
+ importation = Importation (name , node )
970
1008
self .addBinding (node , importation )
971
1009
972
1010
def TRY (self , node ):
0 commit comments