test_code.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808
  1. """This module includes tests of the code object representation.
  2. >>> def f(x):
  3. ... def g(y):
  4. ... return x + y
  5. ... return g
  6. ...
  7. >>> dump(f.__code__)
  8. name: f
  9. argcount: 1
  10. posonlyargcount: 0
  11. kwonlyargcount: 0
  12. names: ()
  13. varnames: ('x', 'g')
  14. cellvars: ('x',)
  15. freevars: ()
  16. nlocals: 2
  17. flags: 3
  18. consts: ('None', '<code object g>')
  19. >>> dump(f(4).__code__)
  20. name: g
  21. argcount: 1
  22. posonlyargcount: 0
  23. kwonlyargcount: 0
  24. names: ()
  25. varnames: ('y',)
  26. cellvars: ()
  27. freevars: ('x',)
  28. nlocals: 1
  29. flags: 19
  30. consts: ('None',)
  31. >>> def h(x, y):
  32. ... a = x + y
  33. ... b = x - y
  34. ... c = a * b
  35. ... return c
  36. ...
  37. >>> dump(h.__code__)
  38. name: h
  39. argcount: 2
  40. posonlyargcount: 0
  41. kwonlyargcount: 0
  42. names: ()
  43. varnames: ('x', 'y', 'a', 'b', 'c')
  44. cellvars: ()
  45. freevars: ()
  46. nlocals: 5
  47. flags: 3
  48. consts: ('None',)
  49. >>> def attrs(obj):
  50. ... print(obj.attr1)
  51. ... print(obj.attr2)
  52. ... print(obj.attr3)
  53. >>> dump(attrs.__code__)
  54. name: attrs
  55. argcount: 1
  56. posonlyargcount: 0
  57. kwonlyargcount: 0
  58. names: ('print', 'attr1', 'attr2', 'attr3')
  59. varnames: ('obj',)
  60. cellvars: ()
  61. freevars: ()
  62. nlocals: 1
  63. flags: 3
  64. consts: ('None',)
  65. >>> def optimize_away():
  66. ... 'doc string'
  67. ... 'not a docstring'
  68. ... 53
  69. ... 0x53
  70. >>> dump(optimize_away.__code__)
  71. name: optimize_away
  72. argcount: 0
  73. posonlyargcount: 0
  74. kwonlyargcount: 0
  75. names: ()
  76. varnames: ()
  77. cellvars: ()
  78. freevars: ()
  79. nlocals: 0
  80. flags: 3
  81. consts: ("'doc string'", 'None')
  82. >>> def keywordonly_args(a,b,*,k1):
  83. ... return a,b,k1
  84. ...
  85. >>> dump(keywordonly_args.__code__)
  86. name: keywordonly_args
  87. argcount: 2
  88. posonlyargcount: 0
  89. kwonlyargcount: 1
  90. names: ()
  91. varnames: ('a', 'b', 'k1')
  92. cellvars: ()
  93. freevars: ()
  94. nlocals: 3
  95. flags: 3
  96. consts: ('None',)
  97. >>> def posonly_args(a,b,/,c):
  98. ... return a,b,c
  99. ...
  100. >>> dump(posonly_args.__code__)
  101. name: posonly_args
  102. argcount: 3
  103. posonlyargcount: 2
  104. kwonlyargcount: 0
  105. names: ()
  106. varnames: ('a', 'b', 'c')
  107. cellvars: ()
  108. freevars: ()
  109. nlocals: 3
  110. flags: 3
  111. consts: ('None',)
  112. """
  113. import inspect
  114. import sys
  115. import threading
  116. import doctest
  117. import unittest
  118. import textwrap
  119. import weakref
  120. import dis
  121. try:
  122. import ctypes
  123. except ImportError:
  124. ctypes = None
  125. from test.support import (cpython_only,
  126. check_impl_detail, requires_debug_ranges,
  127. gc_collect)
  128. from test.support.script_helper import assert_python_ok
  129. from test.support import threading_helper
  130. from opcode import opmap
  131. COPY_FREE_VARS = opmap['COPY_FREE_VARS']
  132. def consts(t):
  133. """Yield a doctest-safe sequence of object reprs."""
  134. for elt in t:
  135. r = repr(elt)
  136. if r.startswith("<code object"):
  137. yield "<code object %s>" % elt.co_name
  138. else:
  139. yield r
  140. def dump(co):
  141. """Print out a text representation of a code object."""
  142. for attr in ["name", "argcount", "posonlyargcount",
  143. "kwonlyargcount", "names", "varnames",
  144. "cellvars", "freevars", "nlocals", "flags"]:
  145. print("%s: %s" % (attr, getattr(co, "co_" + attr)))
  146. print("consts:", tuple(consts(co.co_consts)))
  147. # Needed for test_closure_injection below
  148. # Defined at global scope to avoid implicitly closing over __class__
  149. def external_getitem(self, i):
  150. return f"Foreign getitem: {super().__getitem__(i)}"
  151. class CodeTest(unittest.TestCase):
  152. @cpython_only
  153. def test_newempty(self):
  154. import _testcapi
  155. co = _testcapi.code_newempty("filename", "funcname", 15)
  156. self.assertEqual(co.co_filename, "filename")
  157. self.assertEqual(co.co_name, "funcname")
  158. self.assertEqual(co.co_firstlineno, 15)
  159. #Empty code object should raise, but not crash the VM
  160. with self.assertRaises(Exception):
  161. exec(co)
  162. @cpython_only
  163. def test_closure_injection(self):
  164. # From https://bugs.python.org/issue32176
  165. from types import FunctionType
  166. def create_closure(__class__):
  167. return (lambda: __class__).__closure__
  168. def new_code(c):
  169. '''A new code object with a __class__ cell added to freevars'''
  170. return c.replace(co_freevars=c.co_freevars + ('__class__',), co_code=bytes([COPY_FREE_VARS, 1])+c.co_code)
  171. def add_foreign_method(cls, name, f):
  172. code = new_code(f.__code__)
  173. assert not f.__closure__
  174. closure = create_closure(cls)
  175. defaults = f.__defaults__
  176. setattr(cls, name, FunctionType(code, globals(), name, defaults, closure))
  177. class List(list):
  178. pass
  179. add_foreign_method(List, "__getitem__", external_getitem)
  180. # Ensure the closure injection actually worked
  181. function = List.__getitem__
  182. class_ref = function.__closure__[0].cell_contents
  183. self.assertIs(class_ref, List)
  184. # Ensure the zero-arg super() call in the injected method works
  185. obj = List([1, 2, 3])
  186. self.assertEqual(obj[0], "Foreign getitem: 1")
  187. def test_constructor(self):
  188. def func(): pass
  189. co = func.__code__
  190. CodeType = type(co)
  191. # test code constructor
  192. CodeType(co.co_argcount,
  193. co.co_posonlyargcount,
  194. co.co_kwonlyargcount,
  195. co.co_nlocals,
  196. co.co_stacksize,
  197. co.co_flags,
  198. co.co_code,
  199. co.co_consts,
  200. co.co_names,
  201. co.co_varnames,
  202. co.co_filename,
  203. co.co_name,
  204. co.co_qualname,
  205. co.co_firstlineno,
  206. co.co_linetable,
  207. co.co_exceptiontable,
  208. co.co_freevars,
  209. co.co_cellvars)
  210. def test_qualname(self):
  211. self.assertEqual(
  212. CodeTest.test_qualname.__code__.co_qualname,
  213. CodeTest.test_qualname.__qualname__
  214. )
  215. def test_replace(self):
  216. def func():
  217. x = 1
  218. return x
  219. code = func.__code__
  220. # different co_name, co_varnames, co_consts
  221. def func2():
  222. y = 2
  223. z = 3
  224. return y
  225. code2 = func2.__code__
  226. for attr, value in (
  227. ("co_argcount", 0),
  228. ("co_posonlyargcount", 0),
  229. ("co_kwonlyargcount", 0),
  230. ("co_nlocals", 1),
  231. ("co_stacksize", 0),
  232. ("co_flags", code.co_flags | inspect.CO_COROUTINE),
  233. ("co_firstlineno", 100),
  234. ("co_code", code2.co_code),
  235. ("co_consts", code2.co_consts),
  236. ("co_names", ("myname",)),
  237. ("co_varnames", ('spam',)),
  238. ("co_freevars", ("freevar",)),
  239. ("co_cellvars", ("cellvar",)),
  240. ("co_filename", "newfilename"),
  241. ("co_name", "newname"),
  242. ("co_linetable", code2.co_linetable),
  243. ):
  244. with self.subTest(attr=attr, value=value):
  245. new_code = code.replace(**{attr: value})
  246. self.assertEqual(getattr(new_code, attr), value)
  247. new_code = code.replace(co_varnames=code2.co_varnames,
  248. co_nlocals=code2.co_nlocals)
  249. self.assertEqual(new_code.co_varnames, code2.co_varnames)
  250. self.assertEqual(new_code.co_nlocals, code2.co_nlocals)
  251. def test_nlocals_mismatch(self):
  252. def func():
  253. x = 1
  254. return x
  255. co = func.__code__
  256. assert co.co_nlocals > 0;
  257. # First we try the constructor.
  258. CodeType = type(co)
  259. for diff in (-1, 1):
  260. with self.assertRaises(ValueError):
  261. CodeType(co.co_argcount,
  262. co.co_posonlyargcount,
  263. co.co_kwonlyargcount,
  264. # This is the only change.
  265. co.co_nlocals + diff,
  266. co.co_stacksize,
  267. co.co_flags,
  268. co.co_code,
  269. co.co_consts,
  270. co.co_names,
  271. co.co_varnames,
  272. co.co_filename,
  273. co.co_name,
  274. co.co_qualname,
  275. co.co_firstlineno,
  276. co.co_linetable,
  277. co.co_exceptiontable,
  278. co.co_freevars,
  279. co.co_cellvars,
  280. )
  281. # Then we try the replace method.
  282. with self.assertRaises(ValueError):
  283. co.replace(co_nlocals=co.co_nlocals - 1)
  284. with self.assertRaises(ValueError):
  285. co.replace(co_nlocals=co.co_nlocals + 1)
  286. def test_shrinking_localsplus(self):
  287. # Check that PyCode_NewWithPosOnlyArgs resizes both
  288. # localsplusnames and localspluskinds, if an argument is a cell.
  289. def func(arg):
  290. return lambda: arg
  291. code = func.__code__
  292. newcode = code.replace(co_name="func") # Should not raise SystemError
  293. self.assertEqual(code, newcode)
  294. def test_empty_linetable(self):
  295. def func():
  296. pass
  297. new_code = code = func.__code__.replace(co_linetable=b'')
  298. self.assertEqual(list(new_code.co_lines()), [])
  299. @requires_debug_ranges()
  300. def test_co_positions_artificial_instructions(self):
  301. import dis
  302. namespace = {}
  303. exec(textwrap.dedent("""\
  304. try:
  305. 1/0
  306. except Exception as e:
  307. exc = e
  308. """), namespace)
  309. exc = namespace['exc']
  310. traceback = exc.__traceback__
  311. code = traceback.tb_frame.f_code
  312. artificial_instructions = []
  313. for instr, positions in zip(
  314. dis.get_instructions(code, show_caches=True),
  315. code.co_positions(),
  316. strict=True
  317. ):
  318. # If any of the positions is None, then all have to
  319. # be None as well for the case above. There are still
  320. # some places in the compiler, where the artificial instructions
  321. # get assigned the first_lineno but they don't have other positions.
  322. # There is no easy way of inferring them at that stage, so for now
  323. # we don't support it.
  324. self.assertIn(positions.count(None), [0, 3, 4])
  325. if not any(positions):
  326. artificial_instructions.append(instr)
  327. self.assertEqual(
  328. [
  329. (instruction.opname, instruction.argval)
  330. for instruction in artificial_instructions
  331. ],
  332. [
  333. ("PUSH_EXC_INFO", None),
  334. ("LOAD_CONST", None), # artificial 'None'
  335. ("STORE_NAME", "e"), # XX: we know the location for this
  336. ("DELETE_NAME", "e"),
  337. ("RERAISE", 1),
  338. ("COPY", 3),
  339. ("POP_EXCEPT", None),
  340. ("RERAISE", 1)
  341. ]
  342. )
  343. def test_endline_and_columntable_none_when_no_debug_ranges(self):
  344. # Make sure that if `-X no_debug_ranges` is used, there is
  345. # minimal debug info
  346. code = textwrap.dedent("""
  347. def f():
  348. pass
  349. positions = f.__code__.co_positions()
  350. for line, end_line, column, end_column in positions:
  351. assert line == end_line
  352. assert column is None
  353. assert end_column is None
  354. """)
  355. assert_python_ok('-X', 'no_debug_ranges', '-c', code)
  356. def test_endline_and_columntable_none_when_no_debug_ranges_env(self):
  357. # Same as above but using the environment variable opt out.
  358. code = textwrap.dedent("""
  359. def f():
  360. pass
  361. positions = f.__code__.co_positions()
  362. for line, end_line, column, end_column in positions:
  363. assert line == end_line
  364. assert column is None
  365. assert end_column is None
  366. """)
  367. assert_python_ok('-c', code, PYTHONNODEBUGRANGES='1')
  368. # co_positions behavior when info is missing.
  369. @requires_debug_ranges()
  370. def test_co_positions_empty_linetable(self):
  371. def func():
  372. x = 1
  373. new_code = func.__code__.replace(co_linetable=b'')
  374. positions = new_code.co_positions()
  375. for line, end_line, column, end_column in positions:
  376. self.assertIsNone(line)
  377. self.assertEqual(end_line, new_code.co_firstlineno + 1)
  378. def test_code_equality(self):
  379. def f():
  380. try:
  381. a()
  382. except:
  383. b()
  384. else:
  385. c()
  386. finally:
  387. d()
  388. code_a = f.__code__
  389. code_b = code_a.replace(co_linetable=b"")
  390. code_c = code_a.replace(co_exceptiontable=b"")
  391. code_d = code_b.replace(co_exceptiontable=b"")
  392. self.assertNotEqual(code_a, code_b)
  393. self.assertNotEqual(code_a, code_c)
  394. self.assertNotEqual(code_a, code_d)
  395. self.assertNotEqual(code_b, code_c)
  396. self.assertNotEqual(code_b, code_d)
  397. self.assertNotEqual(code_c, code_d)
  398. def isinterned(s):
  399. return s is sys.intern(('_' + s + '_')[1:-1])
  400. class CodeConstsTest(unittest.TestCase):
  401. def find_const(self, consts, value):
  402. for v in consts:
  403. if v == value:
  404. return v
  405. self.assertIn(value, consts) # raises an exception
  406. self.fail('Should never be reached')
  407. def assertIsInterned(self, s):
  408. if not isinterned(s):
  409. self.fail('String %r is not interned' % (s,))
  410. def assertIsNotInterned(self, s):
  411. if isinterned(s):
  412. self.fail('String %r is interned' % (s,))
  413. @cpython_only
  414. def test_interned_string(self):
  415. co = compile('res = "str_value"', '?', 'exec')
  416. v = self.find_const(co.co_consts, 'str_value')
  417. self.assertIsInterned(v)
  418. @cpython_only
  419. def test_interned_string_in_tuple(self):
  420. co = compile('res = ("str_value",)', '?', 'exec')
  421. v = self.find_const(co.co_consts, ('str_value',))
  422. self.assertIsInterned(v[0])
  423. @cpython_only
  424. def test_interned_string_in_frozenset(self):
  425. co = compile('res = a in {"str_value"}', '?', 'exec')
  426. v = self.find_const(co.co_consts, frozenset(('str_value',)))
  427. self.assertIsInterned(tuple(v)[0])
  428. @cpython_only
  429. def test_interned_string_default(self):
  430. def f(a='str_value'):
  431. return a
  432. self.assertIsInterned(f())
  433. @cpython_only
  434. def test_interned_string_with_null(self):
  435. co = compile(r'res = "str\0value!"', '?', 'exec')
  436. v = self.find_const(co.co_consts, 'str\0value!')
  437. self.assertIsNotInterned(v)
  438. class CodeWeakRefTest(unittest.TestCase):
  439. def test_basic(self):
  440. # Create a code object in a clean environment so that we know we have
  441. # the only reference to it left.
  442. namespace = {}
  443. exec("def f(): pass", globals(), namespace)
  444. f = namespace["f"]
  445. del namespace
  446. self.called = False
  447. def callback(code):
  448. self.called = True
  449. # f is now the last reference to the function, and through it, the code
  450. # object. While we hold it, check that we can create a weakref and
  451. # deref it. Then delete it, and check that the callback gets called and
  452. # the reference dies.
  453. coderef = weakref.ref(f.__code__, callback)
  454. self.assertTrue(bool(coderef()))
  455. del f
  456. gc_collect() # For PyPy or other GCs.
  457. self.assertFalse(bool(coderef()))
  458. self.assertTrue(self.called)
  459. # Python implementation of location table parsing algorithm
  460. def read(it):
  461. return next(it)
  462. def read_varint(it):
  463. b = read(it)
  464. val = b & 63;
  465. shift = 0;
  466. while b & 64:
  467. b = read(it)
  468. shift += 6
  469. val |= (b&63) << shift
  470. return val
  471. def read_signed_varint(it):
  472. uval = read_varint(it)
  473. if uval & 1:
  474. return -(uval >> 1)
  475. else:
  476. return uval >> 1
  477. def parse_location_table(code):
  478. line = code.co_firstlineno
  479. it = iter(code.co_linetable)
  480. while True:
  481. try:
  482. first_byte = read(it)
  483. except StopIteration:
  484. return
  485. code = (first_byte >> 3) & 15
  486. length = (first_byte & 7) + 1
  487. if code == 15:
  488. yield (code, length, None, None, None, None)
  489. elif code == 14:
  490. line_delta = read_signed_varint(it)
  491. line += line_delta
  492. end_line = line + read_varint(it)
  493. col = read_varint(it)
  494. if col == 0:
  495. col = None
  496. else:
  497. col -= 1
  498. end_col = read_varint(it)
  499. if end_col == 0:
  500. end_col = None
  501. else:
  502. end_col -= 1
  503. yield (code, length, line, end_line, col, end_col)
  504. elif code == 13: # No column
  505. line_delta = read_signed_varint(it)
  506. line += line_delta
  507. yield (code, length, line, line, None, None)
  508. elif code in (10, 11, 12): # new line
  509. line_delta = code - 10
  510. line += line_delta
  511. column = read(it)
  512. end_column = read(it)
  513. yield (code, length, line, line, column, end_column)
  514. else:
  515. assert (0 <= code < 10)
  516. second_byte = read(it)
  517. column = code << 3 | (second_byte >> 4)
  518. yield (code, length, line, line, column, column + (second_byte & 15))
  519. def positions_from_location_table(code):
  520. for _, length, line, end_line, col, end_col in parse_location_table(code):
  521. for _ in range(length):
  522. yield (line, end_line, col, end_col)
  523. def dedup(lst, prev=object()):
  524. for item in lst:
  525. if item != prev:
  526. yield item
  527. prev = item
  528. def lines_from_postions(positions):
  529. return dedup(l for (l, _, _, _) in positions)
  530. def misshappen():
  531. """
  532. """
  533. x = (
  534. 4
  535. +
  536. y
  537. )
  538. y = (
  539. a
  540. +
  541. b
  542. +
  543. d
  544. )
  545. return q if (
  546. x
  547. ) else p
  548. def bug93662():
  549. example_report_generation_message= (
  550. """
  551. """
  552. ).strip()
  553. raise ValueError()
  554. class CodeLocationTest(unittest.TestCase):
  555. def check_positions(self, func):
  556. pos1 = list(func.__code__.co_positions())
  557. pos2 = list(positions_from_location_table(func.__code__))
  558. for l1, l2 in zip(pos1, pos2):
  559. self.assertEqual(l1, l2)
  560. self.assertEqual(len(pos1), len(pos2))
  561. def test_positions(self):
  562. self.check_positions(parse_location_table)
  563. self.check_positions(misshappen)
  564. self.check_positions(bug93662)
  565. def check_lines(self, func):
  566. co = func.__code__
  567. lines1 = list(dedup(l for (_, _, l) in co.co_lines()))
  568. lines2 = list(lines_from_postions(positions_from_location_table(co)))
  569. for l1, l2 in zip(lines1, lines2):
  570. self.assertEqual(l1, l2)
  571. self.assertEqual(len(lines1), len(lines2))
  572. def test_lines(self):
  573. self.check_lines(parse_location_table)
  574. self.check_lines(misshappen)
  575. self.check_lines(bug93662)
  576. @cpython_only
  577. def test_code_new_empty(self):
  578. # If this test fails, it means that the construction of PyCode_NewEmpty
  579. # needs to be modified! Please update this test *and* PyCode_NewEmpty,
  580. # so that they both stay in sync.
  581. def f():
  582. pass
  583. PY_CODE_LOCATION_INFO_NO_COLUMNS = 13
  584. f.__code__ = f.__code__.replace(
  585. co_firstlineno=42,
  586. co_code=bytes(
  587. [
  588. dis.opmap["RESUME"], 0,
  589. dis.opmap["LOAD_ASSERTION_ERROR"], 0,
  590. dis.opmap["RAISE_VARARGS"], 1,
  591. ]
  592. ),
  593. co_linetable=bytes(
  594. [
  595. (1 << 7)
  596. | (PY_CODE_LOCATION_INFO_NO_COLUMNS << 3)
  597. | (3 - 1),
  598. 0,
  599. ]
  600. ),
  601. )
  602. self.assertRaises(AssertionError, f)
  603. self.assertEqual(
  604. list(f.__code__.co_positions()),
  605. 3 * [(42, 42, None, None)],
  606. )
  607. if check_impl_detail(cpython=True) and ctypes is not None:
  608. py = ctypes.pythonapi
  609. freefunc = ctypes.CFUNCTYPE(None,ctypes.c_voidp)
  610. RequestCodeExtraIndex = py._PyEval_RequestCodeExtraIndex
  611. RequestCodeExtraIndex.argtypes = (freefunc,)
  612. RequestCodeExtraIndex.restype = ctypes.c_ssize_t
  613. SetExtra = py._PyCode_SetExtra
  614. SetExtra.argtypes = (ctypes.py_object, ctypes.c_ssize_t, ctypes.c_voidp)
  615. SetExtra.restype = ctypes.c_int
  616. GetExtra = py._PyCode_GetExtra
  617. GetExtra.argtypes = (ctypes.py_object, ctypes.c_ssize_t,
  618. ctypes.POINTER(ctypes.c_voidp))
  619. GetExtra.restype = ctypes.c_int
  620. LAST_FREED = None
  621. def myfree(ptr):
  622. global LAST_FREED
  623. LAST_FREED = ptr
  624. FREE_FUNC = freefunc(myfree)
  625. FREE_INDEX = RequestCodeExtraIndex(FREE_FUNC)
  626. class CoExtra(unittest.TestCase):
  627. def get_func(self):
  628. # Defining a function causes the containing function to have a
  629. # reference to the code object. We need the code objects to go
  630. # away, so we eval a lambda.
  631. return eval('lambda:42')
  632. def test_get_non_code(self):
  633. f = self.get_func()
  634. self.assertRaises(SystemError, SetExtra, 42, FREE_INDEX,
  635. ctypes.c_voidp(100))
  636. self.assertRaises(SystemError, GetExtra, 42, FREE_INDEX,
  637. ctypes.c_voidp(100))
  638. def test_bad_index(self):
  639. f = self.get_func()
  640. self.assertRaises(SystemError, SetExtra, f.__code__,
  641. FREE_INDEX+100, ctypes.c_voidp(100))
  642. self.assertEqual(GetExtra(f.__code__, FREE_INDEX+100,
  643. ctypes.c_voidp(100)), 0)
  644. def test_free_called(self):
  645. # Verify that the provided free function gets invoked
  646. # when the code object is cleaned up.
  647. f = self.get_func()
  648. SetExtra(f.__code__, FREE_INDEX, ctypes.c_voidp(100))
  649. del f
  650. self.assertEqual(LAST_FREED, 100)
  651. def test_get_set(self):
  652. # Test basic get/set round tripping.
  653. f = self.get_func()
  654. extra = ctypes.c_voidp()
  655. SetExtra(f.__code__, FREE_INDEX, ctypes.c_voidp(200))
  656. # reset should free...
  657. SetExtra(f.__code__, FREE_INDEX, ctypes.c_voidp(300))
  658. self.assertEqual(LAST_FREED, 200)
  659. extra = ctypes.c_voidp()
  660. GetExtra(f.__code__, FREE_INDEX, extra)
  661. self.assertEqual(extra.value, 300)
  662. del f
  663. @threading_helper.requires_working_threading()
  664. def test_free_different_thread(self):
  665. # Freeing a code object on a different thread then
  666. # where the co_extra was set should be safe.
  667. f = self.get_func()
  668. class ThreadTest(threading.Thread):
  669. def __init__(self, f, test):
  670. super().__init__()
  671. self.f = f
  672. self.test = test
  673. def run(self):
  674. del self.f
  675. self.test.assertEqual(LAST_FREED, 500)
  676. SetExtra(f.__code__, FREE_INDEX, ctypes.c_voidp(500))
  677. tt = ThreadTest(f, self)
  678. del f
  679. tt.start()
  680. tt.join()
  681. self.assertEqual(LAST_FREED, 500)
  682. def load_tests(loader, tests, pattern):
  683. tests.addTest(doctest.DocTestSuite())
  684. return tests
  685. if __name__ == "__main__":
  686. unittest.main()