diff options
author | Tim Peters <tim.peters@gmail.com> | 2001-08-17 22:11:27 (GMT) |
---|---|---|
committer | Tim Peters <tim.peters@gmail.com> | 2001-08-17 22:11:27 (GMT) |
commit | 6cd6a82db93fa37a13d0b5312ce6510c4d6967de (patch) | |
tree | c43eca432a18a364fb1c004d4dfcfe904fa7cefd /Lib | |
parent | 10d72552495a1967af7174f6ed52eb3340799c76 (diff) | |
download | cpython-6cd6a82db93fa37a13d0b5312ce6510c4d6967de.zip cpython-6cd6a82db93fa37a13d0b5312ce6510c4d6967de.tar.gz cpython-6cd6a82db93fa37a13d0b5312ce6510c4d6967de.tar.bz2 |
A fiddled version of the rest of Michael Hudson's SF patch
#449043 supporting __future__ in simulated shells
which implements PEP 264.
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/code.py | 7 | ||||
-rw-r--r-- | Lib/codeop.py | 158 |
2 files changed, 126 insertions, 39 deletions
diff --git a/Lib/code.py b/Lib/code.py index d56681c..e2d5065 100644 --- a/Lib/code.py +++ b/Lib/code.py @@ -7,9 +7,9 @@ import sys import traceback -from codeop import compile_command +from codeop import CommandCompiler, compile_command -__all__ = ["InteractiveInterpreter","InteractiveConsole","interact", +__all__ = ["InteractiveInterpreter", "InteractiveConsole", "interact", "compile_command"] def softspace(file, newvalue): @@ -45,6 +45,7 @@ class InteractiveInterpreter: if locals is None: locals = {"__name__": "__console__", "__doc__": None} self.locals = locals + self.compile = CommandCompiler() def runsource(self, source, filename="<input>", symbol="single"): """Compile and run some source in the interpreter. @@ -71,7 +72,7 @@ class InteractiveInterpreter: """ try: - code = compile_command(source, filename, symbol) + code = self.compile(source, filename, symbol) except (OverflowError, SyntaxError, ValueError): # Case 1 self.showsyntaxerror(filename) diff --git a/Lib/codeop.py b/Lib/codeop.py index 3335f09..c97b4df 100644 --- a/Lib/codeop.py +++ b/Lib/codeop.py @@ -1,49 +1,69 @@ -"""Utility to compile possibly incomplete Python source code.""" +r"""Utilities to compile possibly incomplete Python source code. -__all__ = ["compile_command"] +This module provides two interfaces, broadly similar to the builtin +function compile(), that take progam text, a filename and a 'mode' +and: -def compile_command(source, filename="<input>", symbol="single"): - r"""Compile a command and determine whether it is incomplete. +- Return a code object if the command is complete and valid +- Return None if the command is incomplete +- Raise SyntaxError, ValueError or OverflowError if the command is a + syntax error (OverflowError and ValueError can be produced by + malformed literals). - Arguments: +Approach: - source -- the source string; may contain \n characters - filename -- optional filename from which source was read; default "<input>" - symbol -- optional grammar start symbol; "single" (default) or "eval" +First, check if the source consists entirely of blank lines and +comments; if so, replace it with 'pass', because the built-in +parser doesn't always do the right thing for these. - Return value / exceptions raised: +Compile three times: as is, with \n, and with \n\n appended. If it +compiles as is, it's complete. If it compiles with one \n appended, +we expect more. If it doesn't compile either way, we compare the +error we get when compiling with \n or \n\n appended. If the errors +are the same, the code is broken. But if the errors are different, we +expect more. Not intuitive; not even guaranteed to hold in future +releases; but this matches the compiler's behavior from Python 1.4 +through 2.2, at least. - - Return a code object if the command is complete and valid - - Return None if the command is incomplete - - Raise SyntaxError or OverflowError if the command is a syntax error - (OverflowError if the error is in a numeric constant) +Caveat: - Approach: +It is possible (but not likely) that the parser stops parsing with a +successful outcome before reaching the end of the source; in this +case, trailing symbols may be ignored instead of causing an error. +For example, a backslash followed by two newlines may be followed by +arbitrary garbage. This will be fixed once the API for the parser is +better. - First, check if the source consists entirely of blank lines and - comments; if so, replace it with 'pass', because the built-in - parser doesn't always do the right thing for these. +The two interfaces are: - Compile three times: as is, with \n, and with \n\n appended. If - it compiles as is, it's complete. If it compiles with one \n - appended, we expect more. If it doesn't compile either way, we - compare the error we get when compiling with \n or \n\n appended. - If the errors are the same, the code is broken. But if the errors - are different, we expect more. Not intuitive; not even guaranteed - to hold in future releases; but this matches the compiler's - behavior from Python 1.4 through 1.5.2, at least. +compile_command(source, filename, symbol): - Caveat: + Compiles a single command in the manner described above. - It is possible (but not likely) that the parser stops parsing - with a successful outcome before reaching the end of the source; - in this case, trailing symbols may be ignored instead of causing an - error. For example, a backslash followed by two newlines may be - followed by arbitrary garbage. This will be fixed once the API - for the parser is better. +CommandCompiler(): - """ + Instances of this class have __call__ methods identical in + signature to compile_command; the difference is that if the + instance compiles program text containing a __future__ statement, + the instance 'remembers' and compiles all subsequent program texts + with the statement in force. + +The module also provides another class: + +Compile(): + + Instances of this class act like the built-in function compile, + but with 'memory' in the sense described above. +""" + +import __future__ + +_features = [getattr(__future__, fname) + for fname in __future__.all_feature_names] + +__all__ = ["compile_command", "Compile", "CommandCompiler"] +def _maybe_compile(compiler, source, filename, symbol): # Check for source consisting of only blank lines and comments for line in source.split("\n"): line = line.strip() @@ -56,17 +76,17 @@ def compile_command(source, filename="<input>", symbol="single"): code = code1 = code2 = None try: - code = compile(source, filename, symbol) + code = compiler(source, filename, symbol) except SyntaxError, err: pass try: - code1 = compile(source + "\n", filename, symbol) + code1 = compiler(source + "\n", filename, symbol) except SyntaxError, err1: pass try: - code2 = compile(source + "\n\n", filename, symbol) + code2 = compiler(source + "\n\n", filename, symbol) except SyntaxError, err2: pass @@ -82,3 +102,69 @@ def compile_command(source, filename="<input>", symbol="single"): e2 = err2 if not code1 and e1 == e2: raise SyntaxError, err1 + +def compile_command(source, filename="<input>", symbol="single"): + r"""Compile a command and determine whether it is incomplete. + + Arguments: + + source -- the source string; may contain \n characters + filename -- optional filename from which source was read; default + "<input>" + symbol -- optional grammar start symbol; "single" (default) or "eval" + + Return value / exceptions raised: + + - Return a code object if the command is complete and valid + - Return None if the command is incomplete + - Raise SyntaxError, ValueError or OverflowError if the command is a + syntax error (OverflowError and ValueError can be produced by + malformed literals). + """ + return _maybe_compile(compile, source, filename, symbol) + +class Compile: + """Instances of this class behave much like the built-in compile + function, but if one is used to compile text containing a future + statement, it "remembers" and compiles all subsequent program texts + with the statement in force.""" + def __init__(self): + self.flags = 0 + + def __call__(self, source, filename, symbol): + codeob = compile(source, filename, symbol, self.flags, 1) + for feature in _features: + if codeob.co_flags & feature.compiler_flag: + self.flags |= feature.compiler_flag + return codeob + +class CommandCompiler: + """Instances of this class have __call__ methods identical in + signature to compile_command; the difference is that if the + instance compiles program text containing a __future__ statement, + the instance 'remembers' and compiles all subsequent program texts + with the statement in force.""" + + def __init__(self,): + self.compiler = Compile() + + def __call__(self, source, filename="<input>", symbol="single"): + r"""Compile a command and determine whether it is incomplete. + + Arguments: + + source -- the source string; may contain \n characters + filename -- optional filename from which source was read; + default "<input>" + symbol -- optional grammar start symbol; "single" (default) or + "eval" + + Return value / exceptions raised: + + - Return a code object if the command is complete and valid + - Return None if the command is incomplete + - Raise SyntaxError, ValueError or OverflowError if the command is a + syntax error (OverflowError and ValueError can be produced by + malformed literals). + """ + return _maybe_compile(self.compiler, source, filename, symbol) |