https://github.com/python/cpython/commit/6f0432599297635492597e3766259390e8331c62 commit: 6f0432599297635492597e3766259390e8331c62 branch: main author: Eric Snow <ericsnowcurren...@gmail.com> committer: ericsnowcurrently <ericsnowcurren...@gmail.com> date: 2025-04-28T11:55:15-06:00 summary:
gh-132775: Cleanup Related to crossinterp.c Before Further Changes (gh-132974) This change consists of adding tests and moving code around, with some renaming thrown in. files: A Lib/test/_crossinterp_definitions.py A Lib/test/test_crossinterp.py M Include/internal/pycore_crossinterp.h M Lib/test/test__interpreters.py M Modules/_interpchannelsmodule.c M Modules/_interpqueuesmodule.c M Modules/_interpretersmodule.c M Modules/_testinternalcapi.c M Python/crossinterp.c M Python/crossinterp_data_lookup.h diff --git a/Include/internal/pycore_crossinterp.h b/Include/internal/pycore_crossinterp.h index c712bbba742eed..319b86186b1e69 100644 --- a/Include/internal/pycore_crossinterp.h +++ b/Include/internal/pycore_crossinterp.h @@ -57,7 +57,7 @@ struct _xidata { // likely a registered "xidatafunc", is responsible for // ensuring it owns the reference (i.e. incref). PyObject *obj; - // interp is the ID of the owning interpreter of the original + // interpid is the ID of the owning interpreter of the original // object. It corresponds to the active interpreter when // _PyObject_GetXIData() was called. This should only // be set by the cross-interpreter machinery. @@ -93,37 +93,6 @@ PyAPI_FUNC(void) _PyXIData_Free(_PyXIData_t *data); // Users should not need getters for "new_object" or "free". -/* getting cross-interpreter data */ - -typedef int (*xidatafunc)(PyThreadState *tstate, PyObject *, _PyXIData_t *); - -PyAPI_FUNC(PyObject *) _PyXIData_GetNotShareableErrorType(PyThreadState *); -PyAPI_FUNC(void) _PyXIData_SetNotShareableError(PyThreadState *, const char *); -PyAPI_FUNC(void) _PyXIData_FormatNotShareableError( - PyThreadState *, - const char *, - ...); - -PyAPI_FUNC(xidatafunc) _PyXIData_Lookup( - PyThreadState *, - PyObject *); -PyAPI_FUNC(int) _PyObject_CheckXIData( - PyThreadState *, - PyObject *); - -PyAPI_FUNC(int) _PyObject_GetXIData( - PyThreadState *, - PyObject *, - _PyXIData_t *); - - -/* using cross-interpreter data */ - -PyAPI_FUNC(PyObject *) _PyXIData_NewObject(_PyXIData_t *); -PyAPI_FUNC(int) _PyXIData_Release(_PyXIData_t *); -PyAPI_FUNC(int) _PyXIData_ReleaseAndRawFree(_PyXIData_t *); - - /* defining cross-interpreter data */ PyAPI_FUNC(void) _PyXIData_Init( @@ -134,7 +103,7 @@ PyAPI_FUNC(int) _PyXIData_InitWithSize( _PyXIData_t *, PyInterpreterState *interp, const size_t, PyObject *, xid_newobjfunc); -PyAPI_FUNC(void) _PyXIData_Clear( PyInterpreterState *, _PyXIData_t *); +PyAPI_FUNC(void) _PyXIData_Clear(PyInterpreterState *, _PyXIData_t *); // Normally the Init* functions are sufficient. The only time // additional initialization might be needed is to set the "free" func, @@ -156,6 +125,37 @@ PyAPI_FUNC(void) _PyXIData_Clear( PyInterpreterState *, _PyXIData_t *); } while (0) +/* getting cross-interpreter data */ + +typedef int (*xidatafunc)(PyThreadState *tstate, PyObject *, _PyXIData_t *); + +PyAPI_FUNC(PyObject *) _PyXIData_GetNotShareableErrorType(PyThreadState *); +PyAPI_FUNC(void) _PyXIData_SetNotShareableError(PyThreadState *, const char *); +PyAPI_FUNC(void) _PyXIData_FormatNotShareableError( + PyThreadState *, + const char *, + ...); + +PyAPI_FUNC(xidatafunc) _PyXIData_Lookup( + PyThreadState *, + PyObject *); +PyAPI_FUNC(int) _PyObject_CheckXIData( + PyThreadState *, + PyObject *); + +PyAPI_FUNC(int) _PyObject_GetXIData( + PyThreadState *, + PyObject *, + _PyXIData_t *); + + +/* using cross-interpreter data */ + +PyAPI_FUNC(PyObject *) _PyXIData_NewObject(_PyXIData_t *); +PyAPI_FUNC(int) _PyXIData_Release(_PyXIData_t *); +PyAPI_FUNC(int) _PyXIData_ReleaseAndRawFree(_PyXIData_t *); + + /* cross-interpreter data registry */ #define Py_CORE_CROSSINTERP_DATA_REGISTRY_H diff --git a/Lib/test/_crossinterp_definitions.py b/Lib/test/_crossinterp_definitions.py new file mode 100644 index 00000000000000..9b52aea39522f5 --- /dev/null +++ b/Lib/test/_crossinterp_definitions.py @@ -0,0 +1,230 @@ +# This may be loaded as a module, in the current __main__ module, +# or in another __main__ module. + + +####################################### +# functions + +def spam_minimal(): + # no arg defaults or kwarg defaults + # no annotations + # no local vars + # no free vars + # no globals + # no builtins + # no attr access (names) + # no code + return + + +def spam_full(a, b, /, c, d:int=1, *args, e, f:object=None, **kwargs) -> tuple: + # arg defaults, kwarg defaults + # annotations + # all kinds of local vars, except cells + # no free vars + # some globals + # some builtins + # some attr access (names) + x = args + y = kwargs + z = (a, b, c, d) + kwargs['e'] = e + kwargs['f'] = f + extras = list((x, y, z, spam, spam.__name__)) + return tuple(a, b, c, d, e, f, args, kwargs), extras + + +def spam(x): + return x, None + + +def spam_N(x): + def eggs_nested(y): + return None, y + return eggs_nested, x + + +def spam_C(x): + a = 1 + def eggs_closure(y): + return None, y, a, x + return eggs_closure, a, x + + +def spam_NN(x): + def eggs_nested_N(y): + def ham_nested(z): + return None, z + return ham_nested, y + return eggs_nested_N, x + + +def spam_NC(x): + a = 1 + def eggs_nested_C(y): + def ham_closure(z): + return None, z, y, a, x + return ham_closure, y + return eggs_nested_C, a, x + + +def spam_CN(x): + a = 1 + def eggs_closure_N(y): + def ham_C_nested(z): + return None, z + return ham_C_nested, y, a, x + return eggs_closure_N, a, x + + +def spam_CC(x): + a = 1 + def eggs_closure_C(y): + b = 2 + def ham_C_closure(z): + return None, z, b, y, a, x + return ham_C_closure, b, y, a, x + return eggs_closure_N, a, x + + +eggs_nested, *_ = spam_N(1) +eggs_closure, *_ = spam_C(1) +eggs_nested_N, *_ = spam_NN(1) +eggs_nested_C, *_ = spam_NC(1) +eggs_closure_N, *_ = spam_CN(1) +eggs_closure_C, *_ = spam_CC(1) + +ham_nested, *_ = eggs_nested_N(2) +ham_closure, *_ = eggs_nested_C(2) +ham_C_nested, *_ = eggs_closure_N(2) +ham_C_closure, *_ = eggs_closure_C(2) + + +FUNCTIONS = [ + # shallow + spam_minimal, + spam_full, + spam, + # outer func + spam_N, + spam_C, + spam_NN, + spam_NC, + spam_CN, + spam_CC, + # inner func + eggs_nested, + eggs_closure, + eggs_nested_N, + eggs_nested_C, + eggs_closure_N, + eggs_closure_C, + # inner inner func + ham_nested, + ham_closure, + ham_C_nested, + ham_C_closure, +] + + +####################################### +# function-like + +# generators + +def gen_spam_1(*args): + for arg in args: + yield arg + + +def gen_spam_2(*args): + yield from args + + +async def async_spam(): + pass +coro_spam = async_spam() +coro_spam.close() + + +async def asyncgen_spam(*args): + for arg in args: + yield arg +asynccoro_spam = asyncgen_spam(1, 2, 3) + + +FUNCTION_LIKE = [ + gen_spam_1, + gen_spam_2, + async_spam, + coro_spam, # actually FunctionType? + asyncgen_spam, + asynccoro_spam, # actually FunctionType? +] + + +####################################### +# classes + +class Spam: + # minimal + pass + + +class SpamOkay: + def okay(self): + return True + + +class SpamFull: + + a: object + b: object + c: object + + @staticmethod + def staticmeth(cls): + return True + + @classmethod + def classmeth(cls): + return True + + def __new__(cls, *args, **kwargs): + return super().__new__(cls) + + def __init__(self, a, b, c): + self.a = a + self.b = b + self.c = c + + # __repr__ + # __str__ + # ... + + @property + def prop(self): + return True + + +class SubSpamFull(SpamFull): + ... + + +class SubTuple(tuple): + ... + + +def class_eggs_inner(): + class EggsNested: + ... + return EggsNested +EggsNested = class_eggs_inner() + + + +####################################### +# exceptions + +class MimimalError(Exception): + pass diff --git a/Lib/test/test__interpreters.py b/Lib/test/test__interpreters.py index aa46eb4f7b8653..0c43f46300f67d 100644 --- a/Lib/test/test__interpreters.py +++ b/Lib/test/test__interpreters.py @@ -1,8 +1,6 @@ import contextlib -import itertools import os import pickle -import sys from textwrap import dedent import threading import unittest @@ -14,8 +12,7 @@ _interpreters = import_helper.import_module('_interpreters') -_testinternalcapi = import_helper.import_module('_testinternalcapi') -from _interpreters import InterpreterNotFoundError, NotShareableError +from _interpreters import InterpreterNotFoundError ################################## @@ -144,87 +141,6 @@ class SubBytes(bytes): _interpreters.is_shareable(obj)) -class ShareableTypeTests(unittest.TestCase): - - def _assert_values(self, values): - for obj in values: - with self.subTest(obj): - xid = _testinternalcapi.get_crossinterp_data(obj) - got = _testinternalcapi.restore_crossinterp_data(xid) - - self.assertEqual(got, obj) - self.assertIs(type(got), type(obj)) - - def test_singletons(self): - for obj in [None]: - with self.subTest(obj): - xid = _testinternalcapi.get_crossinterp_data(obj) - got = _testinternalcapi.restore_crossinterp_data(xid) - - # XXX What about between interpreters? - self.assertIs(got, obj) - - def test_types(self): - self._assert_values([ - b'spam', - 9999, - ]) - - def test_bytes(self): - self._assert_values(i.to_bytes(2, 'little', signed=True) - for i in range(-1, 258)) - - def test_strs(self): - self._assert_values(['hello world', '你好世界', '']) - - def test_int(self): - self._assert_values(itertools.chain(range(-1, 258), - [sys.maxsize, -sys.maxsize - 1])) - - def test_non_shareable_int(self): - ints = [ - sys.maxsize + 1, - -sys.maxsize - 2, - 2**1000, - ] - for i in ints: - with self.subTest(i): - with self.assertRaises(NotShareableError) as cm: - _testinternalcapi.get_crossinterp_data(i) - self.assertIsInstance(cm.exception.__cause__, OverflowError) - - def test_bool(self): - self._assert_values([True, False]) - - def test_float(self): - self._assert_values([0.0, 1.1, -1.0, 0.12345678, -0.12345678]) - - def test_tuple(self): - self._assert_values([(), (1,), ("hello", "world", ), (1, True, "hello")]) - # Test nesting - self._assert_values([ - ((1,),), - ((1, 2), (3, 4)), - ((1, 2), (3, 4), (5, 6)), - ]) - - def test_tuples_containing_non_shareable_types(self): - non_shareables = [ - Exception(), - object(), - ] - for s in non_shareables: - value = tuple([0, 1.0, s]) - with self.subTest(repr(value)): - with self.assertRaises(NotShareableError): - _testinternalcapi.get_crossinterp_data(value) - # Check nested as well - value = tuple([0, 1., (s,)]) - with self.subTest("nested " + repr(value)): - with self.assertRaises(NotShareableError): - _testinternalcapi.get_crossinterp_data(value) - - class ModuleTests(TestBase): def test_import_in_interpreter(self): diff --git a/Lib/test/test_crossinterp.py b/Lib/test/test_crossinterp.py new file mode 100644 index 00000000000000..e1d1998fefc7fb --- /dev/null +++ b/Lib/test/test_crossinterp.py @@ -0,0 +1,306 @@ +import itertools +import sys +import types +import unittest + +from test.support import import_helper + +_testinternalcapi = import_helper.import_module('_testinternalcapi') +_interpreters = import_helper.import_module('_interpreters') +from _interpreters import NotShareableError + + +from test import _crossinterp_definitions as defs + + +BUILTIN_TYPES = [o for _, o in __builtins__.items() + if isinstance(o, type)] +EXCEPTION_TYPES = [cls for cls in BUILTIN_TYPES + if issubclass(cls, BaseException)] + + +class _GetXIDataTests(unittest.TestCase): + + MODE = None + + def get_xidata(self, obj, *, mode=None): + mode = self._resolve_mode(mode) + return _testinternalcapi.get_crossinterp_data(obj, mode) + + def get_roundtrip(self, obj, *, mode=None): + mode = self._resolve_mode(mode) + xid =_testinternalcapi.get_crossinterp_data(obj, mode) + return _testinternalcapi.restore_crossinterp_data(xid) + + def iter_roundtrip_values(self, values, *, mode=None): + mode = self._resolve_mode(mode) + for obj in values: + with self.subTest(obj): + xid = _testinternalcapi.get_crossinterp_data(obj, mode) + got = _testinternalcapi.restore_crossinterp_data(xid) + yield obj, got + + def assert_roundtrip_equal(self, values, *, mode=None): + for obj, got in self.iter_roundtrip_values(values, mode=mode): + self.assertEqual(got, obj) + self.assertIs(type(got), type(obj)) + + def assert_roundtrip_identical(self, values, *, mode=None): + for obj, got in self.iter_roundtrip_values(values, mode=mode): + # XXX What about between interpreters? + self.assertIs(got, obj) + + def assert_not_shareable(self, values, exctype=None, *, mode=None): + mode = self._resolve_mode(mode) + for obj in values: + with self.subTest(obj): + with self.assertRaises(NotShareableError) as cm: + _testinternalcapi.get_crossinterp_data(obj, mode) + if exctype is not None: + self.assertIsInstance(cm.exception.__cause__, exctype) + + def _resolve_mode(self, mode): + if mode is None: + mode = self.MODE + assert mode + return mode + + +class ShareableTypeTests(_GetXIDataTests): + + MODE = 'xidata' + + def test_singletons(self): + self.assert_roundtrip_identical([ + None, + True, + False, + ]) + self.assert_not_shareable([ + Ellipsis, + NotImplemented, + ]) + + def test_types(self): + self.assert_roundtrip_equal([ + b'spam', + 9999, + ]) + + def test_bytes(self): + values = (i.to_bytes(2, 'little', signed=True) + for i in range(-1, 258)) + self.assert_roundtrip_equal(values) + + def test_strs(self): + self.assert_roundtrip_equal([ + 'hello world', + '你好世界', + '', + ]) + + def test_int(self): + bounds = [sys.maxsize, -sys.maxsize - 1] + values = itertools.chain(range(-1, 258), bounds) + self.assert_roundtrip_equal(values) + + def test_non_shareable_int(self): + ints = [ + sys.maxsize + 1, + -sys.maxsize - 2, + 2**1000, + ] + self.assert_not_shareable(ints, OverflowError) + + def test_float(self): + self.assert_roundtrip_equal([ + 0.0, + 1.1, + -1.0, + 0.12345678, + -0.12345678, + ]) + + def test_tuple(self): + self.assert_roundtrip_equal([ + (), + (1,), + ("hello", "world", ), + (1, True, "hello"), + ]) + # Test nesting + self.assert_roundtrip_equal([ + ((1,),), + ((1, 2), (3, 4)), + ((1, 2), (3, 4), (5, 6)), + ]) + + def test_tuples_containing_non_shareable_types(self): + non_shareables = [ + Exception(), + object(), + ] + for s in non_shareables: + value = tuple([0, 1.0, s]) + with self.subTest(repr(value)): + with self.assertRaises(NotShareableError): + self.get_xidata(value) + # Check nested as well + value = tuple([0, 1., (s,)]) + with self.subTest("nested " + repr(value)): + with self.assertRaises(NotShareableError): + self.get_xidata(value) + + # The rest are not shareable. + + def test_object(self): + self.assert_not_shareable([ + object(), + ]) + + def test_function_object(self): + for func in defs.FUNCTIONS: + assert type(func) is types.FunctionType, func + assert type(defs.SpamOkay.okay) is types.FunctionType, func + assert type(lambda: None) is types.LambdaType + + self.assert_not_shareable([ + *defs.FUNCTIONS, + defs.SpamOkay.okay, + (lambda: None), + ]) + + def test_builtin_function(self): + functions = [ + len, + sys.is_finalizing, + sys.exit, + _testinternalcapi.get_crossinterp_data, + ] + for func in functions: + assert type(func) is types.BuiltinFunctionType, func + + self.assert_not_shareable(functions) + + def test_function_like(self): + self.assert_not_shareable(defs.FUNCTION_LIKE) + + def test_builtin_wrapper(self): + _wrappers = { + defs.SpamOkay().okay: types.MethodType, + [].append: types.BuiltinMethodType, + dict.__dict__['fromkeys']: types.ClassMethodDescriptorType, + types.FunctionType.__code__: types.GetSetDescriptorType, + types.FunctionType.__globals__: types.MemberDescriptorType, + str.join: types.MethodDescriptorType, + object().__str__: types.MethodWrapperType, + object.__init__: types.WrapperDescriptorType, + } + for obj, expected in _wrappers.items(): + assert type(obj) is expected, (obj, expected) + + self.assert_not_shareable([ + *_wrappers, + staticmethod(defs.SpamOkay.okay), + classmethod(defs.SpamOkay.okay), + property(defs.SpamOkay.okay), + ]) + + def test_module(self): + assert type(sys) is types.ModuleType, type(sys) + assert type(defs) is types.ModuleType, type(defs) + assert type(unittest) is types.ModuleType, type(defs) + + assert 'emptymod' not in sys.modules + with import_helper.ready_to_import('emptymod', ''): + import emptymod + + self.assert_not_shareable([ + sys, + defs, + unittest, + emptymod, + ]) + + def test_class(self): + self.assert_not_shareable([ + defs.Spam, + defs.SpamOkay, + defs.SpamFull, + defs.SubSpamFull, + defs.SubTuple, + defs.EggsNested, + ]) + self.assert_not_shareable([ + defs.Spam(), + defs.SpamOkay(), + defs.SpamFull(1, 2, 3), + defs.SubSpamFull(1, 2, 3), + defs.SubTuple([1, 2, 3]), + defs.EggsNested(), + ]) + + def test_builtin_type(self): + self.assert_not_shareable([ + *BUILTIN_TYPES, + *(o for n, o in vars(types).items() + if (isinstance(o, type) and + n not in ('DynamicClassAttribute', '_GeneratorWrapper'))), + ]) + + def test_exception(self): + self.assert_not_shareable([ + defs.MimimalError('error!'), + ]) + + def test_builtin_exception(self): + msg = 'error!' + try: + raise Exception + except Exception as exc: + caught = exc + special = { + BaseExceptionGroup: (msg, [caught]), + ExceptionGroup: (msg, [caught]), +# UnicodeError: (None, msg, None, None, None), + UnicodeEncodeError: ('utf-8', '', 1, 3, msg), + UnicodeDecodeError: ('utf-8', b'', 1, 3, msg), + UnicodeTranslateError: ('', 1, 3, msg), + } + exceptions = [] + for cls in EXCEPTION_TYPES: + args = special.get(cls) or (msg,) + exceptions.append(cls(*args)) + + self.assert_not_shareable(exceptions) + + def test_builtin_objects(self): + ns = {} + exec("""if True: + try: + raise Exception + except Exception as exc: + TRACEBACK = exc.__traceback__ + FRAME = TRACEBACK.tb_frame + """, ns, ns) + + 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 + ns['FRAME'], + # types.TracebackType + ns['TRACEBACK'], + ]) + + +if __name__ == '__main__': + unittest.main() diff --git a/Modules/_interpchannelsmodule.c b/Modules/_interpchannelsmodule.c index 81306019f0f673..172cebcaa4884f 100644 --- a/Modules/_interpchannelsmodule.c +++ b/Modules/_interpchannelsmodule.c @@ -1774,7 +1774,7 @@ channel_send(_channels *channels, int64_t cid, PyObject *obj, } // Convert the object to cross-interpreter data. - _PyXIData_t *data = GLOBAL_MALLOC(_PyXIData_t); + _PyXIData_t *data = _PyXIData_New(); if (data == NULL) { PyThread_release_lock(mutex); return -1; diff --git a/Modules/_interpqueuesmodule.c b/Modules/_interpqueuesmodule.c index fdbf197e27950b..526249a0e1aec3 100644 --- a/Modules/_interpqueuesmodule.c +++ b/Modules/_interpqueuesmodule.c @@ -1138,7 +1138,7 @@ queue_put(_queues *queues, int64_t qid, PyObject *obj, int fmt, int unboundop) assert(queue != NULL); // Convert the object to cross-interpreter data. - _PyXIData_t *data = GLOBAL_MALLOC(_PyXIData_t); + _PyXIData_t *data = _PyXIData_New(); if (data == NULL) { _queue_unmark_waiter(queue, queues->mutex); return -1; diff --git a/Modules/_interpretersmodule.c b/Modules/_interpretersmodule.c index 8ced6873f08967..b22bd29a507f6b 100644 --- a/Modules/_interpretersmodule.c +++ b/Modules/_interpretersmodule.c @@ -1115,67 +1115,6 @@ The code/function must not take any arguments or be a closure\n\ If a function is provided, its code object is used and all its state\n\ is ignored, including its __globals__ dict."); -static PyObject * -interp_call(PyObject *self, PyObject *args, PyObject *kwds) -{ - static char *kwlist[] = {"id", "callable", "args", "kwargs", - "restrict", NULL}; - PyObject *id, *callable; - PyObject *args_obj = NULL; - PyObject *kwargs_obj = NULL; - int restricted = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwds, - "OO|OO$p:" MODULE_NAME_STR ".call", kwlist, - &id, &callable, &args_obj, &kwargs_obj, - &restricted)) - { - return NULL; - } - - int reqready = 1; - PyInterpreterState *interp = \ - resolve_interp(id, restricted, reqready, "make a call in"); - if (interp == NULL) { - return NULL; - } - - if (args_obj != NULL) { - PyErr_SetString(PyExc_ValueError, "got unexpected args"); - return NULL; - } - if (kwargs_obj != NULL) { - PyErr_SetString(PyExc_ValueError, "got unexpected kwargs"); - return NULL; - } - - PyObject *code = (PyObject *)convert_code_arg(callable, MODULE_NAME_STR ".call", - "argument 2", "a function"); - if (code == NULL) { - return NULL; - } - - PyObject *excinfo = NULL; - int res = _interp_exec(self, interp, code, NULL, &excinfo); - Py_DECREF(code); - if (res < 0) { - assert((excinfo == NULL) != (PyErr_Occurred() == NULL)); - return excinfo; - } - Py_RETURN_NONE; -} - -PyDoc_STRVAR(call_doc, -"call(id, callable, args=None, kwargs=None, *, restrict=False)\n\ -\n\ -Call the provided object in the identified interpreter.\n\ -Pass the given args and kwargs, if possible.\n\ -\n\ -\"callable\" may be a plain function with no free vars that takes\n\ -no arguments.\n\ -\n\ -The function's code object is used and all its state\n\ -is ignored, including its __globals__ dict."); - static PyObject * interp_run_string(PyObject *self, PyObject *args, PyObject *kwds) { @@ -1267,6 +1206,67 @@ are not supported. Methods and other callables are not supported either.\n\ \n\ (See " MODULE_NAME_STR ".exec()."); +static PyObject * +interp_call(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"id", "callable", "args", "kwargs", + "restrict", NULL}; + PyObject *id, *callable; + PyObject *args_obj = NULL; + PyObject *kwargs_obj = NULL; + int restricted = 0; + if (!PyArg_ParseTupleAndKeywords(args, kwds, + "OO|OO$p:" MODULE_NAME_STR ".call", kwlist, + &id, &callable, &args_obj, &kwargs_obj, + &restricted)) + { + return NULL; + } + + int reqready = 1; + PyInterpreterState *interp = \ + resolve_interp(id, restricted, reqready, "make a call in"); + if (interp == NULL) { + return NULL; + } + + if (args_obj != NULL) { + PyErr_SetString(PyExc_ValueError, "got unexpected args"); + return NULL; + } + if (kwargs_obj != NULL) { + PyErr_SetString(PyExc_ValueError, "got unexpected kwargs"); + return NULL; + } + + PyObject *code = (PyObject *)convert_code_arg(callable, MODULE_NAME_STR ".call", + "argument 2", "a function"); + if (code == NULL) { + return NULL; + } + + PyObject *excinfo = NULL; + int res = _interp_exec(self, interp, code, NULL, &excinfo); + Py_DECREF(code); + if (res < 0) { + assert((excinfo == NULL) != (PyErr_Occurred() == NULL)); + return excinfo; + } + Py_RETURN_NONE; +} + +PyDoc_STRVAR(call_doc, +"call(id, callable, args=None, kwargs=None, *, restrict=False)\n\ +\n\ +Call the provided object in the identified interpreter.\n\ +Pass the given args and kwargs, if possible.\n\ +\n\ +\"callable\" may be a plain function with no free vars that takes\n\ +no arguments.\n\ +\n\ +The function's code object is used and all its state\n\ +is ignored, including its __globals__ dict."); + static PyObject * object_is_shareable(PyObject *self, PyObject *args, PyObject *kwds) diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index 575e55a0e9c45a..99dca9f77df6be 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -1686,37 +1686,64 @@ interpreter_refcount_linked(PyObject *self, PyObject *idobj) static void _xid_capsule_destructor(PyObject *capsule) { - _PyXIData_t *data = (_PyXIData_t *)PyCapsule_GetPointer(capsule, NULL); - if (data != NULL) { - assert(_PyXIData_Release(data) == 0); - _PyXIData_Free(data); + _PyXIData_t *xidata = (_PyXIData_t *)PyCapsule_GetPointer(capsule, NULL); + if (xidata != NULL) { + assert(_PyXIData_Release(xidata) == 0); + _PyXIData_Free(xidata); } } static PyObject * -get_crossinterp_data(PyObject *self, PyObject *args) +get_crossinterp_data(PyObject *self, PyObject *args, PyObject *kwargs) { - PyThreadState *tstate = _PyThreadState_GET(); - PyObject *obj = NULL; - if (!PyArg_ParseTuple(args, "O:get_crossinterp_data", &obj)) { + PyObject *modeobj = NULL; + static char *kwlist[] = {"obj", "mode", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, + "O|O:get_crossinterp_data", kwlist, + &obj, &modeobj)) + { return NULL; } - - _PyXIData_t *data = _PyXIData_New(); - if (data == NULL) { + const char *mode = NULL; + if (modeobj == NULL || modeobj == Py_None) { + mode = "xidata"; + } + else if (!PyUnicode_Check(modeobj)) { + PyErr_Format(PyExc_TypeError, "expected mode str, got %R", modeobj); return NULL; } - if (_PyObject_GetXIData(tstate, obj, data) != 0) { - _PyXIData_Free(data); + else { + mode = PyUnicode_AsUTF8(modeobj); + if (strlen(mode) == 0) { + mode = "xidata"; + } + } + + PyThreadState *tstate = _PyThreadState_GET(); + _PyXIData_t *xidata = _PyXIData_New(); + if (xidata == NULL) { return NULL; } - PyObject *capsule = PyCapsule_New(data, NULL, _xid_capsule_destructor); + if (strcmp(mode, "xidata") == 0) { + if (_PyObject_GetXIData(tstate, obj, xidata) != 0) { + goto error; + } + } + else { + PyErr_Format(PyExc_ValueError, "unsupported mode %R", modeobj); + goto error; + } + PyObject *capsule = PyCapsule_New(xidata, NULL, _xid_capsule_destructor); if (capsule == NULL) { - assert(_PyXIData_Release(data) == 0); - _PyXIData_Free(data); + assert(_PyXIData_Release(xidata) == 0); + goto error; } return capsule; + +error: + _PyXIData_Free(xidata); + return NULL; } static PyObject * @@ -1727,11 +1754,11 @@ restore_crossinterp_data(PyObject *self, PyObject *args) return NULL; } - _PyXIData_t *data = (_PyXIData_t *)PyCapsule_GetPointer(capsule, NULL); - if (data == NULL) { + _PyXIData_t *xidata = (_PyXIData_t *)PyCapsule_GetPointer(capsule, NULL); + if (xidata == NULL) { return NULL; } - return _PyXIData_NewObject(data); + return _PyXIData_NewObject(xidata); } @@ -2071,7 +2098,8 @@ static PyMethodDef module_functions[] = { {"interpreter_refcount_linked", interpreter_refcount_linked, METH_O}, {"compile_perf_trampoline_entry", compile_perf_trampoline_entry, METH_VARARGS}, {"perf_trampoline_set_persist_after_fork", perf_trampoline_set_persist_after_fork, METH_VARARGS}, - {"get_crossinterp_data", get_crossinterp_data, METH_VARARGS}, + {"get_crossinterp_data", _PyCFunction_CAST(get_crossinterp_data), + METH_VARARGS | METH_KEYWORDS}, {"restore_crossinterp_data", restore_crossinterp_data, METH_VARARGS}, _TESTINTERNALCAPI_TEST_LONG_NUMBITS_METHODDEF {"get_rare_event_counters", get_rare_event_counters, METH_NOARGS}, diff --git a/Python/crossinterp.c b/Python/crossinterp.c index e4368165a35326..8ba88c4b057686 100644 --- a/Python/crossinterp.c +++ b/Python/crossinterp.c @@ -74,7 +74,7 @@ static xidatafunc lookup_getdata(struct _dlcontext *, PyObject *); _PyXIData_t * _PyXIData_New(void) { - _PyXIData_t *xid = PyMem_RawMalloc(sizeof(_PyXIData_t)); + _PyXIData_t *xid = PyMem_RawCalloc(1, sizeof(_PyXIData_t)); if (xid == NULL) { PyErr_NoMemory(); } @@ -93,58 +93,58 @@ _PyXIData_Free(_PyXIData_t *xid) /* defining cross-interpreter data */ static inline void -_xidata_init(_PyXIData_t *data) +_xidata_init(_PyXIData_t *xidata) { // If the value is being reused // then _xidata_clear() should have been called already. - assert(data->data == NULL); - assert(data->obj == NULL); - *data = (_PyXIData_t){0}; - _PyXIData_INTERPID(data) = -1; + assert(xidata->data == NULL); + assert(xidata->obj == NULL); + *xidata = (_PyXIData_t){0}; + _PyXIData_INTERPID(xidata) = -1; } static inline void -_xidata_clear(_PyXIData_t *data) +_xidata_clear(_PyXIData_t *xidata) { // _PyXIData_t only has two members that need to be - // cleaned up, if set: "data" must be freed and "obj" must be decref'ed. + // cleaned up, if set: "xidata" must be freed and "obj" must be decref'ed. // In both cases the original (owning) interpreter must be used, // which is the caller's responsibility to ensure. - if (data->data != NULL) { - if (data->free != NULL) { - data->free(data->data); + if (xidata->data != NULL) { + if (xidata->free != NULL) { + xidata->free(xidata->data); } - data->data = NULL; + xidata->data = NULL; } - Py_CLEAR(data->obj); + Py_CLEAR(xidata->obj); } void -_PyXIData_Init(_PyXIData_t *data, +_PyXIData_Init(_PyXIData_t *xidata, PyInterpreterState *interp, void *shared, PyObject *obj, xid_newobjfunc new_object) { - assert(data != NULL); + assert(xidata != NULL); assert(new_object != NULL); - _xidata_init(data); - data->data = shared; + _xidata_init(xidata); + xidata->data = shared; if (obj != NULL) { assert(interp != NULL); // released in _PyXIData_Clear() - data->obj = Py_NewRef(obj); + xidata->obj = Py_NewRef(obj); } // Ideally every object would know its owning interpreter. // Until then, we have to rely on the caller to identify it // (but we don't need it in all cases). - _PyXIData_INTERPID(data) = (interp != NULL) + _PyXIData_INTERPID(xidata) = (interp != NULL) ? PyInterpreterState_GetID(interp) : -1; - data->new_object = new_object; + xidata->new_object = new_object; } int -_PyXIData_InitWithSize(_PyXIData_t *data, +_PyXIData_InitWithSize(_PyXIData_t *xidata, PyInterpreterState *interp, const size_t size, PyObject *obj, xid_newobjfunc new_object) @@ -153,50 +153,28 @@ _PyXIData_InitWithSize(_PyXIData_t *data, // For now we always free the shared data in the same interpreter // where it was allocated, so the interpreter is required. assert(interp != NULL); - _PyXIData_Init(data, interp, NULL, obj, new_object); - data->data = PyMem_RawMalloc(size); - if (data->data == NULL) { + _PyXIData_Init(xidata, interp, NULL, obj, new_object); + xidata->data = PyMem_RawMalloc(size); + if (xidata->data == NULL) { return -1; } - data->free = PyMem_RawFree; + xidata->free = PyMem_RawFree; return 0; } void -_PyXIData_Clear(PyInterpreterState *interp, _PyXIData_t *data) +_PyXIData_Clear(PyInterpreterState *interp, _PyXIData_t *xidata) { - assert(data != NULL); + assert(xidata != NULL); // This must be called in the owning interpreter. assert(interp == NULL - || _PyXIData_INTERPID(data) == -1 - || _PyXIData_INTERPID(data) == PyInterpreterState_GetID(interp)); - _xidata_clear(data); + || _PyXIData_INTERPID(xidata) == -1 + || _PyXIData_INTERPID(xidata) == PyInterpreterState_GetID(interp)); + _xidata_clear(xidata); } -/* using cross-interpreter data */ - -static int -_check_xidata(PyThreadState *tstate, _PyXIData_t *data) -{ - // data->data can be anything, including NULL, so we don't check it. - - // data->obj may be NULL, so we don't check it. - - if (_PyXIData_INTERPID(data) < 0) { - PyErr_SetString(PyExc_SystemError, "missing interp"); - return -1; - } - - if (data->new_object == NULL) { - PyErr_SetString(PyExc_SystemError, "missing new_object func"); - return -1; - } - - // data->free may be NULL, so we don't check it. - - return 0; -} +/* getting cross-interpreter data */ static inline void _set_xid_lookup_failure(PyThreadState *tstate, PyObject *obj, const char *msg, @@ -216,6 +194,7 @@ _set_xid_lookup_failure(PyThreadState *tstate, PyObject *obj, const char *msg, } } + int _PyObject_CheckXIData(PyThreadState *tstate, PyObject *obj) { @@ -233,15 +212,39 @@ _PyObject_CheckXIData(PyThreadState *tstate, PyObject *obj) return 0; } +static int +_check_xidata(PyThreadState *tstate, _PyXIData_t *xidata) +{ + // xidata->data can be anything, including NULL, so we don't check it. + + // xidata->obj may be NULL, so we don't check it. + + if (_PyXIData_INTERPID(xidata) < 0) { + PyErr_SetString(PyExc_SystemError, "missing interp"); + return -1; + } + + if (xidata->new_object == NULL) { + PyErr_SetString(PyExc_SystemError, "missing new_object func"); + return -1; + } + + // xidata->free may be NULL, so we don't check it. + + return 0; +} + int _PyObject_GetXIData(PyThreadState *tstate, - PyObject *obj, _PyXIData_t *data) + PyObject *obj, _PyXIData_t *xidata) { PyInterpreterState *interp = tstate->interp; - // Reset data before re-populating. - *data = (_PyXIData_t){0}; - _PyXIData_INTERPID(data) = -1; + assert(xidata->data == NULL); + assert(xidata->obj == NULL); + if (xidata->data != NULL || xidata->obj != NULL) { + _PyErr_SetString(tstate, PyExc_ValueError, "xidata not cleared"); + } // Call the "getdata" func for the object. dlcontext_t ctx; @@ -251,13 +254,18 @@ _PyObject_GetXIData(PyThreadState *tstate, Py_INCREF(obj); xidatafunc getdata = lookup_getdata(&ctx, obj); if (getdata == NULL) { + if (PyErr_Occurred()) { + Py_DECREF(obj); + return -1; + } + // Fall back to obj Py_DECREF(obj); if (!_PyErr_Occurred(tstate)) { _set_xid_lookup_failure(tstate, obj, NULL, NULL); } return -1; } - int res = getdata(tstate, obj, data); + int res = getdata(tstate, obj, xidata); Py_DECREF(obj); if (res != 0) { PyObject *cause = _PyErr_GetRaisedException(tstate); @@ -268,19 +276,22 @@ _PyObject_GetXIData(PyThreadState *tstate, } // Fill in the blanks and validate the result. - _PyXIData_INTERPID(data) = PyInterpreterState_GetID(interp); - if (_check_xidata(tstate, data) != 0) { - (void)_PyXIData_Release(data); + _PyXIData_INTERPID(xidata) = PyInterpreterState_GetID(interp); + if (_check_xidata(tstate, xidata) != 0) { + (void)_PyXIData_Release(xidata); return -1; } return 0; } + +/* using cross-interpreter data */ + PyObject * -_PyXIData_NewObject(_PyXIData_t *data) +_PyXIData_NewObject(_PyXIData_t *xidata) { - return data->new_object(data); + return xidata->new_object(xidata); } static int @@ -291,52 +302,52 @@ _call_clear_xidata(void *data) } static int -_xidata_release(_PyXIData_t *data, int rawfree) +_xidata_release(_PyXIData_t *xidata, int rawfree) { - if ((data->data == NULL || data->free == NULL) && data->obj == NULL) { + if ((xidata->data == NULL || xidata->free == NULL) && xidata->obj == NULL) { // Nothing to release! if (rawfree) { - PyMem_RawFree(data); + PyMem_RawFree(xidata); } else { - data->data = NULL; + xidata->data = NULL; } return 0; } // Switch to the original interpreter. PyInterpreterState *interp = _PyInterpreterState_LookUpID( - _PyXIData_INTERPID(data)); + _PyXIData_INTERPID(xidata)); if (interp == NULL) { // The interpreter was already destroyed. // This function shouldn't have been called. // XXX Someone leaked some memory... assert(PyErr_Occurred()); if (rawfree) { - PyMem_RawFree(data); + PyMem_RawFree(xidata); } return -1; } // "Release" the data and/or the object. if (rawfree) { - return _Py_CallInInterpreterAndRawFree(interp, _call_clear_xidata, data); + return _Py_CallInInterpreterAndRawFree(interp, _call_clear_xidata, xidata); } else { - return _Py_CallInInterpreter(interp, _call_clear_xidata, data); + return _Py_CallInInterpreter(interp, _call_clear_xidata, xidata); } } int -_PyXIData_Release(_PyXIData_t *data) +_PyXIData_Release(_PyXIData_t *xidata) { - return _xidata_release(data, 0); + return _xidata_release(xidata, 0); } int -_PyXIData_ReleaseAndRawFree(_PyXIData_t *data) +_PyXIData_ReleaseAndRawFree(_PyXIData_t *xidata) { - return _xidata_release(data, 1); + return _xidata_release(xidata, 1); } @@ -455,15 +466,15 @@ _format_TracebackException(PyObject *tbexc) static int -_release_xid_data(_PyXIData_t *data, int rawfree) +_release_xid_data(_PyXIData_t *xidata, int rawfree) { PyObject *exc = PyErr_GetRaisedException(); int res = rawfree - ? _PyXIData_Release(data) - : _PyXIData_ReleaseAndRawFree(data); + ? _PyXIData_Release(xidata) + : _PyXIData_ReleaseAndRawFree(xidata); if (res < 0) { /* The owning interpreter is already destroyed. */ - _PyXIData_Clear(NULL, data); + _PyXIData_Clear(NULL, xidata); // XXX Emit a warning? PyErr_Clear(); } @@ -1107,7 +1118,7 @@ _PyXI_ApplyError(_PyXI_error *error) typedef struct _sharednsitem { const char *name; - _PyXIData_t *data; + _PyXIData_t *xidata; // We could have a "PyXIData _data" field, so it would // be allocated as part of the item and avoid an extra allocation. // However, doing so adds a bunch of complexity because we must @@ -1132,7 +1143,7 @@ _sharednsitem_init(_PyXI_namespace_item *item, PyObject *key) assert(!_sharednsitem_is_initialized(item)); return -1; } - item->data = NULL; + item->xidata = NULL; assert(_sharednsitem_is_initialized(item)); return 0; } @@ -1140,11 +1151,11 @@ _sharednsitem_init(_PyXI_namespace_item *item, PyObject *key) static int _sharednsitem_has_value(_PyXI_namespace_item *item, int64_t *p_interpid) { - if (item->data == NULL) { + if (item->xidata == NULL) { return 0; } if (p_interpid != NULL) { - *p_interpid = _PyXIData_INTERPID(item->data); + *p_interpid = _PyXIData_INTERPID(item->xidata); } return 1; } @@ -1153,16 +1164,15 @@ static int _sharednsitem_set_value(_PyXI_namespace_item *item, PyObject *value) { assert(_sharednsitem_is_initialized(item)); - assert(item->data == NULL); - item->data = PyMem_RawMalloc(sizeof(_PyXIData_t)); - if (item->data == NULL) { - PyErr_NoMemory(); + assert(item->xidata == NULL); + item->xidata = _PyXIData_New(); + if (item->xidata == NULL) { return -1; } PyThreadState *tstate = PyThreadState_Get(); - if (_PyObject_GetXIData(tstate, value, item->data) != 0) { - PyMem_RawFree(item->data); - item->data = NULL; + if (_PyObject_GetXIData(tstate, value, item->xidata) != 0) { + PyMem_RawFree(item->xidata); + item->xidata = NULL; // The caller may want to propagate PyExc_NotShareableError // if currently switched between interpreters. return -1; @@ -1173,11 +1183,11 @@ _sharednsitem_set_value(_PyXI_namespace_item *item, PyObject *value) static void _sharednsitem_clear_value(_PyXI_namespace_item *item) { - _PyXIData_t *data = item->data; - if (data != NULL) { - item->data = NULL; + _PyXIData_t *xidata = item->xidata; + if (xidata != NULL) { + item->xidata = NULL; int rawfree = 1; - (void)_release_xid_data(data, rawfree); + (void)_release_xid_data(xidata, rawfree); } } @@ -1195,7 +1205,7 @@ static int _sharednsitem_copy_from_ns(struct _sharednsitem *item, PyObject *ns) { assert(item->name != NULL); - assert(item->data == NULL); + assert(item->xidata == NULL); PyObject *value = PyDict_GetItemString(ns, item->name); // borrowed if (value == NULL) { if (PyErr_Occurred()) { @@ -1218,8 +1228,8 @@ _sharednsitem_apply(_PyXI_namespace_item *item, PyObject *ns, PyObject *dflt) return -1; } PyObject *value; - if (item->data != NULL) { - value = _PyXIData_NewObject(item->data); + if (item->xidata != NULL) { + value = _PyXIData_NewObject(item->xidata); if (value == NULL) { Py_DECREF(name); return -1; diff --git a/Python/crossinterp_data_lookup.h b/Python/crossinterp_data_lookup.h index 8358ba650739d1..6af208dcdd13c0 100644 --- a/Python/crossinterp_data_lookup.h +++ b/Python/crossinterp_data_lookup.h @@ -354,25 +354,25 @@ struct _shared_bytes_data { }; static PyObject * -_new_bytes_object(_PyXIData_t *data) +_new_bytes_object(_PyXIData_t *xidata) { - struct _shared_bytes_data *shared = (struct _shared_bytes_data *)(data->data); + struct _shared_bytes_data *shared = (struct _shared_bytes_data *)(xidata->data); return PyBytes_FromStringAndSize(shared->bytes, shared->len); } static int -_bytes_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *data) +_bytes_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *xidata) { if (_PyXIData_InitWithSize( - data, tstate->interp, sizeof(struct _shared_bytes_data), obj, + xidata, tstate->interp, sizeof(struct _shared_bytes_data), obj, _new_bytes_object ) < 0) { return -1; } - struct _shared_bytes_data *shared = (struct _shared_bytes_data *)data->data; + struct _shared_bytes_data *shared = (struct _shared_bytes_data *)xidata->data; if (PyBytes_AsStringAndSize(obj, &shared->bytes, &shared->len) < 0) { - _PyXIData_Clear(tstate->interp, data); + _PyXIData_Clear(tstate->interp, xidata); return -1; } return 0; @@ -387,23 +387,23 @@ struct _shared_str_data { }; static PyObject * -_new_str_object(_PyXIData_t *data) +_new_str_object(_PyXIData_t *xidata) { - struct _shared_str_data *shared = (struct _shared_str_data *)(data->data); + struct _shared_str_data *shared = (struct _shared_str_data *)(xidata->data); return PyUnicode_FromKindAndData(shared->kind, shared->buffer, shared->len); } static int -_str_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *data) +_str_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *xidata) { if (_PyXIData_InitWithSize( - data, tstate->interp, sizeof(struct _shared_str_data), obj, + xidata, tstate->interp, sizeof(struct _shared_str_data), obj, _new_str_object ) < 0) { return -1; } - struct _shared_str_data *shared = (struct _shared_str_data *)data->data; + struct _shared_str_data *shared = (struct _shared_str_data *)xidata->data; shared->kind = PyUnicode_KIND(obj); shared->buffer = PyUnicode_DATA(obj); shared->len = PyUnicode_GET_LENGTH(obj); @@ -413,13 +413,13 @@ _str_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *data) // int static PyObject * -_new_long_object(_PyXIData_t *data) +_new_long_object(_PyXIData_t *xidata) { - return PyLong_FromSsize_t((Py_ssize_t)(data->data)); + return PyLong_FromSsize_t((Py_ssize_t)(xidata->data)); } static int -_long_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *data) +_long_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *xidata) { /* Note that this means the size of shareable ints is bounded by * sys.maxsize. Hence on 32-bit architectures that is half the @@ -432,31 +432,31 @@ _long_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *data) } return -1; } - _PyXIData_Init(data, tstate->interp, (void *)value, NULL, _new_long_object); - // data->obj and data->free remain NULL + _PyXIData_Init(xidata, tstate->interp, (void *)value, NULL, _new_long_object); + // xidata->obj and xidata->free remain NULL return 0; } // float static PyObject * -_new_float_object(_PyXIData_t *data) +_new_float_object(_PyXIData_t *xidata) { - double * value_ptr = data->data; + double * value_ptr = xidata->data; return PyFloat_FromDouble(*value_ptr); } static int -_float_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *data) +_float_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *xidata) { if (_PyXIData_InitWithSize( - data, tstate->interp, sizeof(double), NULL, + xidata, tstate->interp, sizeof(double), NULL, _new_float_object ) < 0) { return -1; } - double *shared = (double *)data->data; + double *shared = (double *)xidata->data; *shared = PyFloat_AsDouble(obj); return 0; } @@ -464,38 +464,38 @@ _float_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *data) // None static PyObject * -_new_none_object(_PyXIData_t *data) +_new_none_object(_PyXIData_t *xidata) { // XXX Singleton refcounts are problematic across interpreters... return Py_NewRef(Py_None); } static int -_none_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *data) +_none_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *xidata) { - _PyXIData_Init(data, tstate->interp, NULL, NULL, _new_none_object); - // data->data, data->obj and data->free remain NULL + _PyXIData_Init(xidata, tstate->interp, NULL, NULL, _new_none_object); + // xidata->data, xidata->obj and xidata->free remain NULL return 0; } // bool static PyObject * -_new_bool_object(_PyXIData_t *data) +_new_bool_object(_PyXIData_t *xidata) { - if (data->data){ + if (xidata->data){ Py_RETURN_TRUE; } Py_RETURN_FALSE; } static int -_bool_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *data) +_bool_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *xidata) { - _PyXIData_Init(data, tstate->interp, + _PyXIData_Init(xidata, tstate->interp, (void *) (Py_IsTrue(obj) ? (uintptr_t) 1 : (uintptr_t) 0), NULL, _new_bool_object); - // data->obj and data->free remain NULL + // xidata->obj and xidata->free remain NULL return 0; } @@ -503,20 +503,20 @@ _bool_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *data) struct _shared_tuple_data { Py_ssize_t len; - _PyXIData_t **data; + _PyXIData_t **items; }; static PyObject * -_new_tuple_object(_PyXIData_t *data) +_new_tuple_object(_PyXIData_t *xidata) { - struct _shared_tuple_data *shared = (struct _shared_tuple_data *)(data->data); + struct _shared_tuple_data *shared = (struct _shared_tuple_data *)(xidata->data); PyObject *tuple = PyTuple_New(shared->len); if (tuple == NULL) { return NULL; } for (Py_ssize_t i = 0; i < shared->len; i++) { - PyObject *item = _PyXIData_NewObject(shared->data[i]); + PyObject *item = _PyXIData_NewObject(shared->items[i]); if (item == NULL){ Py_DECREF(tuple); return NULL; @@ -534,19 +534,19 @@ _tuple_shared_free(void* data) int64_t interpid = PyInterpreterState_GetID(_PyInterpreterState_GET()); #endif for (Py_ssize_t i = 0; i < shared->len; i++) { - if (shared->data[i] != NULL) { - assert(_PyXIData_INTERPID(shared->data[i]) == interpid); - _PyXIData_Release(shared->data[i]); - PyMem_RawFree(shared->data[i]); - shared->data[i] = NULL; + if (shared->items[i] != NULL) { + assert(_PyXIData_INTERPID(shared->items[i]) == interpid); + _PyXIData_Release(shared->items[i]); + PyMem_RawFree(shared->items[i]); + shared->items[i] = NULL; } } - PyMem_Free(shared->data); + PyMem_Free(shared->items); PyMem_RawFree(shared); } static int -_tuple_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *data) +_tuple_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *xidata) { Py_ssize_t len = PyTuple_GET_SIZE(obj); if (len < 0) { @@ -559,32 +559,32 @@ _tuple_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *data) } shared->len = len; - shared->data = (_PyXIData_t **) PyMem_Calloc(shared->len, sizeof(_PyXIData_t *)); - if (shared->data == NULL) { + shared->items = (_PyXIData_t **) PyMem_Calloc(shared->len, sizeof(_PyXIData_t *)); + if (shared->items == NULL) { PyErr_NoMemory(); return -1; } for (Py_ssize_t i = 0; i < shared->len; i++) { - _PyXIData_t *data = _PyXIData_New(); - if (data == NULL) { + _PyXIData_t *xidata_i = _PyXIData_New(); + if (xidata_i == NULL) { goto error; // PyErr_NoMemory already set } PyObject *item = PyTuple_GET_ITEM(obj, i); int res = -1; if (!_Py_EnterRecursiveCallTstate(tstate, " while sharing a tuple")) { - res = _PyObject_GetXIData(tstate, item, data); + res = _PyObject_GetXIData(tstate, item, xidata_i); _Py_LeaveRecursiveCallTstate(tstate); } if (res < 0) { - PyMem_RawFree(data); + PyMem_RawFree(xidata_i); goto error; } - shared->data[i] = data; + shared->items[i] = xidata_i; } - _PyXIData_Init(data, tstate->interp, shared, obj, _new_tuple_object); - data->free = _tuple_shared_free; + _PyXIData_Init(xidata, tstate->interp, shared, obj, _new_tuple_object); + _PyXIData_SET_FREE(xidata, _tuple_shared_free); return 0; error: _______________________________________________ Python-checkins mailing list -- python-checkins@python.org To unsubscribe send an email to python-checkins-le...@python.org https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: arch...@mail-archive.com