Content-Length: 576700 | pFad | http://github.com/python/cpython/pull/111575/commits/e73b9cc944e99802fada50752dc177e7cb4267e9

06 gh-76785: Add Interpreter.bind() by ericsnowcurrently · Pull Request #111575 · python/cpython · GitHub
Skip to content

gh-76785: Add Interpreter.bind() #111575

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
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
Next Next commit
Return an ExceptionSnapshot from _interpreters.exec().
  • Loading branch information
ericsnowcurrently committed Nov 6, 2023
commit e73b9cc944e99802fada50752dc177e7cb4267e9
13 changes: 12 additions & 1 deletion Lib/test/support/interpreters.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,18 @@ def run(self, src_str, /, *, init=None):
that time, the previous interpreter is allowed to run
in other threads.
"""
_interpreters.exec(self._id, src_str, init)
err = _interpreters.exec(self._id, src_str, init)
if err is not None:
if err.name is not None:
if err.msg is not None:
msg = f'{err.name}: {err.msg}'
else:
msg = err.name
elif err.msg is not None:
msg = err.msg
else:
msg = None
raise RunFailedError(msg)


def create_channel():
Expand Down
12 changes: 6 additions & 6 deletions Lib/test/test__xxinterpchannels.py
Original file line number Diff line number Diff line change
Expand Up @@ -1017,16 +1017,16 @@ def test_close_multiple_users(self):
_channels.recv({cid})
"""))
channels.close(cid)
with self.assertRaises(interpreters.RunFailedError) as cm:
interpreters.run_string(id1, dedent(f"""

excsnap = interpreters.run_string(id1, dedent(f"""
_channels.send({cid}, b'spam')
"""))
self.assertIn('ChannelClosedError', str(cm.exception))
with self.assertRaises(interpreters.RunFailedError) as cm:
interpreters.run_string(id2, dedent(f"""
self.assertIn('ChannelClosedError', excsnap.type)

excsnap = interpreters.run_string(id2, dedent(f"""
_channels.send({cid}, b'spam')
"""))
self.assertIn('ChannelClosedError', str(cm.exception))
self.assertIn('ChannelClosedError', excsnap.type)

def test_close_multiple_times(self):
cid = channels.create()
Expand Down
37 changes: 20 additions & 17 deletions Lib/test/test__xxsubinterpreters.py
Original file line number Diff line number Diff line change
Expand Up @@ -743,30 +743,33 @@ def assert_run_failed(self, exctype, msg=None):
"{}: {}".format(exctype.__name__, msg))

def test_invalid_syntax(self):
with self.assert_run_failed(SyntaxError):
# missing close paren
interpreters.run_string(self.id, 'print("spam"')
# missing close paren
exc = interpreters.run_string(self.id, 'print("spam"')
self.assertEqual(exc.type, 'SyntaxError')

def test_failure(self):
with self.assert_run_failed(Exception, 'spam'):
interpreters.run_string(self.id, 'raise Exception("spam")')
exc = interpreters.run_string(self.id, 'raise Exception("spam")')
self.assertEqual(exc.type, 'Exception')
self.assertEqual(exc.msg, 'spam')

def test_SystemExit(self):
with self.assert_run_failed(SystemExit, '42'):
interpreters.run_string(self.id, 'raise SystemExit(42)')
exc = interpreters.run_string(self.id, 'raise SystemExit(42)')
self.assertEqual(exc.type, 'SystemExit')
self.assertEqual(exc.msg, '42')

def test_sys_exit(self):
with self.assert_run_failed(SystemExit):
interpreters.run_string(self.id, dedent("""
import sys
sys.exit()
"""))
exc = interpreters.run_string(self.id, dedent("""
import sys
sys.exit()
"""))
self.assertEqual(exc.type, 'SystemExit')

with self.assert_run_failed(SystemExit, '42'):
interpreters.run_string(self.id, dedent("""
import sys
sys.exit(42)
"""))
exc = interpreters.run_string(self.id, dedent("""
import sys
sys.exit(42)
"""))
self.assertEqual(exc.type, 'SystemExit')
self.assertEqual(exc.msg, '42')

def test_with_shared(self):
r, w = os.pipe()
Expand Down
10 changes: 6 additions & 4 deletions Lib/test/test_import/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1968,10 +1968,12 @@ def test_disallowed_reimport(self):
print(_testsinglephase)
''')
interpid = _interpreters.create()
with self.assertRaises(_interpreters.RunFailedError):
_interpreters.run_string(interpid, script)
with self.assertRaises(_interpreters.RunFailedError):
_interpreters.run_string(interpid, script)

excsnap = _interpreters.run_string(interpid, script)
self.assertIsNot(excsnap, None)

excsnap = _interpreters.run_string(interpid, script)
self.assertIsNot(excsnap, None)


class TestSinglePhaseSnapshot(ModuleSnapshot):
Expand Down
22 changes: 8 additions & 14 deletions Lib/test/test_importlib/test_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -655,25 +655,19 @@ def test_magic_number(self):
@unittest.skipIf(_interpreters is None, 'subinterpreters required')
class IncompatibleExtensionModuleRestrictionsTests(unittest.TestCase):

ERROR = re.compile("^ImportError: module (.*) does not support loading in subinterpreters")

def run_with_own_gil(self, script):
interpid = _interpreters.create(isolated=True)
try:
_interpreters.run_string(interpid, script)
except _interpreters.RunFailedError as exc:
if m := self.ERROR.match(str(exc)):
modname, = m.groups()
raise ImportError(modname)
excsnap = _interpreters.run_string(interpid, script)
if excsnap is not None:
if excsnap.type == 'ImportError':
raise ImportError(excsnap.msg)

def run_with_shared_gil(self, script):
interpid = _interpreters.create(isolated=False)
try:
_interpreters.run_string(interpid, script)
except _interpreters.RunFailedError as exc:
if m := self.ERROR.match(str(exc)):
modname, = m.groups()
raise ImportError(modname)
excsnap = _interpreters.run_string(interpid, script)
if excsnap is not None:
if excsnap.type == 'ImportError':
raise ImportError(excsnap.msg)

@unittest.skipIf(_testsinglephase is None, "test requires _testsinglephase module")
def test_single_phase_init_module(self):
Expand Down
60 changes: 29 additions & 31 deletions Modules/_xxsubinterpretersmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -237,18 +237,17 @@ _run_script(PyObject *ns, const char *codestr, Py_ssize_t codestrlen, int flags)
static int
_run_in_interpreter(PyInterpreterState *interp,
const char *codestr, Py_ssize_t codestrlen,
PyObject *shareables, int flags,
PyObject *excwrapper)
PyObject *shareables, int flags, PyObject **excsnap)
{
assert(!PyErr_Occurred());
_PyXI_session session = {0};

// Prep and switch interpreters.
if (_PyXI_Enter(&session, interp, shareables) < 0) {
assert(!PyErr_Occurred());
_PyXI_ApplyExceptionInfo(session.exc, excwrapper);
assert(PyErr_Occurred());
return -1;
*excsnap = _PyXI_ResolveCapturedException(&session, NULL);
assert((PyErr_Occurred() == NULL) != (*excsnap == NULL));
return PyErr_Occurred() ? -1 : 0;
}

// Run the script.
Expand All @@ -258,9 +257,12 @@ _run_in_interpreter(PyInterpreterState *interp,
_PyXI_Exit(&session);

// Propagate any exception out to the caller.
assert(!PyErr_Occurred());
if (res < 0) {
_PyXI_ApplyCapturedException(&session, excwrapper);
*excsnap = _PyXI_ResolveCapturedException(&session, NULL);
assert((PyErr_Occurred() == NULL) != (*excsnap == NULL));
if (!PyErr_Occurred()) {
res = 0;
}
}
else {
assert(!_PyXI_HasCapturedException(&session));
Expand Down Expand Up @@ -528,14 +530,15 @@ convert_code_arg(PyObject *arg, const char *fname, const char *displayname,
return code;
}

static int
static PyObject *
_interp_exec(PyObject *self,
PyObject *id_arg, PyObject *code_arg, PyObject *shared_arg)
{
// Look up the interpreter.
PyInterpreterState *interp = PyInterpreterID_LookUp(id_arg);
if (interp == NULL) {
return -1;
assert(PyErr_Occurred());
return NULL;
}

// Extract code.
Expand All @@ -545,20 +548,24 @@ _interp_exec(PyObject *self,
const char *codestr = get_code_str(code_arg,
&codestrlen, &bytes_obj, &flags);
if (codestr == NULL) {
return -1;
assert(PyErr_Occurred());
return NULL;
}

// Run the code in the interpreter.
module_state *state = get_module_state(self);
assert(state != NULL);
PyObject *excsnap = NULL;
int res = _run_in_interpreter(interp, codestr, codestrlen,
shared_arg, flags, state->RunFailedError);
shared_arg, flags, &excsnap);
Py_XDECREF(bytes_obj);
if (res < 0) {
return -1;
assert(PyErr_Occurred());
assert(excsnap == NULL);
return NULL;
}

return 0;
else if (excsnap != NULL) {
return excsnap;
}
Py_RETURN_NONE;
}

static PyObject *
Expand Down Expand Up @@ -586,12 +593,9 @@ interp_exec(PyObject *self, PyObject *args, PyObject *kwds)
return NULL;
}

int res = _interp_exec(self, id, code, shared);
PyObject *res = _interp_exec(self, id, code, shared);
Py_DECREF(code);
if (res < 0) {
return NULL;
}
Py_RETURN_NONE;
return res;
}

PyDoc_STRVAR(exec_doc,
Expand Down Expand Up @@ -629,12 +633,9 @@ interp_run_string(PyObject *self, PyObject *args, PyObject *kwds)
return NULL;
}

int res = _interp_exec(self, id, script, shared);
PyObject *res = _interp_exec(self, id, script, shared);
Py_DECREF(script);
if (res < 0) {
return NULL;
}
Py_RETURN_NONE;
return res;
}

PyDoc_STRVAR(run_string_doc,
Expand Down Expand Up @@ -663,12 +664,9 @@ interp_run_func(PyObject *self, PyObject *args, PyObject *kwds)
return NULL;
}

int res = _interp_exec(self, id, (PyObject *)code, shared);
PyObject *res = _interp_exec(self, id, (PyObject *)code, shared);
Py_DECREF(code);
if (res < 0) {
return NULL;
}
Py_RETURN_NONE;
return res;
}

PyDoc_STRVAR(run_func_doc,
Expand Down








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: http://github.com/python/cpython/pull/111575/commits/e73b9cc944e99802fada50752dc177e7cb4267e9

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy