diff options
Diffstat (limited to 'Lib/test/test_peepholer.py')
| -rw-r--r-- | Lib/test/test_peepholer.py | 143 |
1 files changed, 109 insertions, 34 deletions
diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py index 7e05f49..e268ae2 100644 --- a/Lib/test/test_peepholer.py +++ b/Lib/test/test_peepholer.py @@ -1,6 +1,7 @@ import dis +import re import sys -from cStringIO import StringIO +from io import StringIO import unittest def disassemble(func): @@ -19,14 +20,15 @@ def dis_single(line): class TestTranforms(unittest.TestCase): def test_unot(self): - # UNARY_NOT POP_JUMP_IF_FALSE --> POP_JUMP_IF_TRUE + # UNARY_NOT POP_JUMP_IF_FALSE --> POP_JUMP_IF_TRUE' def unot(x): if not x == 2: del x asm = disassemble(unot) for elem in ('UNARY_NOT', 'POP_JUMP_IF_FALSE'): self.assertNotIn(elem, asm) - self.assertIn('POP_JUMP_IF_TRUE', asm) + for elem in ('POP_JUMP_IF_TRUE',): + self.assertIn(elem, asm) def test_elim_inversion_of_is_or_in(self): for line, elem in ( @@ -38,16 +40,24 @@ class TestTranforms(unittest.TestCase): asm = dis_single(line) self.assertIn(elem, asm) - def test_none_as_constant(self): - # LOAD_GLOBAL None --> LOAD_CONST None + def test_global_as_constant(self): + # LOAD_GLOBAL None/True/False --> LOAD_CONST None/True/False def f(x): None + None return x - asm = disassemble(f) - for elem in ('LOAD_GLOBAL',): - self.assertNotIn(elem, asm) - for elem in ('LOAD_CONST', '(None)'): - self.assertIn(elem, asm) + def g(x): + True + return x + def h(x): + False + return x + for func, name in ((f, 'None'), (g, 'True'), (h, 'False')): + asm = disassemble(func) + for elem in ('LOAD_GLOBAL',): + self.assertNotIn(elem, asm) + for elem in ('LOAD_CONST', '('+name+')'): + self.assertIn(elem, asm) def f(): 'Adding a docstring made this test fail in Py2.5.0' return None @@ -106,6 +116,54 @@ class TestTranforms(unittest.TestCase): 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ],) + def test_folding_of_lists_of_constants(self): + for line, elem in ( + # in/not in constants with BUILD_LIST should be folded to a tuple: + ('a in [1,2,3]', '(1, 2, 3)'), + ('a not in ["a","b","c"]', "(('a', 'b', 'c'))"), + ('a in [None, 1, None]', '((None, 1, None))'), + ('a not in [(1, 2), 3, 4]', '(((1, 2), 3, 4))'), + ): + asm = dis_single(line) + self.assertIn(elem, asm) + self.assertNotIn('BUILD_LIST', asm) + + def test_folding_of_sets_of_constants(self): + for line, elem in ( + # in/not in constants with BUILD_SET should be folded to a frozenset: + ('a in {1,2,3}', frozenset({1, 2, 3})), + ('a not in {"a","b","c"}', frozenset({'a', 'c', 'b'})), + ('a in {None, 1, None}', frozenset({1, None})), + ('a not in {(1, 2), 3, 4}', frozenset({(1, 2), 3, 4})), + ('a in {1, 2, 3, 3, 2, 1}', frozenset({1, 2, 3})), + ): + asm = dis_single(line) + self.assertNotIn('BUILD_SET', asm) + + # Verify that the frozenset 'elem' is in the disassembly + # The ordering of the elements in repr( frozenset ) isn't + # guaranteed, so we jump through some hoops to ensure that we have + # the frozenset we expect: + self.assertIn('frozenset', asm) + # Extract the frozenset literal from the disassembly: + m = re.match(r'.*(frozenset\({.*}\)).*', asm, re.DOTALL) + self.assertTrue(m) + self.assertEqual(eval(m.group(1)), elem) + + # Ensure that the resulting code actually works: + def f(a): + return a in {1, 2, 3} + + def g(a): + return a not in {1, 2, 3} + + self.assertTrue(f(3)) + self.assertTrue(not f(4)) + + self.assertTrue(not g(3)) + self.assertTrue(g(4)) + + def test_folding_of_binops_on_constants(self): for line, elem in ( ('a = 2+3+4', '(9)'), # chained fold @@ -139,28 +197,28 @@ class TestTranforms(unittest.TestCase): def test_binary_subscr_on_unicode(self): # unicode strings don't get optimized - asm = dis_single('u"foo"[0]') - self.assertNotIn("(u'f')", asm) + asm = dis_single('"foo"[0]') + self.assertNotIn("('f')", asm) self.assertIn('BINARY_SUBSCR', asm) - asm = dis_single('u"\u0061\uffff"[1]') - self.assertNotIn("(u'\\uffff')", asm) + asm = dis_single('"\u0061\uffff"[1]') + self.assertNotIn("('\\uffff')", asm) self.assertIn('BINARY_SUBSCR', asm) # out of range - asm = dis_single('u"fuu"[10]') + asm = dis_single('"fuu"[10]') self.assertIn('BINARY_SUBSCR', asm) # non-BMP char (see #5057) - asm = dis_single('u"\U00012345"[0]') + asm = dis_single('"\U00012345"[0]') self.assertIn('BINARY_SUBSCR', asm) - asm = dis_single('u"\U00012345abcdef"[3]') + asm = dis_single('"\U00012345abcdef"[3]') self.assertIn('BINARY_SUBSCR', asm) def test_folding_of_unaryops_on_constants(self): for line, elem in ( - ('`1`', "('1')"), # unary convert ('-0.5', '(-0.5)'), # unary negative ('~-2', '(1)'), # unary invert + ('+1', '(1)'), # unary positive ): asm = dis_single(line) self.assertIn(elem, asm, asm) @@ -220,25 +278,42 @@ class TestTranforms(unittest.TestCase): self.assertEqual(asm.split().count('JUMP_ABSOLUTE'), 1) self.assertEqual(asm.split().count('RETURN_VALUE'), 2) + def test_make_function_doesnt_bail(self): + def f(): + def g()->1+1: + pass + return g + asm = disassemble(f) + self.assertNotIn('BINARY_ADD', asm) + +class TestBuglets(unittest.TestCase): + + def test_bug_11510(self): + # folded constant set optimization was commingled with the tuple + # unpacking optimization which would fail if the set had duplicate + # elements so that the set length was unexpected + def f(): + x, y = {1, 1} + return x, y + with self.assertRaises(ValueError): + f() + def test_main(verbose=None): import sys - from test import test_support - test_classes = (TestTranforms,) - - with test_support.check_py3k_warnings( - ("backquote not supported", SyntaxWarning)): - test_support.run_unittest(*test_classes) - - # verify reference counting - if verbose and hasattr(sys, "gettotalrefcount"): - import gc - counts = [None] * 5 - for i in xrange(len(counts)): - test_support.run_unittest(*test_classes) - gc.collect() - counts[i] = sys.gettotalrefcount() - print counts + from test import support + test_classes = (TestTranforms, TestBuglets) + support.run_unittest(*test_classes) + + # verify reference counting + if verbose and hasattr(sys, "gettotalrefcount"): + import gc + counts = [None] * 5 + for i in range(len(counts)): + support.run_unittest(*test_classes) + gc.collect() + counts[i] = sys.gettotalrefcount() + print(counts) if __name__ == "__main__": test_main(verbose=True) |
