mirror of https://github.com/python/cpython
gh-81137: deprecate assignment of code object to a function of a mismatched type (#111823)
This commit is contained in:
parent
178861b193
commit
2f9cb7e095
|
@ -401,6 +401,12 @@ Deprecated
|
||||||
(as has always been the case for an executing frame).
|
(as has always been the case for an executing frame).
|
||||||
(Contributed by Irit Katriel in :gh:`79932`.)
|
(Contributed by Irit Katriel in :gh:`79932`.)
|
||||||
|
|
||||||
|
* Assignment to a function's ``__code__`` attribute where the new code
|
||||||
|
object's type does not match the function's type, is deprecated. The
|
||||||
|
different types are: plain function, generator, async generator and
|
||||||
|
coroutine.
|
||||||
|
(Contributed by Irit Katriel in :gh:`81137`.)
|
||||||
|
|
||||||
|
|
||||||
Pending Removal in Python 3.14
|
Pending Removal in Python 3.14
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|
|
@ -2,6 +2,7 @@ import textwrap
|
||||||
import types
|
import types
|
||||||
import typing
|
import typing
|
||||||
import unittest
|
import unittest
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
|
||||||
def global_function():
|
def global_function():
|
||||||
|
@ -70,6 +71,27 @@ class FunctionPropertiesTest(FuncAttrsTest):
|
||||||
test.__code__ = self.b.__code__
|
test.__code__ = self.b.__code__
|
||||||
self.assertEqual(test(), 3) # self.b always returns 3, arbitrarily
|
self.assertEqual(test(), 3) # self.b always returns 3, arbitrarily
|
||||||
|
|
||||||
|
def test_invalid___code___assignment(self):
|
||||||
|
def A(): pass
|
||||||
|
def B(): yield
|
||||||
|
async def C(): yield
|
||||||
|
async def D(x): await x
|
||||||
|
|
||||||
|
for src in [A, B, C, D]:
|
||||||
|
for dst in [A, B, C, D]:
|
||||||
|
if src == dst:
|
||||||
|
continue
|
||||||
|
|
||||||
|
assert src.__code__.co_flags != dst.__code__.co_flags
|
||||||
|
prev = dst.__code__
|
||||||
|
try:
|
||||||
|
with self.assertWarnsRegex(DeprecationWarning, 'code object of non-matching type'):
|
||||||
|
dst.__code__ = src.__code__
|
||||||
|
finally:
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.filterwarnings('ignore', '', DeprecationWarning)
|
||||||
|
dst.__code__ = prev
|
||||||
|
|
||||||
def test___globals__(self):
|
def test___globals__(self):
|
||||||
self.assertIs(self.b.__globals__, globals())
|
self.assertIs(self.b.__globals__, globals())
|
||||||
self.cannot_set_attr(self.b, '__globals__', 2,
|
self.cannot_set_attr(self.b, '__globals__', 2,
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Deprecate assignment to a function's ``__code__`` field when the new code
|
||||||
|
object is of a mismatched type (e.g., from a generator to a plain function).
|
|
@ -557,6 +557,20 @@ func_set_code(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(ignored))
|
||||||
nclosure, nfree);
|
nclosure, nfree);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PyObject *func_code = PyFunction_GET_CODE(op);
|
||||||
|
int old_flags = ((PyCodeObject *)func_code)->co_flags;
|
||||||
|
int new_flags = ((PyCodeObject *)value)->co_flags;
|
||||||
|
int mask = CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR;
|
||||||
|
if ((old_flags & mask) != (new_flags & mask)) {
|
||||||
|
if (PyErr_Warn(PyExc_DeprecationWarning,
|
||||||
|
"Assigning a code object of non-matching type is deprecated "
|
||||||
|
"(e.g., from a generator to a plain function)") < 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
handle_func_event(PyFunction_EVENT_MODIFY_CODE, op, value);
|
handle_func_event(PyFunction_EVENT_MODIFY_CODE, op, value);
|
||||||
_PyFunction_SetVersion(op, 0);
|
_PyFunction_SetVersion(op, 0);
|
||||||
Py_XSETREF(op->func_code, Py_NewRef(value));
|
Py_XSETREF(op->func_code, Py_NewRef(value));
|
||||||
|
|
Loading…
Reference in New Issue