1# Minimal tests for dis module
2
3from test.test_support import run_unittest
4import unittest
5import sys
6import dis
7import StringIO
8
9
10def _f(a):
11    print a
12    return 1
13
14dis_f = """\
15 %-4d         0 LOAD_FAST                0 (a)
16              3 PRINT_ITEM
17              4 PRINT_NEWLINE
18
19 %-4d         5 LOAD_CONST               1 (1)
20              8 RETURN_VALUE
21"""%(_f.func_code.co_firstlineno + 1,
22     _f.func_code.co_firstlineno + 2)
23
24
25def bug708901():
26    for res in range(1,
27                     10):
28        pass
29
30dis_bug708901 = """\
31 %-4d         0 SETUP_LOOP              23 (to 26)
32              3 LOAD_GLOBAL              0 (range)
33              6 LOAD_CONST               1 (1)
34
35 %-4d         9 LOAD_CONST               2 (10)
36             12 CALL_FUNCTION            2
37             15 GET_ITER
38        >>   16 FOR_ITER                 6 (to 25)
39             19 STORE_FAST               0 (res)
40
41 %-4d        22 JUMP_ABSOLUTE           16
42        >>   25 POP_BLOCK
43        >>   26 LOAD_CONST               0 (None)
44             29 RETURN_VALUE
45"""%(bug708901.func_code.co_firstlineno + 1,
46     bug708901.func_code.co_firstlineno + 2,
47     bug708901.func_code.co_firstlineno + 3)
48
49
50def bug1333982(x=[]):
51    assert 0, ([s for s in x] +
52              1)
53    pass
54
55dis_bug1333982 = """\
56 %-4d         0 LOAD_CONST               1 (0)
57              3 POP_JUMP_IF_TRUE        41
58              6 LOAD_GLOBAL              0 (AssertionError)
59              9 BUILD_LIST               0
60             12 LOAD_FAST                0 (x)
61             15 GET_ITER
62        >>   16 FOR_ITER                12 (to 31)
63             19 STORE_FAST               1 (s)
64             22 LOAD_FAST                1 (s)
65             25 LIST_APPEND              2
66             28 JUMP_ABSOLUTE           16
67
68 %-4d   >>   31 LOAD_CONST               2 (1)
69             34 BINARY_ADD
70             35 CALL_FUNCTION            1
71             38 RAISE_VARARGS            1
72
73 %-4d   >>   41 LOAD_CONST               0 (None)
74             44 RETURN_VALUE
75"""%(bug1333982.func_code.co_firstlineno + 1,
76     bug1333982.func_code.co_firstlineno + 2,
77     bug1333982.func_code.co_firstlineno + 3)
78
79_BIG_LINENO_FORMAT = """\
80%3d           0 LOAD_GLOBAL              0 (spam)
81              3 POP_TOP
82              4 LOAD_CONST               0 (None)
83              7 RETURN_VALUE
84"""
85
86class DisTests(unittest.TestCase):
87    def do_disassembly_test(self, func, expected):
88        s = StringIO.StringIO()
89        save_stdout = sys.stdout
90        sys.stdout = s
91        dis.dis(func)
92        sys.stdout = save_stdout
93        got = s.getvalue()
94        # Trim trailing blanks (if any).
95        lines = got.split('\n')
96        lines = [line.rstrip() for line in lines]
97        expected = expected.split("\n")
98        import difflib
99        if expected != lines:
100            self.fail(
101                "events did not match expectation:\n" +
102                "\n".join(difflib.ndiff(expected,
103                                        lines)))
104
105    def test_opmap(self):
106        self.assertEqual(dis.opmap["STOP_CODE"], 0)
107        self.assertIn(dis.opmap["LOAD_CONST"], dis.hasconst)
108        self.assertIn(dis.opmap["STORE_NAME"], dis.hasname)
109
110    def test_opname(self):
111        self.assertEqual(dis.opname[dis.opmap["LOAD_FAST"]], "LOAD_FAST")
112
113    def test_boundaries(self):
114        self.assertEqual(dis.opmap["EXTENDED_ARG"], dis.EXTENDED_ARG)
115        self.assertEqual(dis.opmap["STORE_NAME"], dis.HAVE_ARGUMENT)
116
117    def test_dis(self):
118        self.do_disassembly_test(_f, dis_f)
119
120    def test_bug_708901(self):
121        self.do_disassembly_test(bug708901, dis_bug708901)
122
123    def test_bug_1333982(self):
124        # This one is checking bytecodes generated for an `assert` statement,
125        # so fails if the tests are run with -O.  Skip this test then.
126        if __debug__:
127            self.do_disassembly_test(bug1333982, dis_bug1333982)
128
129    def test_big_linenos(self):
130        def func(count):
131            namespace = {}
132            func = "def foo():\n " + "".join(["\n "] * count + ["spam\n"])
133            exec func in namespace
134            return namespace['foo']
135
136        # Test all small ranges
137        for i in xrange(1, 300):
138            expected = _BIG_LINENO_FORMAT % (i + 2)
139            self.do_disassembly_test(func(i), expected)
140
141        # Test some larger ranges too
142        for i in xrange(300, 5000, 10):
143            expected = _BIG_LINENO_FORMAT % (i + 2)
144            self.do_disassembly_test(func(i), expected)
145
146def test_main():
147    run_unittest(DisTests)
148
149
150if __name__ == "__main__":
151    test_main()
152