Skip to content

Commit 283ddab

Browse files
Add Interpreter.get().
1 parent 8e99e66 commit 283ddab

File tree

3 files changed

+120
-0
lines changed

3 files changed

+120
-0
lines changed

Lib/test/support/interpreters.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,16 @@ def bind(self, ns=None, /, **kwargs):
112112
ns = dict(ns, **kwargs) if ns is not None else kwargs
113113
_interpreters.set___main___attrs(self._id, ns)
114114

115+
# XXX getattr?
116+
def get(self, name, default=None, /):
117+
"""Return the attr value from the interpreter's __main__.
118+
119+
The value must be shareable.
120+
"""
121+
found = _interpreters.get___main___attrs(self._id, (name,), default)
122+
assert len(found) == 1, found
123+
return found[name]
124+
115125
# XXX Rename "run" to "exec"?
116126
# XXX Do not allow init to overwrite (by default)?
117127
def run(self, src_str, /, *, init=None):

Lib/test/test_interpreters.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,36 @@ def test_not_shareable(self):
526526
interp.run('print(spam)')
527527

528528

529+
class TestInterpreterGet(TestBase):
530+
531+
def test_empty(self):
532+
interp = interpreters.create()
533+
with self.assertRaises(TypeError):
534+
interp.get()
535+
536+
def test_found(self):
537+
interp = interpreters.create()
538+
obj1 = interp.get('__name__')
539+
interp.bind(spam=42)
540+
obj2 = interp.get('spam')
541+
542+
self.assertEqual(obj1, '__main__')
543+
self.assertEqual(obj2, 42)
544+
545+
def test_not_found(self):
546+
interp = interpreters.create()
547+
obj1 = interp.get('spam')
548+
obj2 = interp.get('spam', 'eggs')
549+
550+
self.assertIs(obj1, None)
551+
self.assertEqual(obj2, 'eggs')
552+
553+
def test_not_shareable(self):
554+
interp = interpreters.create()
555+
with self.assertRaises(ValueError):
556+
interp.get('__builtins__')
557+
558+
529559
class TestInterpreterRun(TestBase):
530560

531561
def test_success(self):

Modules/_xxsubinterpretersmodule.c

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,84 @@ PyDoc_STRVAR(set___main___attrs_doc,
456456
\n\
457457
Bind the given attributes in the interpreter's __main__ module.");
458458

459+
static PyObject *
460+
interp_get___main___attrs(PyObject *self, PyObject *args)
461+
{
462+
PyObject *id, *names;
463+
PyObject *dflt = Py_None;
464+
if (!PyArg_ParseTuple(args, "OO|O:" MODULE_NAME ".get___main___attrs",
465+
&id, &names, &dflt))
466+
{
467+
return NULL;
468+
}
469+
470+
// Look up the interpreter.
471+
PyInterpreterState *interp = PyInterpreterID_LookUp(id);
472+
if (interp == NULL) {
473+
return NULL;
474+
}
475+
476+
// Prep the result.
477+
PyObject *found = PyDict_New();
478+
if (found == NULL) {
479+
return NULL;
480+
}
481+
482+
// Set up the shared ns.
483+
_PyXI_namespace *shared = _PyXI_NamespaceFromNames(names);
484+
if (shared == NULL) {
485+
if (!PyErr_Occurred()) {
486+
PyErr_SetString(PyExc_ValueError, "expected non-empty list of names");
487+
}
488+
Py_DECREF(found);
489+
return NULL;
490+
}
491+
492+
_PyXI_session session = {0};
493+
494+
// Prep and switch interpreters, including apply the updates.
495+
if (_PyXI_Enter(&session, interp, NULL) < 0) {
496+
Py_DECREF(found);
497+
assert(!PyErr_Occurred());
498+
_PyXI_ApplyCapturedException(&session, NULL);
499+
assert(PyErr_Occurred());
500+
return NULL;
501+
}
502+
503+
// Extract the requested attrs from __main__.
504+
int res = _PyXI_FillNamespaceFromDict(shared, session.main_ns, &session);
505+
506+
// Clean up and switch back.
507+
_PyXI_Exit(&session);
508+
509+
if (res == 0) {
510+
assert(!PyErr_Occurred());
511+
// Copy the objects into the result dict.
512+
if (_PyXI_ApplyNamespace(shared, found, dflt) < 0) {
513+
Py_CLEAR(found);
514+
}
515+
}
516+
else {
517+
if (!PyErr_Occurred()) {
518+
_PyXI_ApplyCapturedException(&session, NULL);
519+
assert(PyErr_Occurred());
520+
}
521+
else {
522+
assert(!_PyXI_HasCapturedException(&session));
523+
}
524+
Py_CLEAR(found);
525+
}
526+
527+
_PyXI_FreeNamespace(shared);
528+
return found;
529+
}
530+
531+
PyDoc_STRVAR(get___main___attrs_doc,
532+
"get___main___attrs(id, names, default=None, /)\n\
533+
\n\
534+
Look up the given attributes in the interpreter's __main__ module.\n\
535+
Return the default if not found.");
536+
459537
static PyUnicodeObject *
460538
convert_script_arg(PyObject *arg, const char *fname, const char *displayname,
461539
const char *expected)
@@ -754,6 +832,8 @@ static PyMethodDef module_functions[] = {
754832

755833
{"set___main___attrs", _PyCFunction_CAST(interp_set___main___attrs),
756834
METH_VARARGS, set___main___attrs_doc},
835+
{"get___main___attrs", _PyCFunction_CAST(interp_get___main___attrs),
836+
METH_VARARGS, get___main___attrs_doc},
757837
{"is_shareable", _PyCFunction_CAST(object_is_shareable),
758838
METH_VARARGS | METH_KEYWORDS, is_shareable_doc},
759839

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy