Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 4b499dd

Browse files
committed
- Finally fixed the bug in compile() and exec where a string ending
with an indented code block but no newline would raise SyntaxError. This would have been a four-line change in parsetok.c... Except codeop.py depends on this behavior, so a compilation flag had to be invented that causes the tokenizer to revert to the old behavior; this required extra changes to 2 .h files, 2 .c files, and 2 .py files. (Fixes SF bug #501622.)
1 parent 5aa3da6 commit 4b499dd

10 files changed

Lines changed: 47 additions & 13 deletions

File tree

Include/parsetok.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ typedef struct {
2121
#define PyPARSE_YIELD_IS_KEYWORD 0x0001
2222
#endif
2323

24+
#define PyPARSE_DONT_IMPLY_DEDENT 0x0002
25+
2426
PyAPI_FUNC(node *) PyParser_ParseString(const char *, grammar *, int,
2527
perrdetail *);
2628
PyAPI_FUNC(node *) PyParser_ParseFile (FILE *, const char *, grammar *, int,

Include/pythonrun.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ extern "C" {
1010
#define PyCF_MASK (CO_FUTURE_DIVISION)
1111
#define PyCF_MASK_OBSOLETE (CO_GENERATOR_ALLOWED | CO_NESTED)
1212
#define PyCF_SOURCE_IS_UTF8 0x0100
13+
#define PyCF_DONT_IMPLY_DEDENT 0x0200
1314

1415
typedef struct {
1516
int cf_flags; /* bitmask of CO_xxx flags relevant to future */

Lib/code.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,4 +303,5 @@ def interact(banner=None, readfunc=None, local=None):
303303

304304

305305
if __name__ == '__main__':
306-
interact()
306+
import pdb
307+
pdb.run("interact()\n")

Lib/codeop.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@
6363

6464
__all__ = ["compile_command", "Compile", "CommandCompiler"]
6565

66+
PyCF_DONT_IMPLY_DEDENT = 0x200 # Matches pythonrun.h
67+
6668
def _maybe_compile(compiler, source, filename, symbol):
6769
# Check for source consisting of only blank lines and comments
6870
for line in source.split("\n"):
@@ -103,6 +105,9 @@ def _maybe_compile(compiler, source, filename, symbol):
103105
if not code1 and e1 == e2:
104106
raise SyntaxError, err1
105107

108+
def _compile(source, filename, symbol):
109+
return compile(source, filename, symbol, PyCF_DONT_IMPLY_DEDENT)
110+
106111
def compile_command(source, filename="<input>", symbol="single"):
107112
r"""Compile a command and determine whether it is incomplete.
108113
@@ -121,15 +126,15 @@ def compile_command(source, filename="<input>", symbol="single"):
121126
syntax error (OverflowError and ValueError can be produced by
122127
malformed literals).
123128
"""
124-
return _maybe_compile(compile, source, filename, symbol)
129+
return _maybe_compile(_compile, source, filename, symbol)
125130

126131
class Compile:
127132
"""Instances of this class behave much like the built-in compile
128133
function, but if one is used to compile text containing a future
129134
statement, it "remembers" and compiles all subsequent program texts
130135
with the statement in force."""
131136
def __init__(self):
132-
self.flags = 0
137+
self.flags = PyCF_DONT_IMPLY_DEDENT
133138

134139
def __call__(self, source, filename, symbol):
135140
codeob = compile(source, filename, symbol, self.flags, 1)

Lib/test/test_codeop.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
import unittest
66
from test.test_support import run_unittest
77

8-
from codeop import compile_command
8+
from codeop import compile_command, PyCF_DONT_IMPLY_DEDENT
99

1010
class CodeopTests(unittest.TestCase):
1111

1212
def assertValid(self, str, symbol='single'):
1313
'''succeed iff str is a valid piece of code'''
14-
expected = compile(str, "<input>", symbol)
14+
expected = compile(str, "<input>", symbol, PyCF_DONT_IMPLY_DEDENT)
1515
self.assertEquals( compile_command(str, "<input>", symbol), expected)
1616

1717

@@ -42,7 +42,8 @@ def test_valid(self):
4242

4343
# special case
4444
self.assertEquals(compile_command(""),
45-
compile("pass", "<input>", 'single'))
45+
compile("pass", "<input>", 'single',
46+
PyCF_DONT_IMPLY_DEDENT))
4647

4748
av("3**3","eval")
4849
av("(lambda z: \n z**3)","eval")

Lib/test/test_compile.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,15 @@ def expect_error(s):
8989
expect_error("1e-")
9090
expect_error("3-4e/21")
9191

92+
if verbose:
93+
print "testing compile() of indented block w/o trailing newline"
94+
95+
s = """
96+
if 1:
97+
if 2:
98+
pass"""
99+
compile(s, "<string>", "exec")
100+
92101

93102
if verbose:
94103
print "testing literals with leading zeroes"

Misc/NEWS

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ What's New in Python 2.3 alpha 2?
1212
Core and builtins
1313
-----------------
1414

15+
- Finally fixed the bug in compile() and exec where a string ending
16+
with an indented code block but no newline would raise SyntaxError.
17+
This would have been a four-line change in parsetok.c... Except
18+
codeop.py depends on this behavior, so a compilation flag had to be
19+
invented that causes the tokenizer to revert to the old behavior;
20+
this required extra changes to 2 .h files, 2 .c files, and 2 .py
21+
files.
22+
1523
- If a new-style class defines neither __new__ nor __init__, its
1624
constructor would ignore all arguments. This is changed now: the
1725
constructor refuses arguments in this case. This might break code

Parser/parsetok.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,15 @@ parsetok(struct tok_state *tok, grammar *g, int start, perrdetail *err_ret,
130130
if (type == ENDMARKER && started) {
131131
type = NEWLINE; /* Add an extra newline */
132132
started = 0;
133+
/* Add the right number of dedent tokens,
134+
except if a certain flag is given --
135+
codeop.py uses this. */
136+
if (tok->indent &&
137+
!(flags & PyPARSE_DONT_IMPLY_DEDENT))
138+
{
139+
tok->pendin = -tok->indent;
140+
tok->indent = 0;
141+
}
133142
}
134143
else
135144
started = 1;

Python/bltinmodule.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,9 @@ builtin_compile(PyObject *self, PyObject *args)
380380
return NULL;
381381
}
382382

383-
if (supplied_flags & ~(PyCF_MASK | PyCF_MASK_OBSOLETE)) {
383+
if (supplied_flags &
384+
~(PyCF_MASK | PyCF_MASK_OBSOLETE | PyCF_DONT_IMPLY_DEDENT))
385+
{
384386
PyErr_SetString(PyExc_ValueError,
385387
"compile(): unrecognised flags");
386388
return NULL;

Python/pythonrun.c

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -548,13 +548,9 @@ PyRun_InteractiveOne(FILE *fp, const char *filename)
548548
}
549549

550550
/* compute parser flags based on compiler flags */
551-
#if 0 /* future keyword */
552551
#define PARSER_FLAGS(flags) \
553-
(((flags) && (flags)->cf_flags & CO_GENERATOR_ALLOWED) ? \
554-
PyPARSE_YIELD_IS_KEYWORD : 0)
555-
#else
556-
#define PARSER_FLAGS(flags) 0
557-
#endif
552+
(((flags) && (flags)->cf_flags & PyCF_DONT_IMPLY_DEDENT) ? \
553+
PyPARSE_DONT_IMPLY_DEDENT : 0)
558554

559555
int
560556
PyRun_InteractiveOneFlags(FILE *fp, const char *filename, PyCompilerFlags *flags)

0 commit comments

Comments
 (0)