python (3.12.0)
1 import dis
2 from itertools import combinations, product
3 import sys
4 import textwrap
5 import unittest
6
7 from test import support
8 from test.support.bytecode_helper import BytecodeTestCase, CfgOptimizationTestCase
9
10
11 def compile_pattern_with_fast_locals(pattern):
12 source = textwrap.dedent(
13 f"""
14 def f(x):
15 match x:
16 case {pattern}:
17 pass
18 """
19 )
20 namespace = {}
21 exec(source, namespace)
22 return namespace["f"].__code__
23
24
25 def count_instr_recursively(f, opname):
26 count = 0
27 for instr in dis.get_instructions(f):
28 if instr.opname == opname:
29 count += 1
30 if hasattr(f, '__code__'):
31 f = f.__code__
32 for c in f.co_consts:
33 if hasattr(c, 'co_code'):
34 count += count_instr_recursively(c, opname)
35 return count
36
37
38 class ESC[4;38;5;81mTestTranforms(ESC[4;38;5;149mBytecodeTestCase):
39
40 def check_jump_targets(self, code):
41 instructions = list(dis.get_instructions(code))
42 targets = {instr.offset: instr for instr in instructions}
43 for instr in instructions:
44 if 'JUMP_' not in instr.opname:
45 continue
46 tgt = targets[instr.argval]
47 # jump to unconditional jump
48 if tgt.opname in ('JUMP_BACKWARD', 'JUMP_FORWARD'):
49 self.fail(f'{instr.opname} at {instr.offset} '
50 f'jumps to {tgt.opname} at {tgt.offset}')
51 # unconditional jump to RETURN_VALUE
52 if (instr.opname in ('JUMP_BACKWARD', 'JUMP_FORWARD') and
53 tgt.opname == 'RETURN_VALUE'):
54 self.fail(f'{instr.opname} at {instr.offset} '
55 f'jumps to {tgt.opname} at {tgt.offset}')
56
57 def check_lnotab(self, code):
58 "Check that the lnotab byte offsets are sensible."
59 code = dis._get_code_object(code)
60 lnotab = list(dis.findlinestarts(code))
61 # Don't bother checking if the line info is sensible, because
62 # most of the line info we can get at comes from lnotab.
63 min_bytecode = min(t[0] for t in lnotab)
64 max_bytecode = max(t[0] for t in lnotab)
65 self.assertGreaterEqual(min_bytecode, 0)
66 self.assertLess(max_bytecode, len(code.co_code))
67 # This could conceivably test more (and probably should, as there
68 # aren't very many tests of lnotab), if peepholer wasn't scheduled
69 # to be replaced anyway.
70
71 def test_unot(self):
72 # UNARY_NOT POP_JUMP_IF_FALSE --> POP_JUMP_IF_TRUE'
73 def unot(x):
74 if not x == 2:
75 del x
76 self.assertNotInBytecode(unot, 'UNARY_NOT')
77 self.assertNotInBytecode(unot, 'POP_JUMP_IF_FALSE')
78 self.assertInBytecode(unot, 'POP_JUMP_IF_TRUE')
79 self.check_lnotab(unot)
80
81 def test_elim_inversion_of_is_or_in(self):
82 for line, cmp_op, invert in (
83 ('not a is b', 'IS_OP', 1,),
84 ('not a is not b', 'IS_OP', 0,),
85 ('not a in b', 'CONTAINS_OP', 1,),
86 ('not a not in b', 'CONTAINS_OP', 0,),
87 ):
88 with self.subTest(line=line):
89 code = compile(line, '', 'single')
90 self.assertInBytecode(code, cmp_op, invert)
91 self.check_lnotab(code)
92
93 def test_global_as_constant(self):
94 # LOAD_GLOBAL None/True/False --> LOAD_CONST None/True/False
95 def f():
96 x = None
97 x = None
98 return x
99 def g():
100 x = True
101 return x
102 def h():
103 x = False
104 return x
105
106 for func, elem in ((f, None), (g, True), (h, False)):
107 with self.subTest(func=func):
108 self.assertNotInBytecode(func, 'LOAD_GLOBAL')
109 self.assertInBytecode(func, 'LOAD_CONST', elem)
110 self.check_lnotab(func)
111
112 def f():
113 'Adding a docstring made this test fail in Py2.5.0'
114 return None
115
116 self.assertNotInBytecode(f, 'LOAD_GLOBAL')
117 self.assertInBytecode(f, 'RETURN_CONST', None)
118 self.check_lnotab(f)
119
120 def test_while_one(self):
121 # Skip over: LOAD_CONST trueconst POP_JUMP_IF_FALSE xx
122 def f():
123 while 1:
124 pass
125 return list
126 for elem in ('LOAD_CONST', 'POP_JUMP_IF_FALSE'):
127 self.assertNotInBytecode(f, elem)
128 for elem in ('JUMP_BACKWARD',):
129 self.assertInBytecode(f, elem)
130 self.check_lnotab(f)
131
132 def test_pack_unpack(self):
133 for line, elem in (
134 ('a, = a,', 'RETURN_CONST',),
135 ('a, b = a, b', 'SWAP',),
136 ('a, b, c = a, b, c', 'SWAP',),
137 ):
138 with self.subTest(line=line):
139 code = compile(line,'','single')
140 self.assertInBytecode(code, elem)
141 self.assertNotInBytecode(code, 'BUILD_TUPLE')
142 self.assertNotInBytecode(code, 'UNPACK_SEQUENCE')
143 self.check_lnotab(code)
144
145 def test_folding_of_tuples_of_constants(self):
146 for line, elem in (
147 ('a = 1,2,3', (1, 2, 3)),
148 ('("a","b","c")', ('a', 'b', 'c')),
149 ('a,b,c = 1,2,3', (1, 2, 3)),
150 ('(None, 1, None)', (None, 1, None)),
151 ('((1, 2), 3, 4)', ((1, 2), 3, 4)),
152 ):
153 with self.subTest(line=line):
154 code = compile(line,'','single')
155 self.assertInBytecode(code, 'LOAD_CONST', elem)
156 self.assertNotInBytecode(code, 'BUILD_TUPLE')
157 self.check_lnotab(code)
158
159 # Long tuples should be folded too.
160 code = compile(repr(tuple(range(10000))),'','single')
161 self.assertNotInBytecode(code, 'BUILD_TUPLE')
162 # One LOAD_CONST for the tuple, one for the None return value
163 load_consts = [instr for instr in dis.get_instructions(code)
164 if instr.opname == 'LOAD_CONST']
165 self.assertEqual(len(load_consts), 1)
166 self.check_lnotab(code)
167
168 # Bug 1053819: Tuple of constants misidentified when presented with:
169 # . . . opcode_with_arg 100 unary_opcode BUILD_TUPLE 1 . . .
170 # The following would segfault upon compilation
171 def crater():
172 (~[
173 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
174 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
175 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
176 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
177 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
178 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
179 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
180 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
181 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
182 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
183 ],)
184 self.check_lnotab(crater)
185
186 def test_folding_of_lists_of_constants(self):
187 for line, elem in (
188 # in/not in constants with BUILD_LIST should be folded to a tuple:
189 ('a in [1,2,3]', (1, 2, 3)),
190 ('a not in ["a","b","c"]', ('a', 'b', 'c')),
191 ('a in [None, 1, None]', (None, 1, None)),
192 ('a not in [(1, 2), 3, 4]', ((1, 2), 3, 4)),
193 ):
194 with self.subTest(line=line):
195 code = compile(line, '', 'single')
196 self.assertInBytecode(code, 'LOAD_CONST', elem)
197 self.assertNotInBytecode(code, 'BUILD_LIST')
198 self.check_lnotab(code)
199
200 def test_folding_of_sets_of_constants(self):
201 for line, elem in (
202 # in/not in constants with BUILD_SET should be folded to a frozenset:
203 ('a in {1,2,3}', frozenset({1, 2, 3})),
204 ('a not in {"a","b","c"}', frozenset({'a', 'c', 'b'})),
205 ('a in {None, 1, None}', frozenset({1, None})),
206 ('a not in {(1, 2), 3, 4}', frozenset({(1, 2), 3, 4})),
207 ('a in {1, 2, 3, 3, 2, 1}', frozenset({1, 2, 3})),
208 ):
209 with self.subTest(line=line):
210 code = compile(line, '', 'single')
211 self.assertNotInBytecode(code, 'BUILD_SET')
212 self.assertInBytecode(code, 'LOAD_CONST', elem)
213 self.check_lnotab(code)
214
215 # Ensure that the resulting code actually works:
216 def f(a):
217 return a in {1, 2, 3}
218
219 def g(a):
220 return a not in {1, 2, 3}
221
222 self.assertTrue(f(3))
223 self.assertTrue(not f(4))
224 self.check_lnotab(f)
225
226 self.assertTrue(not g(3))
227 self.assertTrue(g(4))
228 self.check_lnotab(g)
229
230
231 def test_folding_of_binops_on_constants(self):
232 for line, elem in (
233 ('a = 2+3+4', 9), # chained fold
234 ('"@"*4', '@@@@'), # check string ops
235 ('a="abc" + "def"', 'abcdef'), # check string ops
236 ('a = 3**4', 81), # binary power
237 ('a = 3*4', 12), # binary multiply
238 ('a = 13//4', 3), # binary floor divide
239 ('a = 14%4', 2), # binary modulo
240 ('a = 2+3', 5), # binary add
241 ('a = 13-4', 9), # binary subtract
242 ('a = (12,13)[1]', 13), # binary subscr
243 ('a = 13 << 2', 52), # binary lshift
244 ('a = 13 >> 2', 3), # binary rshift
245 ('a = 13 & 7', 5), # binary and
246 ('a = 13 ^ 7', 10), # binary xor
247 ('a = 13 | 7', 15), # binary or
248 ):
249 with self.subTest(line=line):
250 code = compile(line, '', 'single')
251 self.assertInBytecode(code, 'LOAD_CONST', elem)
252 for instr in dis.get_instructions(code):
253 self.assertFalse(instr.opname.startswith('BINARY_'))
254 self.check_lnotab(code)
255
256 # Verify that unfoldables are skipped
257 code = compile('a=2+"b"', '', 'single')
258 self.assertInBytecode(code, 'LOAD_CONST', 2)
259 self.assertInBytecode(code, 'LOAD_CONST', 'b')
260 self.check_lnotab(code)
261
262 # Verify that large sequences do not result from folding
263 code = compile('a="x"*10000', '', 'single')
264 self.assertInBytecode(code, 'LOAD_CONST', 10000)
265 self.assertNotIn("x"*10000, code.co_consts)
266 self.check_lnotab(code)
267 code = compile('a=1<<1000', '', 'single')
268 self.assertInBytecode(code, 'LOAD_CONST', 1000)
269 self.assertNotIn(1<<1000, code.co_consts)
270 self.check_lnotab(code)
271 code = compile('a=2**1000', '', 'single')
272 self.assertInBytecode(code, 'LOAD_CONST', 1000)
273 self.assertNotIn(2**1000, code.co_consts)
274 self.check_lnotab(code)
275
276 def test_binary_subscr_on_unicode(self):
277 # valid code get optimized
278 code = compile('"foo"[0]', '', 'single')
279 self.assertInBytecode(code, 'LOAD_CONST', 'f')
280 self.assertNotInBytecode(code, 'BINARY_SUBSCR')
281 self.check_lnotab(code)
282 code = compile('"\u0061\uffff"[1]', '', 'single')
283 self.assertInBytecode(code, 'LOAD_CONST', '\uffff')
284 self.assertNotInBytecode(code,'BINARY_SUBSCR')
285 self.check_lnotab(code)
286
287 # With PEP 393, non-BMP char get optimized
288 code = compile('"\U00012345"[0]', '', 'single')
289 self.assertInBytecode(code, 'LOAD_CONST', '\U00012345')
290 self.assertNotInBytecode(code, 'BINARY_SUBSCR')
291 self.check_lnotab(code)
292
293 # invalid code doesn't get optimized
294 # out of range
295 code = compile('"fuu"[10]', '', 'single')
296 self.assertInBytecode(code, 'BINARY_SUBSCR')
297 self.check_lnotab(code)
298
299 def test_folding_of_unaryops_on_constants(self):
300 for line, elem in (
301 ('-0.5', -0.5), # unary negative
302 ('-0.0', -0.0), # -0.0
303 ('-(1.0-1.0)', -0.0), # -0.0 after folding
304 ('-0', 0), # -0
305 ('~-2', 1), # unary invert
306 ('+1', 1), # unary positive
307 ):
308 with self.subTest(line=line):
309 code = compile(line, '', 'single')
310 self.assertInBytecode(code, 'LOAD_CONST', elem)
311 for instr in dis.get_instructions(code):
312 self.assertFalse(instr.opname.startswith('UNARY_'))
313 self.check_lnotab(code)
314
315 # Check that -0.0 works after marshaling
316 def negzero():
317 return -(1.0-1.0)
318
319 for instr in dis.get_instructions(negzero):
320 self.assertFalse(instr.opname.startswith('UNARY_'))
321 self.check_lnotab(negzero)
322
323 # Verify that unfoldables are skipped
324 for line, elem, opname in (
325 ('-"abc"', 'abc', 'UNARY_NEGATIVE'),
326 ('~"abc"', 'abc', 'UNARY_INVERT'),
327 ):
328 with self.subTest(line=line):
329 code = compile(line, '', 'single')
330 self.assertInBytecode(code, 'LOAD_CONST', elem)
331 self.assertInBytecode(code, opname)
332 self.check_lnotab(code)
333
334 def test_elim_extra_return(self):
335 # RETURN LOAD_CONST None RETURN --> RETURN
336 def f(x):
337 return x
338 self.assertNotInBytecode(f, 'LOAD_CONST', None)
339 returns = [instr for instr in dis.get_instructions(f)
340 if instr.opname == 'RETURN_VALUE']
341 self.assertEqual(len(returns), 1)
342 self.check_lnotab(f)
343
344 def test_elim_jump_to_return(self):
345 # JUMP_FORWARD to RETURN --> RETURN
346 def f(cond, true_value, false_value):
347 # Intentionally use two-line expression to test issue37213.
348 return (true_value if cond
349 else false_value)
350 self.check_jump_targets(f)
351 self.assertNotInBytecode(f, 'JUMP_FORWARD')
352 self.assertNotInBytecode(f, 'JUMP_BACKWARD')
353 returns = [instr for instr in dis.get_instructions(f)
354 if instr.opname == 'RETURN_VALUE']
355 self.assertEqual(len(returns), 2)
356 self.check_lnotab(f)
357
358 def test_elim_jump_to_uncond_jump(self):
359 # POP_JUMP_IF_FALSE to JUMP_FORWARD --> POP_JUMP_IF_FALSE to non-jump
360 def f():
361 if a:
362 # Intentionally use two-line expression to test issue37213.
363 if (c
364 or d):
365 foo()
366 else:
367 baz()
368 self.check_jump_targets(f)
369 self.check_lnotab(f)
370
371 def test_elim_jump_to_uncond_jump2(self):
372 # POP_JUMP_IF_FALSE to JUMP_BACKWARD --> POP_JUMP_IF_FALSE to non-jump
373 def f():
374 while a:
375 # Intentionally use two-line expression to test issue37213.
376 if (c
377 or d):
378 a = foo()
379 self.check_jump_targets(f)
380 self.check_lnotab(f)
381
382 def test_elim_jump_to_uncond_jump3(self):
383 # Intentionally use two-line expressions to test issue37213.
384 # POP_JUMP_IF_FALSE to POP_JUMP_IF_FALSE --> POP_JUMP_IF_FALSE to non-jump
385 def f(a, b, c):
386 return ((a and b)
387 and c)
388 self.check_jump_targets(f)
389 self.check_lnotab(f)
390 self.assertEqual(count_instr_recursively(f, 'POP_JUMP_IF_FALSE'), 2)
391 # POP_JUMP_IF_TRUE to POP_JUMP_IF_TRUE --> POP_JUMP_IF_TRUE to non-jump
392 def f(a, b, c):
393 return ((a or b)
394 or c)
395 self.check_jump_targets(f)
396 self.check_lnotab(f)
397 self.assertEqual(count_instr_recursively(f, 'POP_JUMP_IF_TRUE'), 2)
398 # JUMP_IF_FALSE_OR_POP to JUMP_IF_TRUE_OR_POP --> POP_JUMP_IF_FALSE to non-jump
399 def f(a, b, c):
400 return ((a and b)
401 or c)
402 self.check_jump_targets(f)
403 self.check_lnotab(f)
404 self.assertEqual(count_instr_recursively(f, 'POP_JUMP_IF_FALSE'), 1)
405 self.assertEqual(count_instr_recursively(f, 'POP_JUMP_IF_TRUE'), 1)
406 # POP_JUMP_IF_TRUE to POP_JUMP_IF_FALSE --> POP_JUMP_IF_TRUE to non-jump
407 def f(a, b, c):
408 return ((a or b)
409 and c)
410 self.check_jump_targets(f)
411 self.check_lnotab(f)
412 self.assertEqual(count_instr_recursively(f, 'POP_JUMP_IF_FALSE'), 1)
413 self.assertEqual(count_instr_recursively(f, 'POP_JUMP_IF_TRUE'), 1)
414
415 def test_elim_jump_to_uncond_jump4(self):
416 def f():
417 for i in range(5):
418 if i > 3:
419 print(i)
420 self.check_jump_targets(f)
421
422 def test_elim_jump_after_return1(self):
423 # Eliminate dead code: jumps immediately after returns can't be reached
424 def f(cond1, cond2):
425 if cond1: return 1
426 if cond2: return 2
427 while 1:
428 return 3
429 while 1:
430 if cond1: return 4
431 return 5
432 return 6
433 self.assertNotInBytecode(f, 'JUMP_FORWARD')
434 self.assertNotInBytecode(f, 'JUMP_BACKWARD')
435 returns = [instr for instr in dis.get_instructions(f)
436 if instr.opname == 'RETURN_VALUE']
437 self.assertLessEqual(len(returns), 6)
438 self.check_lnotab(f)
439
440 def test_make_function_doesnt_bail(self):
441 def f():
442 def g()->1+1:
443 pass
444 return g
445 self.assertNotInBytecode(f, 'BINARY_OP')
446 self.check_lnotab(f)
447
448 def test_constant_folding(self):
449 # Issue #11244: aggressive constant folding.
450 exprs = [
451 '3 * -5',
452 '-3 * 5',
453 '2 * (3 * 4)',
454 '(2 * 3) * 4',
455 '(-1, 2, 3)',
456 '(1, -2, 3)',
457 '(1, 2, -3)',
458 '(1, 2, -3) * 6',
459 'lambda x: x in {(3 * -5) + (-1 - 6), (1, -2, 3) * 2, None}',
460 ]
461 for e in exprs:
462 with self.subTest(e=e):
463 code = compile(e, '', 'single')
464 for instr in dis.get_instructions(code):
465 self.assertFalse(instr.opname.startswith('UNARY_'))
466 self.assertFalse(instr.opname.startswith('BINARY_'))
467 self.assertFalse(instr.opname.startswith('BUILD_'))
468 self.check_lnotab(code)
469
470 def test_in_literal_list(self):
471 def containtest():
472 return x in [a, b]
473 self.assertEqual(count_instr_recursively(containtest, 'BUILD_LIST'), 0)
474 self.check_lnotab(containtest)
475
476 def test_iterate_literal_list(self):
477 def forloop():
478 for x in [a, b]:
479 pass
480 self.assertEqual(count_instr_recursively(forloop, 'BUILD_LIST'), 0)
481 self.check_lnotab(forloop)
482
483 def test_condition_with_binop_with_bools(self):
484 def f():
485 if True or False:
486 return 1
487 return 0
488 self.assertEqual(f(), 1)
489 self.check_lnotab(f)
490
491 def test_if_with_if_expression(self):
492 # Check bpo-37289
493 def f(x):
494 if (True if x else False):
495 return True
496 return False
497 self.assertTrue(f(True))
498 self.check_lnotab(f)
499
500 def test_trailing_nops(self):
501 # Check the lnotab of a function that even after trivial
502 # optimization has trailing nops, which the lnotab adjustment has to
503 # handle properly (bpo-38115).
504 def f(x):
505 while 1:
506 return 3
507 while 1:
508 return 5
509 return 6
510 self.check_lnotab(f)
511
512 def test_assignment_idiom_in_comprehensions(self):
513 def listcomp():
514 return [y for x in a for y in [f(x)]]
515 self.assertEqual(count_instr_recursively(listcomp, 'FOR_ITER'), 1)
516 def setcomp():
517 return {y for x in a for y in [f(x)]}
518 self.assertEqual(count_instr_recursively(setcomp, 'FOR_ITER'), 1)
519 def dictcomp():
520 return {y: y for x in a for y in [f(x)]}
521 self.assertEqual(count_instr_recursively(dictcomp, 'FOR_ITER'), 1)
522 def genexpr():
523 return (y for x in a for y in [f(x)])
524 self.assertEqual(count_instr_recursively(genexpr, 'FOR_ITER'), 1)
525
526 @support.requires_resource('cpu')
527 def test_format_combinations(self):
528 flags = '-+ #0'
529 testcases = [
530 *product(('', '1234', 'абвг'), 'sra'),
531 *product((1234, -1234), 'duioxX'),
532 *product((1234.5678901, -1234.5678901), 'duifegFEG'),
533 *product((float('inf'), -float('inf')), 'fegFEG'),
534 ]
535 width_precs = [
536 *product(('', '1', '30'), ('', '.', '.0', '.2')),
537 ('', '.40'),
538 ('30', '.40'),
539 ]
540 for value, suffix in testcases:
541 for width, prec in width_precs:
542 for r in range(len(flags) + 1):
543 for spec in combinations(flags, r):
544 fmt = '%' + ''.join(spec) + width + prec + suffix
545 with self.subTest(fmt=fmt, value=value):
546 s1 = fmt % value
547 s2 = eval(f'{fmt!r} % (x,)', {'x': value})
548 self.assertEqual(s2, s1, f'{fmt = }')
549
550 def test_format_misc(self):
551 def format(fmt, *values):
552 vars = [f'x{i+1}' for i in range(len(values))]
553 if len(vars) == 1:
554 args = '(' + vars[0] + ',)'
555 else:
556 args = '(' + ', '.join(vars) + ')'
557 return eval(f'{fmt!r} % {args}', dict(zip(vars, values)))
558
559 self.assertEqual(format('string'), 'string')
560 self.assertEqual(format('x = %s!', 1234), 'x = 1234!')
561 self.assertEqual(format('x = %d!', 1234), 'x = 1234!')
562 self.assertEqual(format('x = %x!', 1234), 'x = 4d2!')
563 self.assertEqual(format('x = %f!', 1234), 'x = 1234.000000!')
564 self.assertEqual(format('x = %s!', 1234.5678901), 'x = 1234.5678901!')
565 self.assertEqual(format('x = %f!', 1234.5678901), 'x = 1234.567890!')
566 self.assertEqual(format('x = %d!', 1234.5678901), 'x = 1234!')
567 self.assertEqual(format('x = %s%% %%%%', 1234), 'x = 1234% %%')
568 self.assertEqual(format('x = %s!', '%% %s'), 'x = %% %s!')
569 self.assertEqual(format('x = %s, y = %d', 12, 34), 'x = 12, y = 34')
570
571 def test_format_errors(self):
572 with self.assertRaisesRegex(TypeError,
573 'not enough arguments for format string'):
574 eval("'%s' % ()")
575 with self.assertRaisesRegex(TypeError,
576 'not all arguments converted during string formatting'):
577 eval("'%s' % (x, y)", {'x': 1, 'y': 2})
578 with self.assertRaisesRegex(ValueError, 'incomplete format'):
579 eval("'%s%' % (x,)", {'x': 1234})
580 with self.assertRaisesRegex(ValueError, 'incomplete format'):
581 eval("'%s%%%' % (x,)", {'x': 1234})
582 with self.assertRaisesRegex(TypeError,
583 'not enough arguments for format string'):
584 eval("'%s%z' % (x,)", {'x': 1234})
585 with self.assertRaisesRegex(ValueError, 'unsupported format character'):
586 eval("'%s%z' % (x, 5)", {'x': 1234})
587 with self.assertRaisesRegex(TypeError, 'a real number is required, not str'):
588 eval("'%d' % (x,)", {'x': '1234'})
589 with self.assertRaisesRegex(TypeError, 'an integer is required, not float'):
590 eval("'%x' % (x,)", {'x': 1234.56})
591 with self.assertRaisesRegex(TypeError, 'an integer is required, not str'):
592 eval("'%x' % (x,)", {'x': '1234'})
593 with self.assertRaisesRegex(TypeError, 'must be real number, not str'):
594 eval("'%f' % (x,)", {'x': '1234'})
595 with self.assertRaisesRegex(TypeError,
596 'not enough arguments for format string'):
597 eval("'%s, %s' % (x, *y)", {'x': 1, 'y': []})
598 with self.assertRaisesRegex(TypeError,
599 'not all arguments converted during string formatting'):
600 eval("'%s, %s' % (x, *y)", {'x': 1, 'y': [2, 3]})
601
602 def test_static_swaps_unpack_two(self):
603 def f(a, b):
604 a, b = a, b
605 b, a = a, b
606 self.assertNotInBytecode(f, "SWAP")
607
608 def test_static_swaps_unpack_three(self):
609 def f(a, b, c):
610 a, b, c = a, b, c
611 a, c, b = a, b, c
612 b, a, c = a, b, c
613 b, c, a = a, b, c
614 c, a, b = a, b, c
615 c, b, a = a, b, c
616 self.assertNotInBytecode(f, "SWAP")
617
618 def test_static_swaps_match_mapping(self):
619 for a, b, c in product("_a", "_b", "_c"):
620 pattern = f"{{'a': {a}, 'b': {b}, 'c': {c}}}"
621 with self.subTest(pattern):
622 code = compile_pattern_with_fast_locals(pattern)
623 self.assertNotInBytecode(code, "SWAP")
624
625 def test_static_swaps_match_class(self):
626 forms = [
627 "C({}, {}, {})",
628 "C({}, {}, c={})",
629 "C({}, b={}, c={})",
630 "C(a={}, b={}, c={})"
631 ]
632 for a, b, c in product("_a", "_b", "_c"):
633 for form in forms:
634 pattern = form.format(a, b, c)
635 with self.subTest(pattern):
636 code = compile_pattern_with_fast_locals(pattern)
637 self.assertNotInBytecode(code, "SWAP")
638
639 def test_static_swaps_match_sequence(self):
640 swaps = {"*_, b, c", "a, *_, c", "a, b, *_"}
641 forms = ["{}, {}, {}", "{}, {}, *{}", "{}, *{}, {}", "*{}, {}, {}"]
642 for a, b, c in product("_a", "_b", "_c"):
643 for form in forms:
644 pattern = form.format(a, b, c)
645 with self.subTest(pattern):
646 code = compile_pattern_with_fast_locals(pattern)
647 if pattern in swaps:
648 # If this fails... great! Remove this pattern from swaps
649 # to prevent regressing on any improvement:
650 self.assertInBytecode(code, "SWAP")
651 else:
652 self.assertNotInBytecode(code, "SWAP")
653
654
655 class ESC[4;38;5;81mTestBuglets(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
656
657 def test_bug_11510(self):
658 # folded constant set optimization was commingled with the tuple
659 # unpacking optimization which would fail if the set had duplicate
660 # elements so that the set length was unexpected
661 def f():
662 x, y = {1, 1}
663 return x, y
664 with self.assertRaises(ValueError):
665 f()
666
667 def test_bpo_42057(self):
668 for i in range(10):
669 try:
670 raise Exception
671 except Exception or Exception:
672 pass
673
674 def test_bpo_45773_pop_jump_if_true(self):
675 compile("while True or spam: pass", "<test>", "exec")
676
677 def test_bpo_45773_pop_jump_if_false(self):
678 compile("while True or not spam: pass", "<test>", "exec")
679
680
681 class ESC[4;38;5;81mTestMarkingVariablesAsUnKnown(ESC[4;38;5;149mBytecodeTestCase):
682
683 def setUp(self):
684 self.addCleanup(sys.settrace, sys.gettrace())
685 sys.settrace(None)
686
687 def test_load_fast_known_simple(self):
688 def f():
689 x = 1
690 y = x + x
691 self.assertInBytecode(f, 'LOAD_FAST')
692
693 def test_load_fast_unknown_simple(self):
694 def f():
695 if condition():
696 x = 1
697 print(x)
698 self.assertInBytecode(f, 'LOAD_FAST_CHECK')
699 self.assertNotInBytecode(f, 'LOAD_FAST')
700
701 def test_load_fast_unknown_because_del(self):
702 def f():
703 x = 1
704 del x
705 print(x)
706 self.assertInBytecode(f, 'LOAD_FAST_CHECK')
707 self.assertNotInBytecode(f, 'LOAD_FAST')
708
709 def test_load_fast_known_because_parameter(self):
710 def f1(x):
711 print(x)
712 self.assertInBytecode(f1, 'LOAD_FAST')
713 self.assertNotInBytecode(f1, 'LOAD_FAST_CHECK')
714
715 def f2(*, x):
716 print(x)
717 self.assertInBytecode(f2, 'LOAD_FAST')
718 self.assertNotInBytecode(f2, 'LOAD_FAST_CHECK')
719
720 def f3(*args):
721 print(args)
722 self.assertInBytecode(f3, 'LOAD_FAST')
723 self.assertNotInBytecode(f3, 'LOAD_FAST_CHECK')
724
725 def f4(**kwargs):
726 print(kwargs)
727 self.assertInBytecode(f4, 'LOAD_FAST')
728 self.assertNotInBytecode(f4, 'LOAD_FAST_CHECK')
729
730 def f5(x=0):
731 print(x)
732 self.assertInBytecode(f5, 'LOAD_FAST')
733 self.assertNotInBytecode(f5, 'LOAD_FAST_CHECK')
734
735 def test_load_fast_known_because_already_loaded(self):
736 def f():
737 if condition():
738 x = 1
739 print(x)
740 print(x)
741 self.assertInBytecode(f, 'LOAD_FAST_CHECK')
742 self.assertInBytecode(f, 'LOAD_FAST')
743
744 def test_load_fast_known_multiple_branches(self):
745 def f():
746 if condition():
747 x = 1
748 else:
749 x = 2
750 print(x)
751 self.assertInBytecode(f, 'LOAD_FAST')
752 self.assertNotInBytecode(f, 'LOAD_FAST_CHECK')
753
754 def test_load_fast_unknown_after_error(self):
755 def f():
756 try:
757 res = 1 / 0
758 except ZeroDivisionError:
759 pass
760 return res
761 # LOAD_FAST (known) still occurs in the no-exception branch.
762 # Assert that it doesn't occur in the LOAD_FAST_CHECK branch.
763 self.assertInBytecode(f, 'LOAD_FAST_CHECK')
764
765 def test_load_fast_unknown_after_error_2(self):
766 def f():
767 try:
768 1 / 0
769 except:
770 print(a, b, c, d, e, f, g)
771 a = b = c = d = e = f = g = 1
772 self.assertInBytecode(f, 'LOAD_FAST_CHECK')
773 self.assertNotInBytecode(f, 'LOAD_FAST')
774
775 def test_load_fast_too_many_locals(self):
776 # When there get to be too many locals to analyze completely,
777 # later locals are all converted to LOAD_FAST_CHECK, except
778 # when a store or prior load occurred in the same basicblock.
779 def f():
780 a00 = a01 = a02 = a03 = a04 = a05 = a06 = a07 = a08 = a09 = 1
781 a10 = a11 = a12 = a13 = a14 = a15 = a16 = a17 = a18 = a19 = 1
782 a20 = a21 = a22 = a23 = a24 = a25 = a26 = a27 = a28 = a29 = 1
783 a30 = a31 = a32 = a33 = a34 = a35 = a36 = a37 = a38 = a39 = 1
784 a40 = a41 = a42 = a43 = a44 = a45 = a46 = a47 = a48 = a49 = 1
785 a50 = a51 = a52 = a53 = a54 = a55 = a56 = a57 = a58 = a59 = 1
786 a60 = a61 = a62 = a63 = a64 = a65 = a66 = a67 = a68 = a69 = 1
787 a70 = a71 = a72 = a73 = a74 = a75 = a76 = a77 = a78 = a79 = 1
788 del a72, a73
789 print(a73)
790 print(a70, a71, a72, a73)
791 while True:
792 print(a00, a01, a62, a63)
793 print(a64, a65, a78, a79)
794
795 for i in 0, 1, 62, 63:
796 # First 64 locals: analyze completely
797 self.assertInBytecode(f, 'LOAD_FAST', f"a{i:02}")
798 self.assertNotInBytecode(f, 'LOAD_FAST_CHECK', f"a{i:02}")
799 for i in 64, 65, 78, 79:
800 # Locals >=64 not in the same basicblock
801 self.assertInBytecode(f, 'LOAD_FAST_CHECK', f"a{i:02}")
802 self.assertNotInBytecode(f, 'LOAD_FAST', f"a{i:02}")
803 for i in 70, 71:
804 # Locals >=64 in the same basicblock
805 self.assertInBytecode(f, 'LOAD_FAST', f"a{i:02}")
806 self.assertNotInBytecode(f, 'LOAD_FAST_CHECK', f"a{i:02}")
807 # del statements should invalidate within basicblocks.
808 self.assertInBytecode(f, 'LOAD_FAST_CHECK', "a72")
809 self.assertNotInBytecode(f, 'LOAD_FAST', "a72")
810 # previous checked loads within a basicblock enable unchecked loads
811 self.assertInBytecode(f, 'LOAD_FAST_CHECK', "a73")
812 self.assertInBytecode(f, 'LOAD_FAST', "a73")
813
814 def test_setting_lineno_no_undefined(self):
815 code = textwrap.dedent("""\
816 def f():
817 x = y = 2
818 if not x:
819 return 4
820 for i in range(55):
821 x + 6
822 L = 7
823 L = 8
824 L = 9
825 L = 10
826 """)
827 ns = {}
828 exec(code, ns)
829 f = ns['f']
830 self.assertInBytecode(f, "LOAD_FAST")
831 self.assertNotInBytecode(f, "LOAD_FAST_CHECK")
832 co_code = f.__code__.co_code
833 def trace(frame, event, arg):
834 if event == 'line' and frame.f_lineno == 9:
835 frame.f_lineno = 3
836 sys.settrace(None)
837 return None
838 return trace
839 sys.settrace(trace)
840 result = f()
841 self.assertIsNone(result)
842 self.assertInBytecode(f, "LOAD_FAST")
843 self.assertNotInBytecode(f, "LOAD_FAST_CHECK")
844 self.assertEqual(f.__code__.co_code, co_code)
845
846 def test_setting_lineno_one_undefined(self):
847 code = textwrap.dedent("""\
848 def f():
849 x = y = 2
850 if not x:
851 return 4
852 for i in range(55):
853 x + 6
854 del x
855 L = 8
856 L = 9
857 L = 10
858 """)
859 ns = {}
860 exec(code, ns)
861 f = ns['f']
862 self.assertInBytecode(f, "LOAD_FAST")
863 self.assertNotInBytecode(f, "LOAD_FAST_CHECK")
864 co_code = f.__code__.co_code
865 def trace(frame, event, arg):
866 if event == 'line' and frame.f_lineno == 9:
867 frame.f_lineno = 3
868 sys.settrace(None)
869 return None
870 return trace
871 e = r"assigning None to 1 unbound local"
872 with self.assertWarnsRegex(RuntimeWarning, e):
873 sys.settrace(trace)
874 result = f()
875 self.assertEqual(result, 4)
876 self.assertInBytecode(f, "LOAD_FAST")
877 self.assertNotInBytecode(f, "LOAD_FAST_CHECK")
878 self.assertEqual(f.__code__.co_code, co_code)
879
880 def test_setting_lineno_two_undefined(self):
881 code = textwrap.dedent("""\
882 def f():
883 x = y = 2
884 if not x:
885 return 4
886 for i in range(55):
887 x + 6
888 del x, y
889 L = 8
890 L = 9
891 L = 10
892 """)
893 ns = {}
894 exec(code, ns)
895 f = ns['f']
896 self.assertInBytecode(f, "LOAD_FAST")
897 self.assertNotInBytecode(f, "LOAD_FAST_CHECK")
898 co_code = f.__code__.co_code
899 def trace(frame, event, arg):
900 if event == 'line' and frame.f_lineno == 9:
901 frame.f_lineno = 3
902 sys.settrace(None)
903 return None
904 return trace
905 e = r"assigning None to 2 unbound locals"
906 with self.assertWarnsRegex(RuntimeWarning, e):
907 sys.settrace(trace)
908 result = f()
909 self.assertEqual(result, 4)
910 self.assertInBytecode(f, "LOAD_FAST")
911 self.assertNotInBytecode(f, "LOAD_FAST_CHECK")
912 self.assertEqual(f.__code__.co_code, co_code)
913
914 def make_function_with_no_checks(self):
915 code = textwrap.dedent("""\
916 def f():
917 x = 2
918 L = 3
919 L = 4
920 L = 5
921 if not L:
922 x + 7
923 y = 2
924 """)
925 ns = {}
926 exec(code, ns)
927 f = ns['f']
928 self.assertInBytecode(f, "LOAD_FAST")
929 self.assertNotInBytecode(f, "LOAD_FAST_CHECK")
930 return f
931
932 def test_deleting_local_warns_and_assigns_none(self):
933 f = self.make_function_with_no_checks()
934 co_code = f.__code__.co_code
935 def trace(frame, event, arg):
936 if event == 'line' and frame.f_lineno == 4:
937 del frame.f_locals["x"]
938 sys.settrace(None)
939 return None
940 return trace
941 e = r"assigning None to unbound local 'x'"
942 with self.assertWarnsRegex(RuntimeWarning, e):
943 sys.settrace(trace)
944 f()
945 self.assertInBytecode(f, "LOAD_FAST")
946 self.assertNotInBytecode(f, "LOAD_FAST_CHECK")
947 self.assertEqual(f.__code__.co_code, co_code)
948
949 def test_modifying_local_does_not_add_check(self):
950 f = self.make_function_with_no_checks()
951 def trace(frame, event, arg):
952 if event == 'line' and frame.f_lineno == 4:
953 frame.f_locals["x"] = 42
954 sys.settrace(None)
955 return None
956 return trace
957 sys.settrace(trace)
958 f()
959 self.assertInBytecode(f, "LOAD_FAST")
960 self.assertNotInBytecode(f, "LOAD_FAST_CHECK")
961
962 def test_initializing_local_does_not_add_check(self):
963 f = self.make_function_with_no_checks()
964 def trace(frame, event, arg):
965 if event == 'line' and frame.f_lineno == 4:
966 frame.f_locals["y"] = 42
967 sys.settrace(None)
968 return None
969 return trace
970 sys.settrace(trace)
971 f()
972 self.assertInBytecode(f, "LOAD_FAST")
973 self.assertNotInBytecode(f, "LOAD_FAST_CHECK")
974
975
976 class ESC[4;38;5;81mDirectCfgOptimizerTests(ESC[4;38;5;149mCfgOptimizationTestCase):
977
978 def cfg_optimization_test(self, insts, expected_insts,
979 consts=None, expected_consts=None,
980 nlocals=0):
981 if expected_consts is None:
982 expected_consts = consts
983 opt_insts, opt_consts = self.get_optimized(insts, consts, nlocals)
984 expected_insts = self.normalize_insts(expected_insts)
985 self.assertInstructionsMatch(opt_insts, expected_insts)
986 self.assertEqual(opt_consts, expected_consts)
987
988 def test_conditional_jump_forward_non_const_condition(self):
989 insts = [
990 ('LOAD_NAME', 1, 11),
991 ('POP_JUMP_IF_TRUE', lbl := self.Label(), 12),
992 ('LOAD_CONST', 2, 13),
993 lbl,
994 ('LOAD_CONST', 3, 14),
995 ('RETURN_VALUE', 14),
996 ]
997 expected_insts = [
998 ('LOAD_NAME', 1, 11),
999 ('POP_JUMP_IF_TRUE', lbl := self.Label(), 12),
1000 ('LOAD_CONST', 1, 13),
1001 lbl,
1002 ('RETURN_CONST', 2, 14),
1003 ]
1004 self.cfg_optimization_test(insts,
1005 expected_insts,
1006 consts=[0, 1, 2, 3, 4],
1007 expected_consts=[0, 2, 3])
1008
1009 def test_conditional_jump_forward_const_condition(self):
1010 # The unreachable branch of the jump is removed, the jump
1011 # becomes redundant and is replaced by a NOP (for the lineno)
1012
1013 insts = [
1014 ('LOAD_CONST', 3, 11),
1015 ('POP_JUMP_IF_TRUE', lbl := self.Label(), 12),
1016 ('LOAD_CONST', 2, 13),
1017 lbl,
1018 ('LOAD_CONST', 3, 14),
1019 ('RETURN_VALUE', 14),
1020 ]
1021 expected_insts = [
1022 ('NOP', 11),
1023 ('NOP', 12),
1024 ('RETURN_CONST', 1, 14),
1025 ]
1026 self.cfg_optimization_test(insts,
1027 expected_insts,
1028 consts=[0, 1, 2, 3, 4],
1029 expected_consts=[0, 3])
1030
1031 def test_conditional_jump_backward_non_const_condition(self):
1032 insts = [
1033 lbl1 := self.Label(),
1034 ('LOAD_NAME', 1, 11),
1035 ('POP_JUMP_IF_TRUE', lbl1, 12),
1036 ('LOAD_NAME', 2, 13),
1037 ('RETURN_VALUE', 13),
1038 ]
1039 expected = [
1040 lbl := self.Label(),
1041 ('LOAD_NAME', 1, 11),
1042 ('POP_JUMP_IF_TRUE', lbl, 12),
1043 ('LOAD_NAME', 2, 13),
1044 ('RETURN_VALUE', 13),
1045 ]
1046 self.cfg_optimization_test(insts, expected, consts=list(range(5)))
1047
1048 def test_conditional_jump_backward_const_condition(self):
1049 # The unreachable branch of the jump is removed
1050 insts = [
1051 lbl1 := self.Label(),
1052 ('LOAD_CONST', 3, 11),
1053 ('POP_JUMP_IF_TRUE', lbl1, 12),
1054 ('LOAD_CONST', 2, 13),
1055 ('RETURN_VALUE', 13),
1056 ]
1057 expected_insts = [
1058 lbl := self.Label(),
1059 ('NOP', 11),
1060 ('JUMP', lbl, 12),
1061 ]
1062 self.cfg_optimization_test(insts, expected_insts, consts=list(range(5)))
1063
1064 def test_no_unsafe_static_swap(self):
1065 # We can't change order of two stores to the same location
1066 insts = [
1067 ('LOAD_CONST', 0, 1),
1068 ('LOAD_CONST', 1, 2),
1069 ('LOAD_CONST', 2, 3),
1070 ('SWAP', 3, 4),
1071 ('STORE_FAST', 1, 4),
1072 ('STORE_FAST', 1, 4),
1073 ('POP_TOP', 0, 4),
1074 ('RETURN_VALUE', 5)
1075 ]
1076 self.cfg_optimization_test(insts, insts, consts=list(range(3)), nlocals=1)
1077
1078 if __name__ == "__main__":
1079 unittest.main()