mirror of
https://github.com/python/cpython.git
synced 2025-07-17 16:25:18 +00:00
gh-132775: Add _PyFunction_GetXIData() (gh-133481)
This commit is contained in:
parent
121ed71f4e
commit
8cf4947b0f
5 changed files with 103 additions and 0 deletions
|
@ -200,6 +200,13 @@ PyAPI_FUNC(int) _PyCode_GetPureScriptXIData(
|
||||||
PyObject *,
|
PyObject *,
|
||||||
_PyXIData_t *);
|
_PyXIData_t *);
|
||||||
|
|
||||||
|
// _PyObject_GetXIData() for functions
|
||||||
|
PyAPI_FUNC(PyObject *) _PyFunction_FromXIData(_PyXIData_t *);
|
||||||
|
PyAPI_FUNC(int) _PyFunction_GetXIData(
|
||||||
|
PyThreadState *,
|
||||||
|
PyObject *,
|
||||||
|
_PyXIData_t *);
|
||||||
|
|
||||||
|
|
||||||
/* using cross-interpreter data */
|
/* using cross-interpreter data */
|
||||||
|
|
||||||
|
|
|
@ -758,6 +758,40 @@ class CodeTests(_GetXIDataTests):
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
|
class ShareableFuncTests(_GetXIDataTests):
|
||||||
|
|
||||||
|
MODE = 'func'
|
||||||
|
|
||||||
|
def test_stateless(self):
|
||||||
|
self.assert_roundtrip_not_equal([
|
||||||
|
*defs.STATELESS_FUNCTIONS,
|
||||||
|
# Generators can be stateless too.
|
||||||
|
*defs.FUNCTION_LIKE,
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_not_stateless(self):
|
||||||
|
self.assert_not_shareable([
|
||||||
|
*(f for f in defs.FUNCTIONS
|
||||||
|
if f not in defs.STATELESS_FUNCTIONS),
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_other_objects(self):
|
||||||
|
self.assert_not_shareable([
|
||||||
|
None,
|
||||||
|
True,
|
||||||
|
False,
|
||||||
|
Ellipsis,
|
||||||
|
NotImplemented,
|
||||||
|
9999,
|
||||||
|
'spam',
|
||||||
|
b'spam',
|
||||||
|
(),
|
||||||
|
[],
|
||||||
|
{},
|
||||||
|
object(),
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
class PureShareableScriptTests(_GetXIDataTests):
|
class PureShareableScriptTests(_GetXIDataTests):
|
||||||
|
|
||||||
MODE = 'script-pure'
|
MODE = 'script-pure'
|
||||||
|
|
|
@ -1989,6 +1989,11 @@ get_crossinterp_data(PyObject *self, PyObject *args, PyObject *kwargs)
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (strcmp(mode, "func") == 0) {
|
||||||
|
if (_PyFunction_GetXIData(tstate, obj, xidata) != 0) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (strcmp(mode, "script") == 0) {
|
else if (strcmp(mode, "script") == 0) {
|
||||||
if (_PyCode_GetScriptXIData(tstate, obj, xidata) != 0) {
|
if (_PyCode_GetScriptXIData(tstate, obj, xidata) != 0) {
|
||||||
goto error;
|
goto error;
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "pycore_initconfig.h" // _PyStatus_OK()
|
#include "pycore_initconfig.h" // _PyStatus_OK()
|
||||||
#include "pycore_namespace.h" // _PyNamespace_New()
|
#include "pycore_namespace.h" // _PyNamespace_New()
|
||||||
#include "pycore_pythonrun.h" // _Py_SourceAsString()
|
#include "pycore_pythonrun.h" // _Py_SourceAsString()
|
||||||
|
#include "pycore_setobject.h" // _PySet_NextEntry()
|
||||||
#include "pycore_typeobject.h" // _PyStaticType_InitBuiltin()
|
#include "pycore_typeobject.h" // _PyStaticType_InitBuiltin()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -677,6 +677,60 @@ _PyCode_GetXIData(PyThreadState *tstate, PyObject *obj, _PyXIData_t *xidata)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// function
|
||||||
|
|
||||||
|
PyObject *
|
||||||
|
_PyFunction_FromXIData(_PyXIData_t *xidata)
|
||||||
|
{
|
||||||
|
// For now "stateless" functions are the only ones we must accommodate.
|
||||||
|
|
||||||
|
PyObject *code = _PyMarshal_ReadObjectFromXIData(xidata);
|
||||||
|
if (code == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
// Create a new function.
|
||||||
|
assert(PyCode_Check(code));
|
||||||
|
PyObject *globals = PyDict_New();
|
||||||
|
if (globals == NULL) {
|
||||||
|
Py_DECREF(code);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
PyObject *func = PyFunction_New(code, globals);
|
||||||
|
Py_DECREF(code);
|
||||||
|
Py_DECREF(globals);
|
||||||
|
return func;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
_PyFunction_GetXIData(PyThreadState *tstate, PyObject *func,
|
||||||
|
_PyXIData_t *xidata)
|
||||||
|
{
|
||||||
|
if (!PyFunction_Check(func)) {
|
||||||
|
const char *msg = "expected a function, got %R";
|
||||||
|
format_notshareableerror(tstate, NULL, 0, msg, func);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (_PyFunction_VerifyStateless(tstate, func) < 0) {
|
||||||
|
PyObject *cause = _PyErr_GetRaisedException(tstate);
|
||||||
|
assert(cause != NULL);
|
||||||
|
const char *msg = "only stateless functions are shareable";
|
||||||
|
set_notshareableerror(tstate, cause, 0, msg);
|
||||||
|
Py_DECREF(cause);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
PyObject *code = PyFunction_GET_CODE(func);
|
||||||
|
|
||||||
|
// Ideally code objects would be immortal and directly shareable.
|
||||||
|
// In the meantime, we use marshal.
|
||||||
|
if (_PyMarshal_GetXIData(tstate, code, xidata) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
// Replace _PyMarshal_ReadObjectFromXIData.
|
||||||
|
// (_PyFunction_FromXIData() will call it.)
|
||||||
|
_PyXIData_SET_NEW_OBJECT(xidata, _PyFunction_FromXIData);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// registration
|
// registration
|
||||||
|
|
||||||
|
@ -717,4 +771,6 @@ _register_builtins_for_crossinterpreter_data(dlregistry_t *xidregistry)
|
||||||
if (_xidregistry_add_type(xidregistry, &PyTuple_Type, _tuple_shared) != 0) {
|
if (_xidregistry_add_type(xidregistry, &PyTuple_Type, _tuple_shared) != 0) {
|
||||||
Py_FatalError("could not register tuple for cross-interpreter sharing");
|
Py_FatalError("could not register tuple for cross-interpreter sharing");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For now, we do not register PyCode_Type or PyFunction_Type.
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue