diff options
Diffstat (limited to 'Lib/test/test_code.py')
-rw-r--r-- | Lib/test/test_code.py | 330 |
1 files changed, 20 insertions, 310 deletions
diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py index 656c46c..142261e 100644 --- a/Lib/test/test_code.py +++ b/Lib/test/test_code.py @@ -6,24 +6,20 @@ ... return g ... ->>> dump(f.__code__) +>>> dump(f.func_code) name: f argcount: 1 -posonlyargcount: 0 -kwonlyargcount: 0 names: () varnames: ('x', 'g') cellvars: ('x',) freevars: () nlocals: 2 flags: 3 -consts: ('None', '<code object g>', "'f.<locals>.g'") +consts: ('None', '<code object g>') ->>> dump(f(4).__code__) +>>> dump(f(4).func_code) name: g argcount: 1 -posonlyargcount: 0 -kwonlyargcount: 0 names: () varnames: ('y',) cellvars: () @@ -38,12 +34,9 @@ consts: ('None',) ... c = a * b ... return c ... - ->>> dump(h.__code__) +>>> dump(h.func_code) name: h argcount: 2 -posonlyargcount: 0 -kwonlyargcount: 0 names: () varnames: ('x', 'y', 'a', 'b', 'c') cellvars: () @@ -53,16 +46,14 @@ flags: 67 consts: ('None',) >>> def attrs(obj): -... print(obj.attr1) -... print(obj.attr2) -... print(obj.attr3) +... print obj.attr1 +... print obj.attr2 +... print obj.attr3 ->>> dump(attrs.__code__) +>>> dump(attrs.func_code) name: attrs argcount: 1 -posonlyargcount: 0 -kwonlyargcount: 0 -names: ('print', 'attr1', 'attr2', 'attr3') +names: ('attr1', 'attr2', 'attr3') varnames: ('obj',) cellvars: () freevars: () @@ -74,13 +65,11 @@ consts: ('None',) ... 'doc string' ... 'not a docstring' ... 53 -... 0x53 +... 53L ->>> dump(optimize_away.__code__) +>>> dump(optimize_away.func_code) name: optimize_away argcount: 0 -posonlyargcount: 0 -kwonlyargcount: 0 names: () varnames: () cellvars: () @@ -89,54 +78,11 @@ nlocals: 0 flags: 67 consts: ("'doc string'", 'None') ->>> def keywordonly_args(a,b,*,k1): -... return a,b,k1 -... - ->>> dump(keywordonly_args.__code__) -name: keywordonly_args -argcount: 2 -posonlyargcount: 0 -kwonlyargcount: 1 -names: () -varnames: ('a', 'b', 'k1') -cellvars: () -freevars: () -nlocals: 3 -flags: 67 -consts: ('None',) - ->>> def posonly_args(a,b,/,c): -... return a,b,c -... - ->>> dump(posonly_args.__code__) -name: posonly_args -argcount: 3 -posonlyargcount: 2 -kwonlyargcount: 0 -names: () -varnames: ('a', 'b', 'c') -cellvars: () -freevars: () -nlocals: 3 -flags: 67 -consts: ('None',) - """ -import inspect -import sys -import threading import unittest import weakref -import opcode -try: - import ctypes -except ImportError: - ctypes = None -from test.support import (run_doctest, run_unittest, cpython_only, - check_impl_detail) +from test.test_support import run_doctest, run_unittest, cpython_only def consts(t): @@ -150,16 +96,11 @@ def consts(t): def dump(co): """Print out a text representation of a code object.""" - for attr in ["name", "argcount", "posonlyargcount", - "kwonlyargcount", "names", "varnames", - "cellvars", "freevars", "nlocals", "flags"]: - print("%s: %s" % (attr, getattr(co, "co_" + attr))) - print("consts:", tuple(consts(co.co_consts))) + for attr in ["name", "argcount", "names", "varnames", "cellvars", + "freevars", "nlocals", "flags"]: + print "%s: %s" % (attr, getattr(co, "co_" + attr)) + print "consts:", tuple(consts(co.co_consts)) -# Needed for test_closure_injection below -# Defined at global scope to avoid implicitly closing over __class__ -def external_getitem(self, i): - return f"Foreign getitem: {super().__getitem__(i)}" class CodeTest(unittest.TestCase): @@ -171,103 +112,9 @@ class CodeTest(unittest.TestCase): self.assertEqual(co.co_name, "funcname") self.assertEqual(co.co_firstlineno, 15) - @cpython_only - def test_closure_injection(self): - # From https://bugs.python.org/issue32176 - from types import FunctionType - - def create_closure(__class__): - return (lambda: __class__).__closure__ - - def new_code(c): - '''A new code object with a __class__ cell added to freevars''' - return c.replace(co_freevars=c.co_freevars + ('__class__',)) - - def add_foreign_method(cls, name, f): - code = new_code(f.__code__) - assert not f.__closure__ - closure = create_closure(cls) - defaults = f.__defaults__ - setattr(cls, name, FunctionType(code, globals(), name, defaults, closure)) - - class List(list): - pass - - add_foreign_method(List, "__getitem__", external_getitem) - - # Ensure the closure injection actually worked - function = List.__getitem__ - class_ref = function.__closure__[0].cell_contents - self.assertIs(class_ref, List) - - # Ensure the code correctly indicates it accesses a free variable - self.assertFalse(function.__code__.co_flags & inspect.CO_NOFREE, - hex(function.__code__.co_flags)) - - # Ensure the zero-arg super() call in the injected method works - obj = List([1, 2, 3]) - self.assertEqual(obj[0], "Foreign getitem: 1") - - def test_constructor(self): - def func(): pass - co = func.__code__ - CodeType = type(co) - - # test code constructor - return CodeType(co.co_argcount, - co.co_posonlyargcount, - co.co_kwonlyargcount, - co.co_nlocals, - co.co_stacksize, - co.co_flags, - co.co_code, - co.co_consts, - co.co_names, - co.co_varnames, - co.co_filename, - co.co_name, - co.co_firstlineno, - co.co_lnotab, - co.co_freevars, - co.co_cellvars) - - def test_replace(self): - def func(): - x = 1 - return x - code = func.__code__ - - # different co_name, co_varnames, co_consts - def func2(): - y = 2 - return y - code2 = func2.__code__ - - for attr, value in ( - ("co_argcount", 0), - ("co_posonlyargcount", 0), - ("co_kwonlyargcount", 0), - ("co_nlocals", 0), - ("co_stacksize", 0), - ("co_flags", code.co_flags | inspect.CO_COROUTINE), - ("co_firstlineno", 100), - ("co_code", code2.co_code), - ("co_consts", code2.co_consts), - ("co_names", ("myname",)), - ("co_varnames", code2.co_varnames), - ("co_freevars", ("freevar",)), - ("co_cellvars", ("cellvar",)), - ("co_filename", "newfilename"), - ("co_name", "newname"), - ("co_lnotab", code2.co_lnotab), - ): - with self.subTest(attr=attr, value=value): - new_code = code.replace(**{attr: value}) - self.assertEqual(getattr(new_code, attr), value) - def isinterned(s): - return s is sys.intern(('_' + s + '_')[1:-1]) + return s is intern(('_' + s + '_')[1:-1]) class CodeConstsTest(unittest.TestCase): @@ -299,12 +146,6 @@ class CodeConstsTest(unittest.TestCase): self.assertIsInterned(v[0]) @cpython_only - def test_interned_string_in_frozenset(self): - co = compile('res = a in {"str_value"}', '?', 'exec') - v = self.find_const(co.co_consts, frozenset(('str_value',))) - self.assertIsInterned(tuple(v)[0]) - - @cpython_only def test_interned_string_default(self): def f(a='str_value'): return a @@ -323,7 +164,7 @@ class CodeWeakRefTest(unittest.TestCase): # Create a code object in a clean environment so that we know we have # the only reference to it left. namespace = {} - exec("def f(): pass", globals(), namespace) + exec "def f(): pass" in globals(), namespace f = namespace["f"] del namespace @@ -342,142 +183,11 @@ class CodeWeakRefTest(unittest.TestCase): self.assertTrue(self.called) -if check_impl_detail(cpython=True) and ctypes is not None: - py = ctypes.pythonapi - freefunc = ctypes.CFUNCTYPE(None,ctypes.c_voidp) - - RequestCodeExtraIndex = py._PyEval_RequestCodeExtraIndex - RequestCodeExtraIndex.argtypes = (freefunc,) - RequestCodeExtraIndex.restype = ctypes.c_ssize_t - - SetExtra = py._PyCode_SetExtra - SetExtra.argtypes = (ctypes.py_object, ctypes.c_ssize_t, ctypes.c_voidp) - SetExtra.restype = ctypes.c_int - - GetExtra = py._PyCode_GetExtra - GetExtra.argtypes = (ctypes.py_object, ctypes.c_ssize_t, - ctypes.POINTER(ctypes.c_voidp)) - GetExtra.restype = ctypes.c_int - - LAST_FREED = None - def myfree(ptr): - global LAST_FREED - LAST_FREED = ptr - - FREE_FUNC = freefunc(myfree) - FREE_INDEX = RequestCodeExtraIndex(FREE_FUNC) - - class CoExtra(unittest.TestCase): - def get_func(self): - # Defining a function causes the containing function to have a - # reference to the code object. We need the code objects to go - # away, so we eval a lambda. - return eval('lambda:42') - - def test_get_non_code(self): - f = self.get_func() - - self.assertRaises(SystemError, SetExtra, 42, FREE_INDEX, - ctypes.c_voidp(100)) - self.assertRaises(SystemError, GetExtra, 42, FREE_INDEX, - ctypes.c_voidp(100)) - - def test_bad_index(self): - f = self.get_func() - self.assertRaises(SystemError, SetExtra, f.__code__, - FREE_INDEX+100, ctypes.c_voidp(100)) - self.assertEqual(GetExtra(f.__code__, FREE_INDEX+100, - ctypes.c_voidp(100)), 0) - - def test_free_called(self): - # Verify that the provided free function gets invoked - # when the code object is cleaned up. - f = self.get_func() - - SetExtra(f.__code__, FREE_INDEX, ctypes.c_voidp(100)) - del f - self.assertEqual(LAST_FREED, 100) - - def test_get_set(self): - # Test basic get/set round tripping. - f = self.get_func() - - extra = ctypes.c_voidp() - - SetExtra(f.__code__, FREE_INDEX, ctypes.c_voidp(200)) - # reset should free... - SetExtra(f.__code__, FREE_INDEX, ctypes.c_voidp(300)) - self.assertEqual(LAST_FREED, 200) - - extra = ctypes.c_voidp() - GetExtra(f.__code__, FREE_INDEX, extra) - self.assertEqual(extra.value, 300) - del f - - def test_free_different_thread(self): - # Freeing a code object on a different thread then - # where the co_extra was set should be safe. - f = self.get_func() - class ThreadTest(threading.Thread): - def __init__(self, f, test): - super().__init__() - self.f = f - self.test = test - def run(self): - del self.f - self.test.assertEqual(LAST_FREED, 500) - - SetExtra(f.__code__, FREE_INDEX, ctypes.c_voidp(500)) - tt = ThreadTest(f, self) - del f - tt.start() - tt.join() - self.assertEqual(LAST_FREED, 500) - - @cpython_only - def test_clean_stack_on_return(self): - - def f(x): - return x - - code = f.__code__ - ct = type(f.__code__) - - # Insert an extra LOAD_FAST, this duplicates the value of - # 'x' in the stack, leaking it if the frame is not properly - # cleaned up upon exit. - - bytecode = list(code.co_code) - bytecode.insert(-2, opcode.opmap['LOAD_FAST']) - bytecode.insert(-2, 0) - - c = ct(code.co_argcount, code.co_posonlyargcount, - code.co_kwonlyargcount, code.co_nlocals, code.co_stacksize+1, - code.co_flags, bytes(bytecode), - code.co_consts, code.co_names, code.co_varnames, - code.co_filename, code.co_name, code.co_firstlineno, - code.co_lnotab, code.co_freevars, code.co_cellvars) - new_function = type(f)(c, f.__globals__, 'nf', f.__defaults__, f.__closure__) - - class Var: - pass - the_object = Var() - var = weakref.ref(the_object) - - new_function(the_object) - - # Check if the_object is leaked - del the_object - assert var() is None - - def test_main(verbose=None): from test import test_code run_doctest(test_code, verbose) - tests = [CodeTest, CodeConstsTest, CodeWeakRefTest] - if check_impl_detail(cpython=True) and ctypes is not None: - tests.append(CoExtra) - run_unittest(*tests) + run_unittest(CodeTest, CodeConstsTest, CodeWeakRefTest) + if __name__ == "__main__": test_main() |