blob: 55faf4c4279fb6ce4be91e3db798fadc0c31e1b7 [file] [log] [blame]
Jeremy Hylton3e0055f2005-10-20 19:59:25 +00001"""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
Neal Norwitz221085d2007-02-25 20:55:47 +00009>>> dump(f.__code__)
Jeremy Hylton3e0055f2005-10-20 19:59:25 +000010name: f
11argcount: 1
Guido van Rossum4f72a782006-10-27 23:31:49 +000012kwonlyargcount: 0
Jeremy Hylton3e0055f2005-10-20 19:59:25 +000013names: ()
14varnames: ('x', 'g')
15cellvars: ('x',)
16freevars: ()
17nlocals: 2
18flags: 3
Antoine Pitrou86a36b52011-11-25 18:56:07 +010019consts: ('None', '<code object g>', "'f.<locals>.g'")
Jeremy Hylton3e0055f2005-10-20 19:59:25 +000020
Neal Norwitz221085d2007-02-25 20:55:47 +000021>>> dump(f(4).__code__)
Jeremy Hylton3e0055f2005-10-20 19:59:25 +000022name: g
23argcount: 1
Guido van Rossum4f72a782006-10-27 23:31:49 +000024kwonlyargcount: 0
Jeremy Hylton3e0055f2005-10-20 19:59:25 +000025names: ()
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
Tim Peters536cf992005-12-25 23:18:31 +000038...
Guido van Rossum4f72a782006-10-27 23:31:49 +000039
Neal Norwitz221085d2007-02-25 20:55:47 +000040>>> dump(h.__code__)
Jeremy Hylton3e0055f2005-10-20 19:59:25 +000041name: h
42argcount: 2
Guido van Rossum4f72a782006-10-27 23:31:49 +000043kwonlyargcount: 0
Jeremy Hylton3e0055f2005-10-20 19:59:25 +000044names: ()
45varnames: ('x', 'y', 'a', 'b', 'c')
46cellvars: ()
47freevars: ()
48nlocals: 5
49flags: 67
50consts: ('None',)
51
52>>> def attrs(obj):
Guido van Rossum7131f842007-02-09 20:13:25 +000053... print(obj.attr1)
54... print(obj.attr2)
55... print(obj.attr3)
Jeremy Hylton3e0055f2005-10-20 19:59:25 +000056
Neal Norwitz221085d2007-02-25 20:55:47 +000057>>> dump(attrs.__code__)
Jeremy Hylton3e0055f2005-10-20 19:59:25 +000058name: attrs
59argcount: 1
Guido van Rossum4f72a782006-10-27 23:31:49 +000060kwonlyargcount: 0
Georg Brandl88fc6642007-02-09 21:28:07 +000061names: ('print', 'attr1', 'attr2', 'attr3')
Jeremy Hylton3e0055f2005-10-20 19:59:25 +000062varnames: ('obj',)
63cellvars: ()
64freevars: ()
65nlocals: 1
66flags: 67
67consts: ('None',)
68
Thomas Wouters0e3f5912006-08-11 14:57:12 +000069>>> def optimize_away():
70... 'doc string'
71... 'not a docstring'
72... 53
Guido van Rossume2a383d2007-01-15 16:59:06 +000073... 0x53
Thomas Wouters0e3f5912006-08-11 14:57:12 +000074
Neal Norwitz221085d2007-02-25 20:55:47 +000075>>> dump(optimize_away.__code__)
Thomas Wouters0e3f5912006-08-11 14:57:12 +000076name: optimize_away
77argcount: 0
Guido van Rossum4f72a782006-10-27 23:31:49 +000078kwonlyargcount: 0
Thomas Wouters0e3f5912006-08-11 14:57:12 +000079names: ()
80varnames: ()
81cellvars: ()
82freevars: ()
83nlocals: 0
84flags: 67
85consts: ("'doc string'", 'None')
86
Guido van Rossum4f72a782006-10-27 23:31:49 +000087>>> def keywordonly_args(a,b,*,k1):
88... return a,b,k1
89...
90
Neal Norwitz221085d2007-02-25 20:55:47 +000091>>> dump(keywordonly_args.__code__)
Guido van Rossum4f72a782006-10-27 23:31:49 +000092name: keywordonly_args
93argcount: 2
94kwonlyargcount: 1
95names: ()
96varnames: ('a', 'b', 'k1')
97cellvars: ()
98freevars: ()
99nlocals: 3
100flags: 67
101consts: ('None',)
102
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000103"""
104
Nick Coghlan078f1812017-12-03 11:12:20 +1000105import inspect
Serhiy Storchaka00a0fc12016-09-30 10:07:26 +0300106import sys
Dino Viehlandf3cffd22017-06-21 14:44:36 -0700107import threading
Alexandre Vassalotti7b82b402009-07-21 04:30:03 +0000108import unittest
Collin Winter4222e9c2010-03-18 22:46:40 +0000109import weakref
Victor Stinnera4b091e2017-06-23 15:08:55 +0200110try:
111 import ctypes
112except ImportError:
113 ctypes = None
Dino Viehlandf3cffd22017-06-21 14:44:36 -0700114from test.support import (run_doctest, run_unittest, cpython_only,
115 check_impl_detail)
Alexandre Vassalotti7b82b402009-07-21 04:30:03 +0000116
Collin Winter4222e9c2010-03-18 22:46:40 +0000117
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000118def consts(t):
119 """Yield a doctest-safe sequence of object reprs."""
120 for elt in t:
121 r = repr(elt)
122 if r.startswith("<code object"):
123 yield "<code object %s>" % elt.co_name
124 else:
125 yield r
126
127def dump(co):
128 """Print out a text representation of a code object."""
Guido van Rossum4f72a782006-10-27 23:31:49 +0000129 for attr in ["name", "argcount", "kwonlyargcount", "names", "varnames",
130 "cellvars", "freevars", "nlocals", "flags"]:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000131 print("%s: %s" % (attr, getattr(co, "co_" + attr)))
132 print("consts:", tuple(consts(co.co_consts)))
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000133
Nick Coghlan078f1812017-12-03 11:12:20 +1000134# Needed for test_closure_injection below
135# Defined at global scope to avoid implicitly closing over __class__
136def external_getitem(self, i):
137 return f"Foreign getitem: {super().__getitem__(i)}"
Alexandre Vassalotti7b82b402009-07-21 04:30:03 +0000138
139class CodeTest(unittest.TestCase):
140
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +0200141 @cpython_only
Alexandre Vassalotti7b82b402009-07-21 04:30:03 +0000142 def test_newempty(self):
Serhiy Storchaka5cfc79d2014-02-07 10:06:39 +0200143 import _testcapi
Alexandre Vassalotti7b82b402009-07-21 04:30:03 +0000144 co = _testcapi.code_newempty("filename", "funcname", 15)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000145 self.assertEqual(co.co_filename, "filename")
146 self.assertEqual(co.co_name, "funcname")
147 self.assertEqual(co.co_firstlineno, 15)
Alexandre Vassalotti7b82b402009-07-21 04:30:03 +0000148
Nick Coghlan078f1812017-12-03 11:12:20 +1000149 @cpython_only
150 def test_closure_injection(self):
151 # From https://bugs.python.org/issue32176
152 from types import FunctionType, CodeType
153
154 def create_closure(__class__):
155 return (lambda: __class__).__closure__
156
157 def new_code(c):
158 '''A new code object with a __class__ cell added to freevars'''
159 return CodeType(
160 c.co_argcount, c.co_kwonlyargcount, c.co_nlocals,
161 c.co_stacksize, c.co_flags, c.co_code, c.co_consts, c.co_names,
162 c.co_varnames, c.co_filename, c.co_name, c.co_firstlineno,
163 c.co_lnotab, c.co_freevars + ('__class__',), c.co_cellvars)
164
165 def add_foreign_method(cls, name, f):
166 code = new_code(f.__code__)
167 assert not f.__closure__
168 closure = create_closure(cls)
169 defaults = f.__defaults__
170 setattr(cls, name, FunctionType(code, globals(), name, defaults, closure))
171
172 class List(list):
173 pass
174
175 add_foreign_method(List, "__getitem__", external_getitem)
176
177 # Ensure the closure injection actually worked
178 function = List.__getitem__
179 class_ref = function.__closure__[0].cell_contents
180 self.assertIs(class_ref, List)
181
182 # Ensure the code correctly indicates it accesses a free variable
183 self.assertFalse(function.__code__.co_flags & inspect.CO_NOFREE,
184 hex(function.__code__.co_flags))
185
186 # Ensure the zero-arg super() call in the injected method works
187 obj = List([1, 2, 3])
188 self.assertEqual(obj[0], "Foreign getitem: 1")
Serhiy Storchaka09f3d082016-10-04 18:17:22 +0300189
190def isinterned(s):
191 return s is sys.intern(('_' + s + '_')[1:-1])
192
Serhiy Storchaka00a0fc12016-09-30 10:07:26 +0300193class CodeConstsTest(unittest.TestCase):
194
195 def find_const(self, consts, value):
196 for v in consts:
197 if v == value:
198 return v
Serhiy Storchaka09f3d082016-10-04 18:17:22 +0300199 self.assertIn(value, consts) # raises an exception
200 self.fail('Should never be reached')
Serhiy Storchaka00a0fc12016-09-30 10:07:26 +0300201
202 def assertIsInterned(self, s):
Serhiy Storchaka09f3d082016-10-04 18:17:22 +0300203 if not isinterned(s):
Serhiy Storchaka00a0fc12016-09-30 10:07:26 +0300204 self.fail('String %r is not interned' % (s,))
205
Serhiy Storchaka09f3d082016-10-04 18:17:22 +0300206 def assertIsNotInterned(self, s):
207 if isinterned(s):
208 self.fail('String %r is interned' % (s,))
209
Serhiy Storchaka00a0fc12016-09-30 10:07:26 +0300210 @cpython_only
211 def test_interned_string(self):
212 co = compile('res = "str_value"', '?', 'exec')
213 v = self.find_const(co.co_consts, 'str_value')
214 self.assertIsInterned(v)
215
216 @cpython_only
217 def test_interned_string_in_tuple(self):
218 co = compile('res = ("str_value",)', '?', 'exec')
219 v = self.find_const(co.co_consts, ('str_value',))
220 self.assertIsInterned(v[0])
221
222 @cpython_only
223 def test_interned_string_in_frozenset(self):
224 co = compile('res = a in {"str_value"}', '?', 'exec')
225 v = self.find_const(co.co_consts, frozenset(('str_value',)))
226 self.assertIsInterned(tuple(v)[0])
227
228 @cpython_only
229 def test_interned_string_default(self):
230 def f(a='str_value'):
231 return a
232 self.assertIsInterned(f())
233
Serhiy Storchaka09f3d082016-10-04 18:17:22 +0300234 @cpython_only
235 def test_interned_string_with_null(self):
236 co = compile(r'res = "str\0value!"', '?', 'exec')
237 v = self.find_const(co.co_consts, 'str\0value!')
238 self.assertIsNotInterned(v)
239
Alexandre Vassalotti7b82b402009-07-21 04:30:03 +0000240
Collin Winter4222e9c2010-03-18 22:46:40 +0000241class CodeWeakRefTest(unittest.TestCase):
242
243 def test_basic(self):
244 # Create a code object in a clean environment so that we know we have
245 # the only reference to it left.
246 namespace = {}
247 exec("def f(): pass", globals(), namespace)
248 f = namespace["f"]
249 del namespace
250
251 self.called = False
252 def callback(code):
253 self.called = True
254
255 # f is now the last reference to the function, and through it, the code
256 # object. While we hold it, check that we can create a weakref and
257 # deref it. Then delete it, and check that the callback gets called and
258 # the reference dies.
259 coderef = weakref.ref(f.__code__, callback)
260 self.assertTrue(bool(coderef()))
261 del f
262 self.assertFalse(bool(coderef()))
263 self.assertTrue(self.called)
264
265
Victor Stinnera4b091e2017-06-23 15:08:55 +0200266if check_impl_detail(cpython=True) and ctypes is not None:
Dino Viehlandf3cffd22017-06-21 14:44:36 -0700267 py = ctypes.pythonapi
268 freefunc = ctypes.CFUNCTYPE(None,ctypes.c_voidp)
269
270 RequestCodeExtraIndex = py._PyEval_RequestCodeExtraIndex
271 RequestCodeExtraIndex.argtypes = (freefunc,)
272 RequestCodeExtraIndex.restype = ctypes.c_ssize_t
273
274 SetExtra = py._PyCode_SetExtra
275 SetExtra.argtypes = (ctypes.py_object, ctypes.c_ssize_t, ctypes.c_voidp)
276 SetExtra.restype = ctypes.c_int
277
278 GetExtra = py._PyCode_GetExtra
279 GetExtra.argtypes = (ctypes.py_object, ctypes.c_ssize_t,
280 ctypes.POINTER(ctypes.c_voidp))
281 GetExtra.restype = ctypes.c_int
282
283 LAST_FREED = None
284 def myfree(ptr):
285 global LAST_FREED
286 LAST_FREED = ptr
287
288 FREE_FUNC = freefunc(myfree)
289 FREE_INDEX = RequestCodeExtraIndex(FREE_FUNC)
290
291 class CoExtra(unittest.TestCase):
292 def get_func(self):
293 # Defining a function causes the containing function to have a
294 # reference to the code object. We need the code objects to go
295 # away, so we eval a lambda.
296 return eval('lambda:42')
297
298 def test_get_non_code(self):
299 f = self.get_func()
300
301 self.assertRaises(SystemError, SetExtra, 42, FREE_INDEX,
302 ctypes.c_voidp(100))
303 self.assertRaises(SystemError, GetExtra, 42, FREE_INDEX,
304 ctypes.c_voidp(100))
305
306 def test_bad_index(self):
307 f = self.get_func()
308 self.assertRaises(SystemError, SetExtra, f.__code__,
309 FREE_INDEX+100, ctypes.c_voidp(100))
310 self.assertEqual(GetExtra(f.__code__, FREE_INDEX+100,
311 ctypes.c_voidp(100)), 0)
312
313 def test_free_called(self):
314 # Verify that the provided free function gets invoked
315 # when the code object is cleaned up.
316 f = self.get_func()
317
318 SetExtra(f.__code__, FREE_INDEX, ctypes.c_voidp(100))
319 del f
320 self.assertEqual(LAST_FREED, 100)
321
322 def test_get_set(self):
323 # Test basic get/set round tripping.
324 f = self.get_func()
325
326 extra = ctypes.c_voidp()
327
328 SetExtra(f.__code__, FREE_INDEX, ctypes.c_voidp(200))
329 # reset should free...
330 SetExtra(f.__code__, FREE_INDEX, ctypes.c_voidp(300))
331 self.assertEqual(LAST_FREED, 200)
332
333 extra = ctypes.c_voidp()
334 GetExtra(f.__code__, FREE_INDEX, extra)
335 self.assertEqual(extra.value, 300)
336 del f
337
338 def test_free_different_thread(self):
339 # Freeing a code object on a different thread then
340 # where the co_extra was set should be safe.
341 f = self.get_func()
342 class ThreadTest(threading.Thread):
343 def __init__(self, f, test):
344 super().__init__()
345 self.f = f
346 self.test = test
347 def run(self):
348 del self.f
349 self.test.assertEqual(LAST_FREED, 500)
350
351 SetExtra(f.__code__, FREE_INDEX, ctypes.c_voidp(500))
352 tt = ThreadTest(f, self)
353 del f
354 tt.start()
355 tt.join()
356 self.assertEqual(LAST_FREED, 500)
357
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000358def test_main(verbose=None):
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000359 from test import test_code
360 run_doctest(test_code, verbose)
Dino Viehlandf3cffd22017-06-21 14:44:36 -0700361 tests = [CodeTest, CodeConstsTest, CodeWeakRefTest]
Victor Stinnera4b091e2017-06-23 15:08:55 +0200362 if check_impl_detail(cpython=True) and ctypes is not None:
Dino Viehlandf3cffd22017-06-21 14:44:36 -0700363 tests.append(CoExtra)
364 run_unittest(*tests)
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000365
Collin Winter4222e9c2010-03-18 22:46:40 +0000366if __name__ == "__main__":
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000367 test_main()