Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
Add Interpreter.get().
  • Loading branch information
ericsnowcurrently committed Nov 6, 2023
commit 283ddab1dd9d58ccd2deb880b7a7d32bf3c4d98b
10 changes: 10 additions & 0 deletions Lib/test/support/interpreters.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,16 @@ def bind(self, ns=None, /, **kwargs):
ns = dict(ns, **kwargs) if ns is not None else kwargs
_interpreters.set___main___attrs(self._id, ns)

# XXX getattr?
def get(self, name, default=None, /):
"""Return the attr value from the interpreter's __main__.

The value must be shareable.
"""
found = _interpreters.get___main___attrs(self._id, (name,), default)
assert len(found) == 1, found
return found[name]

# XXX Rename "run" to "exec"?
# XXX Do not allow init to overwrite (by default)?
def run(self, src_str, /, *, init=None):
Expand Down
30 changes: 30 additions & 0 deletions Lib/test/test_interpreters.py
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,36 @@ def test_not_shareable(self):
interp.run('print(spam)')


class TestInterpreterGet(TestBase):

def test_empty(self):
interp = interpreters.create()
with self.assertRaises(TypeError):
interp.get()

def test_found(self):
interp = interpreters.create()
obj1 = interp.get('__name__')
interp.bind(spam=42)
obj2 = interp.get('spam')

self.assertEqual(obj1, '__main__')
self.assertEqual(obj2, 42)

def test_not_found(self):
interp = interpreters.create()
obj1 = interp.get('spam')
obj2 = interp.get('spam', 'eggs')

self.assertIs(obj1, None)
self.assertEqual(obj2, 'eggs')

def test_not_shareable(self):
interp = interpreters.create()
with self.assertRaises(ValueError):
interp.get('__builtins__')


class TestInterpreterRun(TestBase):

def test_success(self):
Expand Down
80 changes: 80 additions & 0 deletions Modules/_xxsubinterpretersmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,84 @@ PyDoc_STRVAR(set___main___attrs_doc,
\n\
Bind the given attributes in the interpreter's __main__ module.");

static PyObject *
interp_get___main___attrs(PyObject *self, PyObject *args)
{
PyObject *id, *names;
PyObject *dflt = Py_None;
if (!PyArg_ParseTuple(args, "OO|O:" MODULE_NAME ".get___main___attrs",
&id, &names, &dflt))
{
return NULL;
}

// Look up the interpreter.
PyInterpreterState *interp = PyInterpreterID_LookUp(id);
if (interp == NULL) {
return NULL;
}

// Prep the result.
PyObject *found = PyDict_New();
if (found == NULL) {
return NULL;
}

// Set up the shared ns.
_PyXI_namespace *shared = _PyXI_NamespaceFromNames(names);
if (shared == NULL) {
if (!PyErr_Occurred()) {
PyErr_SetString(PyExc_ValueError, "expected non-empty list of names");
}
Py_DECREF(found);
return NULL;
}

_PyXI_session session = {0};

// Prep and switch interpreters, including apply the updates.
if (_PyXI_Enter(&session, interp, NULL) < 0) {
Py_DECREF(found);
assert(!PyErr_Occurred());
_PyXI_ApplyCapturedException(&session, NULL);
assert(PyErr_Occurred());
return NULL;
}

// Extract the requested attrs from __main__.
int res = _PyXI_FillNamespaceFromDict(shared, session.main_ns, &session);

// Clean up and switch back.
_PyXI_Exit(&session);

if (res == 0) {
assert(!PyErr_Occurred());
// Copy the objects into the result dict.
if (_PyXI_ApplyNamespace(shared, found, dflt) < 0) {
Py_CLEAR(found);
}
}
else {
if (!PyErr_Occurred()) {
_PyXI_ApplyCapturedException(&session, NULL);
assert(PyErr_Occurred());
}
else {
assert(!_PyXI_HasCapturedException(&session));
}
Py_CLEAR(found);
}

_PyXI_FreeNamespace(shared);
return found;
}

PyDoc_STRVAR(get___main___attrs_doc,
"get___main___attrs(id, names, default=None, /)\n\
\n\
Look up the given attributes in the interpreter's __main__ module.\n\
Return the default if not found.");

static PyUnicodeObject *
convert_script_arg(PyObject *arg, const char *fname, const char *displayname,
const char *expected)
Expand Down Expand Up @@ -754,6 +832,8 @@ static PyMethodDef module_functions[] = {

{"set___main___attrs", _PyCFunction_CAST(interp_set___main___attrs),
METH_VARARGS, set___main___attrs_doc},
{"get___main___attrs", _PyCFunction_CAST(interp_get___main___attrs),
METH_VARARGS, get___main___attrs_doc},
{"is_shareable", _PyCFunction_CAST(object_is_shareable),
METH_VARARGS | METH_KEYWORDS, is_shareable_doc},

Expand Down