# 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__)