test_peepholer.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681
  1. import dis
  2. from itertools import combinations, product
  3. import textwrap
  4. import unittest
  5. from test.support.bytecode_helper import BytecodeTestCase
  6. def compile_pattern_with_fast_locals(pattern):
  7. source = textwrap.dedent(
  8. f"""
  9. def f(x):
  10. match x:
  11. case {pattern}:
  12. pass
  13. """
  14. )
  15. namespace = {}
  16. exec(source, namespace)
  17. return namespace["f"].__code__
  18. def count_instr_recursively(f, opname):
  19. count = 0
  20. for instr in dis.get_instructions(f):
  21. if instr.opname == opname:
  22. count += 1
  23. if hasattr(f, '__code__'):
  24. f = f.__code__
  25. for c in f.co_consts:
  26. if hasattr(c, 'co_code'):
  27. count += count_instr_recursively(c, opname)
  28. return count
  29. class TestTranforms(BytecodeTestCase):
  30. def check_jump_targets(self, code):
  31. instructions = list(dis.get_instructions(code))
  32. targets = {instr.offset: instr for instr in instructions}
  33. for instr in instructions:
  34. if 'JUMP_' not in instr.opname:
  35. continue
  36. tgt = targets[instr.argval]
  37. # jump to unconditional jump
  38. if tgt.opname in ('JUMP_ABSOLUTE', 'JUMP_FORWARD'):
  39. self.fail(f'{instr.opname} at {instr.offset} '
  40. f'jumps to {tgt.opname} at {tgt.offset}')
  41. # unconditional jump to RETURN_VALUE
  42. if (instr.opname in ('JUMP_ABSOLUTE', 'JUMP_FORWARD') and
  43. tgt.opname == 'RETURN_VALUE'):
  44. self.fail(f'{instr.opname} at {instr.offset} '
  45. f'jumps to {tgt.opname} at {tgt.offset}')
  46. # JUMP_IF_*_OR_POP jump to conditional jump
  47. if '_OR_POP' in instr.opname and 'JUMP_IF_' in tgt.opname:
  48. self.fail(f'{instr.opname} at {instr.offset} '
  49. f'jumps to {tgt.opname} at {tgt.offset}')
  50. def check_lnotab(self, code):
  51. "Check that the lnotab byte offsets are sensible."
  52. code = dis._get_code_object(code)
  53. lnotab = list(dis.findlinestarts(code))
  54. # Don't bother checking if the line info is sensible, because
  55. # most of the line info we can get at comes from lnotab.
  56. min_bytecode = min(t[0] for t in lnotab)
  57. max_bytecode = max(t[0] for t in lnotab)
  58. self.assertGreaterEqual(min_bytecode, 0)
  59. self.assertLess(max_bytecode, len(code.co_code))
  60. # This could conceivably test more (and probably should, as there
  61. # aren't very many tests of lnotab), if peepholer wasn't scheduled
  62. # to be replaced anyway.
  63. def test_unot(self):
  64. # UNARY_NOT POP_JUMP_IF_FALSE --> POP_JUMP_IF_TRUE'
  65. def unot(x):
  66. if not x == 2:
  67. del x
  68. self.assertNotInBytecode(unot, 'UNARY_NOT')
  69. self.assertNotInBytecode(unot, 'POP_JUMP_FORWARD_IF_FALSE')
  70. self.assertNotInBytecode(unot, 'POP_JUMP_BACKWARD_IF_FALSE')
  71. self.assertInBytecode(unot, 'POP_JUMP_FORWARD_IF_TRUE')
  72. self.check_lnotab(unot)
  73. def test_elim_inversion_of_is_or_in(self):
  74. for line, cmp_op, invert in (
  75. ('not a is b', 'IS_OP', 1,),
  76. ('not a is not b', 'IS_OP', 0,),
  77. ('not a in b', 'CONTAINS_OP', 1,),
  78. ('not a not in b', 'CONTAINS_OP', 0,),
  79. ):
  80. with self.subTest(line=line):
  81. code = compile(line, '', 'single')
  82. self.assertInBytecode(code, cmp_op, invert)
  83. self.check_lnotab(code)
  84. def test_global_as_constant(self):
  85. # LOAD_GLOBAL None/True/False --> LOAD_CONST None/True/False
  86. def f():
  87. x = None
  88. x = None
  89. return x
  90. def g():
  91. x = True
  92. return x
  93. def h():
  94. x = False
  95. return x
  96. for func, elem in ((f, None), (g, True), (h, False)):
  97. with self.subTest(func=func):
  98. self.assertNotInBytecode(func, 'LOAD_GLOBAL')
  99. self.assertInBytecode(func, 'LOAD_CONST', elem)
  100. self.check_lnotab(func)
  101. def f():
  102. 'Adding a docstring made this test fail in Py2.5.0'
  103. return None
  104. self.assertNotInBytecode(f, 'LOAD_GLOBAL')
  105. self.assertInBytecode(f, 'LOAD_CONST', None)
  106. self.check_lnotab(f)
  107. def test_while_one(self):
  108. # Skip over: LOAD_CONST trueconst POP_JUMP_IF_FALSE xx
  109. def f():
  110. while 1:
  111. pass
  112. return list
  113. for elem in ('LOAD_CONST', 'POP_JUMP_IF_FALSE'):
  114. self.assertNotInBytecode(f, elem)
  115. for elem in ('JUMP_BACKWARD',):
  116. self.assertInBytecode(f, elem)
  117. self.check_lnotab(f)
  118. def test_pack_unpack(self):
  119. for line, elem in (
  120. ('a, = a,', 'LOAD_CONST',),
  121. ('a, b = a, b', 'SWAP',),
  122. ('a, b, c = a, b, c', 'SWAP',),
  123. ):
  124. with self.subTest(line=line):
  125. code = compile(line,'','single')
  126. self.assertInBytecode(code, elem)
  127. self.assertNotInBytecode(code, 'BUILD_TUPLE')
  128. self.assertNotInBytecode(code, 'UNPACK_SEQUENCE')
  129. self.check_lnotab(code)
  130. def test_folding_of_tuples_of_constants(self):
  131. for line, elem in (
  132. ('a = 1,2,3', (1, 2, 3)),
  133. ('("a","b","c")', ('a', 'b', 'c')),
  134. ('a,b,c = 1,2,3', (1, 2, 3)),
  135. ('(None, 1, None)', (None, 1, None)),
  136. ('((1, 2), 3, 4)', ((1, 2), 3, 4)),
  137. ):
  138. with self.subTest(line=line):
  139. code = compile(line,'','single')
  140. self.assertInBytecode(code, 'LOAD_CONST', elem)
  141. self.assertNotInBytecode(code, 'BUILD_TUPLE')
  142. self.check_lnotab(code)
  143. # Long tuples should be folded too.
  144. code = compile(repr(tuple(range(10000))),'','single')
  145. self.assertNotInBytecode(code, 'BUILD_TUPLE')
  146. # One LOAD_CONST for the tuple, one for the None return value
  147. load_consts = [instr for instr in dis.get_instructions(code)
  148. if instr.opname == 'LOAD_CONST']
  149. self.assertEqual(len(load_consts), 2)
  150. self.check_lnotab(code)
  151. # Bug 1053819: Tuple of constants misidentified when presented with:
  152. # . . . opcode_with_arg 100 unary_opcode BUILD_TUPLE 1 . . .
  153. # The following would segfault upon compilation
  154. def crater():
  155. (~[
  156. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
  157. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
  158. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
  159. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
  160. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
  161. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
  162. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
  163. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
  164. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
  165. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
  166. ],)
  167. self.check_lnotab(crater)
  168. def test_folding_of_lists_of_constants(self):
  169. for line, elem in (
  170. # in/not in constants with BUILD_LIST should be folded to a tuple:
  171. ('a in [1,2,3]', (1, 2, 3)),
  172. ('a not in ["a","b","c"]', ('a', 'b', 'c')),
  173. ('a in [None, 1, None]', (None, 1, None)),
  174. ('a not in [(1, 2), 3, 4]', ((1, 2), 3, 4)),
  175. ):
  176. with self.subTest(line=line):
  177. code = compile(line, '', 'single')
  178. self.assertInBytecode(code, 'LOAD_CONST', elem)
  179. self.assertNotInBytecode(code, 'BUILD_LIST')
  180. self.check_lnotab(code)
  181. def test_folding_of_sets_of_constants(self):
  182. for line, elem in (
  183. # in/not in constants with BUILD_SET should be folded to a frozenset:
  184. ('a in {1,2,3}', frozenset({1, 2, 3})),
  185. ('a not in {"a","b","c"}', frozenset({'a', 'c', 'b'})),
  186. ('a in {None, 1, None}', frozenset({1, None})),
  187. ('a not in {(1, 2), 3, 4}', frozenset({(1, 2), 3, 4})),
  188. ('a in {1, 2, 3, 3, 2, 1}', frozenset({1, 2, 3})),
  189. ):
  190. with self.subTest(line=line):
  191. code = compile(line, '', 'single')
  192. self.assertNotInBytecode(code, 'BUILD_SET')
  193. self.assertInBytecode(code, 'LOAD_CONST', elem)
  194. self.check_lnotab(code)
  195. # Ensure that the resulting code actually works:
  196. def f(a):
  197. return a in {1, 2, 3}
  198. def g(a):
  199. return a not in {1, 2, 3}
  200. self.assertTrue(f(3))
  201. self.assertTrue(not f(4))
  202. self.check_lnotab(f)
  203. self.assertTrue(not g(3))
  204. self.assertTrue(g(4))
  205. self.check_lnotab(g)
  206. def test_folding_of_binops_on_constants(self):
  207. for line, elem in (
  208. ('a = 2+3+4', 9), # chained fold
  209. ('"@"*4', '@@@@'), # check string ops
  210. ('a="abc" + "def"', 'abcdef'), # check string ops
  211. ('a = 3**4', 81), # binary power
  212. ('a = 3*4', 12), # binary multiply
  213. ('a = 13//4', 3), # binary floor divide
  214. ('a = 14%4', 2), # binary modulo
  215. ('a = 2+3', 5), # binary add
  216. ('a = 13-4', 9), # binary subtract
  217. ('a = (12,13)[1]', 13), # binary subscr
  218. ('a = 13 << 2', 52), # binary lshift
  219. ('a = 13 >> 2', 3), # binary rshift
  220. ('a = 13 & 7', 5), # binary and
  221. ('a = 13 ^ 7', 10), # binary xor
  222. ('a = 13 | 7', 15), # binary or
  223. ):
  224. with self.subTest(line=line):
  225. code = compile(line, '', 'single')
  226. self.assertInBytecode(code, 'LOAD_CONST', elem)
  227. for instr in dis.get_instructions(code):
  228. self.assertFalse(instr.opname.startswith('BINARY_'))
  229. self.check_lnotab(code)
  230. # Verify that unfoldables are skipped
  231. code = compile('a=2+"b"', '', 'single')
  232. self.assertInBytecode(code, 'LOAD_CONST', 2)
  233. self.assertInBytecode(code, 'LOAD_CONST', 'b')
  234. self.check_lnotab(code)
  235. # Verify that large sequences do not result from folding
  236. code = compile('a="x"*10000', '', 'single')
  237. self.assertInBytecode(code, 'LOAD_CONST', 10000)
  238. self.assertNotIn("x"*10000, code.co_consts)
  239. self.check_lnotab(code)
  240. code = compile('a=1<<1000', '', 'single')
  241. self.assertInBytecode(code, 'LOAD_CONST', 1000)
  242. self.assertNotIn(1<<1000, code.co_consts)
  243. self.check_lnotab(code)
  244. code = compile('a=2**1000', '', 'single')
  245. self.assertInBytecode(code, 'LOAD_CONST', 1000)
  246. self.assertNotIn(2**1000, code.co_consts)
  247. self.check_lnotab(code)
  248. def test_binary_subscr_on_unicode(self):
  249. # valid code get optimized
  250. code = compile('"foo"[0]', '', 'single')
  251. self.assertInBytecode(code, 'LOAD_CONST', 'f')
  252. self.assertNotInBytecode(code, 'BINARY_SUBSCR')
  253. self.check_lnotab(code)
  254. code = compile('"\u0061\uffff"[1]', '', 'single')
  255. self.assertInBytecode(code, 'LOAD_CONST', '\uffff')
  256. self.assertNotInBytecode(code,'BINARY_SUBSCR')
  257. self.check_lnotab(code)
  258. # With PEP 393, non-BMP char get optimized
  259. code = compile('"\U00012345"[0]', '', 'single')
  260. self.assertInBytecode(code, 'LOAD_CONST', '\U00012345')
  261. self.assertNotInBytecode(code, 'BINARY_SUBSCR')
  262. self.check_lnotab(code)
  263. # invalid code doesn't get optimized
  264. # out of range
  265. code = compile('"fuu"[10]', '', 'single')
  266. self.assertInBytecode(code, 'BINARY_SUBSCR')
  267. self.check_lnotab(code)
  268. def test_folding_of_unaryops_on_constants(self):
  269. for line, elem in (
  270. ('-0.5', -0.5), # unary negative
  271. ('-0.0', -0.0), # -0.0
  272. ('-(1.0-1.0)', -0.0), # -0.0 after folding
  273. ('-0', 0), # -0
  274. ('~-2', 1), # unary invert
  275. ('+1', 1), # unary positive
  276. ):
  277. with self.subTest(line=line):
  278. code = compile(line, '', 'single')
  279. self.assertInBytecode(code, 'LOAD_CONST', elem)
  280. for instr in dis.get_instructions(code):
  281. self.assertFalse(instr.opname.startswith('UNARY_'))
  282. self.check_lnotab(code)
  283. # Check that -0.0 works after marshaling
  284. def negzero():
  285. return -(1.0-1.0)
  286. for instr in dis.get_instructions(negzero):
  287. self.assertFalse(instr.opname.startswith('UNARY_'))
  288. self.check_lnotab(negzero)
  289. # Verify that unfoldables are skipped
  290. for line, elem, opname in (
  291. ('-"abc"', 'abc', 'UNARY_NEGATIVE'),
  292. ('~"abc"', 'abc', 'UNARY_INVERT'),
  293. ):
  294. with self.subTest(line=line):
  295. code = compile(line, '', 'single')
  296. self.assertInBytecode(code, 'LOAD_CONST', elem)
  297. self.assertInBytecode(code, opname)
  298. self.check_lnotab(code)
  299. def test_elim_extra_return(self):
  300. # RETURN LOAD_CONST None RETURN --> RETURN
  301. def f(x):
  302. return x
  303. self.assertNotInBytecode(f, 'LOAD_CONST', None)
  304. returns = [instr for instr in dis.get_instructions(f)
  305. if instr.opname == 'RETURN_VALUE']
  306. self.assertEqual(len(returns), 1)
  307. self.check_lnotab(f)
  308. @unittest.skip("Following gh-92228 the return has two predecessors "
  309. "and that prevents jump elimination.")
  310. def test_elim_jump_to_return(self):
  311. # JUMP_FORWARD to RETURN --> RETURN
  312. def f(cond, true_value, false_value):
  313. # Intentionally use two-line expression to test issue37213.
  314. return (true_value if cond
  315. else false_value)
  316. self.check_jump_targets(f)
  317. self.assertNotInBytecode(f, 'JUMP_FORWARD')
  318. self.assertNotInBytecode(f, 'JUMP_ABSOLUTE')
  319. returns = [instr for instr in dis.get_instructions(f)
  320. if instr.opname == 'RETURN_VALUE']
  321. self.assertEqual(len(returns), 2)
  322. self.check_lnotab(f)
  323. def test_elim_jump_to_uncond_jump(self):
  324. # POP_JUMP_IF_FALSE to JUMP_FORWARD --> POP_JUMP_IF_FALSE to non-jump
  325. def f():
  326. if a:
  327. # Intentionally use two-line expression to test issue37213.
  328. if (c
  329. or d):
  330. foo()
  331. else:
  332. baz()
  333. self.check_jump_targets(f)
  334. self.check_lnotab(f)
  335. def test_elim_jump_to_uncond_jump2(self):
  336. # POP_JUMP_IF_FALSE to JUMP_ABSOLUTE --> POP_JUMP_IF_FALSE to non-jump
  337. def f():
  338. while a:
  339. # Intentionally use two-line expression to test issue37213.
  340. if (c
  341. or d):
  342. a = foo()
  343. self.check_jump_targets(f)
  344. self.check_lnotab(f)
  345. def test_elim_jump_to_uncond_jump3(self):
  346. # Intentionally use two-line expressions to test issue37213.
  347. # JUMP_IF_FALSE_OR_POP to JUMP_IF_FALSE_OR_POP --> JUMP_IF_FALSE_OR_POP to non-jump
  348. def f(a, b, c):
  349. return ((a and b)
  350. and c)
  351. self.check_jump_targets(f)
  352. self.check_lnotab(f)
  353. self.assertEqual(count_instr_recursively(f, 'JUMP_IF_FALSE_OR_POP'), 2)
  354. # JUMP_IF_TRUE_OR_POP to JUMP_IF_TRUE_OR_POP --> JUMP_IF_TRUE_OR_POP to non-jump
  355. def f(a, b, c):
  356. return ((a or b)
  357. or c)
  358. self.check_jump_targets(f)
  359. self.check_lnotab(f)
  360. self.assertEqual(count_instr_recursively(f, 'JUMP_IF_TRUE_OR_POP'), 2)
  361. # JUMP_IF_FALSE_OR_POP to JUMP_IF_TRUE_OR_POP --> POP_JUMP_IF_FALSE to non-jump
  362. def f(a, b, c):
  363. return ((a and b)
  364. or c)
  365. self.check_jump_targets(f)
  366. self.check_lnotab(f)
  367. self.assertNotInBytecode(f, 'JUMP_IF_FALSE_OR_POP')
  368. self.assertInBytecode(f, 'JUMP_IF_TRUE_OR_POP')
  369. self.assertInBytecode(f, 'POP_JUMP_FORWARD_IF_FALSE')
  370. # JUMP_IF_TRUE_OR_POP to JUMP_IF_FALSE_OR_POP --> POP_JUMP_IF_TRUE to non-jump
  371. def f(a, b, c):
  372. return ((a or b)
  373. and c)
  374. self.check_jump_targets(f)
  375. self.check_lnotab(f)
  376. self.assertNotInBytecode(f, 'JUMP_IF_TRUE_OR_POP')
  377. self.assertInBytecode(f, 'JUMP_IF_FALSE_OR_POP')
  378. self.assertInBytecode(f, 'POP_JUMP_FORWARD_IF_TRUE')
  379. def test_elim_jump_after_return1(self):
  380. # Eliminate dead code: jumps immediately after returns can't be reached
  381. def f(cond1, cond2):
  382. if cond1: return 1
  383. if cond2: return 2
  384. while 1:
  385. return 3
  386. while 1:
  387. if cond1: return 4
  388. return 5
  389. return 6
  390. self.assertNotInBytecode(f, 'JUMP_FORWARD')
  391. self.assertNotInBytecode(f, 'JUMP_ABSOLUTE')
  392. returns = [instr for instr in dis.get_instructions(f)
  393. if instr.opname == 'RETURN_VALUE']
  394. self.assertLessEqual(len(returns), 6)
  395. self.check_lnotab(f)
  396. def test_make_function_doesnt_bail(self):
  397. def f():
  398. def g()->1+1:
  399. pass
  400. return g
  401. self.assertNotInBytecode(f, 'BINARY_OP')
  402. self.check_lnotab(f)
  403. def test_constant_folding(self):
  404. # Issue #11244: aggressive constant folding.
  405. exprs = [
  406. '3 * -5',
  407. '-3 * 5',
  408. '2 * (3 * 4)',
  409. '(2 * 3) * 4',
  410. '(-1, 2, 3)',
  411. '(1, -2, 3)',
  412. '(1, 2, -3)',
  413. '(1, 2, -3) * 6',
  414. 'lambda x: x in {(3 * -5) + (-1 - 6), (1, -2, 3) * 2, None}',
  415. ]
  416. for e in exprs:
  417. with self.subTest(e=e):
  418. code = compile(e, '', 'single')
  419. for instr in dis.get_instructions(code):
  420. self.assertFalse(instr.opname.startswith('UNARY_'))
  421. self.assertFalse(instr.opname.startswith('BINARY_'))
  422. self.assertFalse(instr.opname.startswith('BUILD_'))
  423. self.check_lnotab(code)
  424. def test_in_literal_list(self):
  425. def containtest():
  426. return x in [a, b]
  427. self.assertEqual(count_instr_recursively(containtest, 'BUILD_LIST'), 0)
  428. self.check_lnotab(containtest)
  429. def test_iterate_literal_list(self):
  430. def forloop():
  431. for x in [a, b]:
  432. pass
  433. self.assertEqual(count_instr_recursively(forloop, 'BUILD_LIST'), 0)
  434. self.check_lnotab(forloop)
  435. def test_condition_with_binop_with_bools(self):
  436. def f():
  437. if True or False:
  438. return 1
  439. return 0
  440. self.assertEqual(f(), 1)
  441. self.check_lnotab(f)
  442. def test_if_with_if_expression(self):
  443. # Check bpo-37289
  444. def f(x):
  445. if (True if x else False):
  446. return True
  447. return False
  448. self.assertTrue(f(True))
  449. self.check_lnotab(f)
  450. def test_trailing_nops(self):
  451. # Check the lnotab of a function that even after trivial
  452. # optimization has trailing nops, which the lnotab adjustment has to
  453. # handle properly (bpo-38115).
  454. def f(x):
  455. while 1:
  456. return 3
  457. while 1:
  458. return 5
  459. return 6
  460. self.check_lnotab(f)
  461. def test_assignment_idiom_in_comprehensions(self):
  462. def listcomp():
  463. return [y for x in a for y in [f(x)]]
  464. self.assertEqual(count_instr_recursively(listcomp, 'FOR_ITER'), 1)
  465. def setcomp():
  466. return {y for x in a for y in [f(x)]}
  467. self.assertEqual(count_instr_recursively(setcomp, 'FOR_ITER'), 1)
  468. def dictcomp():
  469. return {y: y for x in a for y in [f(x)]}
  470. self.assertEqual(count_instr_recursively(dictcomp, 'FOR_ITER'), 1)
  471. def genexpr():
  472. return (y for x in a for y in [f(x)])
  473. self.assertEqual(count_instr_recursively(genexpr, 'FOR_ITER'), 1)
  474. def test_format_combinations(self):
  475. flags = '-+ #0'
  476. testcases = [
  477. *product(('', '1234', 'абвг'), 'sra'),
  478. *product((1234, -1234), 'duioxX'),
  479. *product((1234.5678901, -1234.5678901), 'duifegFEG'),
  480. *product((float('inf'), -float('inf')), 'fegFEG'),
  481. ]
  482. width_precs = [
  483. *product(('', '1', '30'), ('', '.', '.0', '.2')),
  484. ('', '.40'),
  485. ('30', '.40'),
  486. ]
  487. for value, suffix in testcases:
  488. for width, prec in width_precs:
  489. for r in range(len(flags) + 1):
  490. for spec in combinations(flags, r):
  491. fmt = '%' + ''.join(spec) + width + prec + suffix
  492. with self.subTest(fmt=fmt, value=value):
  493. s1 = fmt % value
  494. s2 = eval(f'{fmt!r} % (x,)', {'x': value})
  495. self.assertEqual(s2, s1, f'{fmt = }')
  496. def test_format_misc(self):
  497. def format(fmt, *values):
  498. vars = [f'x{i+1}' for i in range(len(values))]
  499. if len(vars) == 1:
  500. args = '(' + vars[0] + ',)'
  501. else:
  502. args = '(' + ', '.join(vars) + ')'
  503. return eval(f'{fmt!r} % {args}', dict(zip(vars, values)))
  504. self.assertEqual(format('string'), 'string')
  505. self.assertEqual(format('x = %s!', 1234), 'x = 1234!')
  506. self.assertEqual(format('x = %d!', 1234), 'x = 1234!')
  507. self.assertEqual(format('x = %x!', 1234), 'x = 4d2!')
  508. self.assertEqual(format('x = %f!', 1234), 'x = 1234.000000!')
  509. self.assertEqual(format('x = %s!', 1234.5678901), 'x = 1234.5678901!')
  510. self.assertEqual(format('x = %f!', 1234.5678901), 'x = 1234.567890!')
  511. self.assertEqual(format('x = %d!', 1234.5678901), 'x = 1234!')
  512. self.assertEqual(format('x = %s%% %%%%', 1234), 'x = 1234% %%')
  513. self.assertEqual(format('x = %s!', '%% %s'), 'x = %% %s!')
  514. self.assertEqual(format('x = %s, y = %d', 12, 34), 'x = 12, y = 34')
  515. def test_format_errors(self):
  516. with self.assertRaisesRegex(TypeError,
  517. 'not enough arguments for format string'):
  518. eval("'%s' % ()")
  519. with self.assertRaisesRegex(TypeError,
  520. 'not all arguments converted during string formatting'):
  521. eval("'%s' % (x, y)", {'x': 1, 'y': 2})
  522. with self.assertRaisesRegex(ValueError, 'incomplete format'):
  523. eval("'%s%' % (x,)", {'x': 1234})
  524. with self.assertRaisesRegex(ValueError, 'incomplete format'):
  525. eval("'%s%%%' % (x,)", {'x': 1234})
  526. with self.assertRaisesRegex(TypeError,
  527. 'not enough arguments for format string'):
  528. eval("'%s%z' % (x,)", {'x': 1234})
  529. with self.assertRaisesRegex(ValueError, 'unsupported format character'):
  530. eval("'%s%z' % (x, 5)", {'x': 1234})
  531. with self.assertRaisesRegex(TypeError, 'a real number is required, not str'):
  532. eval("'%d' % (x,)", {'x': '1234'})
  533. with self.assertRaisesRegex(TypeError, 'an integer is required, not float'):
  534. eval("'%x' % (x,)", {'x': 1234.56})
  535. with self.assertRaisesRegex(TypeError, 'an integer is required, not str'):
  536. eval("'%x' % (x,)", {'x': '1234'})
  537. with self.assertRaisesRegex(TypeError, 'must be real number, not str'):
  538. eval("'%f' % (x,)", {'x': '1234'})
  539. with self.assertRaisesRegex(TypeError,
  540. 'not enough arguments for format string'):
  541. eval("'%s, %s' % (x, *y)", {'x': 1, 'y': []})
  542. with self.assertRaisesRegex(TypeError,
  543. 'not all arguments converted during string formatting'):
  544. eval("'%s, %s' % (x, *y)", {'x': 1, 'y': [2, 3]})
  545. def test_static_swaps_unpack_two(self):
  546. def f(a, b):
  547. a, b = a, b
  548. b, a = a, b
  549. self.assertNotInBytecode(f, "SWAP")
  550. def test_static_swaps_unpack_three(self):
  551. def f(a, b, c):
  552. a, b, c = a, b, c
  553. a, c, b = a, b, c
  554. b, a, c = a, b, c
  555. b, c, a = a, b, c
  556. c, a, b = a, b, c
  557. c, b, a = a, b, c
  558. self.assertNotInBytecode(f, "SWAP")
  559. def test_static_swaps_match_mapping(self):
  560. for a, b, c in product("_a", "_b", "_c"):
  561. pattern = f"{{'a': {a}, 'b': {b}, 'c': {c}}}"
  562. with self.subTest(pattern):
  563. code = compile_pattern_with_fast_locals(pattern)
  564. self.assertNotInBytecode(code, "SWAP")
  565. def test_static_swaps_match_class(self):
  566. forms = [
  567. "C({}, {}, {})",
  568. "C({}, {}, c={})",
  569. "C({}, b={}, c={})",
  570. "C(a={}, b={}, c={})"
  571. ]
  572. for a, b, c in product("_a", "_b", "_c"):
  573. for form in forms:
  574. pattern = form.format(a, b, c)
  575. with self.subTest(pattern):
  576. code = compile_pattern_with_fast_locals(pattern)
  577. self.assertNotInBytecode(code, "SWAP")
  578. def test_static_swaps_match_sequence(self):
  579. swaps = {"*_, b, c", "a, *_, c", "a, b, *_"}
  580. forms = ["{}, {}, {}", "{}, {}, *{}", "{}, *{}, {}", "*{}, {}, {}"]
  581. for a, b, c in product("_a", "_b", "_c"):
  582. for form in forms:
  583. pattern = form.format(a, b, c)
  584. with self.subTest(pattern):
  585. code = compile_pattern_with_fast_locals(pattern)
  586. if pattern in swaps:
  587. # If this fails... great! Remove this pattern from swaps
  588. # to prevent regressing on any improvement:
  589. self.assertInBytecode(code, "SWAP")
  590. else:
  591. self.assertNotInBytecode(code, "SWAP")
  592. class TestBuglets(unittest.TestCase):
  593. def test_bug_11510(self):
  594. # folded constant set optimization was commingled with the tuple
  595. # unpacking optimization which would fail if the set had duplicate
  596. # elements so that the set length was unexpected
  597. def f():
  598. x, y = {1, 1}
  599. return x, y
  600. with self.assertRaises(ValueError):
  601. f()
  602. def test_bpo_42057(self):
  603. for i in range(10):
  604. try:
  605. raise Exception
  606. except Exception or Exception:
  607. pass
  608. def test_bpo_45773_pop_jump_if_true(self):
  609. compile("while True or spam: pass", "<test>", "exec")
  610. def test_bpo_45773_pop_jump_if_false(self):
  611. compile("while True or not spam: pass", "<test>", "exec")
  612. if __name__ == "__main__":
  613. unittest.main()