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

Reply via email to