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