Skip to content

Commit a9f05d6

Browse files
authored
bpo-37032: Add CodeType.replace() method (GH-13542)
1 parent 561612d commit a9f05d6

File tree

8 files changed

+401
-33
lines changed

8 files changed

+401
-33
lines changed

Doc/whatsnew/3.8.rst

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,9 @@ Other Language Changes
240240
and Windows use this to properly terminate scripts in interactive sessions.
241241
(Contributed by Google via Gregory P. Smith in :issue:`1054041`.)
242242

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

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

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

10561060

10571061
Changes in the C API

Lib/modulefinder.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -619,13 +619,7 @@ def replace_paths_in_code(self, co):
619619
if isinstance(consts[i], type(co)):
620620
consts[i] = self.replace_paths_in_code(consts[i])
621621

622-
return types.CodeType(co.co_argcount, co.co_posonlyargcount,
623-
co.co_kwonlyargcount, co.co_nlocals,
624-
co.co_stacksize, co.co_flags,
625-
co.co_code, tuple(consts), co.co_names,
626-
co.co_varnames, new_filename, co.co_name,
627-
co.co_firstlineno, co.co_lnotab, co.co_freevars,
628-
co.co_cellvars)
622+
return co.replace(co_consts=tuple(consts), co_filename=new_filename)
629623

630624

631625
def test():

Lib/test/test_code.py

Lines changed: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -174,18 +174,14 @@ def test_newempty(self):
174174
@cpython_only
175175
def test_closure_injection(self):
176176
# From https://bugs.python.org/issue32176
177-
from types import FunctionType, CodeType
177+
from types import FunctionType
178178

179179
def create_closure(__class__):
180180
return (lambda: __class__).__closure__
181181

182182
def new_code(c):
183183
'''A new code object with a __class__ cell added to freevars'''
184-
return CodeType(
185-
c.co_argcount, c.co_posonlyargcount, c.co_kwonlyargcount, c.co_nlocals,
186-
c.co_stacksize, c.co_flags, c.co_code, c.co_consts, c.co_names,
187-
c.co_varnames, c.co_filename, c.co_name, c.co_firstlineno,
188-
c.co_lnotab, c.co_freevars + ('__class__',), c.co_cellvars)
184+
return c.replace(co_freevars=c.co_freevars + ('__class__',))
189185

190186
def add_foreign_method(cls, name, f):
191187
code = new_code(f.__code__)
@@ -212,6 +208,64 @@ class List(list):
212208
obj = List([1, 2, 3])
213209
self.assertEqual(obj[0], "Foreign getitem: 1")
214210

211+
def test_constructor(self):
212+
def func(): pass
213+
co = func.__code__
214+
CodeType = type(co)
215+
216+
# test code constructor
217+
return CodeType(co.co_argcount,
218+
co.co_posonlyargcount,
219+
co.co_kwonlyargcount,
220+
co.co_nlocals,
221+
co.co_stacksize,
222+
co.co_flags,
223+
co.co_code,
224+
co.co_consts,
225+
co.co_names,
226+
co.co_varnames,
227+
co.co_filename,
228+
co.co_name,
229+
co.co_firstlineno,
230+
co.co_lnotab,
231+
co.co_freevars,
232+
co.co_cellvars)
233+
234+
def test_replace(self):
235+
def func():
236+
x = 1
237+
return x
238+
code = func.__code__
239+
240+
# different co_name, co_varnames, co_consts
241+
def func2():
242+
y = 2
243+
return y
244+
code2 = func.__code__
245+
246+
for attr, value in (
247+
("co_argcount", 0),
248+
("co_posonlyargcount", 0),
249+
("co_kwonlyargcount", 0),
250+
("co_nlocals", 0),
251+
("co_stacksize", 0),
252+
("co_flags", code.co_flags | inspect.CO_COROUTINE),
253+
("co_firstlineno", 100),
254+
("co_code", code2.co_code),
255+
("co_consts", code2.co_consts),
256+
("co_names", ("myname",)),
257+
("co_varnames", code2.co_varnames),
258+
("co_freevars", ("freevar",)),
259+
("co_cellvars", ("cellvar",)),
260+
("co_filename", "newfilename"),
261+
("co_name", "newname"),
262+
("co_lnotab", code2.co_lnotab),
263+
):
264+
with self.subTest(attr=attr, value=value):
265+
new_code = code.replace(**{attr: value})
266+
self.assertEqual(getattr(new_code, attr), value)
267+
268+
215269
def isinterned(s):
216270
return s is sys.intern(('_' + s + '_')[1:-1])
217271

Lib/test/test_import/__init__.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -674,13 +674,7 @@ def test_foreign_code(self):
674674
foreign_code = importlib.import_module.__code__
675675
pos = constants.index(1)
676676
constants[pos] = foreign_code
677-
code = type(code)(code.co_argcount, code.co_posonlyargcount,
678-
code.co_kwonlyargcount,
679-
code.co_nlocals, code.co_stacksize,
680-
code.co_flags, code.co_code, tuple(constants),
681-
code.co_names, code.co_varnames, code.co_filename,
682-
code.co_name, code.co_firstlineno, code.co_lnotab,
683-
code.co_freevars, code.co_cellvars)
677+
code = code.replace(co_consts=tuple(constants))
684678
with open(self.compiled_name, "wb") as f:
685679
f.write(header)
686680
marshal.dump(code, f)

Lib/types.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -262,14 +262,8 @@ def coroutine(func):
262262
if co_flags & 0x20:
263263
# TODO: Implement this in C.
264264
co = func.__code__
265-
func.__code__ = CodeType(
266-
co.co_argcount, co.co_posonlyargcount, co.co_kwonlyargcount, co.co_nlocals,
267-
co.co_stacksize,
268-
co.co_flags | 0x100, # 0x100 == CO_ITERABLE_COROUTINE
269-
co.co_code,
270-
co.co_consts, co.co_names, co.co_varnames, co.co_filename,
271-
co.co_name, co.co_firstlineno, co.co_lnotab, co.co_freevars,
272-
co.co_cellvars)
265+
# 0x100 == CO_ITERABLE_COROUTINE
266+
func.__code__ = co.replace(co_flags=co.co_flags | 0x100)
273267
return func
274268

275269
# The following code is primarily to support functions that
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added new ``replace()`` method to the code type (:class:`types.CodeType`).

0 commit comments

Comments
 (0)