# Minimal tests for dis module | |
from test.test_support import run_unittest | |
import unittest | |
import sys | |
import dis | |
import StringIO | |
def _f(a): | |
print a | |
return 1 | |
dis_f = """\ | |
%-4d 0 LOAD_FAST 0 (a) | |
3 PRINT_ITEM | |
4 PRINT_NEWLINE | |
%-4d 5 LOAD_CONST 1 (1) | |
8 RETURN_VALUE | |
"""%(_f.func_code.co_firstlineno + 1, | |
_f.func_code.co_firstlineno + 2) | |
def bug708901(): | |
for res in range(1, | |
10): | |
pass | |
dis_bug708901 = """\ | |
%-4d 0 SETUP_LOOP 23 (to 26) | |
3 LOAD_GLOBAL 0 (range) | |
6 LOAD_CONST 1 (1) | |
%-4d 9 LOAD_CONST 2 (10) | |
12 CALL_FUNCTION 2 | |
15 GET_ITER | |
>> 16 FOR_ITER 6 (to 25) | |
19 STORE_FAST 0 (res) | |
%-4d 22 JUMP_ABSOLUTE 16 | |
>> 25 POP_BLOCK | |
>> 26 LOAD_CONST 0 (None) | |
29 RETURN_VALUE | |
"""%(bug708901.func_code.co_firstlineno + 1, | |
bug708901.func_code.co_firstlineno + 2, | |
bug708901.func_code.co_firstlineno + 3) | |
def bug1333982(x=[]): | |
assert 0, ([s for s in x] + | |
1) | |
pass | |
dis_bug1333982 = """\ | |
%-4d 0 LOAD_CONST 1 (0) | |
3 POP_JUMP_IF_TRUE 38 | |
6 LOAD_GLOBAL 0 (AssertionError) | |
9 BUILD_LIST 0 | |
12 LOAD_FAST 0 (x) | |
15 GET_ITER | |
>> 16 FOR_ITER 12 (to 31) | |
19 STORE_FAST 1 (s) | |
22 LOAD_FAST 1 (s) | |
25 LIST_APPEND 2 | |
28 JUMP_ABSOLUTE 16 | |
%-4d >> 31 LOAD_CONST 2 (1) | |
34 BINARY_ADD | |
35 RAISE_VARARGS 2 | |
%-4d >> 38 LOAD_CONST 0 (None) | |
41 RETURN_VALUE | |
"""%(bug1333982.func_code.co_firstlineno + 1, | |
bug1333982.func_code.co_firstlineno + 2, | |
bug1333982.func_code.co_firstlineno + 3) | |
_BIG_LINENO_FORMAT = """\ | |
%3d 0 LOAD_GLOBAL 0 (spam) | |
3 POP_TOP | |
4 LOAD_CONST 0 (None) | |
7 RETURN_VALUE | |
""" | |
class DisTests(unittest.TestCase): | |
def do_disassembly_test(self, func, expected): | |
s = StringIO.StringIO() | |
save_stdout = sys.stdout | |
sys.stdout = s | |
dis.dis(func) | |
sys.stdout = save_stdout | |
got = s.getvalue() | |
# Trim trailing blanks (if any). | |
lines = got.split('\n') | |
lines = [line.rstrip() for line in lines] | |
expected = expected.split("\n") | |
import difflib | |
if expected != lines: | |
self.fail( | |
"events did not match expectation:\n" + | |
"\n".join(difflib.ndiff(expected, | |
lines))) | |
def test_opmap(self): | |
self.assertEqual(dis.opmap["STOP_CODE"], 0) | |
self.assertIn(dis.opmap["LOAD_CONST"], dis.hasconst) | |
self.assertIn(dis.opmap["STORE_NAME"], dis.hasname) | |
def test_opname(self): | |
self.assertEqual(dis.opname[dis.opmap["LOAD_FAST"]], "LOAD_FAST") | |
def test_boundaries(self): | |
self.assertEqual(dis.opmap["EXTENDED_ARG"], dis.EXTENDED_ARG) | |
self.assertEqual(dis.opmap["STORE_NAME"], dis.HAVE_ARGUMENT) | |
def test_dis(self): | |
self.do_disassembly_test(_f, dis_f) | |
def test_bug_708901(self): | |
self.do_disassembly_test(bug708901, dis_bug708901) | |
def test_bug_1333982(self): | |
# This one is checking bytecodes generated for an `assert` statement, | |
# so fails if the tests are run with -O. Skip this test then. | |
if __debug__: | |
self.do_disassembly_test(bug1333982, dis_bug1333982) | |
def test_big_linenos(self): | |
def func(count): | |
namespace = {} | |
func = "def foo():\n " + "".join(["\n "] * count + ["spam\n"]) | |
exec func in namespace | |
return namespace['foo'] | |
# Test all small ranges | |
for i in xrange(1, 300): | |
expected = _BIG_LINENO_FORMAT % (i + 2) | |
self.do_disassembly_test(func(i), expected) | |
# Test some larger ranges too | |
for i in xrange(300, 5000, 10): | |
expected = _BIG_LINENO_FORMAT % (i + 2) | |
self.do_disassembly_test(func(i), expected) | |
def test_main(): | |
run_unittest(DisTests) | |
if __name__ == "__main__": | |
test_main() |