Skip to content
Permalink
Browse files

bpo-37032: Add CodeType.replace() method (GH-13542)

  • Loading branch information...
vstinner committed May 24, 2019
1 parent 561612d commit a9f05d69ccbf3c75cdd604c25094282697789a62
@@ -240,6 +240,9 @@ Other Language Changes
and Windows use this to properly terminate scripts in interactive sessions.
(Contributed by Google via Gregory P. Smith in :issue:`1054041`.)

* Added new ``replace()`` method to the code type (:class:`types.CodeType`).
(Contributed by Victor Stinner in :issue:`37032`.)


New Modules
===========
@@ -1051,7 +1054,8 @@ Changes in the Python API

* :class:`types.CodeType` has a new parameter in the second position of the
constructor (*posonlyargcount*) to support positional-only arguments defined
in :pep:`570`.
in :pep:`570`. A new ``replace()`` method of :class:`types.CodeType` can be
used to make the code future-proof.


Changes in the C API
@@ -619,13 +619,7 @@ def replace_paths_in_code(self, co):
if isinstance(consts[i], type(co)):
consts[i] = self.replace_paths_in_code(consts[i])

return types.CodeType(co.co_argcount, co.co_posonlyargcount,
co.co_kwonlyargcount, co.co_nlocals,
co.co_stacksize, co.co_flags,
co.co_code, tuple(consts), co.co_names,
co.co_varnames, new_filename, co.co_name,
co.co_firstlineno, co.co_lnotab, co.co_freevars,
co.co_cellvars)
return co.replace(co_consts=tuple(consts), co_filename=new_filename)


def test():
@@ -174,18 +174,14 @@ def test_newempty(self):
@cpython_only
def test_closure_injection(self):
# From https://bugs.python.org/issue32176
from types import FunctionType, CodeType
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 CodeType(
c.co_argcount, c.co_posonlyargcount, c.co_kwonlyargcount, c.co_nlocals,
c.co_stacksize, c.co_flags, c.co_code, c.co_consts, c.co_names,
c.co_varnames, c.co_filename, c.co_name, c.co_firstlineno,
c.co_lnotab, c.co_freevars + ('__class__',), c.co_cellvars)
return c.replace(co_freevars=c.co_freevars + ('__class__',))

def add_foreign_method(cls, name, f):
code = new_code(f.__code__)
@@ -212,6 +208,64 @@ class List(list):
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 = func.__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])

@@ -674,13 +674,7 @@ def test_foreign_code(self):
foreign_code = importlib.import_module.__code__
pos = constants.index(1)
constants[pos] = foreign_code
code = type(code)(code.co_argcount, code.co_posonlyargcount,
code.co_kwonlyargcount,
code.co_nlocals, code.co_stacksize,
code.co_flags, code.co_code, tuple(constants),
code.co_names, code.co_varnames, code.co_filename,
code.co_name, code.co_firstlineno, code.co_lnotab,
code.co_freevars, code.co_cellvars)
code = code.replace(co_consts=tuple(constants))
with open(self.compiled_name, "wb") as f:
f.write(header)
marshal.dump(code, f)
@@ -262,14 +262,8 @@ def coroutine(func):
if co_flags & 0x20:
# TODO: Implement this in C.
co = func.__code__
func.__code__ = CodeType(
co.co_argcount, co.co_posonlyargcount, co.co_kwonlyargcount, co.co_nlocals,
co.co_stacksize,
co.co_flags | 0x100, # 0x100 == CO_ITERABLE_COROUTINE
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)
# 0x100 == CO_ITERABLE_COROUTINE
func.__code__ = co.replace(co_flags=co.co_flags | 0x100)
return func

# The following code is primarily to support functions that
@@ -0,0 +1 @@
Added new ``replace()`` method to the code type (:class:`types.CodeType`).

0 comments on commit a9f05d6

Please sign in to comment.
You can’t perform that action at this time.