diff --git a/Lib/test/embed1.c b/Lib/test/embed1.c new file mode 100644 --- /dev/null +++ b/Lib/test/embed1.c @@ -0,0 +1,50 @@ +#include +#include + +void print_subinterp(void) +{ + /* Just output some debug stuff */ + PyThreadState *ts = PyThreadState_Get(); + printf("interp %p : ", ts); + fflush(stdout); + PyRun_SimpleString( + "import sys;" + "print('id(modules) =', id(sys.modules));" + "sys.stdout.flush()" + ); +} + +int main(int argc, char *argv[]) +{ + PyThreadState *mainthread, *subthread; + PyGILState_STATE gilstate; + int i, j; + + for (i=0; i<3; i++) { + printf("--- Pass %d ---\n", i); + Py_SetProgramName(L"subembed"); + Py_Initialize(); + mainthread = PyThreadState_Get(); + + PyEval_InitThreads(); + PyEval_ReleaseThread(mainthread); + + gilstate = PyGILState_Ensure(); + print_subinterp(); + PyThreadState_Swap(NULL); + + for (j=0; j<3; j++) { + subthread = Py_NewInterpreter(); + print_subinterp(); + Py_EndInterpreter(subthread); + } + + PyThreadState_Swap(mainthread); + print_subinterp(); + PyGILState_Release(gilstate); + + PyEval_RestoreThread(mainthread); + Py_Finalize(); + } + return 0; +} diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py new file mode 100644 --- /dev/null +++ b/Lib/test/test_embed.py @@ -0,0 +1,81 @@ +# Run the _testcapi module tests (tests for the Python/C API): by defn, +# these are all functions _testcapi exports whose name begins with 'test_'. + +import os +import subprocess +import sys +import time +import unittest +import sysconfig + +from distutils.ccompiler import new_compiler +from distutils.sysconfig import customize_compiler +from test import support + +here = os.path.abspath(os.path.dirname(__file__)) +base = os.path.dirname(os.path.dirname(here)) + + +def get_config_var(v): + return sysconfig.get_config_var(v) or '' + + +class EmbeddingTest(unittest.TestCase): + + def make_exe(self, srcfile): + compiler = new_compiler() + customize_compiler(compiler) + # We reproduce the logic for embedding by hand since distutils is so broken + cc = get_config_var('CC') + cflags = get_config_var('PY_CORE_CFLAGS') or get_config_var('CFLAGS') + if cc and cflags: + # For some reason distutils uses compiler_so, not compiler... + compiler.set_executables(compiler_so=cc + ' ' + cflags) + ld = get_config_var('LINKCC') or cc + ldflags = get_config_var('LDFLAGS') + ' ' + get_config_var('LINKFORSHARED') + if ld: + compiler.set_executables(linker_exe=ld + ' ' + ldflags) + libs = [] + for v in ['LDLIBRARY', 'LIBS', 'SYSLIBS']: + libs += get_config_var(v).split() + + build_dir = support.TESTFN + os.mkdir(build_dir) + self.addCleanup(support.rmtree, build_dir) + srcpath = os.path.join(here, srcfile) + objpath, = compiler.object_filenames([srcpath], output_dir=build_dir) + exepath = os.path.join(build_dir, + os.path.splitext(os.path.basename(srcfile))[0]) + compiler.compile([srcpath], output_dir=build_dir) + compiler.link_executable([objpath] + libs, exepath) + if compiler.exe_extension is not None: + exepath += compiler.exe_extension + return exepath + + def test_subinterps(self): + # XXX only tested under Unix checkouts + # This is needed otherwise we get a fatal error: + # "Py_Initialize: Unable to get the locale encoding + # LookupError: no codec search functions registered: can't find encoding" + self.addCleanup(os.chdir, os.getcwd()) + os.chdir(base) + exe = self.make_exe("embed1.c") + p = subprocess.Popen([exe], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + (out, err) = p.communicate() + self.assertEqual(p.returncode, 0, + "bad returncode %d, stderr is %r" % + (p.returncode, err)) + if support.verbose: + print() + print(out.decode('latin1')) + print(err.decode('latin1')) + + +def test_main(): + support.run_unittest(EmbeddingTest) + + +if __name__ == "__main__": + test_main()