1import unittest
2import sys
3import _ast
4from test import test_support
5import textwrap
6
7class TestSpecifics(unittest.TestCase):
8
9    def test_no_ending_newline(self):
10        compile("hi", "<test>", "exec")
11        compile("hi\r", "<test>", "exec")
12
13    def test_empty(self):
14        compile("", "<test>", "exec")
15
16    def test_other_newlines(self):
17        compile("\r\n", "<test>", "exec")
18        compile("\r", "<test>", "exec")
19        compile("hi\r\nstuff\r\ndef f():\n    pass\r", "<test>", "exec")
20        compile("this_is\rreally_old_mac\rdef f():\n    pass", "<test>", "exec")
21
22    def test_debug_assignment(self):
23        # catch assignments to __debug__
24        self.assertRaises(SyntaxError, compile, '__debug__ = 1', '?', 'single')
25        import __builtin__
26        prev = __builtin__.__debug__
27        setattr(__builtin__, '__debug__', 'sure')
28        setattr(__builtin__, '__debug__', prev)
29
30    def test_argument_handling(self):
31        # detect duplicate positional and keyword arguments
32        self.assertRaises(SyntaxError, eval, 'lambda a,a:0')
33        self.assertRaises(SyntaxError, eval, 'lambda a,a=1:0')
34        self.assertRaises(SyntaxError, eval, 'lambda a=1,a=1:0')
35        try:
36            exec 'def f(a, a): pass'
37            self.fail("duplicate arguments")
38        except SyntaxError:
39            pass
40        try:
41            exec 'def f(a = 0, a = 1): pass'
42            self.fail("duplicate keyword arguments")
43        except SyntaxError:
44            pass
45        try:
46            exec 'def f(a): global a; a = 1'
47            self.fail("variable is global and local")
48        except SyntaxError:
49            pass
50
51    def test_syntax_error(self):
52        self.assertRaises(SyntaxError, compile, "1+*3", "filename", "exec")
53
54    def test_none_keyword_arg(self):
55        self.assertRaises(SyntaxError, compile, "f(None=1)", "<string>", "exec")
56
57    def test_duplicate_global_local(self):
58        try:
59            exec 'def f(a): global a; a = 1'
60            self.fail("variable is global and local")
61        except SyntaxError:
62            pass
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' in g, m
80        self.assertEqual(m.results, ('z', 12))
81        try:
82            exec 'z = b' in g, m
83        except NameError:
84            pass
85        else:
86            self.fail('Did not detect a KeyError')
87        exec 'z = dir()' in g, m
88        self.assertEqual(m.results, ('z', list('xyz')))
89        exec 'z = globals()' in g, m
90        self.assertEqual(m.results, ('z', g))
91        exec 'z = locals()' in g, m
92        self.assertEqual(m.results, ('z', m))
93        try:
94            exec 'z = b' in m
95        except TypeError:
96            pass
97        else:
98            self.fail('Did not validate globals as a real dict')
99
100        class A:
101            "Non-mapping"
102            pass
103        m = A()
104        try:
105            exec 'z = a' in g, m
106        except TypeError:
107            pass
108        else:
109            self.fail('Did not validate locals as a mapping')
110
111        # Verify that dict subclasses work as well
112        class D(dict):
113            def __getitem__(self, key):
114                if key == 'a':
115                    return 12
116                return dict.__getitem__(self, key)
117        d = D()
118        exec 'z = a' in g, d
119        self.assertEqual(d['z'], 12)
120
121    def test_extended_arg(self):
122        longexpr = 'x = x or ' + '-x' * 2500
123        code = '''
124def f(x):
125    %s
126    %s
127    %s
128    %s
129    %s
130    %s
131    %s
132    %s
133    %s
134    %s
135    # the expressions above have no effect, x == argument
136    while x:
137        x -= 1
138        # EXTENDED_ARG/JUMP_ABSOLUTE here
139    return x
140''' % ((longexpr,)*10)
141        exec code
142        self.assertEqual(f(5), 0)
143
144    def test_complex_args(self):
145
146        with test_support.check_py3k_warnings(
147                ("tuple parameter unpacking has been removed", SyntaxWarning)):
148            exec textwrap.dedent('''
149        def comp_args((a, b)):
150            return a,b
151        self.assertEqual(comp_args((1, 2)), (1, 2))
152
153        def comp_args((a, b)=(3, 4)):
154            return a, b
155        self.assertEqual(comp_args((1, 2)), (1, 2))
156        self.assertEqual(comp_args(), (3, 4))
157
158        def comp_args(a, (b, c)):
159            return a, b, c
160        self.assertEqual(comp_args(1, (2, 3)), (1, 2, 3))
161
162        def comp_args(a=2, (b, c)=(3, 4)):
163            return a, b, c
164        self.assertEqual(comp_args(1, (2, 3)), (1, 2, 3))
165        self.assertEqual(comp_args(), (2, 3, 4))
166        ''')
167
168    def test_argument_order(self):
169        try:
170            exec 'def f(a=1, (b, c)): pass'
171            self.fail("non-default args after default")
172        except SyntaxError:
173            pass
174
175    def test_float_literals(self):
176        # testing bad float literals
177        self.assertRaises(SyntaxError, eval, "2e")
178        self.assertRaises(SyntaxError, eval, "2.0e+")
179        self.assertRaises(SyntaxError, eval, "1e-")
180        self.assertRaises(SyntaxError, eval, "3-4e/21")
181
182    def test_indentation(self):
183        # testing compile() of indented block w/o trailing newline"
184        s = """
185if 1:
186    if 2:
187        pass"""
188        compile(s, "<string>", "exec")
189
190    # This test is probably specific to CPython and may not generalize
191    # to other implementations.  We are trying to ensure that when
192    # the first line of code starts after 256, correct line numbers
193    # in tracebacks are still produced.
194    def test_leading_newlines(self):
195        s256 = "".join(["\n"] * 256 + ["spam"])
196        co = compile(s256, 'fn', 'exec')
197        self.assertEqual(co.co_firstlineno, 257)
198        self.assertEqual(co.co_lnotab, '')
199
200    def test_literals_with_leading_zeroes(self):
201        for arg in ["077787", "0xj", "0x.", "0e",  "090000000000000",
202                    "080000000000000", "000000000000009", "000000000000008",
203                    "0b42", "0BADCAFE", "0o123456789", "0b1.1", "0o4.2",
204                    "0b101j2", "0o153j2", "0b100e1", "0o777e1", "0o8", "0o78"]:
205            self.assertRaises(SyntaxError, eval, arg)
206
207        self.assertEqual(eval("0777"), 511)
208        self.assertEqual(eval("0777L"), 511)
209        self.assertEqual(eval("000777"), 511)
210        self.assertEqual(eval("0xff"), 255)
211        self.assertEqual(eval("0xffL"), 255)
212        self.assertEqual(eval("0XfF"), 255)
213        self.assertEqual(eval("0777."), 777)
214        self.assertEqual(eval("0777.0"), 777)
215        self.assertEqual(eval("000000000000000000000000000000000000000000000000000777e0"), 777)
216        self.assertEqual(eval("0777e1"), 7770)
217        self.assertEqual(eval("0e0"), 0)
218        self.assertEqual(eval("0000E-012"), 0)
219        self.assertEqual(eval("09.5"), 9.5)
220        self.assertEqual(eval("0777j"), 777j)
221        self.assertEqual(eval("00j"), 0j)
222        self.assertEqual(eval("00.0"), 0)
223        self.assertEqual(eval("0e3"), 0)
224        self.assertEqual(eval("090000000000000."), 90000000000000.)
225        self.assertEqual(eval("090000000000000.0000000000000000000000"), 90000000000000.)
226        self.assertEqual(eval("090000000000000e0"), 90000000000000.)
227        self.assertEqual(eval("090000000000000e-0"), 90000000000000.)
228        self.assertEqual(eval("090000000000000j"), 90000000000000j)
229        self.assertEqual(eval("000000000000007"), 7)
230        self.assertEqual(eval("000000000000008."), 8.)
231        self.assertEqual(eval("000000000000009."), 9.)
232        self.assertEqual(eval("0b101010"), 42)
233        self.assertEqual(eval("-0b000000000010"), -2)
234        self.assertEqual(eval("0o777"), 511)
235        self.assertEqual(eval("-0o0000010"), -8)
236        self.assertEqual(eval("020000000000.0"), 20000000000.0)
237        self.assertEqual(eval("037777777777e0"), 37777777777.0)
238        self.assertEqual(eval("01000000000000000000000.0"),
239                         1000000000000000000000.0)
240
241    def test_unary_minus(self):
242        # Verify treatment of unary minus on negative numbers SF bug #660455
243        if sys.maxint == 2147483647:
244            # 32-bit machine
245            all_one_bits = '0xffffffff'
246            self.assertEqual(eval(all_one_bits), 4294967295L)
247            self.assertEqual(eval("-" + all_one_bits), -4294967295L)
248        elif sys.maxint == 9223372036854775807:
249            # 64-bit machine
250            all_one_bits = '0xffffffffffffffff'
251            self.assertEqual(eval(all_one_bits), 18446744073709551615L)
252            self.assertEqual(eval("-" + all_one_bits), -18446744073709551615L)
253        else:
254            self.fail("How many bits *does* this machine have???")
255        # Verify treatment of constant folding on -(sys.maxint+1)
256        # i.e. -2147483648 on 32 bit platforms.  Should return int, not long.
257        self.assertIsInstance(eval("%s" % (-sys.maxint - 1)), int)
258        self.assertIsInstance(eval("%s" % (-sys.maxint - 2)), long)
259
260    if sys.maxint == 9223372036854775807:
261        def test_32_63_bit_values(self):
262            a = +4294967296  # 1 << 32
263            b = -4294967296  # 1 << 32
264            c = +281474976710656  # 1 << 48
265            d = -281474976710656  # 1 << 48
266            e = +4611686018427387904  # 1 << 62
267            f = -4611686018427387904  # 1 << 62
268            g = +9223372036854775807  # 1 << 63 - 1
269            h = -9223372036854775807  # 1 << 63 - 1
270
271            for variable in self.test_32_63_bit_values.func_code.co_consts:
272                if variable is not None:
273                    self.assertIsInstance(variable, int)
274
275    def test_sequence_unpacking_error(self):
276        # Verify sequence packing/unpacking with "or".  SF bug #757818
277        i,j = (1, -1) or (-1, 1)
278        self.assertEqual(i, 1)
279        self.assertEqual(j, -1)
280
281    def test_none_assignment(self):
282        stmts = [
283            'None = 0',
284            'None += 0',
285            '__builtins__.None = 0',
286            'def None(): pass',
287            'class None: pass',
288            '(a, None) = 0, 0',
289            'for None in range(10): pass',
290            'def f(None): pass',
291            'import None',
292            'import x as None',
293            'from x import None',
294            'from x import y as None'
295        ]
296        for stmt in stmts:
297            stmt += "\n"
298            self.assertRaises(SyntaxError, compile, stmt, 'tmp', 'single')
299            self.assertRaises(SyntaxError, compile, stmt, 'tmp', 'exec')
300        # This is ok.
301        compile("from None import x", "tmp", "exec")
302        compile("from x import None as y", "tmp", "exec")
303        compile("import None as x", "tmp", "exec")
304
305    def test_import(self):
306        succeed = [
307            'import sys',
308            'import os, sys',
309            'import os as bar',
310            'import os.path as bar',
311            'from __future__ import nested_scopes, generators',
312            'from __future__ import (nested_scopes,\ngenerators)',
313            'from __future__ import (nested_scopes,\ngenerators,)',
314            'from sys import stdin, stderr, stdout',
315            'from sys import (stdin, stderr,\nstdout)',
316            'from sys import (stdin, stderr,\nstdout,)',
317            'from sys import (stdin\n, stderr, stdout)',
318            'from sys import (stdin\n, stderr, stdout,)',
319            'from sys import stdin as si, stdout as so, stderr as se',
320            'from sys import (stdin as si, stdout as so, stderr as se)',
321            'from sys import (stdin as si, stdout as so, stderr as se,)',
322            ]
323        fail = [
324            'import (os, sys)',
325            'import (os), (sys)',
326            'import ((os), (sys))',
327            'import (sys',
328            'import sys)',
329            'import (os,)',
330            'import os As bar',
331            'import os.path a bar',
332            'from sys import stdin As stdout',
333            'from sys import stdin a stdout',
334            'from (sys) import stdin',
335            'from __future__ import (nested_scopes',
336            'from __future__ import nested_scopes)',
337            'from __future__ import nested_scopes,\ngenerators',
338            'from sys import (stdin',
339            'from sys import stdin)',
340            'from sys import stdin, stdout,\nstderr',
341            'from sys import stdin si',
342            'from sys import stdin,'
343            'from sys import (*)',
344            'from sys import (stdin,, stdout, stderr)',
345            'from sys import (stdin, stdout),',
346            ]
347        for stmt in succeed:
348            compile(stmt, 'tmp', 'exec')
349        for stmt in fail:
350            self.assertRaises(SyntaxError, compile, stmt, 'tmp', 'exec')
351
352    def test_for_distinct_code_objects(self):
353        # SF bug 1048870
354        def f():
355            f1 = lambda x=1: x
356            f2 = lambda x=2: x
357            return f1, f2
358        f1, f2 = f()
359        self.assertNotEqual(id(f1.func_code), id(f2.func_code))
360
361    def test_lambda_doc(self):
362        l = lambda: "foo"
363        self.assertIsNone(l.__doc__)
364
365    def test_unicode_encoding(self):
366        code = u"# -*- coding: utf-8 -*-\npass\n"
367        self.assertRaises(SyntaxError, compile, code, "tmp", "exec")
368
369    def test_subscripts(self):
370        # SF bug 1448804
371        # Class to make testing subscript results easy
372        class str_map(object):
373            def __init__(self):
374                self.data = {}
375            def __getitem__(self, key):
376                return self.data[str(key)]
377            def __setitem__(self, key, value):
378                self.data[str(key)] = value
379            def __delitem__(self, key):
380                del self.data[str(key)]
381            def __contains__(self, key):
382                return str(key) in self.data
383        d = str_map()
384        # Index
385        d[1] = 1
386        self.assertEqual(d[1], 1)
387        d[1] += 1
388        self.assertEqual(d[1], 2)
389        del d[1]
390        self.assertNotIn(1, d)
391        # Tuple of indices
392        d[1, 1] = 1
393        self.assertEqual(d[1, 1], 1)
394        d[1, 1] += 1
395        self.assertEqual(d[1, 1], 2)
396        del d[1, 1]
397        self.assertNotIn((1, 1), d)
398        # Simple slice
399        d[1:2] = 1
400        self.assertEqual(d[1:2], 1)
401        d[1:2] += 1
402        self.assertEqual(d[1:2], 2)
403        del d[1:2]
404        self.assertNotIn(slice(1, 2), d)
405        # Tuple of simple slices
406        d[1:2, 1:2] = 1
407        self.assertEqual(d[1:2, 1:2], 1)
408        d[1:2, 1:2] += 1
409        self.assertEqual(d[1:2, 1:2], 2)
410        del d[1:2, 1:2]
411        self.assertNotIn((slice(1, 2), slice(1, 2)), d)
412        # Extended slice
413        d[1:2:3] = 1
414        self.assertEqual(d[1:2:3], 1)
415        d[1:2:3] += 1
416        self.assertEqual(d[1:2:3], 2)
417        del d[1:2:3]
418        self.assertNotIn(slice(1, 2, 3), d)
419        # Tuple of extended slices
420        d[1:2:3, 1:2:3] = 1
421        self.assertEqual(d[1:2:3, 1:2:3], 1)
422        d[1:2:3, 1:2:3] += 1
423        self.assertEqual(d[1:2:3, 1:2:3], 2)
424        del d[1:2:3, 1:2:3]
425        self.assertNotIn((slice(1, 2, 3), slice(1, 2, 3)), d)
426        # Ellipsis
427        d[...] = 1
428        self.assertEqual(d[...], 1)
429        d[...] += 1
430        self.assertEqual(d[...], 2)
431        del d[...]
432        self.assertNotIn(Ellipsis, d)
433        # Tuple of Ellipses
434        d[..., ...] = 1
435        self.assertEqual(d[..., ...], 1)
436        d[..., ...] += 1
437        self.assertEqual(d[..., ...], 2)
438        del d[..., ...]
439        self.assertNotIn((Ellipsis, Ellipsis), d)
440
441    def test_mangling(self):
442        class A:
443            def f():
444                __mangled = 1
445                __not_mangled__ = 2
446                import __mangled_mod
447                import __package__.module
448
449        self.assertIn("_A__mangled", A.f.func_code.co_varnames)
450        self.assertIn("__not_mangled__", A.f.func_code.co_varnames)
451        self.assertIn("_A__mangled_mod", A.f.func_code.co_varnames)
452        self.assertIn("__package__", A.f.func_code.co_varnames)
453
454    def test_compile_ast(self):
455        fname = __file__
456        if fname.lower().endswith(('pyc', 'pyo')):
457            fname = fname[:-1]
458        with open(fname, 'r') as f:
459            fcontents = f.read()
460        sample_code = [
461            ['<assign>', 'x = 5'],
462            ['<print1>', 'print 1'],
463            ['<printv>', 'print v'],
464            ['<printTrue>', 'print True'],
465            ['<printList>', 'print []'],
466            ['<ifblock>', """if True:\n    pass\n"""],
467            ['<forblock>', """for n in [1, 2, 3]:\n    print n\n"""],
468            ['<deffunc>', """def foo():\n    pass\nfoo()\n"""],
469            [fname, fcontents],
470        ]
471
472        for fname, code in sample_code:
473            co1 = compile(code, '%s1' % fname, 'exec')
474            ast = compile(code, '%s2' % fname, 'exec', _ast.PyCF_ONLY_AST)
475            self.assertTrue(type(ast) == _ast.Module)
476            co2 = compile(ast, '%s3' % fname, 'exec')
477            self.assertEqual(co1, co2)
478            # the code object's filename comes from the second compilation step
479            self.assertEqual(co2.co_filename, '%s3' % fname)
480
481        # raise exception when node type doesn't match with compile mode
482        co1 = compile('print 1', '<string>', 'exec', _ast.PyCF_ONLY_AST)
483        self.assertRaises(TypeError, compile, co1, '<ast>', 'eval')
484
485        # raise exception when node type is no start node
486        self.assertRaises(TypeError, compile, _ast.If(), '<ast>', 'exec')
487
488        # raise exception when node has invalid children
489        ast = _ast.Module()
490        ast.body = [_ast.BoolOp()]
491        self.assertRaises(TypeError, compile, ast, '<ast>', 'exec')
492
493
494def test_main():
495    test_support.run_unittest(TestSpecifics)
496
497if __name__ == "__main__":
498    test_main()
499