From: Diane Trout Date: Tue, 5 Dec 2006 23:54:57 +0000 (+0000) Subject: make compiled in python extension initialization more flexable X-Git-Url: http://woldlab.caltech.edu/gitweb/?p=mussa.git;a=commitdiff_plain;h=90c246fb6284a6d191eed079f73c38812092a4b9 make compiled in python extension initialization more flexable Because of weird library ordering issues, my test case that tested running python code was looking for compiled in python modules that weren't available yet it wouldn't link properly. I solved this by making it possible to provide a list of what compiled-in python extensions should be initialized when you launch the interpreter, so the mussagl program, which does have all the right components linked in is what is specifying what modules should be provided to the python interpreter. Now I just need to fix the build system so you can compile mussa without all this python crud if needed. And maybe decide how to launch the gui from a stanard python interpreter. --- diff --git a/py/CMakeLists.txt b/py/CMakeLists.txt index a2466f9..fb537a5 100644 --- a/py/CMakeLists.txt +++ b/py/CMakeLists.txt @@ -35,20 +35,36 @@ IF(BOOST_PYTHON_LIBRARY) optimized ${QT_QTCORE_LIBRARY} debug ${QT_QTCORE_LIBRARY_DEBUG} ) - SET(STATIC_PYTHON_TARGETS "") - SET(SHARED_PYTHON_TARGETS "") - + GET_MUSSA_COMPILE_FLAGS(PY_CFLAGS) + GET_MUSSA_LINK_FLAGS(PY_LDFLAGS) + + # configure static core python library ADD_LIBRARY(mussa_py STATIC ${SOURCES}) - SET(${STATIC_PYTHON_TARGETS} "${${STATIC_PYTHON_TARGETS}} mussa_py") + SET_TARGET_PROPERTIES(mussa_py PROPERTIES + COMPILE_FLAGS "${PY_CFLAGS}" + LINK_FLAGS "${PY_LDFLAGS}") + + # configure core (shared) python module ADD_LIBRARY(mussa MODULE ${SOURCES}) - SET(${SHARED_PYTHON_TARGETS} "${${SHARED_PYTHON_TARGETS}} mussa") + SET_TARGET_PROPERTIES(mussa PROPERTIES + PREFIX "" + COMPILE_FLAGS "${PY_CFLAGS}" + LINK_FLAGS "${PY_LDFLAGS}") TARGET_LINK_LIBRARIES(mussa ${MUSSA_LIBRARIES} ) + # configure static gui python library ADD_LIBRARY(mussaqui_py STATIC ${QUI_SOURCES}) - SET(${STATIC_PYTHON_TARGETS} "${${STATIC_PYTHON_TARGETS}} mussaqui_py") - # ADD_LIBRARY(mussaqui MODULE ${QUI_SOURCES}) - # SET(${SHARED_PYTHON_TARGETS} "${${SHARED_PYTHON_TARGETS}} mussaqui") + SET_TARGET_PROPERTIES(mussa_py PROPERTIES + COMPILE_FLAGS "${PY_CFLAGS}" + LINK_FLAGS "${PY_LDFLAGS}") + + # configure gui (shared) python library + #ADD_LIBRARY(mussaqui MODULE ${QUI_SOURCES}) + #SET_TARGET_PROPERTIES(mussaqui PROPERTIES + # PREFIX "" + # COMPILE_FLAGS "${PY_CFLAGS}" + # LINK_FLAGS "${PY_LDFLAGS}") #TARGET_LINK_LIBRARIES(mussaqui # mussa_qui # ${MUSSA_LIBRARIES} @@ -58,13 +74,6 @@ IF(BOOST_PYTHON_LIBRARY) # debug ${QT_QTOPENGL_LIBRARY_DEBUG} # ) - # don't put the "lib" prefix infront of our shared modules - SET_TARGET_PROPERTIES(${SHARED_PYTHON_TARGETS} - PROPERTIES - PREFIX "" - ) - GET_MUSSA_COMPILE_FLAGS(PY_CFLAGS) - GET_MUSSA_LINK_FLAGS(PY_LDFLAGS) SET_SOURCE_FILES_PROPERTIES( ${SOURCES} ${QUI_SOURCES} diff --git a/py/python.cpp b/py/python.cpp index d43ee2f..75a844f 100644 --- a/py/python.cpp +++ b/py/python.cpp @@ -9,32 +9,60 @@ namespace py = boost::python; #include namespace alg = boost::algorithm; + MussaPython::MussaPython() +{ +} + +void MussaPython::add_module(const std::string &name, python_module_init initfunc) +{ + python_modules.push_back( module_item(name, initfunc) ); +} + +void MussaPython::AppendInittab(const module_item &item) +{ + PyImport_AppendInittab(const_cast(item.first.c_str()), item.second); +} + +void MussaPython::init_interpreter() { try { - // add our mussa module to our environment - PyImport_AppendInittab("mussa", &initmussa); - PyImport_AppendInittab("mussaqui", &initmussaqui); + // add any compiled in modules to our environment + for_each(python_modules.begin(), python_modules.end(), AppendInittab); // go ahead and finish initalizing python Py_Initialize(); // get reference to the global namespace py::object main_module( (py::handle<>(py::borrowed(PyImport_AddModule("__main__")))) ); + // setting the main_namespace must come before any calls to run or eval main_namespace = main_module.attr("__dict__"); - // FIXME: this really should be a configuration file? - run("import __main__\n" - "import mussa\n" - "import mussaqui"); + run("import __main__\n"); + // import modules that were added to us into the interpreter + for(module_list::const_iterator item = python_modules.begin(); + item != python_modules.end(); + ++item) + { + std::string code("import " + item->first); + run(code); + } } catch (py::error_already_set e) { PyErr_Print(); } } +py::object MussaPython::get_namespace() +{ + if (main_namespace.ptr() == Py_None) { + init_interpreter(); + } + return main_namespace; +} + void MussaPython::run(std::string code) { try { - PyObject *global_ptr = main_namespace.ptr(); + PyObject *global_ptr = get_namespace().ptr(); py::object result( py::handle<>( (PyRun_String(code.c_str(), Py_file_input, global_ptr, global_ptr) ))); @@ -46,7 +74,7 @@ void MussaPython::run(std::string code) py::object MussaPython::eval(std::string code) { try { - PyObject *global_ptr = main_namespace.ptr(); + PyObject *global_ptr = get_namespace().ptr(); py::object result( py::handle<>( (PyRun_String(code.c_str(), Py_eval_input, global_ptr, global_ptr) ))); @@ -93,7 +121,7 @@ py::object MussaPython::operator[](std::string name) string_vector split_name; alg::split(split_name, name, alg::is_any_of(".")); - py::object lookup = main_namespace["__main__"]; + py::object lookup = get_namespace()["__main__"]; for (string_vector::const_iterator name_i = split_name.begin(); name_i != split_name.end(); @@ -105,11 +133,11 @@ py::object MussaPython::operator[](std::string name) } //! return a reference to a single mussa python interpreter -MussaPython& get_py() +MussaPython *get_py() { static MussaPython *py; if (!py) { py = new MussaPython; } - return *py; + return py; } \ No newline at end of file diff --git a/py/python.hpp b/py/python.hpp index a9fc025..990305f 100644 --- a/py/python.hpp +++ b/py/python.hpp @@ -2,15 +2,21 @@ #define _MUSSA_PYTHON_HPP_ #include #include - -extern "C" void initmussa(); -extern "C" void initmussaqui(); +#include +#include //! Create a singleton class to manage our reference to the python interpreter class MussaPython { public: + typedef void (*python_module_init)(void); MussaPython(); + //! initalize interpreter + void init_interpreter(); + //! add a python module that was statically compiled into this executable + void add_module(const std::string& name, python_module_init); + //! return main python namespace(initializing the interpreter if needed) + boost::python::object get_namespace(); //! pass multi-statement code block to the python interpreter void run(std::string); //! pass single expression to the python interpreter and return the result @@ -21,12 +27,16 @@ class MussaPython { void simple_interpreter(FILE *fp=stdin); //! return an object in the python namespace boost::python::object operator[](std::string); - protected: + typedef std::pair module_item; + typedef std::list module_list; + module_list python_modules; boost::python::object main_namespace; + + static void AppendInittab(const module_item &item); }; //! return a reference to a single mussa python interpreter -MussaPython& get_py(); +MussaPython *get_py(); #endif // _MUSSA_PYTHON_HPP_ diff --git a/py/test/test_python.cpp b/py/test/test_python.cpp index c33b7ad..a733bea 100644 --- a/py/test/test_python.cpp +++ b/py/test/test_python.cpp @@ -8,21 +8,21 @@ namespace py = boost::python; BOOST_AUTO_TEST_CASE( execute_python ) { - get_py().run("x = 3"); - int x = py::extract(get_py().eval("x")); + get_py()->run("x = 3"); + int x = py::extract(get_py()->eval("x")); BOOST_CHECK_EQUAL(x, 3); } BOOST_AUTO_TEST_CASE( lookup_python ) { - get_py().run("import os"); - py::object splitext = get_py()["os.path.splitext"]; + get_py()->run("import os"); + py::object splitext = (*get_py())["os.path.splitext"]; py::object result = splitext("/home/diane/foo.txt"); std::string ext = py::extract(result[1]); BOOST_CHECK_EQUAL(ext, ".txt"); - get_py().run("from os.path import splitext"); - py::object splitext2 = get_py()["splitext"]; + get_py()->run("from os.path import splitext"); + py::object splitext2 = (*get_py())["splitext"]; py::object result2 = splitext("/home/diane/bar.txt"); std::string ext2 = py::extract(result2[1]); BOOST_CHECK_EQUAL(ext, ext2); diff --git a/qui/mussagl.cpp b/qui/mussagl.cpp index 55afefd..3001cbb 100644 --- a/qui/mussagl.cpp +++ b/qui/mussagl.cpp @@ -3,6 +3,8 @@ using namespace boost::filesystem; #ifdef USE_PYTHON #include "py/python.hpp" +extern "C" void initmussa(); +extern "C" void initmussaqui(); #endif #include "qui/MussaWindow.hpp" @@ -65,10 +67,22 @@ int main(int argc, char **argv) // allow the user to keep the interpreter open even after // closing all the windows app.setQuitOnLastWindowClosed(false); - const InterpreterThread *interp = thread.create_interpreter(); + InterpreterThread *interp = thread.create_interpreter(); + if (!interp) { + std::cerr << "Unable to initialize interpeter thread" << std::endl; + return 1; + } + MussaPython *py = interp->get_py(); + if (!py) { + std::cerr << "Unable to initialize python interpreter" << std::endl; + return 1; + } + py->add_module("mussa", &initmussa); + py->add_module("mussaqui", &initmussaqui); // quit when the interpreter exits QObject::connect(interp, SIGNAL(finished()), &app, SLOT(quit())); + interp->start(); app.exec(); } else #endif /* USE_PYTHON */ diff --git a/qui/threading/InterpreterThread.cpp b/qui/threading/InterpreterThread.cpp index 45171e7..bb7bae8 100644 --- a/qui/threading/InterpreterThread.cpp +++ b/qui/threading/InterpreterThread.cpp @@ -4,7 +4,12 @@ void InterpreterThread::run() { - get_py().interpreter(); + ::get_py()->interpreter(); +} + +MussaPython *InterpreterThread::get_py() +{ + return ::get_py(); } InterpreterThread::InterpreterThread() diff --git a/qui/threading/InterpreterThread.hpp b/qui/threading/InterpreterThread.hpp index dd8aea0..a2ad09f 100644 --- a/qui/threading/InterpreterThread.hpp +++ b/qui/threading/InterpreterThread.hpp @@ -3,10 +3,12 @@ #include +class MussaPython; class InterpreterThread : public QThread { Q_OBJECT public: void run(); + MussaPython *get_py(); private: //! only let ThreadManager create this object. InterpreterThread(); diff --git a/qui/threading/ThreadManager.cpp b/qui/threading/ThreadManager.cpp index 8abf214..1586c13 100644 --- a/qui/threading/ThreadManager.cpp +++ b/qui/threading/ThreadManager.cpp @@ -18,17 +18,16 @@ ThreadManager::ThreadManager() get_gui_proxy(); } -const InterpreterThread *ThreadManager::create_interpreter() { - +InterpreterThread *ThreadManager::create_interpreter() +{ static QMutex interpreter_lock; static InterpreterThread *interpreter_thread; if (interpreter_lock.tryLock()) { // we're the first thread interpreter_thread = new InterpreterThread(); - interpreter_thread->start(); } - return interpreter_thread; // someone already started a copy of the interpreter + return interpreter_thread; } GuiProxy *ThreadManager::get_gui_proxy() diff --git a/qui/threading/ThreadManager.hpp b/qui/threading/ThreadManager.hpp index 3e878a0..89d8984 100644 --- a/qui/threading/ThreadManager.hpp +++ b/qui/threading/ThreadManager.hpp @@ -10,7 +10,7 @@ class ThreadManager { public: //! make a python interpreter - const InterpreterThread *create_interpreter(); + InterpreterThread *create_interpreter(); static GuiProxy *get_gui_proxy(); private: ThreadManager();