1"""This module includes tests of the code object representation.
2
3>>> def f(x):
4...     def g(y):
5...         return x + y
6...     return g
7...
8
9>>> dump(f.__code__)
10name: f
11argcount: 1
12kwonlyargcount: 0
13names: ()
14varnames: ('x', 'g')
15cellvars: ('x',)
16freevars: ()
17nlocals: 2
18flags: 3
19consts: ('None', '<code object g>', "'f.<locals>.g'")
20
21>>> dump(f(4).__code__)
22name: g
23argcount: 1
24kwonlyargcount: 0
25names: ()
26varnames: ('y',)
27cellvars: ()
28freevars: ('x',)
29nlocals: 1
30flags: 19
31consts: ('None',)
32
33>>> def h(x, y):
34...     a = x + y
35...     b = x - y
36...     c = a * b
37...     return c
38...
39
40>>> dump(h.__code__)
41name: h
42argcount: 2
43kwonlyargcount: 0
44names: ()
45varnames: ('x', 'y', 'a', 'b', 'c')
46cellvars: ()
47freevars: ()
48nlocals: 5
49flags: 67
50consts: ('None',)
51
52>>> def attrs(obj):
53...     print(obj.attr1)
54...     print(obj.attr2)
55...     print(obj.attr3)
56
57>>> dump(attrs.__code__)
58name: attrs
59argcount: 1
60kwonlyargcount: 0
61names: ('print', 'attr1', 'attr2', 'attr3')
62varnames: ('obj',)
63cellvars: ()
64freevars: ()
65nlocals: 1
66flags: 67
67consts: ('None',)
68
69>>> def optimize_away():
70...     'doc string'
71...     'not a docstring'
72...     53
73...     0x53
74
75>>> dump(optimize_away.__code__)
76name: optimize_away
77argcount: 0
78kwonlyargcount: 0
79names: ()
80varnames: ()
81cellvars: ()
82freevars: ()
83nlocals: 0
84flags: 67
85consts: ("'doc string'", 'None')
86
87>>> def keywordonly_args(a,b,*,k1):
88...     return a,b,k1
89...
90
91>>> dump(keywordonly_args.__code__)
92name: keywordonly_args
93argcount: 2
94kwonlyargcount: 1
95names: ()
96varnames: ('a', 'b', 'k1')
97cellvars: ()
98freevars: ()
99nlocals: 3
100flags: 67
101consts: ('None',)
102
103"""
104
105import sys
106import unittest
107import weakref
108from test.support import run_doctest, run_unittest, cpython_only
109
110
111def consts(t):
112    """Yield a doctest-safe sequence of object reprs."""
113    for elt in t:
114        r = repr(elt)
115        if r.startswith("<code object"):
116            yield "<code object %s>" % elt.co_name
117        else:
118            yield r
119
120def dump(co):
121    """Print out a text representation of a code object."""
122    for attr in ["name", "argcount", "kwonlyargcount", "names", "varnames",
123                 "cellvars", "freevars", "nlocals", "flags"]:
124        print("%s: %s" % (attr, getattr(co, "co_" + attr)))
125    print("consts:", tuple(consts(co.co_consts)))
126
127
128class CodeTest(unittest.TestCase):
129
130    @cpython_only
131    def test_newempty(self):
132        import _testcapi
133        co = _testcapi.code_newempty("filename", "funcname", 15)
134        self.assertEqual(co.co_filename, "filename")
135        self.assertEqual(co.co_name, "funcname")
136        self.assertEqual(co.co_firstlineno, 15)
137
138
139def isinterned(s):
140    return s is sys.intern(('_' + s + '_')[1:-1])
141
142class CodeConstsTest(unittest.TestCase):
143
144    def find_const(self, consts, value):
145        for v in consts:
146            if v == value:
147                return v
148        self.assertIn(value, consts)  # raises an exception
149        self.fail('Should never be reached')
150
151    def assertIsInterned(self, s):
152        if not isinterned(s):
153            self.fail('String %r is not interned' % (s,))
154
155    def assertIsNotInterned(self, s):
156        if isinterned(s):
157            self.fail('String %r is interned' % (s,))
158
159    @cpython_only
160    def test_interned_string(self):
161        co = compile('res = "str_value"', '?', 'exec')
162        v = self.find_const(co.co_consts, 'str_value')
163        self.assertIsInterned(v)
164
165    @cpython_only
166    def test_interned_string_in_tuple(self):
167        co = compile('res = ("str_value",)', '?', 'exec')
168        v = self.find_const(co.co_consts, ('str_value',))
169        self.assertIsInterned(v[0])
170
171    @cpython_only
172    def test_interned_string_in_frozenset(self):
173        co = compile('res = a in {"str_value"}', '?', 'exec')
174        v = self.find_const(co.co_consts, frozenset(('str_value',)))
175        self.assertIsInterned(tuple(v)[0])
176
177    @cpython_only
178    def test_interned_string_default(self):
179        def f(a='str_value'):
180            return a
181        self.assertIsInterned(f())
182
183    @cpython_only
184    def test_interned_string_with_null(self):
185        co = compile(r'res = "str\0value!"', '?', 'exec')
186        v = self.find_const(co.co_consts, 'str\0value!')
187        self.assertIsNotInterned(v)
188
189
190class CodeWeakRefTest(unittest.TestCase):
191
192    def test_basic(self):
193        # Create a code object in a clean environment so that we know we have
194        # the only reference to it left.
195        namespace = {}
196        exec("def f(): pass", globals(), namespace)
197        f = namespace["f"]
198        del namespace
199
200        self.called = False
201        def callback(code):
202            self.called = True
203
204        # f is now the last reference to the function, and through it, the code
205        # object.  While we hold it, check that we can create a weakref and
206        # deref it.  Then delete it, and check that the callback gets called and
207        # the reference dies.
208        coderef = weakref.ref(f.__code__, callback)
209        self.assertTrue(bool(coderef()))
210        del f
211        self.assertFalse(bool(coderef()))
212        self.assertTrue(self.called)
213
214
215def test_main(verbose=None):
216    from test import test_code
217    run_doctest(test_code, verbose)
218    run_unittest(CodeTest, CodeConstsTest, CodeWeakRefTest)
219
220
221if __name__ == "__main__":
222    test_main()
223