1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
|
# mode: run
# tag: pep489, subinterpreter
PYTHON setup.py build_ext --inplace
PYTHON -c "import subtest; subtest.run_main()"
PYTHON -c "import subtest; subtest.run_sub()"
PYTHON -c "import subtest; subtest.run_main(); subtest.run_sub()"
######## setup.py ########
from Cython.Build.Dependencies import cythonize
from distutils.core import setup
setup(
ext_modules = cythonize("**/*.pyx"),
)
######## subtest.pyx ########
cdef extern from *:
"""
/* Copied from CPython's _testcapi.c module */
static PyObject *run_in_subinterpreter(const char *code) {
int r;
PyThreadState *substate, *mainstate;
mainstate = PyThreadState_Get();
PyThreadState_Swap(NULL);
substate = Py_NewInterpreter();
if (substate == NULL) {
/* Since no new thread state was created, there is no exception to
propagate; raise a fresh one after swapping in the old thread
state. */
PyThreadState_Swap(mainstate);
PyErr_SetString(PyExc_RuntimeError, "sub-interpreter creation failed");
return NULL;
}
r = PyRun_SimpleString(code);
Py_EndInterpreter(substate);
PyThreadState_Swap(mainstate);
return PyLong_FromLong(r);
}
"""
object run_in_subinterpreter(const char *code)
MAIN_HAS_IMPORTED = False
def run_main():
global MAIN_HAS_IMPORTED
MAIN_HAS_IMPORTED = True
import package.subtest
from package import subtest
def run_sub():
assert 0 == run_in_subinterpreter(b'1+1')
assert 0 == run_in_subinterpreter(b'2+2')
# The subinterpreter does not add the current working directory to
# sys.path, so we need to add it manually.
pre = b'import sys; sys.path.insert(0, "."); '
assert 0 == run_in_subinterpreter(pre + b'import package')
assert 0 == run_in_subinterpreter(pre + b'import package')
import sys
result = run_in_subinterpreter(pre + b'import package.subtest')
if not MAIN_HAS_IMPORTED:
assert result == 0, result # imports only in subinterpreters are ok
elif sys.version_info >= (3, 5):
assert result == -1, result # re-import in a different subinterpreter fails in Py3.5+ (with PEP-489)
else:
assert result == 0, result # re-import in a different subinterpreter reuses the module in Py<3.5
######## package/__init__.py ########
######## package/subtest.pyx ########
print("Module loaded: %s" % __name__)
|