1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
|
import dis
import io
import textwrap
import types
from test.support.bytecode_helper import AssemblerTestCase
# Tests for the code-object creation stage of the compiler.
class IsolatedAssembleTests(AssemblerTestCase):
def complete_metadata(self, metadata, filename="myfile.py"):
if metadata is None:
metadata = {}
for key in ['name', 'qualname']:
metadata.setdefault(key, key)
for key in ['consts']:
metadata.setdefault(key, [])
for key in ['names', 'varnames', 'cellvars', 'freevars', 'fasthidden']:
metadata.setdefault(key, {})
for key in ['argcount', 'posonlyargcount', 'kwonlyargcount']:
metadata.setdefault(key, 0)
metadata.setdefault('firstlineno', 1)
metadata.setdefault('filename', filename)
return metadata
def insts_to_code_object(self, insts, metadata):
metadata = self.complete_metadata(metadata)
seq = self.seq_from_insts(insts)
return self.get_code_object(metadata['filename'], seq, metadata)
def assemble_test(self, insts, metadata, expected):
co = self.insts_to_code_object(insts, metadata)
self.assertIsInstance(co, types.CodeType)
expected_metadata = {}
for key, value in metadata.items():
if key == "fasthidden":
# not exposed on code object
continue
if isinstance(value, list):
expected_metadata[key] = tuple(value)
elif isinstance(value, dict):
expected_metadata[key] = tuple(value.keys())
else:
expected_metadata[key] = value
for key, value in expected_metadata.items():
self.assertEqual(getattr(co, "co_" + key), value)
f = types.FunctionType(co, {})
for args, res in expected.items():
self.assertEqual(f(*args), res)
def test_simple_expr(self):
metadata = {
'filename' : 'avg.py',
'name' : 'avg',
'qualname' : 'stats.avg',
'consts' : {2 : 0},
'argcount' : 2,
'varnames' : {'x' : 0, 'y' : 1},
}
# code for "return (x+y)/2"
insts = [
('RESUME', 0),
('LOAD_FAST', 0, 1), # 'x'
('LOAD_FAST', 1, 1), # 'y'
('BINARY_OP', 0, 1), # '+'
('LOAD_CONST', 0, 1), # 2
('BINARY_OP', 11, 1), # '/'
('RETURN_VALUE', None, 1),
]
expected = {(3, 4) : 3.5, (-100, 200) : 50, (10, 18) : 14}
self.assemble_test(insts, metadata, expected)
def test_expression_with_pseudo_instruction_load_closure(self):
def mod_two(x):
def inner():
return x
return inner() % 2
inner_code = mod_two.__code__.co_consts[0]
assert isinstance(inner_code, types.CodeType)
metadata = {
'filename' : 'mod_two.py',
'name' : 'mod_two',
'qualname' : 'nested.mod_two',
'cellvars' : {'x' : 0},
'consts': {None: 0, inner_code: 1, 2: 2},
'argcount' : 1,
'varnames' : {'x' : 0},
}
instructions = [
('RESUME', 0,),
('LOAD_CLOSURE', 0, 1),
('BUILD_TUPLE', 1, 1),
('LOAD_CONST', 1, 1),
('MAKE_FUNCTION', None, 2),
('SET_FUNCTION_ATTRIBUTE', 8, 2),
('PUSH_NULL', None, 1),
('CALL', 0, 2), # (lambda: x)()
('LOAD_CONST', 2, 2), # 2
('BINARY_OP', 6, 2), # %
('RETURN_VALUE', None, 2)
]
expected = {(0,): 0, (1,): 1, (2,): 0, (120,): 0, (121,): 1}
self.assemble_test(instructions, metadata, expected)
def test_exception_table(self):
metadata = {
'filename' : 'exc.py',
'name' : 'exc',
'consts' : {2 : 0},
}
# code for "try: pass\n except: pass"
insts = [
('RESUME', 0),
('SETUP_FINALLY', 4),
('LOAD_CONST', 0),
('RETURN_VALUE', None),
('SETUP_CLEANUP', 10),
('PUSH_EXC_INFO', None),
('POP_TOP', None),
('POP_EXCEPT', None),
('LOAD_CONST', 0),
('RETURN_VALUE', None),
('COPY', 3),
('POP_EXCEPT', None),
('RERAISE', 1),
]
co = self.insts_to_code_object(insts, metadata)
output = io.StringIO()
dis.dis(co, file=output)
exc_table = textwrap.dedent("""
ExceptionTable:
L1 to L2 -> L2 [0]
L2 to L3 -> L3 [1] lasti
""")
self.assertTrue(output.getvalue().endswith(exc_table))
|