gh-132775: Add _PyCode_GetXIData() (gh-133475)

This commit is contained in:
Eric Snow 2025-05-05 17:46:03 -06:00 committed by GitHub
parent e9616110aa
commit ea598730ef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 116 additions and 6 deletions

View File

@ -185,6 +185,13 @@ PyAPI_FUNC(int) _PyMarshal_GetXIData(
PyObject *,
_PyXIData_t *);
// _PyObject_GetXIData() for code objects
PyAPI_FUNC(PyObject *) _PyCode_FromXIData(_PyXIData_t *);
PyAPI_FUNC(int) _PyCode_GetXIData(
PyThreadState *,
PyObject *,
_PyXIData_t *);
/* using cross-interpreter data */

View File

@ -29,6 +29,12 @@ def spam_with_globals_and_builtins():
print(res)
def spam_args_attrs_and_builtins(a, b, /, c, d, *args, e, f, **kwargs):
if args.__len__() > 2:
return None
return a, b, c, d, e, f, args, kwargs
def spam_returns_arg(x):
return x
@ -46,6 +52,10 @@ def spam_with_inner_closure():
eggs()
def spam_annotated(a: int, b: str, c: object) -> tuple:
return a, b, c
def spam_full(a, b, /, c, d:int=1, *args, e, f:object=None, **kwargs) -> tuple:
# arg defaults, kwarg defaults
# annotations
@ -134,9 +144,11 @@ TOP_FUNCTIONS = [
spam_minimal,
spam_with_builtins,
spam_with_globals_and_builtins,
spam_args_attrs_and_builtins,
spam_returns_arg,
spam_with_inner_not_closure,
spam_with_inner_closure,
spam_annotated,
spam_full,
spam,
# outer func
@ -170,7 +182,9 @@ STATELESS_FUNCTIONS = [
spam,
spam_minimal,
spam_with_builtins,
spam_args_attrs_and_builtins,
spam_returns_arg,
spam_annotated,
spam_with_inner_not_closure,
spam_with_inner_closure,
spam_N,

View File

@ -687,6 +687,16 @@ class CodeTest(unittest.TestCase):
'checks': CO_FAST_LOCAL,
'res': CO_FAST_LOCAL,
},
defs.spam_args_attrs_and_builtins: {
'a': POSONLY,
'b': POSONLY,
'c': POSORKW,
'd': POSORKW,
'e': KWONLY,
'f': KWONLY,
'args': VARARGS,
'kwargs': VARKWARGS,
},
defs.spam_returns_arg: {
'x': POSORKW,
},
@ -697,6 +707,11 @@ class CodeTest(unittest.TestCase):
'x': CO_FAST_CELL,
'eggs': CO_FAST_LOCAL,
},
defs.spam_annotated: {
'a': POSORKW,
'b': POSORKW,
'c': POSORKW,
},
defs.spam_full: {
'a': POSONLY,
'b': POSONLY,
@ -892,6 +907,14 @@ class CodeTest(unittest.TestCase):
purelocals=5,
globalvars=6,
),
defs.spam_args_attrs_and_builtins: new_var_counts(
posonly=2,
posorkw=2,
kwonly=2,
varargs=1,
varkwargs=1,
attrs=1,
),
defs.spam_returns_arg: new_var_counts(
posorkw=1,
),
@ -902,6 +925,9 @@ class CodeTest(unittest.TestCase):
othercells=1,
purelocals=1,
),
defs.spam_annotated: new_var_counts(
posorkw=3,
),
defs.spam_full: new_var_counts(
posonly=2,
posorkw=2,

View File

@ -725,6 +725,39 @@ class MarshalTests(_GetXIDataTests):
])
class CodeTests(_GetXIDataTests):
MODE = 'code'
def test_function_code(self):
self.assert_roundtrip_equal_not_identical([
*(f.__code__ for f in defs.FUNCTIONS),
*(f.__code__ for f in defs.FUNCTION_LIKE),
])
def test_functions(self):
self.assert_not_shareable([
*defs.FUNCTIONS,
*defs.FUNCTION_LIKE,
])
def test_other_objects(self):
self.assert_not_shareable([
None,
True,
False,
Ellipsis,
NotImplemented,
9999,
'spam',
b'spam',
(),
[],
{},
object(),
])
class ShareableTypeTests(_GetXIDataTests):
MODE = 'xidata'
@ -817,6 +850,13 @@ class ShareableTypeTests(_GetXIDataTests):
object(),
])
def test_code(self):
# types.CodeType
self.assert_not_shareable([
*(f.__code__ for f in defs.FUNCTIONS),
*(f.__code__ for f in defs.FUNCTION_LIKE),
])
def test_function_object(self):
for func in defs.FUNCTIONS:
assert type(func) is types.FunctionType, func
@ -935,12 +975,6 @@ class ShareableTypeTests(_GetXIDataTests):
self.assert_not_shareable([
types.MappingProxyType({}),
types.SimpleNamespace(),
# types.CodeType
defs.spam_minimal.__code__,
defs.spam_full.__code__,
defs.spam_CC.__code__,
defs.eggs_closure_C.__code__,
defs.ham_C_closure.__code__,
# types.CellType
types.CellType(),
# types.FrameType

View File

@ -1984,6 +1984,11 @@ get_crossinterp_data(PyObject *self, PyObject *args, PyObject *kwargs)
goto error;
}
}
else if (strcmp(mode, "code") == 0) {
if (_PyCode_GetXIData(tstate, obj, xidata) != 0) {
goto error;
}
}
else {
PyErr_Format(PyExc_ValueError, "unsupported mode %R", modeobj);
goto error;

View File

@ -654,6 +654,30 @@ error:
return -1;
}
// code
PyObject *
_PyCode_FromXIData(_PyXIData_t *xidata)
{
return _PyMarshal_ReadObjectFromXIData(xidata);
}
int
_PyCode_GetXIData(PyThreadState *tstate, PyObject *obj, _PyXIData_t *xidata)
{
if (!PyCode_Check(obj)) {
_PyXIData_FormatNotShareableError(tstate, "expected code, got %R", obj);
return -1;
}
if (_PyMarshal_GetXIData(tstate, obj, xidata) < 0) {
return -1;
}
assert(_PyXIData_CHECK_NEW_OBJECT(xidata, _PyMarshal_ReadObjectFromXIData));
_PyXIData_SET_NEW_OBJECT(xidata, _PyCode_FromXIData);
return 0;
}
// registration
static void