1 | r"""Utilities to compile possibly incomplete Python source code.
|
---|
2 |
|
---|
3 | This module provides two interfaces, broadly similar to the builtin
|
---|
4 | function compile(), which take program text, a filename and a 'mode'
|
---|
5 | and:
|
---|
6 |
|
---|
7 | - Return code object if the command is complete and valid
|
---|
8 | - Return None if the command is incomplete
|
---|
9 | - Raise SyntaxError, ValueError or OverflowError if the command is a
|
---|
10 | syntax error (OverflowError and ValueError can be produced by
|
---|
11 | malformed literals).
|
---|
12 |
|
---|
13 | Approach:
|
---|
14 |
|
---|
15 | First, check if the source consists entirely of blank lines and
|
---|
16 | comments; if so, replace it with 'pass', because the built-in
|
---|
17 | parser doesn't always do the right thing for these.
|
---|
18 |
|
---|
19 | Compile three times: as is, with \n, and with \n\n appended. If it
|
---|
20 | compiles as is, it's complete. If it compiles with one \n appended,
|
---|
21 | we expect more. If it doesn't compile either way, we compare the
|
---|
22 | error we get when compiling with \n or \n\n appended. If the errors
|
---|
23 | are the same, the code is broken. But if the errors are different, we
|
---|
24 | expect more. Not intuitive; not even guaranteed to hold in future
|
---|
25 | releases; but this matches the compiler's behavior from Python 1.4
|
---|
26 | through 2.2, at least.
|
---|
27 |
|
---|
28 | Caveat:
|
---|
29 |
|
---|
30 | It is possible (but not likely) that the parser stops parsing with a
|
---|
31 | successful outcome before reaching the end of the source; in this
|
---|
32 | case, trailing symbols may be ignored instead of causing an error.
|
---|
33 | For example, a backslash followed by two newlines may be followed by
|
---|
34 | arbitrary garbage. This will be fixed once the API for the parser is
|
---|
35 | better.
|
---|
36 |
|
---|
37 | The two interfaces are:
|
---|
38 |
|
---|
39 | compile_command(source, filename, symbol):
|
---|
40 |
|
---|
41 | Compiles a single command in the manner described above.
|
---|
42 |
|
---|
43 | CommandCompiler():
|
---|
44 |
|
---|
45 | Instances of this class have __call__ methods identical in
|
---|
46 | signature to compile_command; the difference is that if the
|
---|
47 | instance compiles program text containing a __future__ statement,
|
---|
48 | the instance 'remembers' and compiles all subsequent program texts
|
---|
49 | with the statement in force.
|
---|
50 |
|
---|
51 | The module also provides another class:
|
---|
52 |
|
---|
53 | Compile():
|
---|
54 |
|
---|
55 | Instances of this class act like the built-in function compile,
|
---|
56 | but with 'memory' in the sense described above.
|
---|
57 | """
|
---|
58 |
|
---|
59 | import __future__
|
---|
60 |
|
---|
61 | _features = [getattr(__future__, fname)
|
---|
62 | for fname in __future__.all_feature_names]
|
---|
63 |
|
---|
64 | __all__ = ["compile_command", "Compile", "CommandCompiler"]
|
---|
65 |
|
---|
66 | PyCF_DONT_IMPLY_DEDENT = 0x200 # Matches pythonrun.h
|
---|
67 |
|
---|
68 | def _maybe_compile(compiler, source, filename, symbol):
|
---|
69 | # Check for source consisting of only blank lines and comments
|
---|
70 | for line in source.split("\n"):
|
---|
71 | line = line.strip()
|
---|
72 | if line and line[0] != '#':
|
---|
73 | break # Leave it alone
|
---|
74 | else:
|
---|
75 | if symbol != "eval":
|
---|
76 | source = "pass" # Replace it with a 'pass' statement
|
---|
77 |
|
---|
78 | err = err1 = err2 = None
|
---|
79 | code = code1 = code2 = None
|
---|
80 |
|
---|
81 | try:
|
---|
82 | code = compiler(source, filename, symbol)
|
---|
83 | except SyntaxError, err:
|
---|
84 | pass
|
---|
85 |
|
---|
86 | try:
|
---|
87 | code1 = compiler(source + "\n", filename, symbol)
|
---|
88 | except SyntaxError, err1:
|
---|
89 | pass
|
---|
90 |
|
---|
91 | try:
|
---|
92 | code2 = compiler(source + "\n\n", filename, symbol)
|
---|
93 | except SyntaxError, err2:
|
---|
94 | pass
|
---|
95 |
|
---|
96 | if code:
|
---|
97 | return code
|
---|
98 | if not code1 and repr(err1) == repr(err2):
|
---|
99 | raise SyntaxError, err1
|
---|
100 |
|
---|
101 | def _compile(source, filename, symbol):
|
---|
102 | return compile(source, filename, symbol, PyCF_DONT_IMPLY_DEDENT)
|
---|
103 |
|
---|
104 | def compile_command(source, filename="<input>", symbol="single"):
|
---|
105 | r"""Compile a command and determine whether it is incomplete.
|
---|
106 |
|
---|
107 | Arguments:
|
---|
108 |
|
---|
109 | source -- the source string; may contain \n characters
|
---|
110 | filename -- optional filename from which source was read; default
|
---|
111 | "<input>"
|
---|
112 | symbol -- optional grammar start symbol; "single" (default) or "eval"
|
---|
113 |
|
---|
114 | Return value / exceptions raised:
|
---|
115 |
|
---|
116 | - Return a code object if the command is complete and valid
|
---|
117 | - Return None if the command is incomplete
|
---|
118 | - Raise SyntaxError, ValueError or OverflowError if the command is a
|
---|
119 | syntax error (OverflowError and ValueError can be produced by
|
---|
120 | malformed literals).
|
---|
121 | """
|
---|
122 | return _maybe_compile(_compile, source, filename, symbol)
|
---|
123 |
|
---|
124 | class Compile:
|
---|
125 | """Instances of this class behave much like the built-in compile
|
---|
126 | function, but if one is used to compile text containing a future
|
---|
127 | statement, it "remembers" and compiles all subsequent program texts
|
---|
128 | with the statement in force."""
|
---|
129 | def __init__(self):
|
---|
130 | self.flags = PyCF_DONT_IMPLY_DEDENT
|
---|
131 |
|
---|
132 | def __call__(self, source, filename, symbol):
|
---|
133 | codeob = compile(source, filename, symbol, self.flags, 1)
|
---|
134 | for feature in _features:
|
---|
135 | if codeob.co_flags & feature.compiler_flag:
|
---|
136 | self.flags |= feature.compiler_flag
|
---|
137 | return codeob
|
---|
138 |
|
---|
139 | class CommandCompiler:
|
---|
140 | """Instances of this class have __call__ methods identical in
|
---|
141 | signature to compile_command; the difference is that if the
|
---|
142 | instance compiles program text containing a __future__ statement,
|
---|
143 | the instance 'remembers' and compiles all subsequent program texts
|
---|
144 | with the statement in force."""
|
---|
145 |
|
---|
146 | def __init__(self,):
|
---|
147 | self.compiler = Compile()
|
---|
148 |
|
---|
149 | def __call__(self, source, filename="<input>", symbol="single"):
|
---|
150 | r"""Compile a command and determine whether it is incomplete.
|
---|
151 |
|
---|
152 | Arguments:
|
---|
153 |
|
---|
154 | source -- the source string; may contain \n characters
|
---|
155 | filename -- optional filename from which source was read;
|
---|
156 | default "<input>"
|
---|
157 | symbol -- optional grammar start symbol; "single" (default) or
|
---|
158 | "eval"
|
---|
159 |
|
---|
160 | Return value / exceptions raised:
|
---|
161 |
|
---|
162 | - Return a code object if the command is complete and valid
|
---|
163 | - Return None if the command is incomplete
|
---|
164 | - Raise SyntaxError, ValueError or OverflowError if the command is a
|
---|
165 | syntax error (OverflowError and ValueError can be produced by
|
---|
166 | malformed literals).
|
---|
167 | """
|
---|
168 | return _maybe_compile(self.compiler, source, filename, symbol)
|
---|