17db96d56Sopenharmony_ci"""This module includes tests of the code object representation. 27db96d56Sopenharmony_ci 37db96d56Sopenharmony_ci>>> def f(x): 47db96d56Sopenharmony_ci... def g(y): 57db96d56Sopenharmony_ci... return x + y 67db96d56Sopenharmony_ci... return g 77db96d56Sopenharmony_ci... 87db96d56Sopenharmony_ci 97db96d56Sopenharmony_ci>>> dump(f.__code__) 107db96d56Sopenharmony_ciname: f 117db96d56Sopenharmony_ciargcount: 1 127db96d56Sopenharmony_ciposonlyargcount: 0 137db96d56Sopenharmony_cikwonlyargcount: 0 147db96d56Sopenharmony_cinames: () 157db96d56Sopenharmony_civarnames: ('x', 'g') 167db96d56Sopenharmony_cicellvars: ('x',) 177db96d56Sopenharmony_cifreevars: () 187db96d56Sopenharmony_cinlocals: 2 197db96d56Sopenharmony_ciflags: 3 207db96d56Sopenharmony_ciconsts: ('None', '<code object g>') 217db96d56Sopenharmony_ci 227db96d56Sopenharmony_ci>>> dump(f(4).__code__) 237db96d56Sopenharmony_ciname: g 247db96d56Sopenharmony_ciargcount: 1 257db96d56Sopenharmony_ciposonlyargcount: 0 267db96d56Sopenharmony_cikwonlyargcount: 0 277db96d56Sopenharmony_cinames: () 287db96d56Sopenharmony_civarnames: ('y',) 297db96d56Sopenharmony_cicellvars: () 307db96d56Sopenharmony_cifreevars: ('x',) 317db96d56Sopenharmony_cinlocals: 1 327db96d56Sopenharmony_ciflags: 19 337db96d56Sopenharmony_ciconsts: ('None',) 347db96d56Sopenharmony_ci 357db96d56Sopenharmony_ci>>> def h(x, y): 367db96d56Sopenharmony_ci... a = x + y 377db96d56Sopenharmony_ci... b = x - y 387db96d56Sopenharmony_ci... c = a * b 397db96d56Sopenharmony_ci... return c 407db96d56Sopenharmony_ci... 417db96d56Sopenharmony_ci 427db96d56Sopenharmony_ci>>> dump(h.__code__) 437db96d56Sopenharmony_ciname: h 447db96d56Sopenharmony_ciargcount: 2 457db96d56Sopenharmony_ciposonlyargcount: 0 467db96d56Sopenharmony_cikwonlyargcount: 0 477db96d56Sopenharmony_cinames: () 487db96d56Sopenharmony_civarnames: ('x', 'y', 'a', 'b', 'c') 497db96d56Sopenharmony_cicellvars: () 507db96d56Sopenharmony_cifreevars: () 517db96d56Sopenharmony_cinlocals: 5 527db96d56Sopenharmony_ciflags: 3 537db96d56Sopenharmony_ciconsts: ('None',) 547db96d56Sopenharmony_ci 557db96d56Sopenharmony_ci>>> def attrs(obj): 567db96d56Sopenharmony_ci... print(obj.attr1) 577db96d56Sopenharmony_ci... print(obj.attr2) 587db96d56Sopenharmony_ci... print(obj.attr3) 597db96d56Sopenharmony_ci 607db96d56Sopenharmony_ci>>> dump(attrs.__code__) 617db96d56Sopenharmony_ciname: attrs 627db96d56Sopenharmony_ciargcount: 1 637db96d56Sopenharmony_ciposonlyargcount: 0 647db96d56Sopenharmony_cikwonlyargcount: 0 657db96d56Sopenharmony_cinames: ('print', 'attr1', 'attr2', 'attr3') 667db96d56Sopenharmony_civarnames: ('obj',) 677db96d56Sopenharmony_cicellvars: () 687db96d56Sopenharmony_cifreevars: () 697db96d56Sopenharmony_cinlocals: 1 707db96d56Sopenharmony_ciflags: 3 717db96d56Sopenharmony_ciconsts: ('None',) 727db96d56Sopenharmony_ci 737db96d56Sopenharmony_ci>>> def optimize_away(): 747db96d56Sopenharmony_ci... 'doc string' 757db96d56Sopenharmony_ci... 'not a docstring' 767db96d56Sopenharmony_ci... 53 777db96d56Sopenharmony_ci... 0x53 787db96d56Sopenharmony_ci 797db96d56Sopenharmony_ci>>> dump(optimize_away.__code__) 807db96d56Sopenharmony_ciname: optimize_away 817db96d56Sopenharmony_ciargcount: 0 827db96d56Sopenharmony_ciposonlyargcount: 0 837db96d56Sopenharmony_cikwonlyargcount: 0 847db96d56Sopenharmony_cinames: () 857db96d56Sopenharmony_civarnames: () 867db96d56Sopenharmony_cicellvars: () 877db96d56Sopenharmony_cifreevars: () 887db96d56Sopenharmony_cinlocals: 0 897db96d56Sopenharmony_ciflags: 3 907db96d56Sopenharmony_ciconsts: ("'doc string'", 'None') 917db96d56Sopenharmony_ci 927db96d56Sopenharmony_ci>>> def keywordonly_args(a,b,*,k1): 937db96d56Sopenharmony_ci... return a,b,k1 947db96d56Sopenharmony_ci... 957db96d56Sopenharmony_ci 967db96d56Sopenharmony_ci>>> dump(keywordonly_args.__code__) 977db96d56Sopenharmony_ciname: keywordonly_args 987db96d56Sopenharmony_ciargcount: 2 997db96d56Sopenharmony_ciposonlyargcount: 0 1007db96d56Sopenharmony_cikwonlyargcount: 1 1017db96d56Sopenharmony_cinames: () 1027db96d56Sopenharmony_civarnames: ('a', 'b', 'k1') 1037db96d56Sopenharmony_cicellvars: () 1047db96d56Sopenharmony_cifreevars: () 1057db96d56Sopenharmony_cinlocals: 3 1067db96d56Sopenharmony_ciflags: 3 1077db96d56Sopenharmony_ciconsts: ('None',) 1087db96d56Sopenharmony_ci 1097db96d56Sopenharmony_ci>>> def posonly_args(a,b,/,c): 1107db96d56Sopenharmony_ci... return a,b,c 1117db96d56Sopenharmony_ci... 1127db96d56Sopenharmony_ci 1137db96d56Sopenharmony_ci>>> dump(posonly_args.__code__) 1147db96d56Sopenharmony_ciname: posonly_args 1157db96d56Sopenharmony_ciargcount: 3 1167db96d56Sopenharmony_ciposonlyargcount: 2 1177db96d56Sopenharmony_cikwonlyargcount: 0 1187db96d56Sopenharmony_cinames: () 1197db96d56Sopenharmony_civarnames: ('a', 'b', 'c') 1207db96d56Sopenharmony_cicellvars: () 1217db96d56Sopenharmony_cifreevars: () 1227db96d56Sopenharmony_cinlocals: 3 1237db96d56Sopenharmony_ciflags: 3 1247db96d56Sopenharmony_ciconsts: ('None',) 1257db96d56Sopenharmony_ci 1267db96d56Sopenharmony_ci""" 1277db96d56Sopenharmony_ci 1287db96d56Sopenharmony_ciimport inspect 1297db96d56Sopenharmony_ciimport sys 1307db96d56Sopenharmony_ciimport threading 1317db96d56Sopenharmony_ciimport doctest 1327db96d56Sopenharmony_ciimport unittest 1337db96d56Sopenharmony_ciimport textwrap 1347db96d56Sopenharmony_ciimport weakref 1357db96d56Sopenharmony_ciimport dis 1367db96d56Sopenharmony_ci 1377db96d56Sopenharmony_citry: 1387db96d56Sopenharmony_ci import ctypes 1397db96d56Sopenharmony_ciexcept ImportError: 1407db96d56Sopenharmony_ci ctypes = None 1417db96d56Sopenharmony_cifrom test.support import (cpython_only, 1427db96d56Sopenharmony_ci check_impl_detail, requires_debug_ranges, 1437db96d56Sopenharmony_ci gc_collect) 1447db96d56Sopenharmony_cifrom test.support.script_helper import assert_python_ok 1457db96d56Sopenharmony_cifrom test.support import threading_helper 1467db96d56Sopenharmony_cifrom opcode import opmap 1477db96d56Sopenharmony_ciCOPY_FREE_VARS = opmap['COPY_FREE_VARS'] 1487db96d56Sopenharmony_ci 1497db96d56Sopenharmony_ci 1507db96d56Sopenharmony_cidef consts(t): 1517db96d56Sopenharmony_ci """Yield a doctest-safe sequence of object reprs.""" 1527db96d56Sopenharmony_ci for elt in t: 1537db96d56Sopenharmony_ci r = repr(elt) 1547db96d56Sopenharmony_ci if r.startswith("<code object"): 1557db96d56Sopenharmony_ci yield "<code object %s>" % elt.co_name 1567db96d56Sopenharmony_ci else: 1577db96d56Sopenharmony_ci yield r 1587db96d56Sopenharmony_ci 1597db96d56Sopenharmony_cidef dump(co): 1607db96d56Sopenharmony_ci """Print out a text representation of a code object.""" 1617db96d56Sopenharmony_ci for attr in ["name", "argcount", "posonlyargcount", 1627db96d56Sopenharmony_ci "kwonlyargcount", "names", "varnames", 1637db96d56Sopenharmony_ci "cellvars", "freevars", "nlocals", "flags"]: 1647db96d56Sopenharmony_ci print("%s: %s" % (attr, getattr(co, "co_" + attr))) 1657db96d56Sopenharmony_ci print("consts:", tuple(consts(co.co_consts))) 1667db96d56Sopenharmony_ci 1677db96d56Sopenharmony_ci# Needed for test_closure_injection below 1687db96d56Sopenharmony_ci# Defined at global scope to avoid implicitly closing over __class__ 1697db96d56Sopenharmony_cidef external_getitem(self, i): 1707db96d56Sopenharmony_ci return f"Foreign getitem: {super().__getitem__(i)}" 1717db96d56Sopenharmony_ci 1727db96d56Sopenharmony_ciclass CodeTest(unittest.TestCase): 1737db96d56Sopenharmony_ci 1747db96d56Sopenharmony_ci @cpython_only 1757db96d56Sopenharmony_ci def test_newempty(self): 1767db96d56Sopenharmony_ci import _testcapi 1777db96d56Sopenharmony_ci co = _testcapi.code_newempty("filename", "funcname", 15) 1787db96d56Sopenharmony_ci self.assertEqual(co.co_filename, "filename") 1797db96d56Sopenharmony_ci self.assertEqual(co.co_name, "funcname") 1807db96d56Sopenharmony_ci self.assertEqual(co.co_firstlineno, 15) 1817db96d56Sopenharmony_ci #Empty code object should raise, but not crash the VM 1827db96d56Sopenharmony_ci with self.assertRaises(Exception): 1837db96d56Sopenharmony_ci exec(co) 1847db96d56Sopenharmony_ci 1857db96d56Sopenharmony_ci @cpython_only 1867db96d56Sopenharmony_ci def test_closure_injection(self): 1877db96d56Sopenharmony_ci # From https://bugs.python.org/issue32176 1887db96d56Sopenharmony_ci from types import FunctionType 1897db96d56Sopenharmony_ci 1907db96d56Sopenharmony_ci def create_closure(__class__): 1917db96d56Sopenharmony_ci return (lambda: __class__).__closure__ 1927db96d56Sopenharmony_ci 1937db96d56Sopenharmony_ci def new_code(c): 1947db96d56Sopenharmony_ci '''A new code object with a __class__ cell added to freevars''' 1957db96d56Sopenharmony_ci return c.replace(co_freevars=c.co_freevars + ('__class__',), co_code=bytes([COPY_FREE_VARS, 1])+c.co_code) 1967db96d56Sopenharmony_ci 1977db96d56Sopenharmony_ci def add_foreign_method(cls, name, f): 1987db96d56Sopenharmony_ci code = new_code(f.__code__) 1997db96d56Sopenharmony_ci assert not f.__closure__ 2007db96d56Sopenharmony_ci closure = create_closure(cls) 2017db96d56Sopenharmony_ci defaults = f.__defaults__ 2027db96d56Sopenharmony_ci setattr(cls, name, FunctionType(code, globals(), name, defaults, closure)) 2037db96d56Sopenharmony_ci 2047db96d56Sopenharmony_ci class List(list): 2057db96d56Sopenharmony_ci pass 2067db96d56Sopenharmony_ci 2077db96d56Sopenharmony_ci add_foreign_method(List, "__getitem__", external_getitem) 2087db96d56Sopenharmony_ci 2097db96d56Sopenharmony_ci # Ensure the closure injection actually worked 2107db96d56Sopenharmony_ci function = List.__getitem__ 2117db96d56Sopenharmony_ci class_ref = function.__closure__[0].cell_contents 2127db96d56Sopenharmony_ci self.assertIs(class_ref, List) 2137db96d56Sopenharmony_ci 2147db96d56Sopenharmony_ci # Ensure the zero-arg super() call in the injected method works 2157db96d56Sopenharmony_ci obj = List([1, 2, 3]) 2167db96d56Sopenharmony_ci self.assertEqual(obj[0], "Foreign getitem: 1") 2177db96d56Sopenharmony_ci 2187db96d56Sopenharmony_ci def test_constructor(self): 2197db96d56Sopenharmony_ci def func(): pass 2207db96d56Sopenharmony_ci co = func.__code__ 2217db96d56Sopenharmony_ci CodeType = type(co) 2227db96d56Sopenharmony_ci 2237db96d56Sopenharmony_ci # test code constructor 2247db96d56Sopenharmony_ci CodeType(co.co_argcount, 2257db96d56Sopenharmony_ci co.co_posonlyargcount, 2267db96d56Sopenharmony_ci co.co_kwonlyargcount, 2277db96d56Sopenharmony_ci co.co_nlocals, 2287db96d56Sopenharmony_ci co.co_stacksize, 2297db96d56Sopenharmony_ci co.co_flags, 2307db96d56Sopenharmony_ci co.co_code, 2317db96d56Sopenharmony_ci co.co_consts, 2327db96d56Sopenharmony_ci co.co_names, 2337db96d56Sopenharmony_ci co.co_varnames, 2347db96d56Sopenharmony_ci co.co_filename, 2357db96d56Sopenharmony_ci co.co_name, 2367db96d56Sopenharmony_ci co.co_qualname, 2377db96d56Sopenharmony_ci co.co_firstlineno, 2387db96d56Sopenharmony_ci co.co_linetable, 2397db96d56Sopenharmony_ci co.co_exceptiontable, 2407db96d56Sopenharmony_ci co.co_freevars, 2417db96d56Sopenharmony_ci co.co_cellvars) 2427db96d56Sopenharmony_ci 2437db96d56Sopenharmony_ci def test_qualname(self): 2447db96d56Sopenharmony_ci self.assertEqual( 2457db96d56Sopenharmony_ci CodeTest.test_qualname.__code__.co_qualname, 2467db96d56Sopenharmony_ci CodeTest.test_qualname.__qualname__ 2477db96d56Sopenharmony_ci ) 2487db96d56Sopenharmony_ci 2497db96d56Sopenharmony_ci def test_replace(self): 2507db96d56Sopenharmony_ci def func(): 2517db96d56Sopenharmony_ci x = 1 2527db96d56Sopenharmony_ci return x 2537db96d56Sopenharmony_ci code = func.__code__ 2547db96d56Sopenharmony_ci 2557db96d56Sopenharmony_ci # different co_name, co_varnames, co_consts 2567db96d56Sopenharmony_ci def func2(): 2577db96d56Sopenharmony_ci y = 2 2587db96d56Sopenharmony_ci z = 3 2597db96d56Sopenharmony_ci return y 2607db96d56Sopenharmony_ci code2 = func2.__code__ 2617db96d56Sopenharmony_ci 2627db96d56Sopenharmony_ci for attr, value in ( 2637db96d56Sopenharmony_ci ("co_argcount", 0), 2647db96d56Sopenharmony_ci ("co_posonlyargcount", 0), 2657db96d56Sopenharmony_ci ("co_kwonlyargcount", 0), 2667db96d56Sopenharmony_ci ("co_nlocals", 1), 2677db96d56Sopenharmony_ci ("co_stacksize", 0), 2687db96d56Sopenharmony_ci ("co_flags", code.co_flags | inspect.CO_COROUTINE), 2697db96d56Sopenharmony_ci ("co_firstlineno", 100), 2707db96d56Sopenharmony_ci ("co_code", code2.co_code), 2717db96d56Sopenharmony_ci ("co_consts", code2.co_consts), 2727db96d56Sopenharmony_ci ("co_names", ("myname",)), 2737db96d56Sopenharmony_ci ("co_varnames", ('spam',)), 2747db96d56Sopenharmony_ci ("co_freevars", ("freevar",)), 2757db96d56Sopenharmony_ci ("co_cellvars", ("cellvar",)), 2767db96d56Sopenharmony_ci ("co_filename", "newfilename"), 2777db96d56Sopenharmony_ci ("co_name", "newname"), 2787db96d56Sopenharmony_ci ("co_linetable", code2.co_linetable), 2797db96d56Sopenharmony_ci ): 2807db96d56Sopenharmony_ci with self.subTest(attr=attr, value=value): 2817db96d56Sopenharmony_ci new_code = code.replace(**{attr: value}) 2827db96d56Sopenharmony_ci self.assertEqual(getattr(new_code, attr), value) 2837db96d56Sopenharmony_ci 2847db96d56Sopenharmony_ci new_code = code.replace(co_varnames=code2.co_varnames, 2857db96d56Sopenharmony_ci co_nlocals=code2.co_nlocals) 2867db96d56Sopenharmony_ci self.assertEqual(new_code.co_varnames, code2.co_varnames) 2877db96d56Sopenharmony_ci self.assertEqual(new_code.co_nlocals, code2.co_nlocals) 2887db96d56Sopenharmony_ci 2897db96d56Sopenharmony_ci def test_nlocals_mismatch(self): 2907db96d56Sopenharmony_ci def func(): 2917db96d56Sopenharmony_ci x = 1 2927db96d56Sopenharmony_ci return x 2937db96d56Sopenharmony_ci co = func.__code__ 2947db96d56Sopenharmony_ci assert co.co_nlocals > 0; 2957db96d56Sopenharmony_ci 2967db96d56Sopenharmony_ci # First we try the constructor. 2977db96d56Sopenharmony_ci CodeType = type(co) 2987db96d56Sopenharmony_ci for diff in (-1, 1): 2997db96d56Sopenharmony_ci with self.assertRaises(ValueError): 3007db96d56Sopenharmony_ci CodeType(co.co_argcount, 3017db96d56Sopenharmony_ci co.co_posonlyargcount, 3027db96d56Sopenharmony_ci co.co_kwonlyargcount, 3037db96d56Sopenharmony_ci # This is the only change. 3047db96d56Sopenharmony_ci co.co_nlocals + diff, 3057db96d56Sopenharmony_ci co.co_stacksize, 3067db96d56Sopenharmony_ci co.co_flags, 3077db96d56Sopenharmony_ci co.co_code, 3087db96d56Sopenharmony_ci co.co_consts, 3097db96d56Sopenharmony_ci co.co_names, 3107db96d56Sopenharmony_ci co.co_varnames, 3117db96d56Sopenharmony_ci co.co_filename, 3127db96d56Sopenharmony_ci co.co_name, 3137db96d56Sopenharmony_ci co.co_qualname, 3147db96d56Sopenharmony_ci co.co_firstlineno, 3157db96d56Sopenharmony_ci co.co_linetable, 3167db96d56Sopenharmony_ci co.co_exceptiontable, 3177db96d56Sopenharmony_ci co.co_freevars, 3187db96d56Sopenharmony_ci co.co_cellvars, 3197db96d56Sopenharmony_ci ) 3207db96d56Sopenharmony_ci # Then we try the replace method. 3217db96d56Sopenharmony_ci with self.assertRaises(ValueError): 3227db96d56Sopenharmony_ci co.replace(co_nlocals=co.co_nlocals - 1) 3237db96d56Sopenharmony_ci with self.assertRaises(ValueError): 3247db96d56Sopenharmony_ci co.replace(co_nlocals=co.co_nlocals + 1) 3257db96d56Sopenharmony_ci 3267db96d56Sopenharmony_ci def test_shrinking_localsplus(self): 3277db96d56Sopenharmony_ci # Check that PyCode_NewWithPosOnlyArgs resizes both 3287db96d56Sopenharmony_ci # localsplusnames and localspluskinds, if an argument is a cell. 3297db96d56Sopenharmony_ci def func(arg): 3307db96d56Sopenharmony_ci return lambda: arg 3317db96d56Sopenharmony_ci code = func.__code__ 3327db96d56Sopenharmony_ci newcode = code.replace(co_name="func") # Should not raise SystemError 3337db96d56Sopenharmony_ci self.assertEqual(code, newcode) 3347db96d56Sopenharmony_ci 3357db96d56Sopenharmony_ci def test_empty_linetable(self): 3367db96d56Sopenharmony_ci def func(): 3377db96d56Sopenharmony_ci pass 3387db96d56Sopenharmony_ci new_code = code = func.__code__.replace(co_linetable=b'') 3397db96d56Sopenharmony_ci self.assertEqual(list(new_code.co_lines()), []) 3407db96d56Sopenharmony_ci 3417db96d56Sopenharmony_ci @requires_debug_ranges() 3427db96d56Sopenharmony_ci def test_co_positions_artificial_instructions(self): 3437db96d56Sopenharmony_ci import dis 3447db96d56Sopenharmony_ci 3457db96d56Sopenharmony_ci namespace = {} 3467db96d56Sopenharmony_ci exec(textwrap.dedent("""\ 3477db96d56Sopenharmony_ci try: 3487db96d56Sopenharmony_ci 1/0 3497db96d56Sopenharmony_ci except Exception as e: 3507db96d56Sopenharmony_ci exc = e 3517db96d56Sopenharmony_ci """), namespace) 3527db96d56Sopenharmony_ci 3537db96d56Sopenharmony_ci exc = namespace['exc'] 3547db96d56Sopenharmony_ci traceback = exc.__traceback__ 3557db96d56Sopenharmony_ci code = traceback.tb_frame.f_code 3567db96d56Sopenharmony_ci 3577db96d56Sopenharmony_ci artificial_instructions = [] 3587db96d56Sopenharmony_ci for instr, positions in zip( 3597db96d56Sopenharmony_ci dis.get_instructions(code, show_caches=True), 3607db96d56Sopenharmony_ci code.co_positions(), 3617db96d56Sopenharmony_ci strict=True 3627db96d56Sopenharmony_ci ): 3637db96d56Sopenharmony_ci # If any of the positions is None, then all have to 3647db96d56Sopenharmony_ci # be None as well for the case above. There are still 3657db96d56Sopenharmony_ci # some places in the compiler, where the artificial instructions 3667db96d56Sopenharmony_ci # get assigned the first_lineno but they don't have other positions. 3677db96d56Sopenharmony_ci # There is no easy way of inferring them at that stage, so for now 3687db96d56Sopenharmony_ci # we don't support it. 3697db96d56Sopenharmony_ci self.assertIn(positions.count(None), [0, 3, 4]) 3707db96d56Sopenharmony_ci 3717db96d56Sopenharmony_ci if not any(positions): 3727db96d56Sopenharmony_ci artificial_instructions.append(instr) 3737db96d56Sopenharmony_ci 3747db96d56Sopenharmony_ci self.assertEqual( 3757db96d56Sopenharmony_ci [ 3767db96d56Sopenharmony_ci (instruction.opname, instruction.argval) 3777db96d56Sopenharmony_ci for instruction in artificial_instructions 3787db96d56Sopenharmony_ci ], 3797db96d56Sopenharmony_ci [ 3807db96d56Sopenharmony_ci ("PUSH_EXC_INFO", None), 3817db96d56Sopenharmony_ci ("LOAD_CONST", None), # artificial 'None' 3827db96d56Sopenharmony_ci ("STORE_NAME", "e"), # XX: we know the location for this 3837db96d56Sopenharmony_ci ("DELETE_NAME", "e"), 3847db96d56Sopenharmony_ci ("RERAISE", 1), 3857db96d56Sopenharmony_ci ("COPY", 3), 3867db96d56Sopenharmony_ci ("POP_EXCEPT", None), 3877db96d56Sopenharmony_ci ("RERAISE", 1) 3887db96d56Sopenharmony_ci ] 3897db96d56Sopenharmony_ci ) 3907db96d56Sopenharmony_ci 3917db96d56Sopenharmony_ci def test_endline_and_columntable_none_when_no_debug_ranges(self): 3927db96d56Sopenharmony_ci # Make sure that if `-X no_debug_ranges` is used, there is 3937db96d56Sopenharmony_ci # minimal debug info 3947db96d56Sopenharmony_ci code = textwrap.dedent(""" 3957db96d56Sopenharmony_ci def f(): 3967db96d56Sopenharmony_ci pass 3977db96d56Sopenharmony_ci 3987db96d56Sopenharmony_ci positions = f.__code__.co_positions() 3997db96d56Sopenharmony_ci for line, end_line, column, end_column in positions: 4007db96d56Sopenharmony_ci assert line == end_line 4017db96d56Sopenharmony_ci assert column is None 4027db96d56Sopenharmony_ci assert end_column is None 4037db96d56Sopenharmony_ci """) 4047db96d56Sopenharmony_ci assert_python_ok('-X', 'no_debug_ranges', '-c', code) 4057db96d56Sopenharmony_ci 4067db96d56Sopenharmony_ci def test_endline_and_columntable_none_when_no_debug_ranges_env(self): 4077db96d56Sopenharmony_ci # Same as above but using the environment variable opt out. 4087db96d56Sopenharmony_ci code = textwrap.dedent(""" 4097db96d56Sopenharmony_ci def f(): 4107db96d56Sopenharmony_ci pass 4117db96d56Sopenharmony_ci 4127db96d56Sopenharmony_ci positions = f.__code__.co_positions() 4137db96d56Sopenharmony_ci for line, end_line, column, end_column in positions: 4147db96d56Sopenharmony_ci assert line == end_line 4157db96d56Sopenharmony_ci assert column is None 4167db96d56Sopenharmony_ci assert end_column is None 4177db96d56Sopenharmony_ci """) 4187db96d56Sopenharmony_ci assert_python_ok('-c', code, PYTHONNODEBUGRANGES='1') 4197db96d56Sopenharmony_ci 4207db96d56Sopenharmony_ci # co_positions behavior when info is missing. 4217db96d56Sopenharmony_ci 4227db96d56Sopenharmony_ci @requires_debug_ranges() 4237db96d56Sopenharmony_ci def test_co_positions_empty_linetable(self): 4247db96d56Sopenharmony_ci def func(): 4257db96d56Sopenharmony_ci x = 1 4267db96d56Sopenharmony_ci new_code = func.__code__.replace(co_linetable=b'') 4277db96d56Sopenharmony_ci positions = new_code.co_positions() 4287db96d56Sopenharmony_ci for line, end_line, column, end_column in positions: 4297db96d56Sopenharmony_ci self.assertIsNone(line) 4307db96d56Sopenharmony_ci self.assertEqual(end_line, new_code.co_firstlineno + 1) 4317db96d56Sopenharmony_ci 4327db96d56Sopenharmony_ci def test_code_equality(self): 4337db96d56Sopenharmony_ci def f(): 4347db96d56Sopenharmony_ci try: 4357db96d56Sopenharmony_ci a() 4367db96d56Sopenharmony_ci except: 4377db96d56Sopenharmony_ci b() 4387db96d56Sopenharmony_ci else: 4397db96d56Sopenharmony_ci c() 4407db96d56Sopenharmony_ci finally: 4417db96d56Sopenharmony_ci d() 4427db96d56Sopenharmony_ci code_a = f.__code__ 4437db96d56Sopenharmony_ci code_b = code_a.replace(co_linetable=b"") 4447db96d56Sopenharmony_ci code_c = code_a.replace(co_exceptiontable=b"") 4457db96d56Sopenharmony_ci code_d = code_b.replace(co_exceptiontable=b"") 4467db96d56Sopenharmony_ci self.assertNotEqual(code_a, code_b) 4477db96d56Sopenharmony_ci self.assertNotEqual(code_a, code_c) 4487db96d56Sopenharmony_ci self.assertNotEqual(code_a, code_d) 4497db96d56Sopenharmony_ci self.assertNotEqual(code_b, code_c) 4507db96d56Sopenharmony_ci self.assertNotEqual(code_b, code_d) 4517db96d56Sopenharmony_ci self.assertNotEqual(code_c, code_d) 4527db96d56Sopenharmony_ci 4537db96d56Sopenharmony_ci 4547db96d56Sopenharmony_cidef isinterned(s): 4557db96d56Sopenharmony_ci return s is sys.intern(('_' + s + '_')[1:-1]) 4567db96d56Sopenharmony_ci 4577db96d56Sopenharmony_ciclass CodeConstsTest(unittest.TestCase): 4587db96d56Sopenharmony_ci 4597db96d56Sopenharmony_ci def find_const(self, consts, value): 4607db96d56Sopenharmony_ci for v in consts: 4617db96d56Sopenharmony_ci if v == value: 4627db96d56Sopenharmony_ci return v 4637db96d56Sopenharmony_ci self.assertIn(value, consts) # raises an exception 4647db96d56Sopenharmony_ci self.fail('Should never be reached') 4657db96d56Sopenharmony_ci 4667db96d56Sopenharmony_ci def assertIsInterned(self, s): 4677db96d56Sopenharmony_ci if not isinterned(s): 4687db96d56Sopenharmony_ci self.fail('String %r is not interned' % (s,)) 4697db96d56Sopenharmony_ci 4707db96d56Sopenharmony_ci def assertIsNotInterned(self, s): 4717db96d56Sopenharmony_ci if isinterned(s): 4727db96d56Sopenharmony_ci self.fail('String %r is interned' % (s,)) 4737db96d56Sopenharmony_ci 4747db96d56Sopenharmony_ci @cpython_only 4757db96d56Sopenharmony_ci def test_interned_string(self): 4767db96d56Sopenharmony_ci co = compile('res = "str_value"', '?', 'exec') 4777db96d56Sopenharmony_ci v = self.find_const(co.co_consts, 'str_value') 4787db96d56Sopenharmony_ci self.assertIsInterned(v) 4797db96d56Sopenharmony_ci 4807db96d56Sopenharmony_ci @cpython_only 4817db96d56Sopenharmony_ci def test_interned_string_in_tuple(self): 4827db96d56Sopenharmony_ci co = compile('res = ("str_value",)', '?', 'exec') 4837db96d56Sopenharmony_ci v = self.find_const(co.co_consts, ('str_value',)) 4847db96d56Sopenharmony_ci self.assertIsInterned(v[0]) 4857db96d56Sopenharmony_ci 4867db96d56Sopenharmony_ci @cpython_only 4877db96d56Sopenharmony_ci def test_interned_string_in_frozenset(self): 4887db96d56Sopenharmony_ci co = compile('res = a in {"str_value"}', '?', 'exec') 4897db96d56Sopenharmony_ci v = self.find_const(co.co_consts, frozenset(('str_value',))) 4907db96d56Sopenharmony_ci self.assertIsInterned(tuple(v)[0]) 4917db96d56Sopenharmony_ci 4927db96d56Sopenharmony_ci @cpython_only 4937db96d56Sopenharmony_ci def test_interned_string_default(self): 4947db96d56Sopenharmony_ci def f(a='str_value'): 4957db96d56Sopenharmony_ci return a 4967db96d56Sopenharmony_ci self.assertIsInterned(f()) 4977db96d56Sopenharmony_ci 4987db96d56Sopenharmony_ci @cpython_only 4997db96d56Sopenharmony_ci def test_interned_string_with_null(self): 5007db96d56Sopenharmony_ci co = compile(r'res = "str\0value!"', '?', 'exec') 5017db96d56Sopenharmony_ci v = self.find_const(co.co_consts, 'str\0value!') 5027db96d56Sopenharmony_ci self.assertIsNotInterned(v) 5037db96d56Sopenharmony_ci 5047db96d56Sopenharmony_ci 5057db96d56Sopenharmony_ciclass CodeWeakRefTest(unittest.TestCase): 5067db96d56Sopenharmony_ci 5077db96d56Sopenharmony_ci def test_basic(self): 5087db96d56Sopenharmony_ci # Create a code object in a clean environment so that we know we have 5097db96d56Sopenharmony_ci # the only reference to it left. 5107db96d56Sopenharmony_ci namespace = {} 5117db96d56Sopenharmony_ci exec("def f(): pass", globals(), namespace) 5127db96d56Sopenharmony_ci f = namespace["f"] 5137db96d56Sopenharmony_ci del namespace 5147db96d56Sopenharmony_ci 5157db96d56Sopenharmony_ci self.called = False 5167db96d56Sopenharmony_ci def callback(code): 5177db96d56Sopenharmony_ci self.called = True 5187db96d56Sopenharmony_ci 5197db96d56Sopenharmony_ci # f is now the last reference to the function, and through it, the code 5207db96d56Sopenharmony_ci # object. While we hold it, check that we can create a weakref and 5217db96d56Sopenharmony_ci # deref it. Then delete it, and check that the callback gets called and 5227db96d56Sopenharmony_ci # the reference dies. 5237db96d56Sopenharmony_ci coderef = weakref.ref(f.__code__, callback) 5247db96d56Sopenharmony_ci self.assertTrue(bool(coderef())) 5257db96d56Sopenharmony_ci del f 5267db96d56Sopenharmony_ci gc_collect() # For PyPy or other GCs. 5277db96d56Sopenharmony_ci self.assertFalse(bool(coderef())) 5287db96d56Sopenharmony_ci self.assertTrue(self.called) 5297db96d56Sopenharmony_ci 5307db96d56Sopenharmony_ci# Python implementation of location table parsing algorithm 5317db96d56Sopenharmony_cidef read(it): 5327db96d56Sopenharmony_ci return next(it) 5337db96d56Sopenharmony_ci 5347db96d56Sopenharmony_cidef read_varint(it): 5357db96d56Sopenharmony_ci b = read(it) 5367db96d56Sopenharmony_ci val = b & 63; 5377db96d56Sopenharmony_ci shift = 0; 5387db96d56Sopenharmony_ci while b & 64: 5397db96d56Sopenharmony_ci b = read(it) 5407db96d56Sopenharmony_ci shift += 6 5417db96d56Sopenharmony_ci val |= (b&63) << shift 5427db96d56Sopenharmony_ci return val 5437db96d56Sopenharmony_ci 5447db96d56Sopenharmony_cidef read_signed_varint(it): 5457db96d56Sopenharmony_ci uval = read_varint(it) 5467db96d56Sopenharmony_ci if uval & 1: 5477db96d56Sopenharmony_ci return -(uval >> 1) 5487db96d56Sopenharmony_ci else: 5497db96d56Sopenharmony_ci return uval >> 1 5507db96d56Sopenharmony_ci 5517db96d56Sopenharmony_cidef parse_location_table(code): 5527db96d56Sopenharmony_ci line = code.co_firstlineno 5537db96d56Sopenharmony_ci it = iter(code.co_linetable) 5547db96d56Sopenharmony_ci while True: 5557db96d56Sopenharmony_ci try: 5567db96d56Sopenharmony_ci first_byte = read(it) 5577db96d56Sopenharmony_ci except StopIteration: 5587db96d56Sopenharmony_ci return 5597db96d56Sopenharmony_ci code = (first_byte >> 3) & 15 5607db96d56Sopenharmony_ci length = (first_byte & 7) + 1 5617db96d56Sopenharmony_ci if code == 15: 5627db96d56Sopenharmony_ci yield (code, length, None, None, None, None) 5637db96d56Sopenharmony_ci elif code == 14: 5647db96d56Sopenharmony_ci line_delta = read_signed_varint(it) 5657db96d56Sopenharmony_ci line += line_delta 5667db96d56Sopenharmony_ci end_line = line + read_varint(it) 5677db96d56Sopenharmony_ci col = read_varint(it) 5687db96d56Sopenharmony_ci if col == 0: 5697db96d56Sopenharmony_ci col = None 5707db96d56Sopenharmony_ci else: 5717db96d56Sopenharmony_ci col -= 1 5727db96d56Sopenharmony_ci end_col = read_varint(it) 5737db96d56Sopenharmony_ci if end_col == 0: 5747db96d56Sopenharmony_ci end_col = None 5757db96d56Sopenharmony_ci else: 5767db96d56Sopenharmony_ci end_col -= 1 5777db96d56Sopenharmony_ci yield (code, length, line, end_line, col, end_col) 5787db96d56Sopenharmony_ci elif code == 13: # No column 5797db96d56Sopenharmony_ci line_delta = read_signed_varint(it) 5807db96d56Sopenharmony_ci line += line_delta 5817db96d56Sopenharmony_ci yield (code, length, line, line, None, None) 5827db96d56Sopenharmony_ci elif code in (10, 11, 12): # new line 5837db96d56Sopenharmony_ci line_delta = code - 10 5847db96d56Sopenharmony_ci line += line_delta 5857db96d56Sopenharmony_ci column = read(it) 5867db96d56Sopenharmony_ci end_column = read(it) 5877db96d56Sopenharmony_ci yield (code, length, line, line, column, end_column) 5887db96d56Sopenharmony_ci else: 5897db96d56Sopenharmony_ci assert (0 <= code < 10) 5907db96d56Sopenharmony_ci second_byte = read(it) 5917db96d56Sopenharmony_ci column = code << 3 | (second_byte >> 4) 5927db96d56Sopenharmony_ci yield (code, length, line, line, column, column + (second_byte & 15)) 5937db96d56Sopenharmony_ci 5947db96d56Sopenharmony_cidef positions_from_location_table(code): 5957db96d56Sopenharmony_ci for _, length, line, end_line, col, end_col in parse_location_table(code): 5967db96d56Sopenharmony_ci for _ in range(length): 5977db96d56Sopenharmony_ci yield (line, end_line, col, end_col) 5987db96d56Sopenharmony_ci 5997db96d56Sopenharmony_cidef dedup(lst, prev=object()): 6007db96d56Sopenharmony_ci for item in lst: 6017db96d56Sopenharmony_ci if item != prev: 6027db96d56Sopenharmony_ci yield item 6037db96d56Sopenharmony_ci prev = item 6047db96d56Sopenharmony_ci 6057db96d56Sopenharmony_cidef lines_from_postions(positions): 6067db96d56Sopenharmony_ci return dedup(l for (l, _, _, _) in positions) 6077db96d56Sopenharmony_ci 6087db96d56Sopenharmony_cidef misshappen(): 6097db96d56Sopenharmony_ci """ 6107db96d56Sopenharmony_ci 6117db96d56Sopenharmony_ci 6127db96d56Sopenharmony_ci 6137db96d56Sopenharmony_ci 6147db96d56Sopenharmony_ci 6157db96d56Sopenharmony_ci """ 6167db96d56Sopenharmony_ci x = ( 6177db96d56Sopenharmony_ci 6187db96d56Sopenharmony_ci 6197db96d56Sopenharmony_ci 4 6207db96d56Sopenharmony_ci 6217db96d56Sopenharmony_ci + 6227db96d56Sopenharmony_ci 6237db96d56Sopenharmony_ci y 6247db96d56Sopenharmony_ci 6257db96d56Sopenharmony_ci ) 6267db96d56Sopenharmony_ci y = ( 6277db96d56Sopenharmony_ci a 6287db96d56Sopenharmony_ci + 6297db96d56Sopenharmony_ci b 6307db96d56Sopenharmony_ci + 6317db96d56Sopenharmony_ci 6327db96d56Sopenharmony_ci d 6337db96d56Sopenharmony_ci ) 6347db96d56Sopenharmony_ci return q if ( 6357db96d56Sopenharmony_ci 6367db96d56Sopenharmony_ci x 6377db96d56Sopenharmony_ci 6387db96d56Sopenharmony_ci ) else p 6397db96d56Sopenharmony_ci 6407db96d56Sopenharmony_cidef bug93662(): 6417db96d56Sopenharmony_ci example_report_generation_message= ( 6427db96d56Sopenharmony_ci """ 6437db96d56Sopenharmony_ci """ 6447db96d56Sopenharmony_ci ).strip() 6457db96d56Sopenharmony_ci raise ValueError() 6467db96d56Sopenharmony_ci 6477db96d56Sopenharmony_ci 6487db96d56Sopenharmony_ciclass CodeLocationTest(unittest.TestCase): 6497db96d56Sopenharmony_ci 6507db96d56Sopenharmony_ci def check_positions(self, func): 6517db96d56Sopenharmony_ci pos1 = list(func.__code__.co_positions()) 6527db96d56Sopenharmony_ci pos2 = list(positions_from_location_table(func.__code__)) 6537db96d56Sopenharmony_ci for l1, l2 in zip(pos1, pos2): 6547db96d56Sopenharmony_ci self.assertEqual(l1, l2) 6557db96d56Sopenharmony_ci self.assertEqual(len(pos1), len(pos2)) 6567db96d56Sopenharmony_ci 6577db96d56Sopenharmony_ci def test_positions(self): 6587db96d56Sopenharmony_ci self.check_positions(parse_location_table) 6597db96d56Sopenharmony_ci self.check_positions(misshappen) 6607db96d56Sopenharmony_ci self.check_positions(bug93662) 6617db96d56Sopenharmony_ci 6627db96d56Sopenharmony_ci def check_lines(self, func): 6637db96d56Sopenharmony_ci co = func.__code__ 6647db96d56Sopenharmony_ci lines1 = list(dedup(l for (_, _, l) in co.co_lines())) 6657db96d56Sopenharmony_ci lines2 = list(lines_from_postions(positions_from_location_table(co))) 6667db96d56Sopenharmony_ci for l1, l2 in zip(lines1, lines2): 6677db96d56Sopenharmony_ci self.assertEqual(l1, l2) 6687db96d56Sopenharmony_ci self.assertEqual(len(lines1), len(lines2)) 6697db96d56Sopenharmony_ci 6707db96d56Sopenharmony_ci def test_lines(self): 6717db96d56Sopenharmony_ci self.check_lines(parse_location_table) 6727db96d56Sopenharmony_ci self.check_lines(misshappen) 6737db96d56Sopenharmony_ci self.check_lines(bug93662) 6747db96d56Sopenharmony_ci 6757db96d56Sopenharmony_ci @cpython_only 6767db96d56Sopenharmony_ci def test_code_new_empty(self): 6777db96d56Sopenharmony_ci # If this test fails, it means that the construction of PyCode_NewEmpty 6787db96d56Sopenharmony_ci # needs to be modified! Please update this test *and* PyCode_NewEmpty, 6797db96d56Sopenharmony_ci # so that they both stay in sync. 6807db96d56Sopenharmony_ci def f(): 6817db96d56Sopenharmony_ci pass 6827db96d56Sopenharmony_ci PY_CODE_LOCATION_INFO_NO_COLUMNS = 13 6837db96d56Sopenharmony_ci f.__code__ = f.__code__.replace( 6847db96d56Sopenharmony_ci co_firstlineno=42, 6857db96d56Sopenharmony_ci co_code=bytes( 6867db96d56Sopenharmony_ci [ 6877db96d56Sopenharmony_ci dis.opmap["RESUME"], 0, 6887db96d56Sopenharmony_ci dis.opmap["LOAD_ASSERTION_ERROR"], 0, 6897db96d56Sopenharmony_ci dis.opmap["RAISE_VARARGS"], 1, 6907db96d56Sopenharmony_ci ] 6917db96d56Sopenharmony_ci ), 6927db96d56Sopenharmony_ci co_linetable=bytes( 6937db96d56Sopenharmony_ci [ 6947db96d56Sopenharmony_ci (1 << 7) 6957db96d56Sopenharmony_ci | (PY_CODE_LOCATION_INFO_NO_COLUMNS << 3) 6967db96d56Sopenharmony_ci | (3 - 1), 6977db96d56Sopenharmony_ci 0, 6987db96d56Sopenharmony_ci ] 6997db96d56Sopenharmony_ci ), 7007db96d56Sopenharmony_ci ) 7017db96d56Sopenharmony_ci self.assertRaises(AssertionError, f) 7027db96d56Sopenharmony_ci self.assertEqual( 7037db96d56Sopenharmony_ci list(f.__code__.co_positions()), 7047db96d56Sopenharmony_ci 3 * [(42, 42, None, None)], 7057db96d56Sopenharmony_ci ) 7067db96d56Sopenharmony_ci 7077db96d56Sopenharmony_ci 7087db96d56Sopenharmony_ciif check_impl_detail(cpython=True) and ctypes is not None: 7097db96d56Sopenharmony_ci py = ctypes.pythonapi 7107db96d56Sopenharmony_ci freefunc = ctypes.CFUNCTYPE(None,ctypes.c_voidp) 7117db96d56Sopenharmony_ci 7127db96d56Sopenharmony_ci RequestCodeExtraIndex = py._PyEval_RequestCodeExtraIndex 7137db96d56Sopenharmony_ci RequestCodeExtraIndex.argtypes = (freefunc,) 7147db96d56Sopenharmony_ci RequestCodeExtraIndex.restype = ctypes.c_ssize_t 7157db96d56Sopenharmony_ci 7167db96d56Sopenharmony_ci SetExtra = py._PyCode_SetExtra 7177db96d56Sopenharmony_ci SetExtra.argtypes = (ctypes.py_object, ctypes.c_ssize_t, ctypes.c_voidp) 7187db96d56Sopenharmony_ci SetExtra.restype = ctypes.c_int 7197db96d56Sopenharmony_ci 7207db96d56Sopenharmony_ci GetExtra = py._PyCode_GetExtra 7217db96d56Sopenharmony_ci GetExtra.argtypes = (ctypes.py_object, ctypes.c_ssize_t, 7227db96d56Sopenharmony_ci ctypes.POINTER(ctypes.c_voidp)) 7237db96d56Sopenharmony_ci GetExtra.restype = ctypes.c_int 7247db96d56Sopenharmony_ci 7257db96d56Sopenharmony_ci LAST_FREED = None 7267db96d56Sopenharmony_ci def myfree(ptr): 7277db96d56Sopenharmony_ci global LAST_FREED 7287db96d56Sopenharmony_ci LAST_FREED = ptr 7297db96d56Sopenharmony_ci 7307db96d56Sopenharmony_ci FREE_FUNC = freefunc(myfree) 7317db96d56Sopenharmony_ci FREE_INDEX = RequestCodeExtraIndex(FREE_FUNC) 7327db96d56Sopenharmony_ci 7337db96d56Sopenharmony_ci class CoExtra(unittest.TestCase): 7347db96d56Sopenharmony_ci def get_func(self): 7357db96d56Sopenharmony_ci # Defining a function causes the containing function to have a 7367db96d56Sopenharmony_ci # reference to the code object. We need the code objects to go 7377db96d56Sopenharmony_ci # away, so we eval a lambda. 7387db96d56Sopenharmony_ci return eval('lambda:42') 7397db96d56Sopenharmony_ci 7407db96d56Sopenharmony_ci def test_get_non_code(self): 7417db96d56Sopenharmony_ci f = self.get_func() 7427db96d56Sopenharmony_ci 7437db96d56Sopenharmony_ci self.assertRaises(SystemError, SetExtra, 42, FREE_INDEX, 7447db96d56Sopenharmony_ci ctypes.c_voidp(100)) 7457db96d56Sopenharmony_ci self.assertRaises(SystemError, GetExtra, 42, FREE_INDEX, 7467db96d56Sopenharmony_ci ctypes.c_voidp(100)) 7477db96d56Sopenharmony_ci 7487db96d56Sopenharmony_ci def test_bad_index(self): 7497db96d56Sopenharmony_ci f = self.get_func() 7507db96d56Sopenharmony_ci self.assertRaises(SystemError, SetExtra, f.__code__, 7517db96d56Sopenharmony_ci FREE_INDEX+100, ctypes.c_voidp(100)) 7527db96d56Sopenharmony_ci self.assertEqual(GetExtra(f.__code__, FREE_INDEX+100, 7537db96d56Sopenharmony_ci ctypes.c_voidp(100)), 0) 7547db96d56Sopenharmony_ci 7557db96d56Sopenharmony_ci def test_free_called(self): 7567db96d56Sopenharmony_ci # Verify that the provided free function gets invoked 7577db96d56Sopenharmony_ci # when the code object is cleaned up. 7587db96d56Sopenharmony_ci f = self.get_func() 7597db96d56Sopenharmony_ci 7607db96d56Sopenharmony_ci SetExtra(f.__code__, FREE_INDEX, ctypes.c_voidp(100)) 7617db96d56Sopenharmony_ci del f 7627db96d56Sopenharmony_ci self.assertEqual(LAST_FREED, 100) 7637db96d56Sopenharmony_ci 7647db96d56Sopenharmony_ci def test_get_set(self): 7657db96d56Sopenharmony_ci # Test basic get/set round tripping. 7667db96d56Sopenharmony_ci f = self.get_func() 7677db96d56Sopenharmony_ci 7687db96d56Sopenharmony_ci extra = ctypes.c_voidp() 7697db96d56Sopenharmony_ci 7707db96d56Sopenharmony_ci SetExtra(f.__code__, FREE_INDEX, ctypes.c_voidp(200)) 7717db96d56Sopenharmony_ci # reset should free... 7727db96d56Sopenharmony_ci SetExtra(f.__code__, FREE_INDEX, ctypes.c_voidp(300)) 7737db96d56Sopenharmony_ci self.assertEqual(LAST_FREED, 200) 7747db96d56Sopenharmony_ci 7757db96d56Sopenharmony_ci extra = ctypes.c_voidp() 7767db96d56Sopenharmony_ci GetExtra(f.__code__, FREE_INDEX, extra) 7777db96d56Sopenharmony_ci self.assertEqual(extra.value, 300) 7787db96d56Sopenharmony_ci del f 7797db96d56Sopenharmony_ci 7807db96d56Sopenharmony_ci @threading_helper.requires_working_threading() 7817db96d56Sopenharmony_ci def test_free_different_thread(self): 7827db96d56Sopenharmony_ci # Freeing a code object on a different thread then 7837db96d56Sopenharmony_ci # where the co_extra was set should be safe. 7847db96d56Sopenharmony_ci f = self.get_func() 7857db96d56Sopenharmony_ci class ThreadTest(threading.Thread): 7867db96d56Sopenharmony_ci def __init__(self, f, test): 7877db96d56Sopenharmony_ci super().__init__() 7887db96d56Sopenharmony_ci self.f = f 7897db96d56Sopenharmony_ci self.test = test 7907db96d56Sopenharmony_ci def run(self): 7917db96d56Sopenharmony_ci del self.f 7927db96d56Sopenharmony_ci self.test.assertEqual(LAST_FREED, 500) 7937db96d56Sopenharmony_ci 7947db96d56Sopenharmony_ci SetExtra(f.__code__, FREE_INDEX, ctypes.c_voidp(500)) 7957db96d56Sopenharmony_ci tt = ThreadTest(f, self) 7967db96d56Sopenharmony_ci del f 7977db96d56Sopenharmony_ci tt.start() 7987db96d56Sopenharmony_ci tt.join() 7997db96d56Sopenharmony_ci self.assertEqual(LAST_FREED, 500) 8007db96d56Sopenharmony_ci 8017db96d56Sopenharmony_ci 8027db96d56Sopenharmony_cidef load_tests(loader, tests, pattern): 8037db96d56Sopenharmony_ci tests.addTest(doctest.DocTestSuite()) 8047db96d56Sopenharmony_ci return tests 8057db96d56Sopenharmony_ci 8067db96d56Sopenharmony_ci 8077db96d56Sopenharmony_ciif __name__ == "__main__": 8087db96d56Sopenharmony_ci unittest.main() 809