summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTim Peters <tim.peters@gmail.com>2001-08-17 22:11:27 (GMT)
committerTim Peters <tim.peters@gmail.com>2001-08-17 22:11:27 (GMT)
commit6cd6a82db93fa37a13d0b5312ce6510c4d6967de (patch)
treec43eca432a18a364fb1c004d4dfcfe904fa7cefd
parent10d72552495a1967af7174f6ed52eb3340799c76 (diff)
downloadcpython-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.
-rw-r--r--Lib/code.py7
-rw-r--r--Lib/codeop.py158
-rw-r--r--Misc/NEWS5
-rw-r--r--Python/bltinmodule.c34
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)
diff --git a/Misc/NEWS b/Misc/NEWS
index fe8eef3..3935f0d 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -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 *