1import math
2import os
3import unittest
4import sys
5import _ast
6import tempfile
7import types
8from test import support
9from test.support import script_helper
10
11class TestSpecifics(unittest.TestCase):
12
13    def compile_single(self, source):
14        compile(source, "<single>", "single")
15
16    def assertInvalidSingle(self, source):
17        self.assertRaises(SyntaxError, self.compile_single, source)
18
19    def test_no_ending_newline(self):
20        compile("hi", "<test>", "exec")
21        compile("hi\r", "<test>", "exec")
22
23    def test_empty(self):
24        compile("", "<test>", "exec")
25
26    def test_other_newlines(self):
27        compile("\r\n", "<test>", "exec")
28        compile("\r", "<test>", "exec")
29        compile("hi\r\nstuff\r\ndef f():\n    pass\r", "<test>", "exec")
30        compile("this_is\rreally_old_mac\rdef f():\n    pass", "<test>", "exec")
31
32    def test_debug_assignment(self):
33        # catch assignments to __debug__
34        self.assertRaises(SyntaxError, compile, '__debug__ = 1', '?', 'single')
35        import builtins
36        prev = builtins.__debug__
37        setattr(builtins, '__debug__', 'sure')
38        setattr(builtins, '__debug__', prev)
39
40    def test_argument_handling(self):
41        # detect duplicate positional and keyword arguments
42        self.assertRaises(SyntaxError, eval, 'lambda a,a:0')
43        self.assertRaises(SyntaxError, eval, 'lambda a,a=1:0')
44        self.assertRaises(SyntaxError, eval, 'lambda a=1,a=1:0')
45        self.assertRaises(SyntaxError, exec, 'def f(a, a): pass')
46        self.assertRaises(SyntaxError, exec, 'def f(a = 0, a = 1): pass')
47        self.assertRaises(SyntaxError, exec, 'def f(a): global a; a = 1')
48
49    def test_syntax_error(self):
50        self.assertRaises(SyntaxError, compile, "1+*3", "filename", "exec")
51
52    def test_none_keyword_arg(self):
53        self.assertRaises(SyntaxError, compile, "f(None=1)", "<string>", "exec")
54
55    def test_duplicate_global_local(self):
56        self.assertRaises(SyntaxError, exec, 'def f(a): global a; a = 1')
57
58    def test_exec_with_general_mapping_for_locals(self):
59
60        class M:
61            "Test mapping interface versus possible calls from eval()."
62            def __getitem__(self, key):
63                if key == 'a':
64                    return 12
65                raise KeyError
66            def __setitem__(self, key, value):
67                self.results = (key, value)
68            def keys(self):
69                return list('xyz')
70
71        m = M()
72        g = globals()
73        exec('z = a', g, m)
74        self.assertEqual(m.results, ('z', 12))
75        try:
76            exec('z = b', g, m)
77        except NameError:
78            pass
79        else:
80            self.fail('Did not detect a KeyError')
81        exec('z = dir()', g, m)
82        self.assertEqual(m.results, ('z', list('xyz')))
83        exec('z = globals()', g, m)
84        self.assertEqual(m.results, ('z', g))
85        exec('z = locals()', g, m)
86        self.assertEqual(m.results, ('z', m))
87        self.assertRaises(TypeError, exec, 'z = b', m)
88
89        class A:
90            "Non-mapping"
91            pass
92        m = A()
93        self.assertRaises(TypeError, exec, 'z = a', g, m)
94
95        # Verify that dict subclasses work as well
96        class D(dict):
97            def __getitem__(self, key):
98                if key == 'a':
99                    return 12
100                return dict.__getitem__(self, key)
101        d = D()
102        exec('z = a', g, d)
103        self.assertEqual(d['z'], 12)
104
105    def test_extended_arg(self):
106        longexpr = 'x = x or ' + '-x' * 2500
107        g = {}
108        code = '''
109def f(x):
110    %s
111    %s
112    %s
113    %s
114    %s
115    %s
116    %s
117    %s
118    %s
119    %s
120    # the expressions above have no effect, x == argument
121    while x:
122        x -= 1
123        # EXTENDED_ARG/JUMP_ABSOLUTE here
124    return x
125''' % ((longexpr,)*10)
126        exec(code, g)
127        self.assertEqual(g['f'](5), 0)
128
129    def test_argument_order(self):
130        self.assertRaises(SyntaxError, exec, 'def f(a=1, b): pass')
131
132    def test_float_literals(self):
133        # testing bad float literals
134        self.assertRaises(SyntaxError, eval, "2e")
135        self.assertRaises(SyntaxError, eval, "2.0e+")
136        self.assertRaises(SyntaxError, eval, "1e-")
137        self.assertRaises(SyntaxError, eval, "3-4e/21")
138
139    def test_indentation(self):
140        # testing compile() of indented block w/o trailing newline"
141        s = """
142if 1:
143    if 2:
144        pass"""
145        compile(s, "<string>", "exec")
146
147    # This test is probably specific to CPython and may not generalize
148    # to other implementations.  We are trying to ensure that when
149    # the first line of code starts after 256, correct line numbers
150    # in tracebacks are still produced.
151    def test_leading_newlines(self):
152        s256 = "".join(["\n"] * 256 + ["spam"])
153        co = compile(s256, 'fn', 'exec')
154        self.assertEqual(co.co_firstlineno, 257)
155        self.assertEqual(co.co_lnotab, bytes())
156
157    def test_literals_with_leading_zeroes(self):
158        for arg in ["077787", "0xj", "0x.", "0e",  "090000000000000",
159                    "080000000000000", "000000000000009", "000000000000008",
160                    "0b42", "0BADCAFE", "0o123456789", "0b1.1", "0o4.2",
161                    "0b101j2", "0o153j2", "0b100e1", "0o777e1", "0777",
162                    "000777", "000000000000007"]:
163            self.assertRaises(SyntaxError, eval, arg)
164
165        self.assertEqual(eval("0xff"), 255)
166        self.assertEqual(eval("0777."), 777)
167        self.assertEqual(eval("0777.0"), 777)
168        self.assertEqual(eval("000000000000000000000000000000000000000000000000000777e0"), 777)
169        self.assertEqual(eval("0777e1"), 7770)
170        self.assertEqual(eval("0e0"), 0)
171        self.assertEqual(eval("0000e-012"), 0)
172        self.assertEqual(eval("09.5"), 9.5)
173        self.assertEqual(eval("0777j"), 777j)
174        self.assertEqual(eval("000"), 0)
175        self.assertEqual(eval("00j"), 0j)
176        self.assertEqual(eval("00.0"), 0)
177        self.assertEqual(eval("0e3"), 0)
178        self.assertEqual(eval("090000000000000."), 90000000000000.)
179        self.assertEqual(eval("090000000000000.0000000000000000000000"), 90000000000000.)
180        self.assertEqual(eval("090000000000000e0"), 90000000000000.)
181        self.assertEqual(eval("090000000000000e-0"), 90000000000000.)
182        self.assertEqual(eval("090000000000000j"), 90000000000000j)
183        self.assertEqual(eval("000000000000008."), 8.)
184        self.assertEqual(eval("000000000000009."), 9.)
185        self.assertEqual(eval("0b101010"), 42)
186        self.assertEqual(eval("-0b000000000010"), -2)
187        self.assertEqual(eval("0o777"), 511)
188        self.assertEqual(eval("-0o0000010"), -8)
189
190    def test_unary_minus(self):
191        # Verify treatment of unary minus on negative numbers SF bug #660455
192        if sys.maxsize == 2147483647:
193            # 32-bit machine
194            all_one_bits = '0xffffffff'
195            self.assertEqual(eval(all_one_bits), 4294967295)
196            self.assertEqual(eval("-" + all_one_bits), -4294967295)
197        elif sys.maxsize == 9223372036854775807:
198            # 64-bit machine
199            all_one_bits = '0xffffffffffffffff'
200            self.assertEqual(eval(all_one_bits), 18446744073709551615)
201            self.assertEqual(eval("-" + all_one_bits), -18446744073709551615)
202        else:
203            self.fail("How many bits *does* this machine have???")
204        # Verify treatment of constant folding on -(sys.maxsize+1)
205        # i.e. -2147483648 on 32 bit platforms.  Should return int.
206        self.assertIsInstance(eval("%s" % (-sys.maxsize - 1)), int)
207        self.assertIsInstance(eval("%s" % (-sys.maxsize - 2)), int)
208
209    if sys.maxsize == 9223372036854775807:
210        def test_32_63_bit_values(self):
211            a = +4294967296  # 1 << 32
212            b = -4294967296  # 1 << 32
213            c = +281474976710656  # 1 << 48
214            d = -281474976710656  # 1 << 48
215            e = +4611686018427387904  # 1 << 62
216            f = -4611686018427387904  # 1 << 62
217            g = +9223372036854775807  # 1 << 63 - 1
218            h = -9223372036854775807  # 1 << 63 - 1
219
220            for variable in self.test_32_63_bit_values.__code__.co_consts:
221                if variable is not None:
222                    self.assertIsInstance(variable, int)
223
224    def test_sequence_unpacking_error(self):
225        # Verify sequence packing/unpacking with "or".  SF bug #757818
226        i,j = (1, -1) or (-1, 1)
227        self.assertEqual(i, 1)
228        self.assertEqual(j, -1)
229
230    def test_none_assignment(self):
231        stmts = [
232            'None = 0',
233            'None += 0',
234            '__builtins__.None = 0',
235            'def None(): pass',
236            'class None: pass',
237            '(a, None) = 0, 0',
238            'for None in range(10): pass',
239            'def f(None): pass',
240            'import None',
241            'import x as None',
242            'from x import None',
243            'from x import y as None'
244        ]
245        for stmt in stmts:
246            stmt += "\n"
247            self.assertRaises(SyntaxError, compile, stmt, 'tmp', 'single')
248            self.assertRaises(SyntaxError, compile, stmt, 'tmp', 'exec')
249
250    def test_import(self):
251        succeed = [
252            'import sys',
253            'import os, sys',
254            'import os as bar',
255            'import os.path as bar',
256            'from __future__ import nested_scopes, generators',
257            'from __future__ import (nested_scopes,\ngenerators)',
258            'from __future__ import (nested_scopes,\ngenerators,)',
259            'from sys import stdin, stderr, stdout',
260            'from sys import (stdin, stderr,\nstdout)',
261            'from sys import (stdin, stderr,\nstdout,)',
262            'from sys import (stdin\n, stderr, stdout)',
263            'from sys import (stdin\n, stderr, stdout,)',
264            'from sys import stdin as si, stdout as so, stderr as se',
265            'from sys import (stdin as si, stdout as so, stderr as se)',
266            'from sys import (stdin as si, stdout as so, stderr as se,)',
267            ]
268        fail = [
269            'import (os, sys)',
270            'import (os), (sys)',
271            'import ((os), (sys))',
272            'import (sys',
273            'import sys)',
274            'import (os,)',
275            'import os As bar',
276            'import os.path a bar',
277            'from sys import stdin As stdout',
278            'from sys import stdin a stdout',
279            'from (sys) import stdin',
280            'from __future__ import (nested_scopes',
281            'from __future__ import nested_scopes)',
282            'from __future__ import nested_scopes,\ngenerators',
283            'from sys import (stdin',
284            'from sys import stdin)',
285            'from sys import stdin, stdout,\nstderr',
286            'from sys import stdin si',
287            'from sys import stdin,'
288            'from sys import (*)',
289            'from sys import (stdin,, stdout, stderr)',
290            'from sys import (stdin, stdout),',
291            ]
292        for stmt in succeed:
293            compile(stmt, 'tmp', 'exec')
294        for stmt in fail:
295            self.assertRaises(SyntaxError, compile, stmt, 'tmp', 'exec')
296
297    def test_for_distinct_code_objects(self):
298        # SF bug 1048870
299        def f():
300            f1 = lambda x=1: x
301            f2 = lambda x=2: x
302            return f1, f2
303        f1, f2 = f()
304        self.assertNotEqual(id(f1.__code__), id(f2.__code__))
305
306    def test_lambda_doc(self):
307        l = lambda: "foo"
308        self.assertIsNone(l.__doc__)
309
310    def test_encoding(self):
311        code = b'# -*- coding: badencoding -*-\npass\n'
312        self.assertRaises(SyntaxError, compile, code, 'tmp', 'exec')
313        code = '# -*- coding: badencoding -*-\n"\xc2\xa4"\n'
314        compile(code, 'tmp', 'exec')
315        self.assertEqual(eval(code), '\xc2\xa4')
316        code = '"\xc2\xa4"\n'
317        self.assertEqual(eval(code), '\xc2\xa4')
318        code = b'"\xc2\xa4"\n'
319        self.assertEqual(eval(code), '\xa4')
320        code = b'# -*- coding: latin1 -*-\n"\xc2\xa4"\n'
321        self.assertEqual(eval(code), '\xc2\xa4')
322        code = b'# -*- coding: utf-8 -*-\n"\xc2\xa4"\n'
323        self.assertEqual(eval(code), '\xa4')
324        code = b'# -*- coding: iso8859-15 -*-\n"\xc2\xa4"\n'
325        self.assertEqual(eval(code), '\xc2\u20ac')
326        code = '"""\\\n# -*- coding: iso8859-15 -*-\n\xc2\xa4"""\n'
327        self.assertEqual(eval(code), '# -*- coding: iso8859-15 -*-\n\xc2\xa4')
328        code = b'"""\\\n# -*- coding: iso8859-15 -*-\n\xc2\xa4"""\n'
329        self.assertEqual(eval(code), '# -*- coding: iso8859-15 -*-\n\xa4')
330
331    def test_subscripts(self):
332        # SF bug 1448804
333        # Class to make testing subscript results easy
334        class str_map(object):
335            def __init__(self):
336                self.data = {}
337            def __getitem__(self, key):
338                return self.data[str(key)]
339            def __setitem__(self, key, value):
340                self.data[str(key)] = value
341            def __delitem__(self, key):
342                del self.data[str(key)]
343            def __contains__(self, key):
344                return str(key) in self.data
345        d = str_map()
346        # Index
347        d[1] = 1
348        self.assertEqual(d[1], 1)
349        d[1] += 1
350        self.assertEqual(d[1], 2)
351        del d[1]
352        self.assertNotIn(1, d)
353        # Tuple of indices
354        d[1, 1] = 1
355        self.assertEqual(d[1, 1], 1)
356        d[1, 1] += 1
357        self.assertEqual(d[1, 1], 2)
358        del d[1, 1]
359        self.assertNotIn((1, 1), d)
360        # Simple slice
361        d[1:2] = 1
362        self.assertEqual(d[1:2], 1)
363        d[1:2] += 1
364        self.assertEqual(d[1:2], 2)
365        del d[1:2]
366        self.assertNotIn(slice(1, 2), d)
367        # Tuple of simple slices
368        d[1:2, 1:2] = 1
369        self.assertEqual(d[1:2, 1:2], 1)
370        d[1:2, 1:2] += 1
371        self.assertEqual(d[1:2, 1:2], 2)
372        del d[1:2, 1:2]
373        self.assertNotIn((slice(1, 2), slice(1, 2)), d)
374        # Extended slice
375        d[1:2:3] = 1
376        self.assertEqual(d[1:2:3], 1)
377        d[1:2:3] += 1
378        self.assertEqual(d[1:2:3], 2)
379        del d[1:2:3]
380        self.assertNotIn(slice(1, 2, 3), d)
381        # Tuple of extended slices
382        d[1:2:3, 1:2:3] = 1
383        self.assertEqual(d[1:2:3, 1:2:3], 1)
384        d[1:2:3, 1:2:3] += 1
385        self.assertEqual(d[1:2:3, 1:2:3], 2)
386        del d[1:2:3, 1:2:3]
387        self.assertNotIn((slice(1, 2, 3), slice(1, 2, 3)), d)
388        # Ellipsis
389        d[...] = 1
390        self.assertEqual(d[...], 1)
391        d[...] += 1
392        self.assertEqual(d[...], 2)
393        del d[...]
394        self.assertNotIn(Ellipsis, d)
395        # Tuple of Ellipses
396        d[..., ...] = 1
397        self.assertEqual(d[..., ...], 1)
398        d[..., ...] += 1
399        self.assertEqual(d[..., ...], 2)
400        del d[..., ...]
401        self.assertNotIn((Ellipsis, Ellipsis), d)
402
403    def test_annotation_limit(self):
404        # 16 bits are available for # of annotations, but only 8 bits are
405        # available for the parameter count, hence 255
406        # is the max. Ensure the result of too many annotations is a
407        # SyntaxError.
408        s = "def f(%s): pass"
409        s %= ', '.join('a%d:%d' % (i,i) for i in range(256))
410        self.assertRaises(SyntaxError, compile, s, '?', 'exec')
411        # Test that the max # of annotations compiles.
412        s = "def f(%s): pass"
413        s %= ', '.join('a%d:%d' % (i,i) for i in range(255))
414        compile(s, '?', 'exec')
415
416    def test_mangling(self):
417        class A:
418            def f():
419                __mangled = 1
420                __not_mangled__ = 2
421                import __mangled_mod
422                import __package__.module
423
424        self.assertIn("_A__mangled", A.f.__code__.co_varnames)
425        self.assertIn("__not_mangled__", A.f.__code__.co_varnames)
426        self.assertIn("_A__mangled_mod", A.f.__code__.co_varnames)
427        self.assertIn("__package__", A.f.__code__.co_varnames)
428
429    def test_compile_ast(self):
430        fname = __file__
431        if fname.lower().endswith('pyc'):
432            fname = fname[:-1]
433        with open(fname, 'r') as f:
434            fcontents = f.read()
435        sample_code = [
436            ['<assign>', 'x = 5'],
437            ['<ifblock>', """if True:\n    pass\n"""],
438            ['<forblock>', """for n in [1, 2, 3]:\n    print(n)\n"""],
439            ['<deffunc>', """def foo():\n    pass\nfoo()\n"""],
440            [fname, fcontents],
441        ]
442
443        for fname, code in sample_code:
444            co1 = compile(code, '%s1' % fname, 'exec')
445            ast = compile(code, '%s2' % fname, 'exec', _ast.PyCF_ONLY_AST)
446            self.assertTrue(type(ast) == _ast.Module)
447            co2 = compile(ast, '%s3' % fname, 'exec')
448            self.assertEqual(co1, co2)
449            # the code object's filename comes from the second compilation step
450            self.assertEqual(co2.co_filename, '%s3' % fname)
451
452        # raise exception when node type doesn't match with compile mode
453        co1 = compile('print(1)', '<string>', 'exec', _ast.PyCF_ONLY_AST)
454        self.assertRaises(TypeError, compile, co1, '<ast>', 'eval')
455
456        # raise exception when node type is no start node
457        self.assertRaises(TypeError, compile, _ast.If(), '<ast>', 'exec')
458
459        # raise exception when node has invalid children
460        ast = _ast.Module()
461        ast.body = [_ast.BoolOp()]
462        self.assertRaises(TypeError, compile, ast, '<ast>', 'exec')
463
464    def test_dict_evaluation_order(self):
465        i = 0
466
467        def f():
468            nonlocal i
469            i += 1
470            return i
471
472        d = {f(): f(), f(): f()}
473        self.assertEqual(d, {1: 2, 3: 4})
474
475    def test_compile_filename(self):
476        for filename in 'file.py', b'file.py':
477            code = compile('pass', filename, 'exec')
478            self.assertEqual(code.co_filename, 'file.py')
479        for filename in bytearray(b'file.py'), memoryview(b'file.py'):
480            with self.assertWarns(DeprecationWarning):
481                code = compile('pass', filename, 'exec')
482            self.assertEqual(code.co_filename, 'file.py')
483        self.assertRaises(TypeError, compile, 'pass', list(b'file.py'), 'exec')
484
485    @support.cpython_only
486    def test_same_filename_used(self):
487        s = """def f(): pass\ndef g(): pass"""
488        c = compile(s, "myfile", "exec")
489        for obj in c.co_consts:
490            if isinstance(obj, types.CodeType):
491                self.assertIs(obj.co_filename, c.co_filename)
492
493    def test_single_statement(self):
494        self.compile_single("1 + 2")
495        self.compile_single("\n1 + 2")
496        self.compile_single("1 + 2\n")
497        self.compile_single("1 + 2\n\n")
498        self.compile_single("1 + 2\t\t\n")
499        self.compile_single("1 + 2\t\t\n        ")
500        self.compile_single("1 + 2 # one plus two")
501        self.compile_single("1; 2")
502        self.compile_single("import sys; sys")
503        self.compile_single("def f():\n   pass")
504        self.compile_single("while False:\n   pass")
505        self.compile_single("if x:\n   f(x)")
506        self.compile_single("if x:\n   f(x)\nelse:\n   g(x)")
507        self.compile_single("class T:\n   pass")
508
509    def test_bad_single_statement(self):
510        self.assertInvalidSingle('1\n2')
511        self.assertInvalidSingle('def f(): pass')
512        self.assertInvalidSingle('a = 13\nb = 187')
513        self.assertInvalidSingle('del x\ndel y')
514        self.assertInvalidSingle('f()\ng()')
515        self.assertInvalidSingle('f()\n# blah\nblah()')
516        self.assertInvalidSingle('f()\nxy # blah\nblah()')
517        self.assertInvalidSingle('x = 5 # comment\nx = 6\n')
518
519    def test_particularly_evil_undecodable(self):
520        # Issue 24022
521        src = b'0000\x00\n00000000000\n\x00\n\x9e\n'
522        with tempfile.TemporaryDirectory() as tmpd:
523            fn = os.path.join(tmpd, "bad.py")
524            with open(fn, "wb") as fp:
525                fp.write(src)
526            res = script_helper.run_python_until_end(fn)[0]
527        self.assertIn(b"Non-UTF-8", res.err)
528
529    def test_yet_more_evil_still_undecodable(self):
530        # Issue #25388
531        src = b"#\x00\n#\xfd\n"
532        with tempfile.TemporaryDirectory() as tmpd:
533            fn = os.path.join(tmpd, "bad.py")
534            with open(fn, "wb") as fp:
535                fp.write(src)
536            res = script_helper.run_python_until_end(fn)[0]
537        self.assertIn(b"Non-UTF-8", res.err)
538
539    @support.cpython_only
540    def test_compiler_recursion_limit(self):
541        # Expected limit is sys.getrecursionlimit() * the scaling factor
542        # in symtable.c (currently 3)
543        # We expect to fail *at* that limit, because we use up some of
544        # the stack depth limit in the test suite code
545        # So we check the expected limit and 75% of that
546        # XXX (ncoghlan): duplicating the scaling factor here is a little
547        # ugly. Perhaps it should be exposed somewhere...
548        fail_depth = sys.getrecursionlimit() * 3
549        success_depth = int(fail_depth * 0.75)
550
551        def check_limit(prefix, repeated):
552            expect_ok = prefix + repeated * success_depth
553            self.compile_single(expect_ok)
554            broken = prefix + repeated * fail_depth
555            details = "Compiling ({!r} + {!r} * {})".format(
556                         prefix, repeated, fail_depth)
557            with self.assertRaises(RecursionError, msg=details):
558                self.compile_single(broken)
559
560        check_limit("a", "()")
561        check_limit("a", ".b")
562        check_limit("a", "[0]")
563        check_limit("a", "*a")
564
565    def test_null_terminated(self):
566        # The source code is null-terminated internally, but bytes-like
567        # objects are accepted, which could be not terminated.
568        with self.assertRaisesRegex(ValueError, "cannot contain null"):
569            compile("123\x00", "<dummy>", "eval")
570        with self.assertRaisesRegex(ValueError, "cannot contain null"):
571            compile(memoryview(b"123\x00"), "<dummy>", "eval")
572        code = compile(memoryview(b"123\x00")[1:-1], "<dummy>", "eval")
573        self.assertEqual(eval(code), 23)
574        code = compile(memoryview(b"1234")[1:-1], "<dummy>", "eval")
575        self.assertEqual(eval(code), 23)
576        code = compile(memoryview(b"$23$")[1:-1], "<dummy>", "eval")
577        self.assertEqual(eval(code), 23)
578
579        # Also test when eval() and exec() do the compilation step
580        self.assertEqual(eval(memoryview(b"1234")[1:-1]), 23)
581        namespace = dict()
582        exec(memoryview(b"ax = 123")[1:-1], namespace)
583        self.assertEqual(namespace['x'], 12)
584
585    def check_constant(self, func, expected):
586        for const in func.__code__.co_consts:
587            if repr(const) == repr(expected):
588                break
589        else:
590            self.fail("unable to find constant %r in %r"
591                      % (expected, func.__code__.co_consts))
592
593    # Merging equal constants is not a strict requirement for the Python
594    # semantics, it's a more an implementation detail.
595    @support.cpython_only
596    def test_merge_constants(self):
597        # Issue #25843: compile() must merge constants which are equal
598        # and have the same type.
599
600        def check_same_constant(const):
601            ns = {}
602            code = "f1, f2 = lambda: %r, lambda: %r" % (const, const)
603            exec(code, ns)
604            f1 = ns['f1']
605            f2 = ns['f2']
606            self.assertIs(f1.__code__, f2.__code__)
607            self.check_constant(f1, const)
608            self.assertEqual(repr(f1()), repr(const))
609
610        check_same_constant(None)
611        check_same_constant(0)
612        check_same_constant(0.0)
613        check_same_constant(b'abc')
614        check_same_constant('abc')
615
616        # Note: "lambda: ..." emits "LOAD_CONST Ellipsis",
617        # whereas "lambda: Ellipsis" emits "LOAD_GLOBAL Ellipsis"
618        f1, f2 = lambda: ..., lambda: ...
619        self.assertIs(f1.__code__, f2.__code__)
620        self.check_constant(f1, Ellipsis)
621        self.assertEqual(repr(f1()), repr(Ellipsis))
622
623        # {0} is converted to a constant frozenset({0}) by the peephole
624        # optimizer
625        f1, f2 = lambda x: x in {0}, lambda x: x in {0}
626        self.assertIs(f1.__code__, f2.__code__)
627        self.check_constant(f1, frozenset({0}))
628        self.assertTrue(f1(0))
629
630    def test_dont_merge_constants(self):
631        # Issue #25843: compile() must not merge constants which are equal
632        # but have a different type.
633
634        def check_different_constants(const1, const2):
635            ns = {}
636            exec("f1, f2 = lambda: %r, lambda: %r" % (const1, const2), ns)
637            f1 = ns['f1']
638            f2 = ns['f2']
639            self.assertIsNot(f1.__code__, f2.__code__)
640            self.assertNotEqual(f1.__code__, f2.__code__)
641            self.check_constant(f1, const1)
642            self.check_constant(f2, const2)
643            self.assertEqual(repr(f1()), repr(const1))
644            self.assertEqual(repr(f2()), repr(const2))
645
646        check_different_constants(0, 0.0)
647        check_different_constants(+0.0, -0.0)
648        check_different_constants((0,), (0.0,))
649        check_different_constants('a', b'a')
650        check_different_constants(('a',), (b'a',))
651
652        # check_different_constants() cannot be used because repr(-0j) is
653        # '(-0-0j)', but when '(-0-0j)' is evaluated to 0j: we loose the sign.
654        f1, f2 = lambda: +0.0j, lambda: -0.0j
655        self.assertIsNot(f1.__code__, f2.__code__)
656        self.check_constant(f1, +0.0j)
657        self.check_constant(f2, -0.0j)
658        self.assertEqual(repr(f1()), repr(+0.0j))
659        self.assertEqual(repr(f2()), repr(-0.0j))
660
661        # {0} is converted to a constant frozenset({0}) by the peephole
662        # optimizer
663        f1, f2 = lambda x: x in {0}, lambda x: x in {0.0}
664        self.assertIsNot(f1.__code__, f2.__code__)
665        self.check_constant(f1, frozenset({0}))
666        self.check_constant(f2, frozenset({0.0}))
667        self.assertTrue(f1(0))
668        self.assertTrue(f2(0.0))
669
670    def test_path_like_objects(self):
671        # An implicit test for PyUnicode_FSDecoder().
672        class PathLike:
673            def __init__(self, path):
674                self._path = path
675            def __fspath__(self):
676                return self._path
677
678        compile("42", PathLike("test_compile_pathlike"), "single")
679
680
681class TestStackSize(unittest.TestCase):
682    # These tests check that the computed stack size for a code object
683    # stays within reasonable bounds (see issue #21523 for an example
684    # dysfunction).
685    N = 100
686
687    def check_stack_size(self, code):
688        # To assert that the alleged stack size is not O(N), we
689        # check that it is smaller than log(N).
690        if isinstance(code, str):
691            code = compile(code, "<foo>", "single")
692        max_size = math.ceil(math.log(len(code.co_code)))
693        self.assertLessEqual(code.co_stacksize, max_size)
694
695    def test_and(self):
696        self.check_stack_size("x and " * self.N + "x")
697
698    def test_or(self):
699        self.check_stack_size("x or " * self.N + "x")
700
701    def test_and_or(self):
702        self.check_stack_size("x and x or " * self.N + "x")
703
704    def test_chained_comparison(self):
705        self.check_stack_size("x < " * self.N + "x")
706
707    def test_if_else(self):
708        self.check_stack_size("x if x else " * self.N + "x")
709
710    def test_binop(self):
711        self.check_stack_size("x + " * self.N + "x")
712
713    def test_func_and(self):
714        code = "def f(x):\n"
715        code += "   x and x\n" * self.N
716        self.check_stack_size(code)
717
718
719if __name__ == "__main__":
720    unittest.main()
721