1import dis
2import math
3import os
4import unittest
5import sys
6import ast
7import _ast
8import tempfile
9import types
10import textwrap
11from test import support
12from test.support import script_helper, requires_debug_ranges
13from test.support.os_helper import FakePath
14
15
16class TestSpecifics(unittest.TestCase):
17
18    def compile_single(self, source):
19        compile(source, "<single>", "single")
20
21    def assertInvalidSingle(self, source):
22        self.assertRaises(SyntaxError, self.compile_single, source)
23
24    def test_no_ending_newline(self):
25        compile("hi", "<test>", "exec")
26        compile("hi\r", "<test>", "exec")
27
28    def test_empty(self):
29        compile("", "<test>", "exec")
30
31    def test_other_newlines(self):
32        compile("\r\n", "<test>", "exec")
33        compile("\r", "<test>", "exec")
34        compile("hi\r\nstuff\r\ndef f():\n    pass\r", "<test>", "exec")
35        compile("this_is\rreally_old_mac\rdef f():\n    pass", "<test>", "exec")
36
37    def test_debug_assignment(self):
38        # catch assignments to __debug__
39        self.assertRaises(SyntaxError, compile, '__debug__ = 1', '?', 'single')
40        import builtins
41        prev = builtins.__debug__
42        setattr(builtins, '__debug__', 'sure')
43        self.assertEqual(__debug__, prev)
44        setattr(builtins, '__debug__', prev)
45
46    def test_argument_handling(self):
47        # detect duplicate positional and keyword arguments
48        self.assertRaises(SyntaxError, eval, 'lambda a,a:0')
49        self.assertRaises(SyntaxError, eval, 'lambda a,a=1:0')
50        self.assertRaises(SyntaxError, eval, 'lambda a=1,a=1:0')
51        self.assertRaises(SyntaxError, exec, 'def f(a, a): pass')
52        self.assertRaises(SyntaxError, exec, 'def f(a = 0, a = 1): pass')
53        self.assertRaises(SyntaxError, exec, 'def f(a): global a; a = 1')
54
55    def test_syntax_error(self):
56        self.assertRaises(SyntaxError, compile, "1+*3", "filename", "exec")
57
58    def test_none_keyword_arg(self):
59        self.assertRaises(SyntaxError, compile, "f(None=1)", "<string>", "exec")
60
61    def test_duplicate_global_local(self):
62        self.assertRaises(SyntaxError, exec, 'def f(a): global a; a = 1')
63
64    def test_exec_with_general_mapping_for_locals(self):
65
66        class M:
67            "Test mapping interface versus possible calls from eval()."
68            def __getitem__(self, key):
69                if key == 'a':
70                    return 12
71                raise KeyError
72            def __setitem__(self, key, value):
73                self.results = (key, value)
74            def keys(self):
75                return list('xyz')
76
77        m = M()
78        g = globals()
79        exec('z = a', g, m)
80        self.assertEqual(m.results, ('z', 12))
81        try:
82            exec('z = b', g, m)
83        except NameError:
84            pass
85        else:
86            self.fail('Did not detect a KeyError')
87        exec('z = dir()', g, m)
88        self.assertEqual(m.results, ('z', list('xyz')))
89        exec('z = globals()', g, m)
90        self.assertEqual(m.results, ('z', g))
91        exec('z = locals()', g, m)
92        self.assertEqual(m.results, ('z', m))
93        self.assertRaises(TypeError, exec, 'z = b', m)
94
95        class A:
96            "Non-mapping"
97            pass
98        m = A()
99        self.assertRaises(TypeError, exec, 'z = a', g, m)
100
101        # Verify that dict subclasses work as well
102        class D(dict):
103            def __getitem__(self, key):
104                if key == 'a':
105                    return 12
106                return dict.__getitem__(self, key)
107        d = D()
108        exec('z = a', g, d)
109        self.assertEqual(d['z'], 12)
110
111    def test_extended_arg(self):
112        # default: 1000 * 2.5 = 2500 repetitions
113        repeat = int(sys.getrecursionlimit() * 2.5)
114        longexpr = 'x = x or ' + '-x' * repeat
115        g = {}
116        code = '''
117def f(x):
118    %s
119    %s
120    %s
121    %s
122    %s
123    %s
124    %s
125    %s
126    %s
127    %s
128    # the expressions above have no effect, x == argument
129    while x:
130        x -= 1
131        # EXTENDED_ARG/JUMP_ABSOLUTE here
132    return x
133''' % ((longexpr,)*10)
134        exec(code, g)
135        self.assertEqual(g['f'](5), 0)
136
137    def test_argument_order(self):
138        self.assertRaises(SyntaxError, exec, 'def f(a=1, b): pass')
139
140    def test_float_literals(self):
141        # testing bad float literals
142        self.assertRaises(SyntaxError, eval, "2e")
143        self.assertRaises(SyntaxError, eval, "2.0e+")
144        self.assertRaises(SyntaxError, eval, "1e-")
145        self.assertRaises(SyntaxError, eval, "3-4e/21")
146
147    def test_indentation(self):
148        # testing compile() of indented block w/o trailing newline"
149        s = """
150if 1:
151    if 2:
152        pass"""
153        compile(s, "<string>", "exec")
154
155    # This test is probably specific to CPython and may not generalize
156    # to other implementations.  We are trying to ensure that when
157    # the first line of code starts after 256, correct line numbers
158    # in tracebacks are still produced.
159    def test_leading_newlines(self):
160        s256 = "".join(["\n"] * 256 + ["spam"])
161        co = compile(s256, 'fn', 'exec')
162        self.assertEqual(co.co_firstlineno, 1)
163        lines = list(co.co_lines())
164        self.assertEqual(lines[0][2], 0)
165        self.assertEqual(lines[1][2], 257)
166
167    def test_literals_with_leading_zeroes(self):
168        for arg in ["077787", "0xj", "0x.", "0e",  "090000000000000",
169                    "080000000000000", "000000000000009", "000000000000008",
170                    "0b42", "0BADCAFE", "0o123456789", "0b1.1", "0o4.2",
171                    "0b101j", "0o153j", "0b100e1", "0o777e1", "0777",
172                    "000777", "000000000000007"]:
173            self.assertRaises(SyntaxError, eval, arg)
174
175        self.assertEqual(eval("0xff"), 255)
176        self.assertEqual(eval("0777."), 777)
177        self.assertEqual(eval("0777.0"), 777)
178        self.assertEqual(eval("000000000000000000000000000000000000000000000000000777e0"), 777)
179        self.assertEqual(eval("0777e1"), 7770)
180        self.assertEqual(eval("0e0"), 0)
181        self.assertEqual(eval("0000e-012"), 0)
182        self.assertEqual(eval("09.5"), 9.5)
183        self.assertEqual(eval("0777j"), 777j)
184        self.assertEqual(eval("000"), 0)
185        self.assertEqual(eval("00j"), 0j)
186        self.assertEqual(eval("00.0"), 0)
187        self.assertEqual(eval("0e3"), 0)
188        self.assertEqual(eval("090000000000000."), 90000000000000.)
189        self.assertEqual(eval("090000000000000.0000000000000000000000"), 90000000000000.)
190        self.assertEqual(eval("090000000000000e0"), 90000000000000.)
191        self.assertEqual(eval("090000000000000e-0"), 90000000000000.)
192        self.assertEqual(eval("090000000000000j"), 90000000000000j)
193        self.assertEqual(eval("000000000000008."), 8.)
194        self.assertEqual(eval("000000000000009."), 9.)
195        self.assertEqual(eval("0b101010"), 42)
196        self.assertEqual(eval("-0b000000000010"), -2)
197        self.assertEqual(eval("0o777"), 511)
198        self.assertEqual(eval("-0o0000010"), -8)
199
200    def test_int_literals_too_long(self):
201        n = 3000
202        source = f"a = 1\nb = 2\nc = {'3'*n}\nd = 4"
203        with support.adjust_int_max_str_digits(n):
204            compile(source, "<long_int_pass>", "exec")  # no errors.
205        with support.adjust_int_max_str_digits(n-1):
206            with self.assertRaises(SyntaxError) as err_ctx:
207                compile(source, "<long_int_fail>", "exec")
208            exc = err_ctx.exception
209            self.assertEqual(exc.lineno, 3)
210            self.assertIn('Exceeds the limit ', str(exc))
211            self.assertIn(' Consider hexadecimal ', str(exc))
212
213    def test_unary_minus(self):
214        # Verify treatment of unary minus on negative numbers SF bug #660455
215        if sys.maxsize == 2147483647:
216            # 32-bit machine
217            all_one_bits = '0xffffffff'
218            self.assertEqual(eval(all_one_bits), 4294967295)
219            self.assertEqual(eval("-" + all_one_bits), -4294967295)
220        elif sys.maxsize == 9223372036854775807:
221            # 64-bit machine
222            all_one_bits = '0xffffffffffffffff'
223            self.assertEqual(eval(all_one_bits), 18446744073709551615)
224            self.assertEqual(eval("-" + all_one_bits), -18446744073709551615)
225        else:
226            self.fail("How many bits *does* this machine have???")
227        # Verify treatment of constant folding on -(sys.maxsize+1)
228        # i.e. -2147483648 on 32 bit platforms.  Should return int.
229        self.assertIsInstance(eval("%s" % (-sys.maxsize - 1)), int)
230        self.assertIsInstance(eval("%s" % (-sys.maxsize - 2)), int)
231
232    if sys.maxsize == 9223372036854775807:
233        def test_32_63_bit_values(self):
234            a = +4294967296  # 1 << 32
235            b = -4294967296  # 1 << 32
236            c = +281474976710656  # 1 << 48
237            d = -281474976710656  # 1 << 48
238            e = +4611686018427387904  # 1 << 62
239            f = -4611686018427387904  # 1 << 62
240            g = +9223372036854775807  # 1 << 63 - 1
241            h = -9223372036854775807  # 1 << 63 - 1
242
243            for variable in self.test_32_63_bit_values.__code__.co_consts:
244                if variable is not None:
245                    self.assertIsInstance(variable, int)
246
247    def test_sequence_unpacking_error(self):
248        # Verify sequence packing/unpacking with "or".  SF bug #757818
249        i,j = (1, -1) or (-1, 1)
250        self.assertEqual(i, 1)
251        self.assertEqual(j, -1)
252
253    def test_none_assignment(self):
254        stmts = [
255            'None = 0',
256            'None += 0',
257            '__builtins__.None = 0',
258            'def None(): pass',
259            'class None: pass',
260            '(a, None) = 0, 0',
261            'for None in range(10): pass',
262            'def f(None): pass',
263            'import None',
264            'import x as None',
265            'from x import None',
266            'from x import y as None'
267        ]
268        for stmt in stmts:
269            stmt += "\n"
270            self.assertRaises(SyntaxError, compile, stmt, 'tmp', 'single')
271            self.assertRaises(SyntaxError, compile, stmt, 'tmp', 'exec')
272
273    def test_import(self):
274        succeed = [
275            'import sys',
276            'import os, sys',
277            'import os as bar',
278            'import os.path as bar',
279            'from __future__ import nested_scopes, generators',
280            'from __future__ import (nested_scopes,\ngenerators)',
281            'from __future__ import (nested_scopes,\ngenerators,)',
282            'from sys import stdin, stderr, stdout',
283            'from sys import (stdin, stderr,\nstdout)',
284            'from sys import (stdin, stderr,\nstdout,)',
285            'from sys import (stdin\n, stderr, stdout)',
286            'from sys import (stdin\n, stderr, stdout,)',
287            'from sys import stdin as si, stdout as so, stderr as se',
288            'from sys import (stdin as si, stdout as so, stderr as se)',
289            'from sys import (stdin as si, stdout as so, stderr as se,)',
290            ]
291        fail = [
292            'import (os, sys)',
293            'import (os), (sys)',
294            'import ((os), (sys))',
295            'import (sys',
296            'import sys)',
297            'import (os,)',
298            'import os As bar',
299            'import os.path a bar',
300            'from sys import stdin As stdout',
301            'from sys import stdin a stdout',
302            'from (sys) import stdin',
303            'from __future__ import (nested_scopes',
304            'from __future__ import nested_scopes)',
305            'from __future__ import nested_scopes,\ngenerators',
306            'from sys import (stdin',
307            'from sys import stdin)',
308            'from sys import stdin, stdout,\nstderr',
309            'from sys import stdin si',
310            'from sys import stdin,',
311            'from sys import (*)',
312            'from sys import (stdin,, stdout, stderr)',
313            'from sys import (stdin, stdout),',
314            ]
315        for stmt in succeed:
316            compile(stmt, 'tmp', 'exec')
317        for stmt in fail:
318            self.assertRaises(SyntaxError, compile, stmt, 'tmp', 'exec')
319
320    def test_for_distinct_code_objects(self):
321        # SF bug 1048870
322        def f():
323            f1 = lambda x=1: x
324            f2 = lambda x=2: x
325            return f1, f2
326        f1, f2 = f()
327        self.assertNotEqual(id(f1.__code__), id(f2.__code__))
328
329    def test_lambda_doc(self):
330        l = lambda: "foo"
331        self.assertIsNone(l.__doc__)
332
333    def test_encoding(self):
334        code = b'# -*- coding: badencoding -*-\npass\n'
335        self.assertRaises(SyntaxError, compile, code, 'tmp', 'exec')
336        code = '# -*- coding: badencoding -*-\n"\xc2\xa4"\n'
337        compile(code, 'tmp', 'exec')
338        self.assertEqual(eval(code), '\xc2\xa4')
339        code = '"\xc2\xa4"\n'
340        self.assertEqual(eval(code), '\xc2\xa4')
341        code = b'"\xc2\xa4"\n'
342        self.assertEqual(eval(code), '\xa4')
343        code = b'# -*- coding: latin1 -*-\n"\xc2\xa4"\n'
344        self.assertEqual(eval(code), '\xc2\xa4')
345        code = b'# -*- coding: utf-8 -*-\n"\xc2\xa4"\n'
346        self.assertEqual(eval(code), '\xa4')
347        code = b'# -*- coding: iso8859-15 -*-\n"\xc2\xa4"\n'
348        self.assertEqual(eval(code), '\xc2\u20ac')
349        code = '"""\\\n# -*- coding: iso8859-15 -*-\n\xc2\xa4"""\n'
350        self.assertEqual(eval(code), '# -*- coding: iso8859-15 -*-\n\xc2\xa4')
351        code = b'"""\\\n# -*- coding: iso8859-15 -*-\n\xc2\xa4"""\n'
352        self.assertEqual(eval(code), '# -*- coding: iso8859-15 -*-\n\xa4')
353
354    def test_subscripts(self):
355        # SF bug 1448804
356        # Class to make testing subscript results easy
357        class str_map(object):
358            def __init__(self):
359                self.data = {}
360            def __getitem__(self, key):
361                return self.data[str(key)]
362            def __setitem__(self, key, value):
363                self.data[str(key)] = value
364            def __delitem__(self, key):
365                del self.data[str(key)]
366            def __contains__(self, key):
367                return str(key) in self.data
368        d = str_map()
369        # Index
370        d[1] = 1
371        self.assertEqual(d[1], 1)
372        d[1] += 1
373        self.assertEqual(d[1], 2)
374        del d[1]
375        self.assertNotIn(1, d)
376        # Tuple of indices
377        d[1, 1] = 1
378        self.assertEqual(d[1, 1], 1)
379        d[1, 1] += 1
380        self.assertEqual(d[1, 1], 2)
381        del d[1, 1]
382        self.assertNotIn((1, 1), d)
383        # Simple slice
384        d[1:2] = 1
385        self.assertEqual(d[1:2], 1)
386        d[1:2] += 1
387        self.assertEqual(d[1:2], 2)
388        del d[1:2]
389        self.assertNotIn(slice(1, 2), d)
390        # Tuple of simple slices
391        d[1:2, 1:2] = 1
392        self.assertEqual(d[1:2, 1:2], 1)
393        d[1:2, 1:2] += 1
394        self.assertEqual(d[1:2, 1:2], 2)
395        del d[1:2, 1:2]
396        self.assertNotIn((slice(1, 2), slice(1, 2)), d)
397        # Extended slice
398        d[1:2:3] = 1
399        self.assertEqual(d[1:2:3], 1)
400        d[1:2:3] += 1
401        self.assertEqual(d[1:2:3], 2)
402        del d[1:2:3]
403        self.assertNotIn(slice(1, 2, 3), d)
404        # Tuple of extended slices
405        d[1:2:3, 1:2:3] = 1
406        self.assertEqual(d[1:2:3, 1:2:3], 1)
407        d[1:2:3, 1:2:3] += 1
408        self.assertEqual(d[1:2:3, 1:2:3], 2)
409        del d[1:2:3, 1:2:3]
410        self.assertNotIn((slice(1, 2, 3), slice(1, 2, 3)), d)
411        # Ellipsis
412        d[...] = 1
413        self.assertEqual(d[...], 1)
414        d[...] += 1
415        self.assertEqual(d[...], 2)
416        del d[...]
417        self.assertNotIn(Ellipsis, d)
418        # Tuple of Ellipses
419        d[..., ...] = 1
420        self.assertEqual(d[..., ...], 1)
421        d[..., ...] += 1
422        self.assertEqual(d[..., ...], 2)
423        del d[..., ...]
424        self.assertNotIn((Ellipsis, Ellipsis), d)
425
426    def test_annotation_limit(self):
427        # more than 255 annotations, should compile ok
428        s = "def f(%s): pass"
429        s %= ', '.join('a%d:%d' % (i,i) for i in range(300))
430        compile(s, '?', 'exec')
431
432    def test_mangling(self):
433        class A:
434            def f():
435                __mangled = 1
436                __not_mangled__ = 2
437                import __mangled_mod
438                import __package__.module
439
440        self.assertIn("_A__mangled", A.f.__code__.co_varnames)
441        self.assertIn("__not_mangled__", A.f.__code__.co_varnames)
442        self.assertIn("_A__mangled_mod", A.f.__code__.co_varnames)
443        self.assertIn("__package__", A.f.__code__.co_varnames)
444
445    def test_compile_ast(self):
446        fname = __file__
447        if fname.lower().endswith('pyc'):
448            fname = fname[:-1]
449        with open(fname, encoding='utf-8') as f:
450            fcontents = f.read()
451        sample_code = [
452            ['<assign>', 'x = 5'],
453            ['<ifblock>', """if True:\n    pass\n"""],
454            ['<forblock>', """for n in [1, 2, 3]:\n    print(n)\n"""],
455            ['<deffunc>', """def foo():\n    pass\nfoo()\n"""],
456            [fname, fcontents],
457        ]
458
459        for fname, code in sample_code:
460            co1 = compile(code, '%s1' % fname, 'exec')
461            ast = compile(code, '%s2' % fname, 'exec', _ast.PyCF_ONLY_AST)
462            self.assertTrue(type(ast) == _ast.Module)
463            co2 = compile(ast, '%s3' % fname, 'exec')
464            self.assertEqual(co1, co2)
465            # the code object's filename comes from the second compilation step
466            self.assertEqual(co2.co_filename, '%s3' % fname)
467
468        # raise exception when node type doesn't match with compile mode
469        co1 = compile('print(1)', '<string>', 'exec', _ast.PyCF_ONLY_AST)
470        self.assertRaises(TypeError, compile, co1, '<ast>', 'eval')
471
472        # raise exception when node type is no start node
473        self.assertRaises(TypeError, compile, _ast.If(), '<ast>', 'exec')
474
475        # raise exception when node has invalid children
476        ast = _ast.Module()
477        ast.body = [_ast.BoolOp()]
478        self.assertRaises(TypeError, compile, ast, '<ast>', 'exec')
479
480    def test_dict_evaluation_order(self):
481        i = 0
482
483        def f():
484            nonlocal i
485            i += 1
486            return i
487
488        d = {f(): f(), f(): f()}
489        self.assertEqual(d, {1: 2, 3: 4})
490
491    def test_compile_filename(self):
492        for filename in 'file.py', b'file.py':
493            code = compile('pass', filename, 'exec')
494            self.assertEqual(code.co_filename, 'file.py')
495        for filename in bytearray(b'file.py'), memoryview(b'file.py'):
496            with self.assertWarns(DeprecationWarning):
497                code = compile('pass', filename, 'exec')
498            self.assertEqual(code.co_filename, 'file.py')
499        self.assertRaises(TypeError, compile, 'pass', list(b'file.py'), 'exec')
500
501    @support.cpython_only
502    def test_same_filename_used(self):
503        s = """def f(): pass\ndef g(): pass"""
504        c = compile(s, "myfile", "exec")
505        for obj in c.co_consts:
506            if isinstance(obj, types.CodeType):
507                self.assertIs(obj.co_filename, c.co_filename)
508
509    def test_single_statement(self):
510        self.compile_single("1 + 2")
511        self.compile_single("\n1 + 2")
512        self.compile_single("1 + 2\n")
513        self.compile_single("1 + 2\n\n")
514        self.compile_single("1 + 2\t\t\n")
515        self.compile_single("1 + 2\t\t\n        ")
516        self.compile_single("1 + 2 # one plus two")
517        self.compile_single("1; 2")
518        self.compile_single("import sys; sys")
519        self.compile_single("def f():\n   pass")
520        self.compile_single("while False:\n   pass")
521        self.compile_single("if x:\n   f(x)")
522        self.compile_single("if x:\n   f(x)\nelse:\n   g(x)")
523        self.compile_single("class T:\n   pass")
524        self.compile_single("c = '''\na=1\nb=2\nc=3\n'''")
525
526    def test_bad_single_statement(self):
527        self.assertInvalidSingle('1\n2')
528        self.assertInvalidSingle('def f(): pass')
529        self.assertInvalidSingle('a = 13\nb = 187')
530        self.assertInvalidSingle('del x\ndel y')
531        self.assertInvalidSingle('f()\ng()')
532        self.assertInvalidSingle('f()\n# blah\nblah()')
533        self.assertInvalidSingle('f()\nxy # blah\nblah()')
534        self.assertInvalidSingle('x = 5 # comment\nx = 6\n')
535        self.assertInvalidSingle("c = '''\nd=1\n'''\na = 1\n\nb = 2\n")
536
537    def test_particularly_evil_undecodable(self):
538        # Issue 24022
539        src = b'0000\x00\n00000000000\n\x00\n\x9e\n'
540        with tempfile.TemporaryDirectory() as tmpd:
541            fn = os.path.join(tmpd, "bad.py")
542            with open(fn, "wb") as fp:
543                fp.write(src)
544            res = script_helper.run_python_until_end(fn)[0]
545        self.assertIn(b"source code cannot contain null bytes", res.err)
546
547    def test_yet_more_evil_still_undecodable(self):
548        # Issue #25388
549        src = b"#\x00\n#\xfd\n"
550        with tempfile.TemporaryDirectory() as tmpd:
551            fn = os.path.join(tmpd, "bad.py")
552            with open(fn, "wb") as fp:
553                fp.write(src)
554            res = script_helper.run_python_until_end(fn)[0]
555        self.assertIn(b"source code cannot contain null bytes", res.err)
556
557    @support.cpython_only
558    def test_compiler_recursion_limit(self):
559        # Expected limit is sys.getrecursionlimit() * the scaling factor
560        # in symtable.c (currently 3)
561        # We expect to fail *at* that limit, because we use up some of
562        # the stack depth limit in the test suite code
563        # So we check the expected limit and 75% of that
564        # XXX (ncoghlan): duplicating the scaling factor here is a little
565        # ugly. Perhaps it should be exposed somewhere...
566        fail_depth = sys.getrecursionlimit() * 3
567        crash_depth = sys.getrecursionlimit() * 300
568        success_depth = int(fail_depth * 0.75)
569
570        def check_limit(prefix, repeated, mode="single"):
571            expect_ok = prefix + repeated * success_depth
572            compile(expect_ok, '<test>', mode)
573            for depth in (fail_depth, crash_depth):
574                broken = prefix + repeated * depth
575                details = "Compiling ({!r} + {!r} * {})".format(
576                            prefix, repeated, depth)
577                with self.assertRaises(RecursionError, msg=details):
578                    compile(broken, '<test>', mode)
579
580        check_limit("a", "()")
581        check_limit("a", ".b")
582        check_limit("a", "[0]")
583        check_limit("a", "*a")
584        # XXX Crashes in the parser.
585        # check_limit("a", " if a else a")
586        # check_limit("if a: pass", "\nelif a: pass", mode="exec")
587
588    def test_null_terminated(self):
589        # The source code is null-terminated internally, but bytes-like
590        # objects are accepted, which could be not terminated.
591        with self.assertRaisesRegex(SyntaxError, "cannot contain null"):
592            compile("123\x00", "<dummy>", "eval")
593        with self.assertRaisesRegex(SyntaxError, "cannot contain null"):
594            compile(memoryview(b"123\x00"), "<dummy>", "eval")
595        code = compile(memoryview(b"123\x00")[1:-1], "<dummy>", "eval")
596        self.assertEqual(eval(code), 23)
597        code = compile(memoryview(b"1234")[1:-1], "<dummy>", "eval")
598        self.assertEqual(eval(code), 23)
599        code = compile(memoryview(b"$23$")[1:-1], "<dummy>", "eval")
600        self.assertEqual(eval(code), 23)
601
602        # Also test when eval() and exec() do the compilation step
603        self.assertEqual(eval(memoryview(b"1234")[1:-1]), 23)
604        namespace = dict()
605        exec(memoryview(b"ax = 123")[1:-1], namespace)
606        self.assertEqual(namespace['x'], 12)
607
608    def check_constant(self, func, expected):
609        for const in func.__code__.co_consts:
610            if repr(const) == repr(expected):
611                break
612        else:
613            self.fail("unable to find constant %r in %r"
614                      % (expected, func.__code__.co_consts))
615
616    # Merging equal constants is not a strict requirement for the Python
617    # semantics, it's a more an implementation detail.
618    @support.cpython_only
619    def test_merge_constants(self):
620        # Issue #25843: compile() must merge constants which are equal
621        # and have the same type.
622
623        def check_same_constant(const):
624            ns = {}
625            code = "f1, f2 = lambda: %r, lambda: %r" % (const, const)
626            exec(code, ns)
627            f1 = ns['f1']
628            f2 = ns['f2']
629            self.assertIs(f1.__code__.co_consts, f2.__code__.co_consts)
630            self.check_constant(f1, const)
631            self.assertEqual(repr(f1()), repr(const))
632
633        check_same_constant(None)
634        check_same_constant(0)
635        check_same_constant(0.0)
636        check_same_constant(b'abc')
637        check_same_constant('abc')
638
639        # Note: "lambda: ..." emits "LOAD_CONST Ellipsis",
640        # whereas "lambda: Ellipsis" emits "LOAD_GLOBAL Ellipsis"
641        f1, f2 = lambda: ..., lambda: ...
642        self.assertIs(f1.__code__.co_consts, f2.__code__.co_consts)
643        self.check_constant(f1, Ellipsis)
644        self.assertEqual(repr(f1()), repr(Ellipsis))
645
646        # Merge constants in tuple or frozenset
647        f1, f2 = lambda: "not a name", lambda: ("not a name",)
648        f3 = lambda x: x in {("not a name",)}
649        self.assertIs(f1.__code__.co_consts[1],
650                      f2.__code__.co_consts[1][0])
651        self.assertIs(next(iter(f3.__code__.co_consts[1])),
652                      f2.__code__.co_consts[1])
653
654        # {0} is converted to a constant frozenset({0}) by the peephole
655        # optimizer
656        f1, f2 = lambda x: x in {0}, lambda x: x in {0}
657        self.assertIs(f1.__code__.co_consts, f2.__code__.co_consts)
658        self.check_constant(f1, frozenset({0}))
659        self.assertTrue(f1(0))
660
661    # Merging equal co_linetable is not a strict requirement
662    # for the Python semantics, it's a more an implementation detail.
663    @support.cpython_only
664    def test_merge_code_attrs(self):
665        # See https://bugs.python.org/issue42217
666        f1 = lambda x: x.y.z
667        f2 = lambda a: a.b.c
668
669        self.assertIs(f1.__code__.co_linetable, f2.__code__.co_linetable)
670
671    # Stripping unused constants is not a strict requirement for the
672    # Python semantics, it's a more an implementation detail.
673    @support.cpython_only
674    def test_strip_unused_consts(self):
675        # Python 3.10rc1 appended None to co_consts when None is not used
676        # at all. See bpo-45056.
677        def f1():
678            "docstring"
679            return 42
680        self.assertEqual(f1.__code__.co_consts, ("docstring", 42))
681
682    # This is a regression test for a CPython specific peephole optimizer
683    # implementation bug present in a few releases.  It's assertion verifies
684    # that peephole optimization was actually done though that isn't an
685    # indication of the bugs presence or not (crashing is).
686    @support.cpython_only
687    def test_peephole_opt_unreachable_code_array_access_in_bounds(self):
688        """Regression test for issue35193 when run under clang msan."""
689        def unused_code_at_end():
690            return 3
691            raise RuntimeError("unreachable")
692        # The above function definition will trigger the out of bounds
693        # bug in the peephole optimizer as it scans opcodes past the
694        # RETURN_VALUE opcode.  This does not always crash an interpreter.
695        # When you build with the clang memory sanitizer it reliably aborts.
696        self.assertEqual(
697            'RETURN_VALUE',
698            list(dis.get_instructions(unused_code_at_end))[-1].opname)
699
700    def test_dont_merge_constants(self):
701        # Issue #25843: compile() must not merge constants which are equal
702        # but have a different type.
703
704        def check_different_constants(const1, const2):
705            ns = {}
706            exec("f1, f2 = lambda: %r, lambda: %r" % (const1, const2), ns)
707            f1 = ns['f1']
708            f2 = ns['f2']
709            self.assertIsNot(f1.__code__, f2.__code__)
710            self.assertNotEqual(f1.__code__, f2.__code__)
711            self.check_constant(f1, const1)
712            self.check_constant(f2, const2)
713            self.assertEqual(repr(f1()), repr(const1))
714            self.assertEqual(repr(f2()), repr(const2))
715
716        check_different_constants(0, 0.0)
717        check_different_constants(+0.0, -0.0)
718        check_different_constants((0,), (0.0,))
719        check_different_constants('a', b'a')
720        check_different_constants(('a',), (b'a',))
721
722        # check_different_constants() cannot be used because repr(-0j) is
723        # '(-0-0j)', but when '(-0-0j)' is evaluated to 0j: we loose the sign.
724        f1, f2 = lambda: +0.0j, lambda: -0.0j
725        self.assertIsNot(f1.__code__, f2.__code__)
726        self.check_constant(f1, +0.0j)
727        self.check_constant(f2, -0.0j)
728        self.assertEqual(repr(f1()), repr(+0.0j))
729        self.assertEqual(repr(f2()), repr(-0.0j))
730
731        # {0} is converted to a constant frozenset({0}) by the peephole
732        # optimizer
733        f1, f2 = lambda x: x in {0}, lambda x: x in {0.0}
734        self.assertIsNot(f1.__code__, f2.__code__)
735        self.check_constant(f1, frozenset({0}))
736        self.check_constant(f2, frozenset({0.0}))
737        self.assertTrue(f1(0))
738        self.assertTrue(f2(0.0))
739
740    def test_path_like_objects(self):
741        # An implicit test for PyUnicode_FSDecoder().
742        compile("42", FakePath("test_compile_pathlike"), "single")
743
744    def test_stack_overflow(self):
745        # bpo-31113: Stack overflow when compile a long sequence of
746        # complex statements.
747        compile("if a: b\n" * 200000, "<dummy>", "exec")
748
749    # Multiple users rely on the fact that CPython does not generate
750    # bytecode for dead code blocks. See bpo-37500 for more context.
751    @support.cpython_only
752    def test_dead_blocks_do_not_generate_bytecode(self):
753        def unused_block_if():
754            if 0:
755                return 42
756
757        def unused_block_while():
758            while 0:
759                return 42
760
761        def unused_block_if_else():
762            if 1:
763                return None
764            else:
765                return 42
766
767        def unused_block_while_else():
768            while 1:
769                return None
770            else:
771                return 42
772
773        funcs = [unused_block_if, unused_block_while,
774                 unused_block_if_else, unused_block_while_else]
775
776        for func in funcs:
777            opcodes = list(dis.get_instructions(func))
778            self.assertLessEqual(len(opcodes), 4)
779            self.assertEqual('LOAD_CONST', opcodes[-2].opname)
780            self.assertEqual(None, opcodes[-2].argval)
781            self.assertEqual('RETURN_VALUE', opcodes[-1].opname)
782
783    def test_false_while_loop(self):
784        def break_in_while():
785            while False:
786                break
787
788        def continue_in_while():
789            while False:
790                continue
791
792        funcs = [break_in_while, continue_in_while]
793
794        # Check that we did not raise but we also don't generate bytecode
795        for func in funcs:
796            opcodes = list(dis.get_instructions(func))
797            self.assertEqual(3, len(opcodes))
798            self.assertEqual('LOAD_CONST', opcodes[1].opname)
799            self.assertEqual(None, opcodes[1].argval)
800            self.assertEqual('RETURN_VALUE', opcodes[2].opname)
801
802    def test_consts_in_conditionals(self):
803        def and_true(x):
804            return True and x
805
806        def and_false(x):
807            return False and x
808
809        def or_true(x):
810            return True or x
811
812        def or_false(x):
813            return False or x
814
815        funcs = [and_true, and_false, or_true, or_false]
816
817        # Check that condition is removed.
818        for func in funcs:
819            with self.subTest(func=func):
820                opcodes = list(dis.get_instructions(func))
821                self.assertLessEqual(len(opcodes), 3)
822                self.assertIn('LOAD_', opcodes[-2].opname)
823                self.assertEqual('RETURN_VALUE', opcodes[-1].opname)
824
825    def test_imported_load_method(self):
826        sources = [
827            """\
828            import os
829            def foo():
830                return os.uname()
831            """,
832            """\
833            import os as operating_system
834            def foo():
835                return operating_system.uname()
836            """,
837            """\
838            from os import path
839            def foo(x):
840                return path.join(x)
841            """,
842            """\
843            from os import path as os_path
844            def foo(x):
845                return os_path.join(x)
846            """
847        ]
848        for source in sources:
849            namespace = {}
850            exec(textwrap.dedent(source), namespace)
851            func = namespace['foo']
852            with self.subTest(func=func.__name__):
853                opcodes = list(dis.get_instructions(func))
854                instructions = [opcode.opname for opcode in opcodes]
855                self.assertNotIn('LOAD_METHOD', instructions)
856                self.assertIn('LOAD_ATTR', instructions)
857                self.assertIn('PRECALL', instructions)
858
859    def test_lineno_procedure_call(self):
860        def call():
861            (
862                print()
863            )
864        line1 = call.__code__.co_firstlineno + 1
865        assert line1 not in [line for (_, _, line) in call.__code__.co_lines()]
866
867    def test_lineno_after_implicit_return(self):
868        TRUE = True
869        # Don't use constant True or False, as compiler will remove test
870        def if1(x):
871            x()
872            if TRUE:
873                pass
874        def if2(x):
875            x()
876            if TRUE:
877                pass
878            else:
879                pass
880        def if3(x):
881            x()
882            if TRUE:
883                pass
884            else:
885                return None
886        def if4(x):
887            x()
888            if not TRUE:
889                pass
890        funcs = [ if1, if2, if3, if4]
891        lastlines = [ 3, 3, 3, 2]
892        frame = None
893        def save_caller_frame():
894            nonlocal frame
895            frame = sys._getframe(1)
896        for func, lastline in zip(funcs, lastlines, strict=True):
897            with self.subTest(func=func):
898                func(save_caller_frame)
899                self.assertEqual(frame.f_lineno-frame.f_code.co_firstlineno, lastline)
900
901    def test_lineno_after_no_code(self):
902        def no_code1():
903            "doc string"
904
905        def no_code2():
906            a: int
907
908        for func in (no_code1, no_code2):
909            with self.subTest(func=func):
910                code = func.__code__
911                lines = list(code.co_lines())
912                start, end, line = lines[0]
913                self.assertEqual(start, 0)
914                self.assertEqual(line, code.co_firstlineno)
915
916    def get_code_lines(self, code):
917        last_line = -2
918        res = []
919        for _, _, line in code.co_lines():
920            if line is not None and line != last_line:
921                res.append(line - code.co_firstlineno)
922                last_line = line
923        return res
924
925    def test_lineno_attribute(self):
926        def load_attr():
927            return (
928                o.
929                a
930            )
931        load_attr_lines = [ 0, 2, 3, 1 ]
932
933        def load_method():
934            return (
935                o.
936                m(
937                    0
938                )
939            )
940        load_method_lines = [ 0, 2, 3, 4, 3, 1 ]
941
942        def store_attr():
943            (
944                o.
945                a
946            ) = (
947                v
948            )
949        store_attr_lines = [ 0, 5, 2, 3 ]
950
951        def aug_store_attr():
952            (
953                o.
954                a
955            ) += (
956                v
957            )
958        aug_store_attr_lines = [ 0, 2, 3, 5, 1, 3 ]
959
960        funcs = [ load_attr, load_method, store_attr, aug_store_attr]
961        func_lines = [ load_attr_lines, load_method_lines,
962                 store_attr_lines, aug_store_attr_lines]
963
964        for func, lines in zip(funcs, func_lines, strict=True):
965            with self.subTest(func=func):
966                code_lines = self.get_code_lines(func.__code__)
967                self.assertEqual(lines, code_lines)
968
969    def test_line_number_genexp(self):
970
971        def return_genexp():
972            return (1
973                    for
974                    x
975                    in
976                    y)
977        genexp_lines = [0, 2, 0]
978
979        genexp_code = return_genexp.__code__.co_consts[1]
980        code_lines = self.get_code_lines(genexp_code)
981        self.assertEqual(genexp_lines, code_lines)
982
983    def test_line_number_implicit_return_after_async_for(self):
984
985        async def test(aseq):
986            async for i in aseq:
987                body
988
989        expected_lines = [0, 1, 2, 1]
990        code_lines = self.get_code_lines(test.__code__)
991        self.assertEqual(expected_lines, code_lines)
992
993    def test_big_dict_literal(self):
994        # The compiler has a flushing point in "compiler_dict" that calls compiles
995        # a portion of the dictionary literal when the loop that iterates over the items
996        # reaches 0xFFFF elements but the code was not including the boundary element,
997        # dropping the key at position 0xFFFF. See bpo-41531 for more information
998
999        dict_size = 0xFFFF + 1
1000        the_dict = "{" + ",".join(f"{x}:{x}" for x in range(dict_size)) + "}"
1001        self.assertEqual(len(eval(the_dict)), dict_size)
1002
1003    def test_redundant_jump_in_if_else_break(self):
1004        # Check if bytecode containing jumps that simply point to the next line
1005        # is generated around if-else-break style structures. See bpo-42615.
1006
1007        def if_else_break():
1008            val = 1
1009            while True:
1010                if val > 0:
1011                    val -= 1
1012                else:
1013                    break
1014                val = -1
1015
1016        INSTR_SIZE = 2
1017        HANDLED_JUMPS = (
1018            'POP_JUMP_IF_FALSE',
1019            'POP_JUMP_IF_TRUE',
1020            'JUMP_ABSOLUTE',
1021            'JUMP_FORWARD',
1022        )
1023
1024        for line, instr in enumerate(
1025            dis.Bytecode(if_else_break, show_caches=True)
1026        ):
1027            if instr.opname == 'JUMP_FORWARD':
1028                self.assertNotEqual(instr.arg, 0)
1029            elif instr.opname in HANDLED_JUMPS:
1030                self.assertNotEqual(instr.arg, (line + 1)*INSTR_SIZE)
1031
1032    def test_no_wraparound_jump(self):
1033        # See https://bugs.python.org/issue46724
1034
1035        def while_not_chained(a, b, c):
1036            while not (a < b < c):
1037                pass
1038
1039        for instr in dis.Bytecode(while_not_chained):
1040            self.assertNotEqual(instr.opname, "EXTENDED_ARG")
1041
1042    def test_compare_positions(self):
1043        for opname, op in [
1044            ("COMPARE_OP", "<"),
1045            ("COMPARE_OP", "<="),
1046            ("COMPARE_OP", ">"),
1047            ("COMPARE_OP", ">="),
1048            ("CONTAINS_OP", "in"),
1049            ("CONTAINS_OP", "not in"),
1050            ("IS_OP", "is"),
1051            ("IS_OP", "is not"),
1052        ]:
1053            expr = f'a {op} b {op} c'
1054            expected_positions = 2 * [(2, 2, 0, len(expr))]
1055            for source in [
1056                f"\\\n{expr}", f'if \\\n{expr}: x', f"x if \\\n{expr} else y"
1057            ]:
1058                code = compile(source, "<test>", "exec")
1059                actual_positions = [
1060                    instruction.positions
1061                    for instruction in dis.get_instructions(code)
1062                    if instruction.opname == opname
1063                ]
1064                with self.subTest(source):
1065                    self.assertEqual(actual_positions, expected_positions)
1066
1067    def test_apply_static_swaps(self):
1068        def f(x, y):
1069            a, a = x, y
1070            return a
1071        self.assertEqual(f("x", "y"), "y")
1072
1073    def test_apply_static_swaps_2(self):
1074        def f(x, y, z):
1075            a, b, a = x, y, z
1076            return a
1077        self.assertEqual(f("x", "y", "z"), "z")
1078
1079    def test_apply_static_swaps_3(self):
1080        def f(x, y, z):
1081            a, a, b = x, y, z
1082            return a
1083        self.assertEqual(f("x", "y", "z"), "y")
1084
1085
1086@requires_debug_ranges()
1087class TestSourcePositions(unittest.TestCase):
1088    # Ensure that compiled code snippets have correct line and column numbers
1089    # in `co_positions()`.
1090
1091    def check_positions_against_ast(self, snippet):
1092        # Basic check that makes sure each line and column is at least present
1093        # in one of the AST nodes of the source code.
1094        code = compile(snippet, 'test_compile.py', 'exec')
1095        ast_tree = compile(snippet, 'test_compile.py', 'exec', _ast.PyCF_ONLY_AST)
1096        self.assertTrue(type(ast_tree) == _ast.Module)
1097
1098        # Use an AST visitor that notes all the offsets.
1099        lines, end_lines, columns, end_columns = set(), set(), set(), set()
1100        class SourceOffsetVisitor(ast.NodeVisitor):
1101            def generic_visit(self, node):
1102                super().generic_visit(node)
1103                if not isinstance(node, ast.expr) and not isinstance(node, ast.stmt):
1104                    return
1105                lines.add(node.lineno)
1106                end_lines.add(node.end_lineno)
1107                columns.add(node.col_offset)
1108                end_columns.add(node.end_col_offset)
1109
1110        SourceOffsetVisitor().visit(ast_tree)
1111
1112        # Check against the positions in the code object.
1113        for (line, end_line, col, end_col) in code.co_positions():
1114            if line == 0:
1115                continue # This is an artificial module-start line
1116            # If the offset is not None (indicating missing data), ensure that
1117            # it was part of one of the AST nodes.
1118            if line is not None:
1119                self.assertIn(line, lines)
1120            if end_line is not None:
1121                self.assertIn(end_line, end_lines)
1122            if col is not None:
1123                self.assertIn(col, columns)
1124            if end_col is not None:
1125                self.assertIn(end_col, end_columns)
1126
1127        return code, ast_tree
1128
1129    def assertOpcodeSourcePositionIs(self, code, opcode,
1130            line, end_line, column, end_column, occurrence=1):
1131
1132        for instr, position in zip(
1133            dis.Bytecode(code, show_caches=True), code.co_positions(), strict=True
1134        ):
1135            if instr.opname == opcode:
1136                occurrence -= 1
1137                if not occurrence:
1138                    self.assertEqual(position[0], line)
1139                    self.assertEqual(position[1], end_line)
1140                    self.assertEqual(position[2], column)
1141                    self.assertEqual(position[3], end_column)
1142                    return
1143
1144        self.fail(f"Opcode {opcode} not found in code")
1145
1146    def test_simple_assignment(self):
1147        snippet = "x = 1"
1148        self.check_positions_against_ast(snippet)
1149
1150    def test_compiles_to_extended_op_arg(self):
1151        # Make sure we still have valid positions when the code compiles to an
1152        # EXTENDED_ARG by performing a loop which needs a JUMP_ABSOLUTE after
1153        # a bunch of opcodes.
1154        snippet = "x = x\n" * 10_000
1155        snippet += ("while x != 0:\n"
1156                    "  x -= 1\n"
1157                    "while x != 0:\n"
1158                    "  x +=  1\n"
1159                   )
1160
1161        compiled_code, _ = self.check_positions_against_ast(snippet)
1162
1163        self.assertOpcodeSourcePositionIs(compiled_code, 'BINARY_OP',
1164            line=10_000 + 2, end_line=10_000 + 2,
1165            column=2, end_column=8, occurrence=1)
1166        self.assertOpcodeSourcePositionIs(compiled_code, 'BINARY_OP',
1167            line=10_000 + 4, end_line=10_000 + 4,
1168            column=2, end_column=9, occurrence=2)
1169
1170    def test_multiline_expression(self):
1171        snippet = """\
1172f(
1173    1, 2, 3, 4
1174)
1175"""
1176        compiled_code, _ = self.check_positions_against_ast(snippet)
1177        self.assertOpcodeSourcePositionIs(compiled_code, 'CALL',
1178            line=1, end_line=3, column=0, end_column=1)
1179
1180    def test_very_long_line_end_offset(self):
1181        # Make sure we get the correct column offset for offsets
1182        # too large to store in a byte.
1183        long_string = "a" * 1000
1184        snippet = f"g('{long_string}')"
1185
1186        compiled_code, _ = self.check_positions_against_ast(snippet)
1187        self.assertOpcodeSourcePositionIs(compiled_code, 'CALL',
1188            line=1, end_line=1, column=0, end_column=1005)
1189
1190    def test_complex_single_line_expression(self):
1191        snippet = "a - b @ (c * x['key'] + 23)"
1192
1193        compiled_code, _ = self.check_positions_against_ast(snippet)
1194        self.assertOpcodeSourcePositionIs(compiled_code, 'BINARY_SUBSCR',
1195            line=1, end_line=1, column=13, end_column=21)
1196        self.assertOpcodeSourcePositionIs(compiled_code, 'BINARY_OP',
1197            line=1, end_line=1, column=9, end_column=21, occurrence=1)
1198        self.assertOpcodeSourcePositionIs(compiled_code, 'BINARY_OP',
1199            line=1, end_line=1, column=9, end_column=26, occurrence=2)
1200        self.assertOpcodeSourcePositionIs(compiled_code, 'BINARY_OP',
1201            line=1, end_line=1, column=4, end_column=27, occurrence=3)
1202        self.assertOpcodeSourcePositionIs(compiled_code, 'BINARY_OP',
1203            line=1, end_line=1, column=0, end_column=27, occurrence=4)
1204
1205    def test_multiline_assert_rewritten_as_method_call(self):
1206        # GH-94694: Don't crash if pytest rewrites a multiline assert as a
1207        # method call with the same location information:
1208        tree = ast.parse("assert (\n42\n)")
1209        old_node = tree.body[0]
1210        new_node = ast.Expr(
1211            ast.Call(
1212                ast.Attribute(
1213                    ast.Name("spam", ast.Load()),
1214                    "eggs",
1215                    ast.Load(),
1216                ),
1217                [],
1218                [],
1219            )
1220        )
1221        ast.copy_location(new_node, old_node)
1222        ast.fix_missing_locations(new_node)
1223        tree.body[0] = new_node
1224        compile(tree, "<test>", "exec")
1225
1226    def test_push_null_load_global_positions(self):
1227        source_template = """
1228        import abc, dis
1229        import ast as art
1230
1231        abc = None
1232        dix = dis
1233        ast = art
1234
1235        def f():
1236        {}
1237        """
1238        for body in [
1239            "    abc.a()",
1240            "    art.a()",
1241            "    ast.a()",
1242            "    dis.a()",
1243            "    dix.a()",
1244            "    abc[...]()",
1245            "    art()()",
1246            "   (ast or ...)()",
1247            "   [dis]()",
1248            "   (dix + ...)()",
1249        ]:
1250            with self.subTest(body):
1251                namespace = {}
1252                source = textwrap.dedent(source_template.format(body))
1253                exec(source, namespace)
1254                code = namespace["f"].__code__
1255                self.assertOpcodeSourcePositionIs(
1256                    code,
1257                    "LOAD_GLOBAL",
1258                    line=10,
1259                    end_line=10,
1260                    column=4,
1261                    end_column=7,
1262                )
1263
1264    def test_attribute_augassign(self):
1265        source = "(\n lhs  \n   .    \n     rhs      \n       ) += 42"
1266        code = compile(source, "<test>", "exec")
1267        self.assertOpcodeSourcePositionIs(
1268            code, "LOAD_ATTR", line=4, end_line=4, column=5, end_column=8
1269        )
1270        self.assertOpcodeSourcePositionIs(
1271            code, "STORE_ATTR", line=4, end_line=4, column=5, end_column=8
1272        )
1273
1274    def test_attribute_del(self):
1275        source = "del (\n lhs  \n   .    \n     rhs      \n       )"
1276        code = compile(source, "<test>", "exec")
1277        self.assertOpcodeSourcePositionIs(
1278            code, "DELETE_ATTR", line=4, end_line=4, column=5, end_column=8
1279        )
1280
1281    def test_attribute_load(self):
1282        source = "(\n lhs  \n   .    \n     rhs      \n       )"
1283        code = compile(source, "<test>", "exec")
1284        self.assertOpcodeSourcePositionIs(
1285            code, "LOAD_ATTR", line=4, end_line=4, column=5, end_column=8
1286        )
1287
1288    def test_attribute_store(self):
1289        source = "(\n lhs  \n   .    \n     rhs      \n       ) = 42"
1290        code = compile(source, "<test>", "exec")
1291        self.assertOpcodeSourcePositionIs(
1292            code, "STORE_ATTR", line=4, end_line=4, column=5, end_column=8
1293        )
1294
1295    def test_method_call(self):
1296        source = "(\n lhs  \n   .    \n     rhs      \n       )()"
1297        code = compile(source, "<test>", "exec")
1298        self.assertOpcodeSourcePositionIs(
1299            code, "LOAD_METHOD", line=4, end_line=4, column=5, end_column=8
1300        )
1301        self.assertOpcodeSourcePositionIs(
1302            code, "CALL", line=4, end_line=5, column=5, end_column=10
1303        )
1304
1305    def test_weird_attribute_position_regressions(self):
1306        def f():
1307            (bar.
1308        baz)
1309            (bar.
1310        baz(
1311        ))
1312            files().setdefault(
1313                0
1314            ).setdefault(
1315                0
1316            )
1317        for line, end_line, column, end_column in f.__code__.co_positions():
1318            self.assertIsNotNone(line)
1319            self.assertIsNotNone(end_line)
1320            self.assertIsNotNone(column)
1321            self.assertIsNotNone(end_column)
1322            self.assertLessEqual((line, column), (end_line, end_column))
1323
1324    @support.cpython_only
1325    def test_column_offset_deduplication(self):
1326        # GH-95150: Code with different column offsets shouldn't be merged!
1327        for source in [
1328            "lambda: a",
1329            "(a for b in c)",
1330            "[a for b in c]",
1331            "{a for b in c}",
1332            "{a: b for c in d}",
1333        ]:
1334            with self.subTest(source):
1335                code = compile(f"{source}, {source}", "<test>", "eval")
1336                self.assertEqual(len(code.co_consts), 2)
1337                self.assertIsInstance(code.co_consts[0], types.CodeType)
1338                self.assertIsInstance(code.co_consts[1], types.CodeType)
1339                self.assertNotEqual(code.co_consts[0], code.co_consts[1])
1340                self.assertNotEqual(
1341                    list(code.co_consts[0].co_positions()),
1342                    list(code.co_consts[1].co_positions()),
1343                )
1344
1345
1346class TestExpressionStackSize(unittest.TestCase):
1347    # These tests check that the computed stack size for a code object
1348    # stays within reasonable bounds (see issue #21523 for an example
1349    # dysfunction).
1350    N = 100
1351
1352    def check_stack_size(self, code):
1353        # To assert that the alleged stack size is not O(N), we
1354        # check that it is smaller than log(N).
1355        if isinstance(code, str):
1356            code = compile(code, "<foo>", "single")
1357        max_size = math.ceil(math.log(len(code.co_code)))
1358        self.assertLessEqual(code.co_stacksize, max_size)
1359
1360    def test_and(self):
1361        self.check_stack_size("x and " * self.N + "x")
1362
1363    def test_or(self):
1364        self.check_stack_size("x or " * self.N + "x")
1365
1366    def test_and_or(self):
1367        self.check_stack_size("x and x or " * self.N + "x")
1368
1369    def test_chained_comparison(self):
1370        self.check_stack_size("x < " * self.N + "x")
1371
1372    def test_if_else(self):
1373        self.check_stack_size("x if x else " * self.N + "x")
1374
1375    def test_binop(self):
1376        self.check_stack_size("x + " * self.N + "x")
1377
1378    def test_list(self):
1379        self.check_stack_size("[" + "x, " * self.N + "x]")
1380
1381    def test_tuple(self):
1382        self.check_stack_size("(" + "x, " * self.N + "x)")
1383
1384    def test_set(self):
1385        self.check_stack_size("{" + "x, " * self.N + "x}")
1386
1387    def test_dict(self):
1388        self.check_stack_size("{" + "x:x, " * self.N + "x:x}")
1389
1390    def test_func_args(self):
1391        self.check_stack_size("f(" + "x, " * self.N + ")")
1392
1393    def test_func_kwargs(self):
1394        kwargs = (f'a{i}=x' for i in range(self.N))
1395        self.check_stack_size("f(" +  ", ".join(kwargs) + ")")
1396
1397    def test_meth_args(self):
1398        self.check_stack_size("o.m(" + "x, " * self.N + ")")
1399
1400    def test_meth_kwargs(self):
1401        kwargs = (f'a{i}=x' for i in range(self.N))
1402        self.check_stack_size("o.m(" +  ", ".join(kwargs) + ")")
1403
1404    def test_func_and(self):
1405        code = "def f(x):\n"
1406        code += "   x and x\n" * self.N
1407        self.check_stack_size(code)
1408
1409    def test_stack_3050(self):
1410        M = 3050
1411        code = "x," * M + "=t"
1412        # This raised on 3.10.0 to 3.10.5
1413        compile(code, "<foo>", "single")
1414
1415    def test_stack_3050_2(self):
1416        M = 3050
1417        args = ", ".join(f"arg{i}:type{i}" for i in range(M))
1418        code = f"def f({args}):\n  pass"
1419        # This raised on 3.10.0 to 3.10.5
1420        compile(code, "<foo>", "single")
1421
1422
1423class TestStackSizeStability(unittest.TestCase):
1424    # Check that repeating certain snippets doesn't increase the stack size
1425    # beyond what a single snippet requires.
1426
1427    def check_stack_size(self, snippet, async_=False):
1428        def compile_snippet(i):
1429            ns = {}
1430            script = """def func():\n""" + i * snippet
1431            if async_:
1432                script = "async " + script
1433            code = compile(script, "<script>", "exec")
1434            exec(code, ns, ns)
1435            return ns['func'].__code__
1436
1437        sizes = [compile_snippet(i).co_stacksize for i in range(2, 5)]
1438        if len(set(sizes)) != 1:
1439            import dis, io
1440            out = io.StringIO()
1441            dis.dis(compile_snippet(1), file=out)
1442            self.fail("stack sizes diverge with # of consecutive snippets: "
1443                      "%s\n%s\n%s" % (sizes, snippet, out.getvalue()))
1444
1445    def test_if(self):
1446        snippet = """
1447            if x:
1448                a
1449            """
1450        self.check_stack_size(snippet)
1451
1452    def test_if_else(self):
1453        snippet = """
1454            if x:
1455                a
1456            elif y:
1457                b
1458            else:
1459                c
1460            """
1461        self.check_stack_size(snippet)
1462
1463    def test_try_except_bare(self):
1464        snippet = """
1465            try:
1466                a
1467            except:
1468                b
1469            """
1470        self.check_stack_size(snippet)
1471
1472    def test_try_except_qualified(self):
1473        snippet = """
1474            try:
1475                a
1476            except ImportError:
1477                b
1478            except:
1479                c
1480            else:
1481                d
1482            """
1483        self.check_stack_size(snippet)
1484
1485    def test_try_except_as(self):
1486        snippet = """
1487            try:
1488                a
1489            except ImportError as e:
1490                b
1491            except:
1492                c
1493            else:
1494                d
1495            """
1496        self.check_stack_size(snippet)
1497
1498    def test_try_except_star_qualified(self):
1499        snippet = """
1500            try:
1501                a
1502            except* ImportError:
1503                b
1504            else:
1505                c
1506            """
1507        self.check_stack_size(snippet)
1508
1509    def test_try_except_star_as(self):
1510        snippet = """
1511            try:
1512                a
1513            except* ImportError as e:
1514                b
1515            else:
1516                c
1517            """
1518        self.check_stack_size(snippet)
1519
1520    def test_try_except_star_finally(self):
1521        snippet = """
1522                try:
1523                    a
1524                except* A:
1525                    b
1526                finally:
1527                    c
1528            """
1529        self.check_stack_size(snippet)
1530
1531    def test_try_finally(self):
1532        snippet = """
1533                try:
1534                    a
1535                finally:
1536                    b
1537            """
1538        self.check_stack_size(snippet)
1539
1540    def test_with(self):
1541        snippet = """
1542            with x as y:
1543                a
1544            """
1545        self.check_stack_size(snippet)
1546
1547    def test_while_else(self):
1548        snippet = """
1549            while x:
1550                a
1551            else:
1552                b
1553            """
1554        self.check_stack_size(snippet)
1555
1556    def test_for(self):
1557        snippet = """
1558            for x in y:
1559                a
1560            """
1561        self.check_stack_size(snippet)
1562
1563    def test_for_else(self):
1564        snippet = """
1565            for x in y:
1566                a
1567            else:
1568                b
1569            """
1570        self.check_stack_size(snippet)
1571
1572    def test_for_break_continue(self):
1573        snippet = """
1574            for x in y:
1575                if z:
1576                    break
1577                elif u:
1578                    continue
1579                else:
1580                    a
1581            else:
1582                b
1583            """
1584        self.check_stack_size(snippet)
1585
1586    def test_for_break_continue_inside_try_finally_block(self):
1587        snippet = """
1588            for x in y:
1589                try:
1590                    if z:
1591                        break
1592                    elif u:
1593                        continue
1594                    else:
1595                        a
1596                finally:
1597                    f
1598            else:
1599                b
1600            """
1601        self.check_stack_size(snippet)
1602
1603    def test_for_break_continue_inside_finally_block(self):
1604        snippet = """
1605            for x in y:
1606                try:
1607                    t
1608                finally:
1609                    if z:
1610                        break
1611                    elif u:
1612                        continue
1613                    else:
1614                        a
1615            else:
1616                b
1617            """
1618        self.check_stack_size(snippet)
1619
1620    def test_for_break_continue_inside_except_block(self):
1621        snippet = """
1622            for x in y:
1623                try:
1624                    t
1625                except:
1626                    if z:
1627                        break
1628                    elif u:
1629                        continue
1630                    else:
1631                        a
1632            else:
1633                b
1634            """
1635        self.check_stack_size(snippet)
1636
1637    def test_for_break_continue_inside_with_block(self):
1638        snippet = """
1639            for x in y:
1640                with c:
1641                    if z:
1642                        break
1643                    elif u:
1644                        continue
1645                    else:
1646                        a
1647            else:
1648                b
1649            """
1650        self.check_stack_size(snippet)
1651
1652    def test_return_inside_try_finally_block(self):
1653        snippet = """
1654            try:
1655                if z:
1656                    return
1657                else:
1658                    a
1659            finally:
1660                f
1661            """
1662        self.check_stack_size(snippet)
1663
1664    def test_return_inside_finally_block(self):
1665        snippet = """
1666            try:
1667                t
1668            finally:
1669                if z:
1670                    return
1671                else:
1672                    a
1673            """
1674        self.check_stack_size(snippet)
1675
1676    def test_return_inside_except_block(self):
1677        snippet = """
1678            try:
1679                t
1680            except:
1681                if z:
1682                    return
1683                else:
1684                    a
1685            """
1686        self.check_stack_size(snippet)
1687
1688    def test_return_inside_with_block(self):
1689        snippet = """
1690            with c:
1691                if z:
1692                    return
1693                else:
1694                    a
1695            """
1696        self.check_stack_size(snippet)
1697
1698    def test_async_with(self):
1699        snippet = """
1700            async with x as y:
1701                a
1702            """
1703        self.check_stack_size(snippet, async_=True)
1704
1705    def test_async_for(self):
1706        snippet = """
1707            async for x in y:
1708                a
1709            """
1710        self.check_stack_size(snippet, async_=True)
1711
1712    def test_async_for_else(self):
1713        snippet = """
1714            async for x in y:
1715                a
1716            else:
1717                b
1718            """
1719        self.check_stack_size(snippet, async_=True)
1720
1721    def test_for_break_continue_inside_async_with_block(self):
1722        snippet = """
1723            for x in y:
1724                async with c:
1725                    if z:
1726                        break
1727                    elif u:
1728                        continue
1729                    else:
1730                        a
1731            else:
1732                b
1733            """
1734        self.check_stack_size(snippet, async_=True)
1735
1736    def test_return_inside_async_with_block(self):
1737        snippet = """
1738            async with c:
1739                if z:
1740                    return
1741                else:
1742                    a
1743            """
1744        self.check_stack_size(snippet, async_=True)
1745
1746
1747if __name__ == "__main__":
1748    unittest.main()
1749