Merge tag 'v3.8.0b3' into 3.8
Python 3.8.0b3
diff --git a/.azure-pipelines/windows-release/stage-publish-nugetorg.yml b/.azure-pipelines/windows-release/stage-publish-nugetorg.yml
index 296eb28..570cdb3 100644
--- a/.azure-pipelines/windows-release/stage-publish-nugetorg.yml
+++ b/.azure-pipelines/windows-release/stage-publish-nugetorg.yml
@@ -36,6 +36,6 @@
condition: and(succeeded(), eq(variables['SigningCertificate'], variables['__RealSigningCertificate']))
inputs:
command: push
- packagesToPush: $(Build.BinariesDirectory)\nuget\*.nupkg'
+ packagesToPush: '$(Build.BinariesDirectory)\nuget\*.nupkg'
nuGetFeedType: external
publishFeedCredentials: 'Python on Nuget'
diff --git a/Lib/test/test_cmd_line_script.py b/Lib/test/test_cmd_line_script.py
index d138ca0..b74eeba 100644
--- a/Lib/test/test_cmd_line_script.py
+++ b/Lib/test/test_cmd_line_script.py
@@ -601,6 +601,36 @@
self.assertNotIn("\f", text)
self.assertIn("\n 1 + 1 = 2\n ^", text)
+ def test_syntaxerror_multi_line_fstring(self):
+ script = 'foo = f"""{}\nfoo"""\n'
+ with support.temp_dir() as script_dir:
+ script_name = _make_test_script(script_dir, 'script', script)
+ exitcode, stdout, stderr = assert_python_failure(script_name)
+ self.assertEqual(
+ stderr.splitlines()[-3:],
+ [
+ b' foo = f"""{}',
+ b' ^',
+ b'SyntaxError: f-string: empty expression not allowed',
+ ],
+ )
+
+ def test_syntaxerror_invalid_escape_sequence_multi_line(self):
+ script = 'foo = """\\q\n"""\n'
+ with support.temp_dir() as script_dir:
+ script_name = _make_test_script(script_dir, 'script', script)
+ exitcode, stdout, stderr = assert_python_failure(
+ '-Werror', script_name,
+ )
+ self.assertEqual(
+ stderr.splitlines()[-3:],
+ [
+ b' foo = """\\q',
+ b' ^',
+ b'SyntaxError: invalid escape sequence \\q',
+ ],
+ )
+
def test_consistent_sys_path_for_direct_execution(self):
# This test case ensures that the following all give the same
# sys.path configuration:
diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py
index 56f73f6..9d77f7a 100644
--- a/Lib/test/test_compile.py
+++ b/Lib/test/test_compile.py
@@ -697,6 +697,40 @@
# complex statements.
compile("if a: b\n" * 200000, "<dummy>", "exec")
+ # Multiple users rely on the fact that CPython does not generate
+ # bytecode for dead code blocks. See bpo-37500 for more context.
+ @support.cpython_only
+ def test_dead_blocks_do_not_generate_bytecode(self):
+ def unused_block_if():
+ if 0:
+ return 42
+
+ def unused_block_while():
+ while 0:
+ return 42
+
+ def unused_block_if_else():
+ if 1:
+ return None
+ else:
+ return 42
+
+ def unused_block_while_else():
+ while 1:
+ return None
+ else:
+ return 42
+
+ funcs = [unused_block_if, unused_block_while,
+ unused_block_if_else, unused_block_while_else]
+
+ for func in funcs:
+ opcodes = list(dis.get_instructions(func))
+ self.assertEqual(2, len(opcodes))
+ self.assertEqual('LOAD_CONST', opcodes[0].opname)
+ self.assertEqual(None, opcodes[0].argval)
+ self.assertEqual('RETURN_VALUE', opcodes[1].opname)
+
class TestExpressionStackSize(unittest.TestCase):
# These tests check that the computed stack size for a code object
diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py
index 8451c07..3829746 100644
--- a/Lib/test/test_syntax.py
+++ b/Lib/test/test_syntax.py
@@ -697,18 +697,47 @@
self._check_error("break", "outside loop")
def test_yield_outside_function(self):
- self._check_error("if 0: yield", "outside function")
- self._check_error("class C:\n if 0: yield", "outside function")
+ self._check_error("if 0: yield", "outside function")
+ self._check_error("if 0: yield\nelse: x=1", "outside function")
+ self._check_error("if 1: pass\nelse: yield", "outside function")
+ self._check_error("while 0: yield", "outside function")
+ self._check_error("while 0: yield\nelse: x=1", "outside function")
+ self._check_error("class C:\n if 0: yield", "outside function")
+ self._check_error("class C:\n if 1: pass\n else: yield",
+ "outside function")
+ self._check_error("class C:\n while 0: yield", "outside function")
+ self._check_error("class C:\n while 0: yield\n else: x = 1",
+ "outside function")
def test_return_outside_function(self):
- self._check_error("if 0: return", "outside function")
- self._check_error("class C:\n if 0: return", "outside function")
+ self._check_error("if 0: return", "outside function")
+ self._check_error("if 0: return\nelse: x=1", "outside function")
+ self._check_error("if 1: pass\nelse: return", "outside function")
+ self._check_error("while 0: return", "outside function")
+ self._check_error("class C:\n if 0: return", "outside function")
+ self._check_error("class C:\n while 0: return", "outside function")
+ self._check_error("class C:\n while 0: return\n else: x=1",
+ "outside function")
+ self._check_error("class C:\n if 0: return\n else: x= 1",
+ "outside function")
+ self._check_error("class C:\n if 1: pass\n else: return",
+ "outside function")
def test_break_outside_loop(self):
- self._check_error("if 0: break", "outside loop")
+ self._check_error("if 0: break", "outside loop")
+ self._check_error("if 0: break\nelse: x=1", "outside loop")
+ self._check_error("if 1: pass\nelse: break", "outside loop")
+ self._check_error("class C:\n if 0: break", "outside loop")
+ self._check_error("class C:\n if 1: pass\n else: break",
+ "outside loop")
def test_continue_outside_loop(self):
- self._check_error("if 0: continue", "not properly in loop")
+ self._check_error("if 0: continue", "not properly in loop")
+ self._check_error("if 0: continue\nelse: x=1", "not properly in loop")
+ self._check_error("if 1: pass\nelse: continue", "not properly in loop")
+ self._check_error("class C:\n if 0: continue", "not properly in loop")
+ self._check_error("class C:\n if 1: pass\n else: continue",
+ "not properly in loop")
def test_unexpected_indent(self):
self._check_error("foo()\n bar()\n", "unexpected indent",
diff --git a/Lib/test/test_sys_settrace.py b/Lib/test/test_sys_settrace.py
index 112ea87..fdd7894 100644
--- a/Lib/test/test_sys_settrace.py
+++ b/Lib/test/test_sys_settrace.py
@@ -53,22 +53,52 @@
# following that clause?
-# The entire "while 0:" statement is optimized away. No code
-# exists for it, so the line numbers skip directly from "del x"
-# to "x = 1".
-def arigo_example():
+# Some constructs like "while 0:", "if 0:" or "if 1:...else:..." are optimized
+# away. No code # exists for them, so the line numbers skip directly from
+# "del x" to "x = 1".
+def arigo_example0():
x = 1
del x
while 0:
pass
x = 1
-arigo_example.events = [(0, 'call'),
+arigo_example0.events = [(0, 'call'),
(1, 'line'),
(2, 'line'),
(5, 'line'),
(5, 'return')]
+def arigo_example1():
+ x = 1
+ del x
+ if 0:
+ pass
+ x = 1
+
+arigo_example1.events = [(0, 'call'),
+ (1, 'line'),
+ (2, 'line'),
+ (5, 'line'),
+ (5, 'return')]
+
+def arigo_example2():
+ x = 1
+ del x
+ if 1:
+ x = 1
+ else:
+ pass
+ return None
+
+arigo_example2.events = [(0, 'call'),
+ (1, 'line'),
+ (2, 'line'),
+ (4, 'line'),
+ (7, 'line'),
+ (7, 'return')]
+
+
# check that lines consisting of just one instruction get traced:
def one_instr_line():
x = 1
@@ -349,8 +379,12 @@
def test_01_basic(self):
self.run_test(basic)
- def test_02_arigo(self):
- self.run_test(arigo_example)
+ def test_02_arigo0(self):
+ self.run_test(arigo_example0)
+ def test_02_arigo1(self):
+ self.run_test(arigo_example1)
+ def test_02_arigo2(self):
+ self.run_test(arigo_example2)
def test_03_one_instr(self):
self.run_test(one_instr_line)
def test_04_no_pop_blocks(self):
diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-06-27-15-01-14.bpo-37433.amNGqr.rst b/Misc/NEWS.d/next/Core and Builtins/2019-06-27-15-01-14.bpo-37433.amNGqr.rst
new file mode 100644
index 0000000..794ddbb
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2019-06-27-15-01-14.bpo-37433.amNGqr.rst
@@ -0,0 +1 @@
+Fix ``SyntaxError`` indicator printing too many spaces for multi-line strings - by Anthony Sottile.
diff --git a/Parser/tokenizer.c b/Parser/tokenizer.c
index c2ec659..31fe970 100644
--- a/Parser/tokenizer.c
+++ b/Parser/tokenizer.c
@@ -956,6 +956,7 @@
while (!done) {
Py_ssize_t curstart = tok->start == NULL ? -1 :
tok->start - tok->buf;
+ Py_ssize_t cur_multi_line_start = tok->multi_line_start - tok->buf;
Py_ssize_t curvalid = tok->inp - tok->buf;
Py_ssize_t newsize = curvalid + BUFSIZ;
char *newbuf = tok->buf;
@@ -968,6 +969,7 @@
}
tok->buf = newbuf;
tok->cur = tok->buf + cur;
+ tok->multi_line_start = tok->buf + cur_multi_line_start;
tok->line_start = tok->cur;
tok->inp = tok->buf + curvalid;
tok->end = tok->buf + newsize;
diff --git a/Python/compile.c b/Python/compile.c
index 9cce8ae..0336959 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -161,6 +161,11 @@
int c_optimize; /* optimization level */
int c_interactive; /* true if in interactive mode */
int c_nestlevel;
+ int c_do_not_emit_bytecode; /* The compiler won't emit any bytecode
+ if this value is different from zero.
+ This can be used to temporarily visit
+ nodes without emitting bytecode to
+ check only errors. */
PyObject *c_const_cache; /* Python dict holding all constants,
including names tuple */
@@ -340,6 +345,7 @@
c.c_flags = flags;
c.c_optimize = (optimize == -1) ? config->optimization_level : optimize;
c.c_nestlevel = 0;
+ c.c_do_not_emit_bytecode = 0;
if (!_PyAST_Optimize(mod, arena, c.c_optimize)) {
goto finally;
@@ -1152,6 +1158,9 @@
struct instr *i;
int off;
assert(!HAS_ARG(opcode));
+ if (c->c_do_not_emit_bytecode) {
+ return 1;
+ }
off = compiler_next_instr(c, c->u->u_curblock);
if (off < 0)
return 0;
@@ -1305,6 +1314,10 @@
static Py_ssize_t
compiler_add_const(struct compiler *c, PyObject *o)
{
+ if (c->c_do_not_emit_bytecode) {
+ return 0;
+ }
+
PyObject *key = merge_consts_recursive(c, o);
if (key == NULL) {
return -1;
@@ -1318,6 +1331,10 @@
static int
compiler_addop_load_const(struct compiler *c, PyObject *o)
{
+ if (c->c_do_not_emit_bytecode) {
+ return 1;
+ }
+
Py_ssize_t arg = compiler_add_const(c, o);
if (arg < 0)
return 0;
@@ -1328,6 +1345,10 @@
compiler_addop_o(struct compiler *c, int opcode, PyObject *dict,
PyObject *o)
{
+ if (c->c_do_not_emit_bytecode) {
+ return 1;
+ }
+
Py_ssize_t arg = compiler_add_o(c, dict, o);
if (arg < 0)
return 0;
@@ -1339,6 +1360,11 @@
PyObject *o)
{
Py_ssize_t arg;
+
+ if (c->c_do_not_emit_bytecode) {
+ return 1;
+ }
+
PyObject *mangled = _Py_Mangle(c->u->u_private, o);
if (!mangled)
return 0;
@@ -1359,6 +1385,10 @@
struct instr *i;
int off;
+ if (c->c_do_not_emit_bytecode) {
+ return 1;
+ }
+
/* oparg value is unsigned, but a signed C int is usually used to store
it in the C code (like Python/ceval.c).
@@ -1385,6 +1415,10 @@
struct instr *i;
int off;
+ if (c->c_do_not_emit_bytecode) {
+ return 1;
+ }
+
assert(HAS_ARG(opcode));
assert(b != NULL);
off = compiler_next_instr(c, c->u->u_curblock);
@@ -1519,6 +1553,17 @@
} \
}
+/* These macros allows to check only for errors and not emmit bytecode
+ * while visiting nodes.
+*/
+
+#define BEGIN_DO_NOT_EMIT_BYTECODE { \
+ c->c_do_not_emit_bytecode++;
+
+#define END_DO_NOT_EMIT_BYTECODE \
+ c->c_do_not_emit_bytecode--; \
+}
+
/* Search if variable annotations are present statically in a block. */
static int
@@ -2546,13 +2591,23 @@
return 0;
constant = expr_constant(s->v.If.test);
- /* constant = 0: "if 0" Leave the optimizations to
- * the pephole optimizer to check for syntax errors
- * in the block.
+ /* constant = 0: "if 0"
* constant = 1: "if 1", "if 2", ...
* constant = -1: rest */
- if (constant == 1) {
+ if (constant == 0) {
+ BEGIN_DO_NOT_EMIT_BYTECODE
VISIT_SEQ(c, stmt, s->v.If.body);
+ END_DO_NOT_EMIT_BYTECODE
+ if (s->v.If.orelse) {
+ VISIT_SEQ(c, stmt, s->v.If.orelse);
+ }
+ } else if (constant == 1) {
+ VISIT_SEQ(c, stmt, s->v.If.body);
+ if (s->v.If.orelse) {
+ BEGIN_DO_NOT_EMIT_BYTECODE
+ VISIT_SEQ(c, stmt, s->v.If.orelse);
+ END_DO_NOT_EMIT_BYTECODE
+ }
} else {
if (asdl_seq_LEN(s->v.If.orelse)) {
next = compiler_new_block(c);
@@ -2662,8 +2717,12 @@
int constant = expr_constant(s->v.While.test);
if (constant == 0) {
- if (s->v.While.orelse)
+ BEGIN_DO_NOT_EMIT_BYTECODE
+ VISIT_SEQ(c, stmt, s->v.While.body);
+ END_DO_NOT_EMIT_BYTECODE
+ if (s->v.While.orelse) {
VISIT_SEQ(c, stmt, s->v.While.orelse);
+ }
return 1;
}
loop = compiler_new_block(c);
diff --git a/Tools/msi/uploadrelease.ps1 b/Tools/msi/uploadrelease.ps1
index 469a968..d3673b4 100644
--- a/Tools/msi/uploadrelease.ps1
+++ b/Tools/msi/uploadrelease.ps1
@@ -92,6 +92,7 @@
& $plink -batch $user@$server chgrp downloads $d
& $plink -batch $user@$server chmod g-x,o+rx $d
& $pscp -batch $chm.FullName "$user@${server}:$d"
+ if (-not $?) { throw "Failed to upload $chm" }
$dirs = gci "$build" -Directory
if ($embed) {
@@ -107,6 +108,7 @@
if ($exe) {
& $pscp -batch $exe.FullName "$user@${server}:$d"
+ if (-not $?) { throw "Failed to upload $exe" }
}
if ($msi) {
@@ -115,6 +117,7 @@
& $plink -batch $user@$server chgrp downloads $sd
& $plink -batch $user@$server chmod g-x,o+rx $sd
& $pscp -batch $msi.FullName "$user@${server}:$sd"
+ if (-not $?) { throw "Failed to upload $msi" }
& $plink -batch $user@$server chgrp downloads $sd*
& $plink -batch $user@$server chmod g-x,o+r $sd*
}
@@ -122,6 +125,7 @@
& $plink -batch $user@$server chgrp downloads $d*
& $plink -batch $user@$server chmod g-x,o+r $d*
+ & $pscp -ls "$user@${server}:$d"
}
if (-not $skippurge) {