|
1 """Parser for future statements |
|
2 |
|
3 """ |
|
4 |
|
5 from compiler import ast, walk |
|
6 |
|
7 def is_future(stmt): |
|
8 """Return true if statement is a well-formed future statement""" |
|
9 if not isinstance(stmt, ast.From): |
|
10 return 0 |
|
11 if stmt.modname == "__future__": |
|
12 return 1 |
|
13 else: |
|
14 return 0 |
|
15 |
|
16 class FutureParser: |
|
17 |
|
18 features = ("nested_scopes", "generators", "division", |
|
19 "absolute_import", "with_statement", "print_function", |
|
20 "unicode_literals") |
|
21 |
|
22 def __init__(self): |
|
23 self.found = {} # set |
|
24 |
|
25 def visitModule(self, node): |
|
26 stmt = node.node |
|
27 for s in stmt.nodes: |
|
28 if not self.check_stmt(s): |
|
29 break |
|
30 |
|
31 def check_stmt(self, stmt): |
|
32 if is_future(stmt): |
|
33 for name, asname in stmt.names: |
|
34 if name in self.features: |
|
35 self.found[name] = 1 |
|
36 else: |
|
37 raise SyntaxError, \ |
|
38 "future feature %s is not defined" % name |
|
39 stmt.valid_future = 1 |
|
40 return 1 |
|
41 return 0 |
|
42 |
|
43 def get_features(self): |
|
44 """Return list of features enabled by future statements""" |
|
45 return self.found.keys() |
|
46 |
|
47 class BadFutureParser: |
|
48 """Check for invalid future statements""" |
|
49 |
|
50 def visitFrom(self, node): |
|
51 if hasattr(node, 'valid_future'): |
|
52 return |
|
53 if node.modname != "__future__": |
|
54 return |
|
55 raise SyntaxError, "invalid future statement " + repr(node) |
|
56 |
|
57 def find_futures(node): |
|
58 p1 = FutureParser() |
|
59 p2 = BadFutureParser() |
|
60 walk(node, p1) |
|
61 walk(node, p2) |
|
62 return p1.get_features() |
|
63 |
|
64 if __name__ == "__main__": |
|
65 import sys |
|
66 from compiler import parseFile, walk |
|
67 |
|
68 for file in sys.argv[1:]: |
|
69 print file |
|
70 tree = parseFile(file) |
|
71 v = FutureParser() |
|
72 walk(tree, v) |
|
73 print v.found |
|
74 print |