Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Include/funcobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ typedef struct {
PyObject *func_module; /* The __module__ attribute, can be anything */
PyObject *func_annotations; /* Annotations, a dict or NULL */
vectorcallfunc vectorcall;
uint32_t func_version;

/* Invariant:
* func_closure contains the bindings for func_code->co_freevars, so
Expand Down Expand Up @@ -74,6 +75,8 @@ PyAPI_FUNC(PyObject *) _PyFunction_Vectorcall(
PyObject *const *stack,
size_t nargsf,
PyObject *kwnames);

uint32_t _PyFunction_GetVersionForCurrentState(PyFunctionObject *func);
#endif

/* Macros for direct access to these values. Type checks are *not*
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_sys.py
Original file line number Diff line number Diff line change
Expand Up @@ -1278,7 +1278,7 @@ class C(object): pass
check(x, size('4P3i4cP'))
# function
def func(): pass
check(func, size('14P'))
check(func, size('14Pi'))
class c():
@staticmethod
def foo():
Expand Down
22 changes: 21 additions & 1 deletion Objects/funcobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#include "pycore_pyerrors.h" // _PyErr_Occurred()
#include "structmember.h" // PyMemberDef

static uint32_t next_func_version = 1;

PyObject *
PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname)
{
Expand Down Expand Up @@ -79,7 +81,7 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname
op->func_module = module;
op->func_annotations = NULL;
op->vectorcall = _PyFunction_Vectorcall;

op->func_version = 0;
_PyObject_GC_TRACK(op);
return (PyObject *)op;

Expand All @@ -94,6 +96,19 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname
return NULL;
}

uint32_t _PyFunction_GetVersionForCurrentState(PyFunctionObject *func)
{
if (func->func_version != 0) {
return func->func_version;
}
if (next_func_version == 0) {
return 0;
}
uint32_t v = next_func_version++;
func->func_version = v;
return v;
}

PyObject *
PyFunction_New(PyObject *code, PyObject *globals)
{
Expand Down Expand Up @@ -308,6 +323,7 @@ func_set_code(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(ignored))
nclosure, nfree);
return -1;
}
op->func_version = 0;
Py_INCREF(value);
Py_XSETREF(op->func_code, value);
return 0;
Expand Down Expand Up @@ -392,6 +408,7 @@ func_set_defaults(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(ignored
return -1;
}

op->func_version = 0;
Py_XINCREF(value);
Py_XSETREF(op->func_defaults, value);
return 0;
Expand Down Expand Up @@ -433,6 +450,7 @@ func_set_kwdefaults(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(ignor
return -1;
}

op->func_version = 0;
Py_XINCREF(value);
Py_XSETREF(op->func_kwdefaults, value);
return 0;
Expand Down Expand Up @@ -482,6 +500,7 @@ func_set_annotations(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(igno
"__annotations__ must be set to a dict object");
return -1;
}
op->func_version = 0;
Py_XINCREF(value);
Py_XSETREF(op->func_annotations, value);
return 0;
Expand Down Expand Up @@ -611,6 +630,7 @@ func_new_impl(PyTypeObject *type, PyCodeObject *code, PyObject *globals,
static int
func_clear(PyFunctionObject *op)
{
op->func_version = 0;
Py_CLEAR(op->func_code);
Py_CLEAR(op->func_globals);
Py_CLEAR(op->func_builtins);
Expand Down