8000 gh-81137: deprecate assignment of code object to a function of a mism… · python/cpython@2f9cb7e · GitHub
[go: up one dir, main page]

Skip to content

Commit 2f9cb7e

Browse files
authored
gh-81137: deprecate assignment of code object to a function of a mismatched type (#111823)
1 parent 178861b commit 2f9cb7e

File tree

4 files changed

+44
-0
lines changed

4 files changed

+44
-0
lines changed

Doc/whatsnew/3.13.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,12 @@ Deprecated
401401
(as has always been the case for an executing frame).
402402
(Contributed by Irit Katriel in :gh:`79932`.)
403403

404+
* Assignment to a function's ``__code__`` attribute where the new code
405+
object's type does not match the function's type, is deprecated. The
406+
different types are: plain function, generator, async generator and
407+
coroutine.
408+
(Contributed by Irit Katriel in :gh:`81137`.)
409+
404410

405411
Pending Removal in Python 3.14
406412
------------------------------

Lib/test/test_funcattrs.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import types
33
import typing
44
import unittest
5+
import warnings
56

67

78
def global_function():
@@ -70,6 +71,27 @@ def test(): pass
7071
test.__code__ = self.b.__code__
7172
self.assertEqual(test(), 3) # self.b always returns 3, arbitrarily
7273

74+
def test_invalid___code___assignment(self):
75+
def A(): pass
76+
def B(): yield
77+
async def C(): yield
78+
async def D(x): await x
79+
80+
for src in [A, B, C, D]:
81+
for dst in [A, B, C, D]:
82+
if src == dst:
83+
continue
84+
85+
assert src.__code__.co_flags != dst.__code__.co_flags
86+
prev = dst.__code__
87+
try:
88+
with self.assertWarnsRegex(DeprecationWarning, 'code object of non-matching type'):
89+
dst.__code__ = src.__code__
90+
finally:
91+
with warnings.catch_warnings():
92+
warnings.filterwarnings('ignore', '', DeprecationWarning)
93+
dst.__code__ = prev
94+
7395
def test___globals__(self):
7496
self.assertIs(self.b.__globals__, globals())
7597
self.cannot_set_attr(self.b, '__globals__', 2,
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Deprecate assignment to a function's ``__code__`` field when the new code
2+
object is of a mismatched type (e.g., from a generator to a plain function).

Objects/funcobject.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,20 @@ func_set_code(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(ignored))
557557
nclosure, nfree);
558558
return -1;
559559
}
560+
561+
PyObject *func_code = PyFunction_GET_CODE(op);
562+
int old_flags = ((PyCodeObject *)func_code)->co_flags;
563+
int new_flags = ((PyCodeObject *)value)->co_flags;
564+
int mask = CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR;
565+
if ((old_flags & mask) != (new_flags & mask)) {
566+
if (PyErr_Warn(PyExc_DeprecationWarning,
567+
"Assigning a code object of non-matching type is deprecated "
568+
"(e.g., from a generator to a plain function)") < 0)
569+
{
570+
return -1;
571+
}
572+
}
573+
560574
handle_func_event(PyFunction_EVENT_MODIFY_CODE, op, value);
561575
_PyFunction_SetVersion(op, 0);
562576
Py_XSETREF(op->func_code, Py_NewRef(value));

0 commit comments

Comments
 (0)
0