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