make compiled in python extension initialization more flexable
authorDiane Trout <diane@caltech.edu>
Tue, 5 Dec 2006 23:54:57 +0000 (23:54 +0000)
committerDiane Trout <diane@caltech.edu>
Tue, 5 Dec 2006 23:54:57 +0000 (23:54 +0000)
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.

py/CMakeLists.txt
py/python.cpp
py/python.hpp
py/test/test_python.cpp
qui/mussagl.cpp
qui/threading/InterpreterThread.cpp
qui/threading/InterpreterThread.hpp
qui/threading/ThreadManager.cpp
qui/threading/ThreadManager.hpp

index a2466f9f7894226d6b532d8b3eff8261dd0a09f3..fb537a5019667ebb09b6348e0219f91d68c94efc 100644 (file)
@@ -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}
index d43ee2f74334b1f5f396aaa47a01123944d42405..75a844fbe6d9080b4111e764ab5b6e2592e8f7ee 100644 (file)
@@ -9,32 +9,60 @@ namespace py = boost::python;
 #include <boost/algorithm/string/classification.hpp>
 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<char *>(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
-MussaPythonget_py()
+MussaPython *get_py()
 {
   static MussaPython *py;
   if (!py) {
     py = new MussaPython;
   }
-  return *py;
+  return py;
 }
\ No newline at end of file
index a9fc02534e5efd02073cbc1e2e70a7638defd453..990305f611e4f97c6e68245b1831edbbe1c88a56 100644 (file)
@@ -2,15 +2,21 @@
 #define _MUSSA_PYTHON_HPP_
 #include <boost/python.hpp>
 #include <string>
-
-extern "C" void initmussa();
-extern "C" void initmussaqui();
+#include <list>
+#include <utility>
 
 //! 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<std::string, python_module_init> module_item;
+    typedef std::list<module_item> 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
-MussaPythonget_py();
+MussaPython *get_py();
 
 #endif // _MUSSA_PYTHON_HPP_
index c33b7ad8278e0038769fd8eead8b97ec70a0bd49..a733bea1eaacc345d1d99bb1cc45b07072cc5583 100644 (file)
@@ -8,21 +8,21 @@ namespace py = boost::python;
 
 BOOST_AUTO_TEST_CASE( execute_python )
 {
-  get_py().run("x = 3");
-  int x = py::extract<int>(get_py().eval("x"));
+  get_py()->run("x = 3");
+  int x = py::extract<int>(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<std::string>(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<std::string>(result2[1]);
   BOOST_CHECK_EQUAL(ext, ext2);
index 55afefd02bf37ca5a4fc74470def09ca02306027..3001cbb29ddaeb127b0e89f5541aa8651abdd399 100644 (file)
@@ -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 */
index 45171e7eeb8d18fd174d962a74abc9c46ffc66c4..bb7bae8cc47c9ac6f7fa10970bd7fc82b972c380 100644 (file)
@@ -4,7 +4,12 @@
 
 void InterpreterThread::run()
 {
-  get_py().interpreter();
+  ::get_py()->interpreter();
+}
+
+MussaPython *InterpreterThread::get_py()
+{
+  return ::get_py();
 }
 
 InterpreterThread::InterpreterThread()
index dd8aea01aee41e24afcdd53ae9ce4610950aeeef..a2ad09f38f05567dbeb71485fbc4bb51ac0150a7 100644 (file)
@@ -3,10 +3,12 @@
 
 #include <QThread>
 
+class MussaPython;
 class InterpreterThread : public QThread {
   Q_OBJECT
 public:
   void run();
+  MussaPython *get_py();
 private:
   //! only let ThreadManager create this object.
   InterpreterThread();
index 8abf2149e0b5ef1e55351128c633bf0e93d5bec5..1586c13cef8c2748087c1856ac342a6a9d94050f 100644 (file)
@@ -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()
index 3e878a030975d98aa0c794ba398ca41433747003..89d8984fdb5c37ec1e195e9eef762054b6cd1b05 100644 (file)
@@ -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();