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