summaryrefslogtreecommitdiff
path: root/Lib/test
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/test')
-rw-r--r--Lib/test/test_compile.py34
-rw-r--r--Lib/test/test_syntax.py41
-rw-r--r--Lib/test/test_sys_settrace.py48
3 files changed, 110 insertions, 13 deletions
diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py
index 56f73f6315..9d77f7af05 100644
--- a/Lib/test/test_compile.py
+++ b/Lib/test/test_compile.py
@@ -697,6 +697,40 @@ if 1:
# 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 8451c072f6..3829746f17 100644
--- a/Lib/test/test_syntax.py
+++ b/Lib/test/test_syntax.py
@@ -697,18 +697,47 @@ class SyntaxTestCase(unittest.TestCase):
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 112ea87720..fdd789475d 100644
--- a/Lib/test/test_sys_settrace.py
+++ b/Lib/test/test_sys_settrace.py
@@ -53,22 +53,52 @@ basic.events = [(0, 'call'),
# 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 @@ class TraceTestCase(unittest.TestCase):
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):