[glom/boostpythonretry] Actually works.



commit 3d85e08c8374dbca8474acc9c939460fbfc375d4
Author: Murray Cumming <murrayc murrayc com>
Date:   Sun Feb 7 23:01:11 2010 +0100

    Actually works.

 .../libglom/python_embed/pygdavalue_conversions.cc |   60 +++++++++++-----
 glom/python_embed/glom_python.cc                   |   71 ++++++++++++--------
 2 files changed, 84 insertions(+), 47 deletions(-)
---
diff --git a/glom/libglom/python_embed/pygdavalue_conversions.cc b/glom/libglom/python_embed/pygdavalue_conversions.cc
index 50eaf4c..1fd5c76 100644
--- a/glom/libglom/python_embed/pygdavalue_conversions.cc
+++ b/glom/libglom/python_embed/pygdavalue_conversions.cc
@@ -1,3 +1,9 @@
+#include <Python.h>
+#if PY_VERSION_HEX >= 0x02040000
+# include <datetime.h> /* From Python */
+#endif
+#include "pygdavalue_conversions.h"
+
 #include <boost/python.hpp>
 
 #include "pygdavalue_conversions.h"
@@ -16,7 +22,7 @@
  * Returns: true for success.
  */
 bool
-glom_pygda_value_from_pyobject(GValue *boxed, const boost::python::object& input)
+glom_pygda_value_from_pyobject(GValue* boxed, const boost::python::object& input)
 {
     /* Use an appropriate gda_value_set_*() function.
        We can not know what GValue type is actually wanted, so
@@ -68,38 +74,54 @@ glom_pygda_value_from_pyobject(GValue *boxed, const boost::python::object& input
       const bool val = extractor_bool;
       g_value_init (boxed, G_TYPE_BOOLEAN);
       g_value_set_boolean (boxed, val);
+      return true;
     }
-/*
+    
 #if PY_VERSION_HEX >= 0x02040000
-    } else if (PyDateTime_Check (input)) {
+    // We shouldn't need to call PyDateTime_IMPORT again,
+    // after already doing it in libglom_init(),
+    // but PyDate_Check crashes (with valgrind warnings) if we don't.
+    //
+    // Causes a C++ compiler warning, so we use its definition directly.
+    // See http://bugs.python.org/issue7463.
+    // PyDateTime_IMPORT; //A macro, needed to use PyDate_Check(), PyDateTime_Check(), etc.
+    PyDateTimeAPI = (PyDateTime_CAPI*) PyCObject_Import((char*)"datetime", (char*)"datetime_CAPI");
+    g_assert(PyDateTimeAPI); //This should have been set by the PyDateTime_IMPORT macro
+    
+    //TODO: Find some way to do this with boost::python
+    PyObject* input_c = input.ptr();
+    if (PyDateTime_Check (input_c)) {
          GdaTimestamp gda;
-         gda.year = PyDateTime_GET_YEAR(input);
-         gda.month = PyDateTime_GET_MONTH(input);
-         gda.day = PyDateTime_GET_DAY(input);
-         gda.hour = PyDateTime_DATE_GET_HOUR(input);
-         gda.minute = PyDateTime_DATE_GET_MINUTE(input);
-         gda.second = PyDateTime_DATE_GET_SECOND(input);
+         gda.year = PyDateTime_GET_YEAR(input_c);
+         gda.month = PyDateTime_GET_MONTH(input_c);
+         gda.day = PyDateTime_GET_DAY(input_c);
+         gda.hour = PyDateTime_DATE_GET_HOUR(input_c);
+         gda.minute = PyDateTime_DATE_GET_MINUTE(input_c);
+         gda.second = PyDateTime_DATE_GET_SECOND(input_c);
          gda.timezone = 0;
          gda_value_set_timestamp (boxed, &gda);
-     } else if (PyDate_Check (input)) {
+         return true;
+     } else if (PyDate_Check (input_c)) {
          GDate *gda = g_date_new_dmy(
-           PyDateTime_GET_DAY(input),
-           (GDateMonth)PyDateTime_GET_MONTH(input),
-           PyDateTime_GET_YEAR(input) );
+           PyDateTime_GET_DAY(input_c),
+           (GDateMonth)PyDateTime_GET_MONTH(input_c),
+           PyDateTime_GET_YEAR(input_c) );
          g_value_init (boxed, G_TYPE_DATE);
          g_value_set_boxed(boxed, gda);
          g_date_free(gda);
-     } else if (PyTime_Check (input)) {
+         return true;
+     } else if (PyTime_Check (input_c)) {
          GdaTime gda;
-         gda.hour = PyDateTime_TIME_GET_HOUR(input);
-         gda.minute = PyDateTime_TIME_GET_MINUTE(input);
-         gda.second = PyDateTime_TIME_GET_SECOND(input);
+         gda.hour = PyDateTime_TIME_GET_HOUR(input_c);
+         gda.minute = PyDateTime_TIME_GET_MINUTE(input_c);
+         gda.second = PyDateTime_TIME_GET_SECOND(input_c);
          gda.timezone = 0;
          gda_value_set_time (boxed, &gda);
+         return true;
+     }
 #endif
-*/
 
-    g_warning("Unhandled python type.");
+    //g_warning("Unhandled python type.");
     return false; /* failed. */
 }
 
diff --git a/glom/python_embed/glom_python.cc b/glom/python_embed/glom_python.cc
index ac054c7..a3574a1 100644
--- a/glom/python_embed/glom_python.cc
+++ b/glom/python_embed/glom_python.cc
@@ -126,10 +126,13 @@ void ShowTrace()
   
   if(chrRetval)
   {
-    Glib::ustring message = _("Python Error: \n");
-    message += chrRetval;
-    Gtk::MessageDialog dialog(message, false, Gtk::MESSAGE_ERROR);
-    dialog.run();
+    std::cerr << "Glom: Python Error:" << std::endl << chrRetval << std::endl;
+    
+    //TODO: Move this to the caller.
+    //Glib::ustring message = _("Python Error: \n");
+    //message += chrRetval;
+    //Gtk::MessageDialog dialog(message, false, Gtk::MESSAGE_ERROR);
+    //dialog.run();
   }
   g_free(chrRetval);
 }
@@ -199,7 +202,7 @@ Gnome::Gda::Value glom_evaluate_python_function_implementation(Field::glom_field
   //We did this in main(): Py_Initialize();
 
   boost::python::object pMain = boost::python::import("__main__");
-   boost::python::object pDict(pMain.attr("__dict__")); //TODO: Does boost::python have an equivalent for PyModule_GetDict()?
+  boost::python::object pDict(pMain.attr("__dict__")); //TODO: Does boost::python have an equivalent for PyModule_GetDict()?
   //TODO: Complain that this doesn't work:
   //boost::python::dict pDict = pMain.attr("__dict__"); //TODO: Does boost::python have an equivalent for PyModule_GetDict()?
   if(!pDict)
@@ -267,35 +270,52 @@ Gnome::Gda::Value glom_evaluate_python_function_implementation(Field::glom_field
     HandlePythonError();
   }
   */
-  PyObject* pyValueC = PyRun_String(func_def.c_str(), Py_file_input, 
-    pDict.ptr(), pDict.ptr());
-  if(!pyValueC)
+  
+  //TODO: Complain that exec(std::string(something), pMain) doesn't work.
+  boost::python::object pyValue;
+  try
   {
-    std::cerr << "glom_evaluate_python_function_implementation(): PyRun_String returned null." << std::endl;
+    //TODO: The second dict is optional, and the documentation suggests using pMain as the first argument, but you'll get a 
+    //"TypeError: 'module' object does not support item assignment" error if you omit it.
+    //TODO: Make sure that's documented.
+    pyValue = boost::python::exec(func_def.c_str(), pDict, pDict);
+  }
+  catch(const boost::python::error_already_set& ex)
+  {
+    std::cerr << "glom_evaluate_python_function_implementation():  boost::python::exec() threw error_already_set when using text= " << std::endl << func_def << std::endl;
     ShowTrace();
     return valueResult;
   }
   
-  boost::python::handle<> handle(pyValueC);
-  boost::python::object pyValue(handle);
-  /*
-  if(!pyValue) //if(!pyValue.ptr()) is what we meant.
+  if(!pyValue.ptr())
   {
-    std::cerr << "glom_evaluate_python_function_implementation(): pyValue from PyRun_String() is null. ptr()=" << pyValue.ptr() << std::endl;
+    std::cerr << "glom_evaluate_python_function_implementation(): boost::python::exec failed." << std::endl;
     ShowTrace();
     return valueResult;
   }
-  */
 
   //Call the function:
   {
-    PyObject* pFunc = PyDict_GetItemString(pDict.ptr(), func_name.c_str()); //The result is borrowed, so should not be dereferenced.
-    if(!pFunc)
+    boost::python::object pFunc;
+    try
     {
+      pFunc = pDict[func_name.c_str()];
+    }
+    catch(const boost::python::error_already_set& ex)
+    {
+      std::cerr << "glom_evaluate_python_function_implementation():  pDict[func_name] threw error_already_set when func_name= " << std::endl << func_name << std::endl;
+      ShowTrace();
+      return valueResult;
+    }
+  
+    if(!pFunc.ptr())
+    {
+      std::cerr << "glom_evaluate_python_function_implementation(): pDict[func_name] failed." << std::endl;
       HandlePythonError();
+      return valueResult;
     }
 
-    if(!PyCallable_Check(pFunc))
+    if(!PyCallable_Check(pFunc.ptr()))
     {
       HandlePythonError();
       g_warning("pFunc is not callable.");
@@ -320,16 +340,12 @@ Gnome::Gda::Value glom_evaluate_python_function_implementation(Field::glom_field
       //Fill the record's details:
       PyGlomRecord_SetFields(pParam, field_values, pDocument, table_name, opened_connection);
 
-      PyObject* pArgs = PyTuple_New(1);
-      PyTuple_SetItem(pArgs, 0, (PyObject*)pParam); //The pParam reference is taken by PyTuple_SetItem().
-
       //Call the function with this parameter:
-      PyObject* pyResult = PyObject_CallObject(pFunc, pArgs);
-      Py_DECREF(pArgs);
+      boost::python::object pyResultCpp = pFunc(objRecord);
 
-      if(!pyResult)
+      if(!(pyResultCpp.ptr()))
       {
-        g_warning("pyResult was null");
+        g_warning("pyResult.ptr() was null");
         HandlePythonError();
       }
       else
@@ -338,7 +354,6 @@ Gnome::Gda::Value glom_evaluate_python_function_implementation(Field::glom_field
         bool object_is_gda_value = false;
 
         GValue value = {0, {{0}}};
-        boost::python::object pyResultCpp(boost::python::borrowed(pyResult));
         const bool test = glom_pygda_value_from_pyobject(&value, pyResultCpp);
 
         if(test)
@@ -359,7 +374,7 @@ Gnome::Gda::Value glom_evaluate_python_function_implementation(Field::glom_field
 
           //For instance, if one of the fields was empty, then the calculation might want to return an empty value,
           //instead of returning 0.
-          if(pyResult == Py_None) //Direct comparison is possible and recommended, because there is only one pyNone object.
+          if(pyResultCpp == boost::python::object()) //Check if it is PyNone
           {
             //The result should be an appropriate empty value for this field type:
             valueResult = Conversions::get_empty_value(result_type);
@@ -374,7 +389,7 @@ Gnome::Gda::Value glom_evaluate_python_function_implementation(Field::glom_field
             const char* pchResult = 0;
             try
             {
-              pchResult = boost::python::extract<const char*>(pyResult);
+              pchResult = boost::python::extract<const char*>(pyResultCpp);
             }
             catch(const boost::python::error_already_set& ex)
             {



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]