mirror of https://github.com/python/cpython.git
gh-127274: Defer nested methods (#128012)
Methods (functions defined in class scope) are likely to be cleaned up by the GC anyway. Add a new code flag, `CO_METHOD`, that is set for functions defined in a class scope. Use that when deciding to defer functions.
This commit is contained in:
parent
e163e8d4e1
commit
255762c09f
|
@ -1708,6 +1708,13 @@ which is a bitmap of the following flags:
|
|||
|
||||
.. versionadded:: 3.14
|
||||
|
||||
.. data:: CO_METHOD
|
||||
|
||||
The flag is set when the code object is a function defined in class
|
||||
scope.
|
||||
|
||||
.. versionadded:: 3.14
|
||||
|
||||
.. note::
|
||||
The flags are specific to CPython, and may not be defined in other
|
||||
Python implementations. Furthermore, the flags are an implementation
|
||||
|
|
|
@ -199,6 +199,9 @@ struct PyCodeObject _PyCode_DEF(1);
|
|||
*/
|
||||
#define CO_HAS_DOCSTRING 0x4000000
|
||||
|
||||
/* A function defined in class scope */
|
||||
#define CO_METHOD 0x8000000
|
||||
|
||||
/* This should be defined if a future statement modifies the syntax.
|
||||
For example, when a keyword is added.
|
||||
*/
|
||||
|
|
|
@ -124,6 +124,7 @@ typedef struct _symtable_entry {
|
|||
unsigned ste_can_see_class_scope : 1; /* true if this block can see names bound in an
|
||||
enclosing class scope */
|
||||
unsigned ste_has_docstring : 1; /* true if docstring present */
|
||||
unsigned ste_method : 1; /* true if block is a function block defined in class scope */
|
||||
int ste_comp_iter_expr; /* non-zero if visiting a comprehension range expression */
|
||||
_Py_SourceLocation ste_loc; /* source location of block */
|
||||
struct _symtable_entry *ste_annotation_block; /* symbol table entry for this entry's annotations */
|
||||
|
|
|
@ -162,6 +162,7 @@ def distb(tb=None, *, file=None, show_caches=False, adaptive=False, show_offsets
|
|||
256: "ITERABLE_COROUTINE",
|
||||
512: "ASYNC_GENERATOR",
|
||||
0x4000000: "HAS_DOCSTRING",
|
||||
0x8000000: "METHOD",
|
||||
}
|
||||
|
||||
def pretty_flags(flags):
|
||||
|
|
|
@ -57,6 +57,7 @@
|
|||
"CO_VARARGS",
|
||||
"CO_VARKEYWORDS",
|
||||
"CO_HAS_DOCSTRING",
|
||||
"CO_METHOD",
|
||||
"ClassFoundException",
|
||||
"ClosureVars",
|
||||
"EndOfBlock",
|
||||
|
|
|
@ -850,12 +850,6 @@ def __init__(self, events):
|
|||
def __call__(self, code, offset, val):
|
||||
self.events.append(("return", code.co_name, val))
|
||||
|
||||
# gh-127274: CALL_ALLOC_AND_ENTER_INIT will only cache __init__ methods that
|
||||
# are deferred. We only defer functions defined at the top-level.
|
||||
class ValueErrorRaiser:
|
||||
def __init__(self):
|
||||
raise ValueError()
|
||||
|
||||
|
||||
class ExceptionMonitoringTest(CheckEvents):
|
||||
|
||||
|
@ -1054,6 +1048,9 @@ def func():
|
|||
|
||||
@requires_specialization_ft
|
||||
def test_no_unwind_for_shim_frame(self):
|
||||
class ValueErrorRaiser:
|
||||
def __init__(self):
|
||||
raise ValueError()
|
||||
|
||||
def f():
|
||||
try:
|
||||
|
|
|
@ -493,13 +493,6 @@ def f():
|
|||
self.assertFalse(f())
|
||||
|
||||
|
||||
# gh-127274: CALL_ALLOC_AND_ENTER_INIT will only cache __init__ methods that
|
||||
# are deferred. We only defer functions defined at the top-level.
|
||||
class MyClass:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
|
||||
class InitTakesArg:
|
||||
def __init__(self, arg):
|
||||
self.arg = arg
|
||||
|
@ -536,6 +529,10 @@ def f(x, y):
|
|||
@disabling_optimizer
|
||||
@requires_specialization_ft
|
||||
def test_assign_init_code(self):
|
||||
class MyClass:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def instantiate():
|
||||
return MyClass()
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
Add a new flag, ``CO_METHOD``, to :attr:`~codeobject.co_flags` that
|
||||
indicates whether the code object belongs to a function defined in class
|
||||
scope.
|
|
@ -210,10 +210,14 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname
|
|||
op->func_typeparams = NULL;
|
||||
op->vectorcall = _PyFunction_Vectorcall;
|
||||
op->func_version = FUNC_VERSION_UNSET;
|
||||
if ((code_obj->co_flags & CO_NESTED) == 0) {
|
||||
if (((code_obj->co_flags & CO_NESTED) == 0) ||
|
||||
(code_obj->co_flags & CO_METHOD)) {
|
||||
// Use deferred reference counting for top-level functions, but not
|
||||
// nested functions because they are more likely to capture variables,
|
||||
// which makes prompt deallocation more important.
|
||||
//
|
||||
// Nested methods (functions defined in class scope) are also deferred,
|
||||
// since they will likely be cleaned up by GC anyway.
|
||||
_PyObject_SetDeferredRefcount((PyObject *)op);
|
||||
}
|
||||
_PyObject_GC_TRACK(op);
|
||||
|
|
|
@ -1289,6 +1289,8 @@ compute_code_flags(compiler *c)
|
|||
flags |= CO_VARKEYWORDS;
|
||||
if (ste->ste_has_docstring)
|
||||
flags |= CO_HAS_DOCSTRING;
|
||||
if (ste->ste_method)
|
||||
flags |= CO_METHOD;
|
||||
}
|
||||
|
||||
if (ste->ste_coroutine && !ste->ste_generator) {
|
||||
|
|
|
@ -138,6 +138,13 @@ ste_new(struct symtable *st, identifier name, _Py_block_ty block,
|
|||
|
||||
ste->ste_has_docstring = 0;
|
||||
|
||||
ste->ste_method = 0;
|
||||
if (st->st_cur != NULL &&
|
||||
st->st_cur->ste_type == ClassBlock &&
|
||||
block == FunctionBlock) {
|
||||
ste->ste_method = 1;
|
||||
}
|
||||
|
||||
ste->ste_symbols = PyDict_New();
|
||||
ste->ste_varnames = PyList_New(0);
|
||||
ste->ste_children = PyList_New(0);
|
||||
|
|
Loading…
Reference in New Issue