diff options
-rw-r--r-- | Lib/code.py | 7 | ||||
-rw-r--r-- | Lib/codeop.py | 158 | ||||
-rw-r--r-- | Misc/NEWS | 5 | ||||
-rw-r--r-- | Python/bltinmodule.c | 34 |
4 files changed, 157 insertions, 47 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) @@ -23,6 +23,11 @@ Tests Core +- Future statements are now effective in simulated interactive shells + (like IDLE). This should "just work" by magic, but read Michael + Hudson's "Future statements in simulated shells" PEP 264 for full + details: <http://python.sf.net/peps/pep-0264.html>. + - The type/class unification (PEP 252-253) was integrated into the trunk and is not so tentative any more (the exact specification of some features is still tentative). A lot of work has done on fixing diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index d0f934f..9a179a1 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -377,10 +377,14 @@ builtin_compile(PyObject *self, PyObject *args) char *filename; char *startstr; int start; + int dont_inherit = 0; + int supplied_flags = 0; PyCompilerFlags cf; - if (!PyArg_ParseTuple(args, "sss:compile", &str, &filename, &startstr)) + if (!PyArg_ParseTuple(args, "sss|ii:compile", &str, &filename, + &startstr, &supplied_flags, &dont_inherit)) return NULL; + if (strcmp(startstr, "exec") == 0) start = Py_file_input; else if (strcmp(startstr, "eval") == 0) @@ -392,21 +396,35 @@ builtin_compile(PyObject *self, PyObject *args) "compile() arg 3 must be 'exec' or 'eval' or 'single'"); return NULL; } - cf.cf_flags = 0; - if (PyEval_MergeCompilerFlags(&cf)) - return Py_CompileStringFlags(str, filename, start, &cf); - else - return Py_CompileString(str, filename, start); + + if (supplied_flags & ~(PyCF_MASK | PyCF_MASK_OBSOLETE)) { + PyErr_SetString(PyExc_ValueError, + "compile(): unrecognised flags"); + return NULL; + } + /* XXX Warn if (supplied_flags & PyCF_MASK_OBSOLETE) != 0? */ + + cf.cf_flags = supplied_flags; + if (!dont_inherit) { + PyEval_MergeCompilerFlags(&cf); + } + return Py_CompileStringFlags(str, filename, start, &cf); } static char compile_doc[] = -"compile(source, filename, mode) -> code object\n\ +"compile(source, filename, mode[, flags[, dont_inherit]]) -> code object\n\ \n\ Compile the source string (a Python module, statement or expression)\n\ into a code object that can be executed by the exec statement or eval().\n\ The filename will be used for run-time error messages.\n\ The mode must be 'exec' to compile a module, 'single' to compile a\n\ -single (interactive) statement, or 'eval' to compile an expression."; +single (interactive) statement, or 'eval' to compile an expression.\n\ +The flags argument, if present, controls which future statements influence\n\ +the compilation of the code.\n\ +The dont_inherit argument, if non-zero, stops the compilation inheriting\n\ +the effects of any future statements in effect in the code calling\n\ +compile; if absent or zero these statements do influence the compilation,\n\ +in addition to any features explicitly specified."; static PyObject * |