summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/library/inspect.rst7
-rw-r--r--Include/cpython/code.h3
-rw-r--r--Include/internal/pycore_symtable.h1
-rw-r--r--Lib/dis.py1
-rw-r--r--Lib/inspect.py1
-rw-r--r--Lib/test/test_monitoring.py9
-rw-r--r--Lib/test/test_opcache.py11
-rw-r--r--Misc/NEWS.d/next/Core_and_Builtins/2024-12-17-13-45-33.gh-issue-127274.deNxNC.rst3
-rw-r--r--Objects/funcobject.c6
-rw-r--r--Python/compile.c2
-rw-r--r--Python/symtable.c7
11 files changed, 37 insertions, 14 deletions
diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst
index ca5dac8..0085207 100644
--- a/Doc/library/inspect.rst
+++ b/Doc/library/inspect.rst
@@ -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
diff --git a/Include/cpython/code.h b/Include/cpython/code.h
index c3c0165..cb6261d 100644
--- a/Include/cpython/code.h
+++ b/Include/cpython/code.h
@@ -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.
*/
diff --git a/Include/internal/pycore_symtable.h b/Include/internal/pycore_symtable.h
index 91dac76..b7e2742 100644
--- a/Include/internal/pycore_symtable.h
+++ b/Include/internal/pycore_symtable.h
@@ -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 */
diff --git a/Lib/dis.py b/Lib/dis.py
index aa22404..109c986 100644
--- a/Lib/dis.py
+++ b/Lib/dis.py
@@ -162,6 +162,7 @@ COMPILER_FLAG_NAMES = {
256: "ITERABLE_COROUTINE",
512: "ASYNC_GENERATOR",
0x4000000: "HAS_DOCSTRING",
+ 0x8000000: "METHOD",
}
def pretty_flags(flags):
diff --git a/Lib/inspect.py b/Lib/inspect.py
index b7d8271..5b7c4df 100644
--- a/Lib/inspect.py
+++ b/Lib/inspect.py
@@ -57,6 +57,7 @@ __all__ = [
"CO_VARARGS",
"CO_VARKEYWORDS",
"CO_HAS_DOCSTRING",
+ "CO_METHOD",
"ClassFoundException",
"ClosureVars",
"EndOfBlock",
diff --git a/Lib/test/test_monitoring.py b/Lib/test/test_monitoring.py
index 087ac8d..32b3a6a 100644
--- a/Lib/test/test_monitoring.py
+++ b/Lib/test/test_monitoring.py
@@ -850,12 +850,6 @@ class ReturnRecorder:
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 @@ class ExceptionMonitoringTest(CheckEvents):
@requires_specialization_ft
def test_no_unwind_for_shim_frame(self):
+ class ValueErrorRaiser:
+ def __init__(self):
+ raise ValueError()
def f():
try:
diff --git a/Lib/test/test_opcache.py b/Lib/test/test_opcache.py
index ad0b0c4..ba111b5 100644
--- a/Lib/test/test_opcache.py
+++ b/Lib/test/test_opcache.py
@@ -493,13 +493,6 @@ class TestLoadMethodCache(unittest.TestCase):
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 @@ class TestCallCache(TestBase):
@disabling_optimizer
@requires_specialization_ft
def test_assign_init_code(self):
+ class MyClass:
+ def __init__(self):
+ pass
+
def instantiate():
return MyClass()
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-12-17-13-45-33.gh-issue-127274.deNxNC.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-17-13-45-33.gh-issue-127274.deNxNC.rst
new file mode 100644
index 0000000..a4608fb
--- /dev/null
+++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-12-17-13-45-33.gh-issue-127274.deNxNC.rst
@@ -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.
diff --git a/Objects/funcobject.c b/Objects/funcobject.c
index cca7f01..7b17a9b 100644
--- a/Objects/funcobject.c
+++ b/Objects/funcobject.c
@@ -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);
diff --git a/Python/compile.c b/Python/compile.c
index cbfba7f..ef47083 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -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) {
diff --git a/Python/symtable.c b/Python/symtable.c
index ebddb0b..49bd01b 100644
--- a/Python/symtable.c
+++ b/Python/symtable.c
@@ -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);