1r"""Utilities to compile possibly incomplete Python source code.
2
3This module provides two interfaces, broadly similar to the builtin
4function compile(), which take program text, a filename and a 'mode'
5and:
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
13The two interfaces are:
14
15compile_command(source, filename, symbol):
16
17    Compiles a single command in the manner described above.
18
19CommandCompiler():
20
21    Instances of this class have __call__ methods identical in
22    signature to compile_command; the difference is that if the
23    instance compiles program text containing a __future__ statement,
24    the instance 'remembers' and compiles all subsequent program texts
25    with the statement in force.
26
27The module also provides another class:
28
29Compile():
30
31    Instances of this class act like the built-in function compile,
32    but with 'memory' in the sense described above.
33"""
34
35import __future__
36import warnings
37
38_features = [getattr(__future__, fname)
39             for fname in __future__.all_feature_names]
40
41__all__ = ["compile_command", "Compile", "CommandCompiler"]
42
43# The following flags match the values from Include/cpython/compile.h
44# Caveat emptor: These flags are undocumented on purpose and depending
45# on their effect outside the standard library is **unsupported**.
46PyCF_DONT_IMPLY_DEDENT = 0x200
47PyCF_ALLOW_INCOMPLETE_INPUT = 0x4000
48
49def _maybe_compile(compiler, source, filename, symbol):
50    # Check for source consisting of only blank lines and comments.
51    for line in source.split("\n"):
52        line = line.strip()
53        if line and line[0] != '#':
54            break               # Leave it alone.
55    else:
56        if symbol != "eval":
57            source = "pass"     # Replace it with a 'pass' statement
58
59    # Disable compiler warnings when checking for incomplete input.
60    with warnings.catch_warnings():
61        warnings.simplefilter("ignore", (SyntaxWarning, DeprecationWarning))
62        try:
63            compiler(source, filename, symbol)
64        except SyntaxError:  # Let other compile() errors propagate.
65            try:
66                compiler(source + "\n", filename, symbol)
67                return None
68            except SyntaxError as e:
69                if "incomplete input" in str(e):
70                    return None
71                # fallthrough
72
73    return compiler(source, filename, symbol)
74
75
76def _is_syntax_error(err1, err2):
77    rep1 = repr(err1)
78    rep2 = repr(err2)
79    if "was never closed" in rep1 and "was never closed" in rep2:
80        return False
81    if rep1 == rep2:
82        return True
83    return False
84
85def _compile(source, filename, symbol):
86    return compile(source, filename, symbol, PyCF_DONT_IMPLY_DEDENT | PyCF_ALLOW_INCOMPLETE_INPUT)
87
88def compile_command(source, filename="<input>", symbol="single"):
89    r"""Compile a command and determine whether it is incomplete.
90
91    Arguments:
92
93    source -- the source string; may contain \n characters
94    filename -- optional filename from which source was read; default
95                "<input>"
96    symbol -- optional grammar start symbol; "single" (default), "exec"
97              or "eval"
98
99    Return value / exceptions raised:
100
101    - Return a code object if the command is complete and valid
102    - Return None if the command is incomplete
103    - Raise SyntaxError, ValueError or OverflowError if the command is a
104      syntax error (OverflowError and ValueError can be produced by
105      malformed literals).
106    """
107    return _maybe_compile(_compile, source, filename, symbol)
108
109class Compile:
110    """Instances of this class behave much like the built-in compile
111    function, but if one is used to compile text containing a future
112    statement, it "remembers" and compiles all subsequent program texts
113    with the statement in force."""
114    def __init__(self):
115        self.flags = PyCF_DONT_IMPLY_DEDENT | PyCF_ALLOW_INCOMPLETE_INPUT
116
117    def __call__(self, source, filename, symbol):
118        codeob = compile(source, filename, symbol, self.flags, True)
119        for feature in _features:
120            if codeob.co_flags & feature.compiler_flag:
121                self.flags |= feature.compiler_flag
122        return codeob
123
124class CommandCompiler:
125    """Instances of this class have __call__ methods identical in
126    signature to compile_command; the difference is that if the
127    instance compiles program text containing a __future__ statement,
128    the instance 'remembers' and compiles all subsequent program texts
129    with the statement in force."""
130
131    def __init__(self,):
132        self.compiler = Compile()
133
134    def __call__(self, source, filename="<input>", symbol="single"):
135        r"""Compile a command and determine whether it is incomplete.
136
137        Arguments:
138
139        source -- the source string; may contain \n characters
140        filename -- optional filename from which source was read;
141                    default "<input>"
142        symbol -- optional grammar start symbol; "single" (default) or
143                  "eval"
144
145        Return value / exceptions raised:
146
147        - Return a code object if the command is complete and valid
148        - Return None if the command is incomplete
149        - Raise SyntaxError, ValueError or OverflowError if the command is a
150          syntax error (OverflowError and ValueError can be produced by
151          malformed literals).
152        """
153        return _maybe_compile(self.compiler, source, filename, symbol)
154