• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_ONLY_AST = 0x400
48PyCF_ALLOW_INCOMPLETE_INPUT = 0x4000
49
50def _maybe_compile(compiler, source, filename, symbol):
51    # Check for source consisting of only blank lines and comments.
52    for line in source.split("\n"):
53        line = line.strip()
54        if line and line[0] != '#':
55            break               # Leave it alone.
56    else:
57        if symbol != "eval":
58            source = "pass"     # Replace it with a 'pass' statement
59
60    # Disable compiler warnings when checking for incomplete input.
61    with warnings.catch_warnings():
62        warnings.simplefilter("ignore", (SyntaxWarning, DeprecationWarning))
63        try:
64            compiler(source, filename, symbol)
65        except SyntaxError:  # Let other compile() errors propagate.
66            try:
67                compiler(source + "\n", filename, symbol)
68                return None
69            except _IncompleteInputError as e:
70                return None
71            except SyntaxError as e:
72                pass
73                # fallthrough
74
75    return compiler(source, filename, symbol, incomplete_input=False)
76
77def _compile(source, filename, symbol, incomplete_input=True):
78    flags = 0
79    if incomplete_input:
80        flags |= PyCF_ALLOW_INCOMPLETE_INPUT
81        flags |= PyCF_DONT_IMPLY_DEDENT
82    return compile(source, filename, symbol, flags)
83
84def compile_command(source, filename="<input>", symbol="single"):
85    r"""Compile a command and determine whether it is incomplete.
86
87    Arguments:
88
89    source -- the source string; may contain \n characters
90    filename -- optional filename from which source was read; default
91                "<input>"
92    symbol -- optional grammar start symbol; "single" (default), "exec"
93              or "eval"
94
95    Return value / exceptions raised:
96
97    - Return a code object if the command is complete and valid
98    - Return None if the command is incomplete
99    - Raise SyntaxError, ValueError or OverflowError if the command is a
100      syntax error (OverflowError and ValueError can be produced by
101      malformed literals).
102    """
103    return _maybe_compile(_compile, source, filename, symbol)
104
105class Compile:
106    """Instances of this class behave much like the built-in compile
107    function, but if one is used to compile text containing a future
108    statement, it "remembers" and compiles all subsequent program texts
109    with the statement in force."""
110    def __init__(self):
111        self.flags = PyCF_DONT_IMPLY_DEDENT | PyCF_ALLOW_INCOMPLETE_INPUT
112
113    def __call__(self, source, filename, symbol, flags=0, **kwargs):
114        flags |= self.flags
115        if kwargs.get('incomplete_input', True) is False:
116            flags &= ~PyCF_DONT_IMPLY_DEDENT
117            flags &= ~PyCF_ALLOW_INCOMPLETE_INPUT
118        codeob = compile(source, filename, symbol, flags, True)
119        if flags & PyCF_ONLY_AST:
120            return codeob  # this is an ast.Module in this case
121        for feature in _features:
122            if codeob.co_flags & feature.compiler_flag:
123                self.flags |= feature.compiler_flag
124        return codeob
125
126class CommandCompiler:
127    """Instances of this class have __call__ methods identical in
128    signature to compile_command; the difference is that if the
129    instance compiles program text containing a __future__ statement,
130    the instance 'remembers' and compiles all subsequent program texts
131    with the statement in force."""
132
133    def __init__(self,):
134        self.compiler = Compile()
135
136    def __call__(self, source, filename="<input>", symbol="single"):
137        r"""Compile a command and determine whether it is incomplete.
138
139        Arguments:
140
141        source -- the source string; may contain \n characters
142        filename -- optional filename from which source was read;
143                    default "<input>"
144        symbol -- optional grammar start symbol; "single" (default) or
145                  "eval"
146
147        Return value / exceptions raised:
148
149        - Return a code object if the command is complete and valid
150        - Return None if the command is incomplete
151        - Raise SyntaxError, ValueError or OverflowError if the command is a
152          syntax error (OverflowError and ValueError can be produced by
153          malformed literals).
154        """
155        return _maybe_compile(self.compiler, source, filename, symbol)
156