Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
a8dadd3
Simpler initial implementation.
ZeroIntensity Nov 27, 2025
35cab48
Remove special case.
ZeroIntensity Nov 27, 2025
db0c2ff
Add some basic tests that probably don't work.
ZeroIntensity Nov 27, 2025
1090366
Fix the test.
ZeroIntensity Nov 27, 2025
e0c6e59
Add a test for access in another interpreter.
ZeroIntensity Nov 27, 2025
0867c09
Ensure that copied proxies have the correct interpreters.
ZeroIntensity Nov 27, 2025
cd1d139
Create proxies in the switched interpreter, not the calling interpreter.
ZeroIntensity Nov 27, 2025
6fa7e0a
Some general test improvements.
ZeroIntensity Nov 27, 2025
6b64744
Hold a reference to object proxies in clear callbacks.
ZeroIntensity Nov 27, 2025
5d0b308
Remove the per-object thread state cache.
ZeroIntensity Nov 27, 2025
84a286b
Fix assertion failures when GC clears the proxy before reference coun…
ZeroIntensity Nov 27, 2025
516f3fc
Add support for keyword arguments in proxy calls.
ZeroIntensity Nov 27, 2025
47242c8
Fix leaks in object proxy call slots.
ZeroIntensity Nov 27, 2025
87348d8
Fix a small leak.
ZeroIntensity Nov 27, 2025
abaa434
Merge branch 'main' of https://github.com/python/cpython into shared-…
ZeroIntensity Nov 27, 2025
355b37e
Fix leak at the end of _sharedobjectproxy_wrap_result().
ZeroIntensity Nov 27, 2025
323b96a
Turn the SharedObjectProxy type into a heap type, and fix related leaks.
ZeroIntensity Nov 27, 2025
b414dbb
Remove some problematic assertions.
ZeroIntensity Nov 27, 2025
1effb7a
Ensure that all interpreters are closed in the tests.
ZeroIntensity Nov 27, 2025
c4ac442
Fix assertion failure when creating a new proxy.
ZeroIntensity Nov 27, 2025
1412200
Give each proxy a dedicated reference.
ZeroIntensity Nov 27, 2025
8f82d46
Add a test for calling concurrently.
ZeroIntensity Nov 27, 2025
d206dcd
Add a test for reference cycles.
ZeroIntensity Nov 27, 2025
b37c36a
Add an extra assertion.
ZeroIntensity Nov 27, 2025
7d26ac0
Add a test for concurrent attribute access.
ZeroIntensity Nov 27, 2025
5cc5410
Run formatter on the tests.
ZeroIntensity Nov 27, 2025
04e3778
Goodbye silly clear callback mechanism.
ZeroIntensity Nov 27, 2025
d279720
Use a thread state cache instead of creating a new one for each access.
ZeroIntensity Nov 27, 2025
1b23e2d
Add a test for destruction in another interpreter.
ZeroIntensity Nov 27, 2025
7cd1469
Add a test for ensuring the switched interpreter is correct.
ZeroIntensity Nov 27, 2025
a40da59
Merge branch 'main' of https://github.com/python/cpython into shared-…
ZeroIntensity Feb 23, 2026
37e4556
Implement the __share__() method.
ZeroIntensity Feb 23, 2026
58c255b
Add some more tests.
ZeroIntensity Feb 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add support for keyword arguments in proxy calls.
It's still pretty leaky right now though.
  • Loading branch information
ZeroIntensity committed Nov 27, 2025
commit 516f3fc60c3aa9537f4ebeab8ef4be29964b03c8
1 change: 1 addition & 0 deletions Lib/test/test_interpreters/test_object_proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ def my_function(arg=1, /, *, arg2=2):
self.assertEqual(proxy(2), 71)

interp = interpreters.create()
interp.prepare_main(proxy=proxy)
interp.exec("""if True:
assert isinstance(proxy(), int)
assert proxy() == 70
Expand Down
107 changes: 100 additions & 7 deletions Modules/_interpretersmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -640,10 +640,17 @@ _sharedobjectproxy_wrap_result(SharedObjectProxy *self, PyObject *result,
static PyObject *
sharedobjectproxy_tp_call(PyObject *op, PyObject *args, PyObject *kwargs) {
SharedObjectProxy *self = SharedObjectProxy_CAST(op);
Py_ssize_t size = PyTuple_Size(args);
_PyXI_proxy_share *shared_args_state = PyMem_RawMalloc(size * sizeof(_PyXI_proxy_share));
assert(PyTuple_Check(args));
Py_ssize_t args_size = PyTuple_GET_SIZE(args);
assert(kwargs == NULL || PyDict_Check(kwargs));
Py_ssize_t kwarg_size = kwargs == NULL ? -1 : PyDict_GET_SIZE(kwargs);
_PyXI_proxy_share *shared_args_state = PyMem_RawMalloc(args_size * sizeof(_PyXI_proxy_share));
if (shared_args_state == NULL) {
PyErr_NoMemory();
return NULL;
}

for (Py_ssize_t i = 0; i < size; ++i) {
for (Py_ssize_t i = 0; i < args_size; ++i) {
PyObject *arg = PyTuple_GetItem(args, i);
if (arg == NULL) {
PyMem_RawFree(shared_args_state);
Expand All @@ -656,19 +663,62 @@ sharedobjectproxy_tp_call(PyObject *op, PyObject *args, PyObject *kwargs) {
}
}

struct _dict_pair {
const char *key;
Py_ssize_t key_length;
_PyXI_proxy_share value;
};
struct _dict_pair *kwarg_pairs = NULL;
if (kwargs != NULL) {
kwarg_pairs = PyMem_RawCalloc(kwarg_size, sizeof(struct _dict_pair));
if (kwarg_pairs == NULL) {
PyErr_NoMemory();
PyMem_RawFree(shared_args_state);
return NULL;
}

PyObject *key, *value;
Py_ssize_t pos = 0;
while (PyDict_Next(kwargs, &pos, &key, &value)) {
// XXX Can kwarg keys be dictionary subclasses?
assert(PyUnicode_Check(key));
Py_ssize_t index = pos - 1;
assert(index >= 0);
assert(index < kwarg_size);
struct _dict_pair *pair = &kwarg_pairs[index];
assert(pair->key == NULL);
assert(pair->key_length == 0);
const char *key_str = PyUnicode_AsUTF8AndSize(key, &pair->key_length);
if (key_str == NULL) {
PyMem_RawFree(shared_args_state);
PyMem_RawFree(kwarg_pairs);
return NULL;

}
pair->key = key_str;
if (_sharedobjectproxy_init_share(&pair->value, self, value) < 0) {
PyMem_RawFree(shared_args_state);
PyMem_RawFree(kwarg_pairs);
return NULL;
}
}
}

_PyXI_proxy_state state;
if (_sharedobjectproxy_enter(self, &state) < 0) {
PyMem_RawFree(shared_args_state);
PyMem_RawFree(kwarg_pairs);
return NULL;
}
PyObject *shared_args = PyTuple_New(size);
PyObject *shared_args = PyTuple_New(args_size);
if (shared_args == NULL) {
(void)_sharedobjectproxy_exit(self, &state);
PyMem_RawFree(shared_args_state);
PyMem_RawFree(kwarg_pairs);
return NULL;
}

for (Py_ssize_t i = 0; i < size; ++i) {
for (Py_ssize_t i = 0; i < args_size; ++i) {
PyObject *shared = _sharedobjectproxy_copy_for_interp(&shared_args_state[i]);
if (shared == NULL) {
(void)_sharedobjectproxy_exit(self, &state);
Expand All @@ -678,10 +728,53 @@ sharedobjectproxy_tp_call(PyObject *op, PyObject *args, PyObject *kwargs) {
PyTuple_SET_ITEM(shared_args, i, shared);
}

// kwargs aren't supported yet
PyObject *shared_kwargs = NULL;
if (kwargs != NULL) {
shared_kwargs = PyDict_New();
if (shared_kwargs == NULL) {
Py_DECREF(shared_args);
(void)_sharedobjectproxy_exit(self, &state);
PyMem_RawFree(shared_args_state);
PyMem_RawFree(kwarg_pairs);
return NULL;
}
for (Py_ssize_t i = 0; i < kwarg_size; ++i) {
struct _dict_pair *pair = &kwarg_pairs[i];
assert(pair->key != NULL);
PyObject *key = PyUnicode_FromStringAndSize(pair->key, pair->key_length);
if (key == NULL) {
Py_DECREF(shared_args);
Py_DECREF(shared_kwargs);
(void)_sharedobjectproxy_exit(self, &state);
PyMem_RawFree(shared_args_state);
PyMem_RawFree(kwarg_pairs);
return NULL;
}
PyObject *shared_kwarg = _sharedobjectproxy_copy_for_interp(&pair->value);
if (shared_kwarg == NULL) {
Py_DECREF(shared_args);
Py_DECREF(shared_kwargs);
(void)_sharedobjectproxy_exit(self, &state);
PyMem_RawFree(shared_args_state);
PyMem_RawFree(kwarg_pairs);
return NULL;
}
if (PyDict_SetItem(shared_kwargs, key, shared_kwarg) < 0) {
Py_DECREF(shared_args);
Py_DECREF(shared_kwargs);
(void)_sharedobjectproxy_exit(self, &state);
PyMem_RawFree(shared_args_state);
PyMem_RawFree(kwarg_pairs);
return NULL;
}
}
}


PyObject *res = PyObject_Call(SharedObjectProxy_OBJECT(self),
shared_args, NULL);
shared_args, shared_kwargs);
Py_DECREF(shared_args);
Py_XDECREF(shared_kwargs);

return _sharedobjectproxy_wrap_result(self, res, &state);
}
Expand Down