[glom] Use boost::python instead of the Python C API, simplifying the code.



commit c32655b7a37b91247bbbd672c14b62c277992777
Author: Murray Cumming <murrayc murrayc com>
Date:   Wed Feb 10 10:40:11 2010 +0100

    Use boost::python instead of the Python C API, simplifying the code.
    
    * configure.ac: Check for boost::python.
    * Makefile.am:
    * Makefile_glom.am: Use boost::python CFLAGS and LIBS.
    * macros/ax_boost_python.m4: Added this macro from
    http://www.nongnu.org/autoconf-archive/ax_boost_python.html
    and hacked it to make it actually work.
    
    * glom/python_embed/glom_python.cc: Use BOOST_PYTHON_MODULE() instead
    of the obscure Python C API structs and fields that were here and in
    the other .cc files.
    * glom/main.cc:
    * glom/libglom/python_embed/py_glom_record.[h|cc]
    * glom/libglom/python_embed/py_glom_related.[h|cc]
    * glom/libglom/python_embed/py_glom_relatedrecord.[h|cc]
    * glom/python_embed/python_module/py_glom_module.[h|cc]:
    * tests/test_python_execute_func.cc:
    Use boost::python, for instance via boost::python::object, to
    simplify memory management.
    
    * Makefile_libglom.am:
    * glom/libglom/python_embed/pygdavalue_conversions.[h|c]:
    Change this to a .cc file, using C++, using boost::python.

 ChangeLog                                          |   29 ++-
 Makefile.am                                        |    4 +-
 Makefile_glom.am                                   |    2 +-
 Makefile_libglom.am                                |    4 +-
 configure.ac                                       |    6 +
 glom/libglom/python_embed/py_glom_record.cc        |  295 ++----------
 glom/libglom/python_embed/py_glom_record.h         |   33 +-
 glom/libglom/python_embed/py_glom_related.cc       |  228 ++--------
 glom/libglom/python_embed/py_glom_related.h        |   22 +-
 glom/libglom/python_embed/py_glom_relatedrecord.cc |  500 ++++++--------------
 glom/libglom/python_embed/py_glom_relatedrecord.h  |   32 +-
 glom/libglom/python_embed/pygdavalue_conversions.c |  173 -------
 glom/libglom/python_embed/pygdavalue_conversions.h |   20 +-
 glom/main.cc                                       |    3 +-
 glom/python_embed/glom_python.cc                   |  207 +++++----
 glom/python_embed/python_module/py_glom_module.cc  |   69 ++--
 glom/python_embed/python_module/py_glom_module.h   |    1 -
 macros/ax_boost_python.m4                          |  121 +++++
 macros/mm-python.m4                                |    3 +
 tests/test_python_execute_func.cc                  |   23 +-
 20 files changed, 624 insertions(+), 1151 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 586810f..3e456a7 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,33 @@
+2010-02-10  Murray Cumming  <murrayc murrayc com>
+
+	Use boost::python instead of the Python C API, simplifying the code.
+
+	* configure.ac: Check for boost::python.
+	* Makefile.am:
+	* Makefile_glom.am: Use boost::python CFLAGS and LIBS.
+	* macros/ax_boost_python.m4: Added this macro from 
+	http://www.nongnu.org/autoconf-archive/ax_boost_python.html
+	and hacked it to make it actually work.
+
+	* glom/python_embed/glom_python.cc: Use BOOST_PYTHON_MODULE() instead 
+	of the obscure Python C API structs and fields that were here and in 
+	the other .cc files.
+	* glom/main.cc:
+	* glom/libglom/python_embed/py_glom_record.[h|cc]
+	* glom/libglom/python_embed/py_glom_related.[h|cc]
+	* glom/libglom/python_embed/py_glom_relatedrecord.[h|cc]
+	* glom/python_embed/python_module/py_glom_module.[h|cc]:
+	* tests/test_python_execute_func.cc:
+	Use boost::python, for instance via boost::python::object, to 
+	simplify memory management.
+
+	* Makefile_libglom.am: 
+	* glom/libglom/python_embed/pygdavalue_conversions.[h|c]:
+	Change this to a .cc file, using C++, using boost::python.
+
 2010-02-08  Peter Penz <ppenz openismus com>
 
-  libglom: Fix build issue for Maemo.
+	libglom: Fix build issue for Maemo.
   
 	* glom/libglom/data_structure/glomconversions.cc: get_text_for_gda_value():
 	Add an ifdef for the disabled-exceptions build.
diff --git a/Makefile.am b/Makefile.am
index 0a3375d..d1567b6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -63,7 +63,7 @@ glom_python_embed_python_module_glom_1_14_la_SOURCES =		\
 
 glom_python_embed_python_module_glom_1_14_la_LIBADD = \
 	glom/libglom/libglom-$(GLOM_ABI_VERSION).la \
-	$(LIBGLOM_LIBS) $(PYTHON_LIBS)
+	$(LIBGLOM_LIBS) $(PYTHON_LIBS) $(BOOST_PYTHON_LIBS)
 
 if HOST_WIN32
 pymod_ldflags = -module -avoid-version -no-undefined -shrext .pyd
@@ -175,7 +175,7 @@ glom_defines = \
 -DGLOM_LOCALEDIR=\""$(glom_localedir)"\" \
 -DGLOM_PKGDATADIR=\""$(pkgdatadir)"\"
 
-AM_CPPFLAGS = $(glom_includes) $(GLOM_CFLAGS) $(PYTHON_CPPFLAGS) $(glom_defines)
+AM_CPPFLAGS = $(glom_includes) $(GLOM_CFLAGS) $(PYTHON_CPPFLAGS) $(BOOST_PYTHON_CFLAGS) $(glom_defines)
 AM_CFLAGS   = $(GLOM_WFLAGS)
 AM_CXXFLAGS = $(GLOM_WXXFLAGS)
 
diff --git a/Makefile_glom.am b/Makefile_glom.am
index fcb4fe3..aea4a43 100644
--- a/Makefile_glom.am
+++ b/Makefile_glom.am
@@ -351,7 +351,7 @@ endif
 
 glom_glom_LDADD = $(win_resfile) \
 	glom/libglom/libglom-$(GLOM_ABI_VERSION).la \
-	$(GLOM_LIBS) $(PYTHON_LIBS) $(INTLLIBS)
+	$(GLOM_LIBS) $(PYTHON_LIBS) $(BOOST_PYTHON_LIBS) $(INTLLIBS)
 if !GLOM_ENABLE_MAEMO
 glom_glom_LDADD += -lgettextpo
 endif
diff --git a/Makefile_libglom.am b/Makefile_libglom.am
index 0868d98..a8aa118 100644
--- a/Makefile_libglom.am
+++ b/Makefile_libglom.am
@@ -148,7 +148,7 @@ glom_libglom_libglom_1_14_la_SOURCES =					\
 	glom/libglom/python_embed/py_glom_related.h			\
 	glom/libglom/python_embed/py_glom_relatedrecord.cc		\
 	glom/libglom/python_embed/py_glom_relatedrecord.h		\
-	glom/libglom/python_embed/pygdavalue_conversions.c		\
+	glom/libglom/python_embed/pygdavalue_conversions.cc		\
 	glom/libglom/python_embed/pygdavalue_conversions.h
 
 glom_libglom_libglom_1_14_la_SOURCES +=				\
@@ -167,7 +167,7 @@ glom_libglom_libglom_1_14_la_SOURCES +=				\
 	glom/libglom/connectionpool_backends/sqlite.cc		\
 	glom/libglom/connectionpool_backends/sqlite.h
 
-glom_libglom_libglom_1_14_la_LIBADD = $(LIBGLOM_LIBS) $(PYTHON_LIBS)
+glom_libglom_libglom_1_14_la_LIBADD = $(LIBGLOM_LIBS) $(PYTHON_LIBS) $(BOOST_PYTHON_LIBS)
 if HOST_WIN32
 glom_libglom_libglom_1_14_la_LIBADD += -lws2_32
 endif
diff --git a/configure.ac b/configure.ac
index 55dfaff..0b43566 100644
--- a/configure.ac
+++ b/configure.ac
@@ -200,6 +200,12 @@ AC_CHECK_FUNCS([strptime])
 # For instance: PYTHON=python2.5
 MM_CHECK_MODULE_PYTHON
 
+# Get the CFLAGS and LIBS for boost::python.
+# Note that we have hacked this script to work with MM_CHECK_MODULE_PYTHON instead of AX_PYTHON
+# This does an AC_SUBST() of BOOST_PYTHON_LIBS
+# For the CFLAGS we must assume that boost is at the top-level, for instance in /usr/include/:
+AX_BOOST_PYTHON
+
 AC_ARG_ENABLE([update-mime-database],
               [AS_HELP_STRING([--disable-update-mime-database],
                               [do not run the update-mime-database utility
diff --git a/glom/libglom/python_embed/py_glom_record.cc b/glom/libglom/python_embed/py_glom_record.cc
index 659054d..10b2460 100644
--- a/glom/libglom/python_embed/py_glom_record.cc
+++ b/glom/libglom/python_embed/py_glom_record.cc
@@ -19,10 +19,10 @@
  */
 
 //We need to include this before anything else, to avoid redefinitions:
-#include <Python.h>
-#include <compile.h> /* for the PyCodeObject */
-#include <eval.h> /* for PyEval_EvalCode */
-#include <objimpl.h> /* for PyObject_New() */
+//#include <Python.h>
+
+//#define NO_IMPORT_PYGOBJECT //To avoid a multiple definition in pygtk.
+#include <pygobject.h> //For the PyGObject and PyGBoxed struct definitions.
 
 #include <libglom/python_embed/py_glom_record.h>
 #include <libglom/python_embed/py_glom_related.h>
@@ -34,119 +34,46 @@
 namespace Glom
 {
 
-//Allocate a new object:
-//TODO: Why not parse the args here as well as in Record_init()?
-static PyObject *
-Record_new(PyTypeObject *type, PyObject * /* args */, PyObject * /* kwds */)
-{
-  PyGlomRecord *self  = (PyGlomRecord*)type->tp_alloc(type, 0);
-  if(self)
-  {
-    self->m_related = 0;
-
-    self->m_pMap_field_values = new PyGlomRecord::type_map_field_values();
-  }
-
-  return (PyObject*)self;
-}
-
 //Set the object's member data, from the parameters supplied when creating the object:
-static int
-Record_init(PyObject *self, PyObject * /* args */, PyObject * /* kwds */)
+PyGlomRecord::PyGlomRecord()
+: m_document(0)
 {
-  PyGlomRecord *self_record = (PyGlomRecord*)self;
-
-  //static char *kwlist[] = {"test", 0};
-
-  //if(!PyArg_ParseTupleAndKeywords(args, kwds, "|i", kwlist,
-   //                                 &self->m_test))
-   // return -1;
-
-  if(self_record)
-  {
-    self_record->m_related = 0;
-
-    if(self_record->m_pMap_field_values == 0)
-      self_record->m_pMap_field_values = new PyGlomRecord::type_map_field_values();
-  }
-
-  return 0;
 }
 
-static void
-Record_dealloc(PyObject* self)
+PyGlomRecord::~PyGlomRecord()
 {
-  PyGlomRecord *self_record = (PyGlomRecord*)self;
-
-  if(self_record->m_pMap_field_values)
-  {
-    delete self_record->m_pMap_field_values;
-    self_record->m_pMap_field_values = 0;
-  }
-
-  if(self_record->m_table_name)
-  {
-    delete self_record->m_table_name;
-    self_record->m_table_name = 0;
-  }
-
-  if(self_record->m_connection)
-  {
-    delete self_record->m_connection;
-    self_record->m_connection = 0;
-  }
-
-  self_record->ob_type->tp_free((PyObject*)self_record);
 }
 
-static PyObject *
-Record__get_connection(PyObject* self, void* /* closure */)
+std::string PyGlomRecord::get_table_name() const
 {
-  PyGlomRecord *self_record = (PyGlomRecord*)self;
-
-  if( !self_record->m_connection || !(*(self_record->m_connection)) )
-  {
-    Py_INCREF(Py_None);
-    return Py_None;
-  }
-  else
-  {
-   return pygobject_new( G_OBJECT( (*(self_record->m_connection))->gobj()) ); //Creates a pygda Connection object.
-  }
+  return m_table_name;
 }
 
-static PyObject *
-Record__get_table_name(PyObject* self, void* /* closure */)
+boost::python::object PyGlomRecord::get_connection()
 {
-  PyGlomRecord *self_record = (PyGlomRecord*)self;
-
-  if( !self_record->m_table_name || (*(self_record->m_table_name)).empty() )
+  boost::python::object result;
+  
+  if(m_connection)
   {
-    Py_INCREF(Py_None);
-    return Py_None;
-  }
-  else
-  {
-   const Glib::ustring& the_text = *(self_record->m_table_name); //Avoid that ugly dereference.
-   return PyString_FromString(the_text.c_str());
+    //Ask pygobject to create a PyObject* that wraps our GObject, 
+    //presumably using something from pygda:
+    PyObject* cobject = pygobject_new( G_OBJECT(m_connection->gobj()) );
+    result = boost::python::object( boost::python::borrowed(cobject) );
   }
+  
+  return result;
 }
 
-static PyObject *
-Record__get_related(PyObject* self, void* /* closure */)
+boost::python::object PyGlomRecord::get_related()
 {
-  PyGlomRecord *self_record = (PyGlomRecord*)self;
-
   //We initialize it here, so that this work never happens if it's not needed:
-  if(!(self_record->m_related))
+  if(!m_related)
   {
     //Return a new RelatedRecord:
-    PyObject* new_args = PyTuple_New(0);
-    self_record->m_related = (PyGlomRelated*)PyObject_Call((PyObject*)PyGlomRelated_GetPyType(), new_args, 0);
-    Py_DECREF(new_args);
+    m_related = boost::python::object(new PyGlomRelated()); //TODO_NotSure
 
     //Fill it:
-    Document::type_vec_relationships vecRelationships = self_record->m_document->get_relationships(*(self_record->m_table_name));
+    Document::type_vec_relationships vecRelationships = m_document->get_relationships(m_table_name);
     PyGlomRelated::type_map_relationships map_relationships;
     for(Document::type_vec_relationships::const_iterator iter = vecRelationships.begin(); iter != vecRelationships.end(); ++iter)
     {
@@ -154,178 +81,48 @@ Record__get_related(PyObject* self, void* /* closure */)
         map_relationships[(*iter)->get_name()] = *iter;
     }
 
-    PyGlomRelated_SetRelationships(self_record->m_related, map_relationships);
-
-    self_record->m_related->m_record = self_record;
-    Py_XINCREF(self_record); //unreffed in the self->m_related's _dealloc. //TODO: Is this a circular reference?
+    boost::python::extract<PyGlomRelated*> extractor(m_related);
+    if(extractor.check())
+    {
+      PyGlomRelated* related_cpp = extractor;
+      PyGlomRelated_SetRelationships(related_cpp, map_relationships);
+      related_cpp->m_record = boost::python::object(this); //TODO_NotSure
+    }
   }
 
-  Py_INCREF(self_record->m_related); //Should we do this?
-  return (PyObject*)self_record->m_related;
+  return m_related;
 }
 
-
-static PyGetSetDef Record_getseters[] = {
-    {(char*)"related",
-     (getter)Record__get_related, (setter)0, 0, 0
-    },
-    {(char*)"connection",
-     (getter)Record__get_connection, (setter)0, 0, 0
-    },
-    {(char*)"table_name",
-     (getter)Record__get_table_name, (setter)0, 0, 0
-    },
-    {0, 0, 0, 0, 0, }  // Sentinel
-};
-
-//Adapt to API changes in Python 2.5:
-#if defined(PY_VERSION_HEX) && (PY_VERSION_HEX >= 0x02050000) /* Python 2.5 */
-static Py_ssize_t
-Record_tp_as_mapping_length(PyObject *self)
-{
-  PyGlomRecord *self_record = (PyGlomRecord*)self;
-  return self_record->m_pMap_field_values->size();
-}
-#else
-static int
-Record_tp_as_mapping_length(PyObject *self)
+long PyGlomRecord::len() const
 {
-  PyGlomRecord *self_record = (PyGlomRecord*)self;
-  return (int)(self_record->m_pMap_field_values->size());
+  return m_map_field_values.size();
 }
-#endif
 
-static PyObject *
-Record_tp_as_mapping_getitem(PyObject *self, PyObject *item)
+boost::python::object PyGlomRecord::getitem(boost::python::object cppitem)
 {
-  PyGlomRecord *self_record = (PyGlomRecord*)self;
-
-  if(PyString_Check(item))
+  const std::string key = boost::python::extract<std::string>(cppitem);
+    
+  PyGlomRecord::type_map_field_values::const_iterator iterFind = m_map_field_values.find(key);
+  if(iterFind != m_map_field_values.end())
   {
-    const char* pchKey = PyString_AsString(item);
-    if(pchKey)
-    {
-      const Glib::ustring key(pchKey);
-      if(self_record && self_record->m_pMap_field_values)
-      {
-        PyGlomRecord::type_map_field_values::const_iterator iterFind = self_record->m_pMap_field_values->find(key);
-        if(iterFind != self_record->m_pMap_field_values->end())
-        {
-          return glom_pygda_value_as_pyobject(iterFind->second.gobj(), true /* copy */);
-        }
-        else
-        {
-          g_warning("Record_tp_as_mapping_getitem(): item not found in m_pMap_field_values. size=%d, item=%s", (int)self_record->m_pMap_field_values->size(), pchKey);
-        }
-      }
-      else
-      {
-        g_warning("Record_tp_as_mapping_getitem(): self or self->m_pMap_field_values is NULL.");
-      }
-    }
-    else
-    {
-       g_warning("Record_tp_as_mapping_getitem(): PyString_AsString(item) returned NULL.");
-    }
+    return glom_pygda_value_as_boost_pyobject(iterFind->second);
   }
-  else
-  {
-    g_warning("Record_tp_as_mapping_getitem(): PyString_Check(item) failed.");
-  }
-
-
-  g_warning("Record_tp_as_mapping_getitem(): return null.");
-  PyErr_SetString(PyExc_IndexError, "field not found");
-  return 0;
-}
 
-/*
-static int
-Record_tp_as_mapping_setitem(PyGObject *self, PyObject *item, PyObject *value)
-{
-  Py_INCREF(Py_None);
-  return Py_None;
-}
-*/
-
-static PyMappingMethods Record_tp_as_mapping = {
-    Record_tp_as_mapping_length,
-    Record_tp_as_mapping_getitem,
-    (objobjargproc)0 /* Record_tp_as_mapping_setitem */
-};
-
-
-static PyTypeObject pyglom_RecordType = {
-    PyObject_HEAD_INIT(0)
-    0,                         /*ob_size*/
-    (char*)"glom.Record",             /*tp_name*/
-    sizeof(PyGlomRecord), /*tp_basicsize*/
-    0,                         /*tp_itemsize*/
-    (destructor)Record_dealloc, /*tp_dealloc*/
-    0,                         /*tp_print*/
-    0,                         /*tp_getattr*/
-    0,                         /*tp_setattr*/
-    0,                         /*tp_compare*/
-    0,                         /*tp_repr*/
-    0,                         /*tp_as_number*/
-    0,                         /*tp_as_sequence*/
-    &Record_tp_as_mapping,      /*tp_as_mapping*/
-    0,                         /*tp_hash */
-    0,                         /*tp_call*/
-    0,                         /*tp_str*/
-    0,                         /*tp_getattro*/
-    0,                         /*tp_setattro*/
-    0,                         /*tp_as_buffer*/
-    Py_TPFLAGS_DEFAULT,        /*tp_flags*/
-    (char*)"Glom objects",           /* tp_doc */
-    0,                  /* tp_traverse */
-    0,                   /* tp_clear */
-    0,                   /* tp_richcompare */
-    0,                   /* tp_weaklistoffset */
-    0,                   /* tp_iter */
-    0,                   /* tp_iternext */
-    0 /* Record_methods */,             /* tp_methods */
-    0 /* Record_members */,             /* tp_members */
-    Record_getseters,                   /* tp_getset */
-    0,                         /* tp_base */
-    0,                         /* tp_dict */
-    0,                         /* tp_descr_get */
-    0,                         /* tp_descr_set */
-    0,                         /* tp_dictoffset */
-    (initproc)Record_init,      /* tp_init */
-    0,                         /* tp_alloc */
-    Record_new,                 /* tp_new */
-    0, 0, 0, 0, 0, 0, 0, 0, //TODO: or one more 0 if using a newer Python.
-};
-
-PyTypeObject* PyGlomRecord_GetPyType()
-{
-  return &pyglom_RecordType;
+  return boost::python::object();   
 }
 
-
-
-/*
-static void Record_HandlePythonError()
-{
-  if(PyErr_Occurred())
-    PyErr_Print();
-}
-*/
-
-
 void PyGlomRecord_SetFields(PyGlomRecord* self, const PyGlomRecord::type_map_field_values& field_values, Document* document, const Glib::ustring& table_name, const Glib::RefPtr<Gnome::Gda::Connection>& opened_connection)
 {
-  *(self->m_pMap_field_values) = field_values; //This was allocated in Record_new().
-
-  if(self->m_table_name == 0)
-    self->m_table_name = new Glib::ustring(table_name); //Deleted in Record_dealloc().
+  g_assert(self);
+  
+  self->m_map_field_values = field_values;
 
+  self->m_table_name = table_name;
+  
   if(self->m_document == 0)
     self->m_document = document;
 
-  if(self->m_connection == 0)
-    self->m_connection = new Glib::RefPtr<Gnome::Gda::Connection>(opened_connection);  //Deleted in Record_dealloc().
+  self->m_connection = opened_connection;
 
   /*
   if(self->m_fields_dict == 0)
diff --git a/glom/libglom/python_embed/py_glom_record.h b/glom/libglom/python_embed/py_glom_record.h
index f93a28f..f541768 100644
--- a/glom/libglom/python_embed/py_glom_record.h
+++ b/glom/libglom/python_embed/py_glom_record.h
@@ -21,10 +21,7 @@
 #ifndef GLOM_PYTHON_GLOM_RECORD_H
 #define GLOM_PYTHON_GLOM_RECORD_H
 
-#define NO_IMPORT_PYGOBJECT //To avoid a multiple definition in pygtk.
-#include <pygobject.h> //For the PyGObject and PyGBoxed struct definitions.
-
-#include <Python.h>
+#include <boost/python.hpp>
 
 #include <libglom/document/document.h>
 #include <libglom/data_structure/field.h>
@@ -35,28 +32,38 @@ namespace Glom
 
 class PyGlomRelated;
 
-struct PyGlomRecord
+class PyGlomRecord
 {
 public:
-  PyObject_HEAD
+  PyGlomRecord();
+  ~PyGlomRecord();
+
+  std::string get_table_name() const;
+
+  //TODO: Use a more specific type somehow?
+  boost::python::object get_connection();
+
+  boost::python::object get_related();
 
+  //[] notation:
+  long len() const;
+  boost::python::object getitem(boost::python::object item);
+
+public:
   //PyObject* m_fields_dict; //Dictionary (map) of field names (string) to field values (Gnome::Gda::Value).
   //PyGObject* m_py_gda_connection; //"derived" from PyObject.
   Document* m_document;
-  Glib::ustring* m_table_name;
+  Glib::ustring m_table_name;
 
-  PyGlomRelated* m_related;
+  boost::python::object m_related; //Actually a PyGlomRelated
 
   //Available, for instance, in python via record["name_first"]
   typedef std::map<Glib::ustring, Gnome::Gda::Value> type_map_field_values;
-  //We use a pointer because python will not run the class/struct's default constructor.
-  type_map_field_values* m_pMap_field_values;
+  type_map_field_values m_map_field_values;
 
-  Glib::RefPtr<Gnome::Gda::Connection>* m_connection;
+  Glib::RefPtr<Gnome::Gda::Connection> m_connection;
 };
 
-PyTypeObject* PyGlomRecord_GetPyType();
-
 void PyGlomRecord_SetFields(PyGlomRecord* self, const PyGlomRecord::type_map_field_values& field_values, Document* document, const Glib::ustring& table_name, const Glib::RefPtr<Gnome::Gda::Connection>& opened_connection);
 
 } //namespace Glom
diff --git a/glom/libglom/python_embed/py_glom_related.cc b/glom/libglom/python_embed/py_glom_related.cc
index 9d2a45c..24f9258 100644
--- a/glom/libglom/python_embed/py_glom_related.cc
+++ b/glom/libglom/python_embed/py_glom_related.cc
@@ -18,12 +18,6 @@
  * Boston, MA 02111-1307, USA.
  */
 
-//We need to include this before anything else, to avoid redefinitions:
-#include <Python.h>
-#include <compile.h> /* for the PyCodeObject */
-#include <eval.h> /* for PyEval_EvalCode */
-#include <objimpl.h> /* for PyObject_New() */
-
 #include <libglom/python_embed/py_glom_related.h>
 //#include <libglom/python_embed/py_glom_record.h>
 #include <libglom/python_embed/py_glom_relatedrecord.h>
@@ -36,154 +30,80 @@
 namespace Glom
 {
 
-//Allocate a new object:
-//TODO: Why not parse the args here as well as in Related_init()?
-static PyObject *
-Related_new(PyTypeObject *type, PyObject * /* args */, PyObject * /* kwds */)
+PyGlomRelated::PyGlomRelated()
 {
-  PyGlomRelated *self  = (PyGlomRelated*)type->tp_alloc(type, 0);
-  if(self)
-  {
-    self->m_record = 0;
-
-    self->m_pMap_relationships = new PyGlomRelated::type_map_relationships();
-    self->m_pMap_relatedrecords = new PyGlomRelated::type_map_relatedrecords();
-  }
-
-  return (PyObject*)self;
 }
 
-//Set the object's member data, from the parameters supplied when creating the object:
-static int
-Related_init(PyObject *self, PyObject* /* args */, PyObject* /* kwds */)
+PyGlomRelated::~PyGlomRelated()
 {
-  PyGlomRelated *self_related = (PyGlomRelated*)self;
-
-  if(self_related)
-  {
-    self_related->m_record = 0;
-
-    if(self_related->m_pMap_relationships == 0)
-      self_related->m_pMap_relationships = new PyGlomRelated::type_map_relationships();
-
-    if(self_related->m_pMap_relatedrecords == 0)
-      self_related->m_pMap_relatedrecords = new PyGlomRelated::type_map_relatedrecords();
-  }
-
-  return 0;
 }
 
-static void
-Related_dealloc(PyObject* self)
-{
-  PyGlomRelated *self_related = (PyGlomRelated*)self;
-
-  if(self_related->m_pMap_relationships)
-  {
-    delete self_related->m_pMap_relationships;
-    self_related->m_pMap_relationships = 0;
-  }
-
-  if(self_related->m_record)
-  {
-    Py_XDECREF( (PyObject*)self_related->m_record );
-    self_related->m_record = 0;
-  }
-
-  if(self_related->m_pMap_relatedrecords)
-  {
-    //Unref each item:
-    for(PyGlomRelated::type_map_relatedrecords::iterator iter = self_related->m_pMap_relatedrecords->begin(); iter != self_related->m_pMap_relatedrecords->end(); ++iter)
-    {
-      Py_XDECREF( (PyObject*)(iter->second) );
-    }
 
-    delete self_related->m_pMap_relatedrecords;
-    self_related->m_pMap_relatedrecords = 0;
-  }
-
-  self_related->ob_type->tp_free((PyObject*)self);
-}
-
-
-//Adapt to API changes in Python 2.5:
-#if defined(PY_VERSION_HEX) && (PY_VERSION_HEX >= 0x02050000) /* Python 2.5 */
-static Py_ssize_t
-Related_tp_as_mapping_length(PyObject *self)
+long PyGlomRelated::len() const
 {
-  PyGlomRelated *self_related = (PyGlomRelated*)self;
-  return self_related->m_pMap_relationships->size();
+  return m_map_relationships.size();
 }
-#else
-static int
-Related_tp_as_mapping_length(PyObject *self)
-{
-  PyGlomRelated *self_related = (PyGlomRelated*)self;
-  return (int)(self_related->m_pMap_relationships->size());
-}
-#endif
 
-static PyObject *
-Related_tp_as_mapping_getitem(PyObject *self, PyObject *item)
+boost::python::object PyGlomRelated::getitem(boost::python::object cppitem)
 {
-  PyGlomRelated *self_related = (PyGlomRelated*)self;
-
-  if(PyString_Check(item))
+  boost::python::extract<std::string> extractor(cppitem);
+  if(extractor.check())
   {
-    const char* pchKey = PyString_AsString(item);
-    if(pchKey)
+    const std::string key = extractor;
+    if(!key.empty())
     {
-      const Glib::ustring key(pchKey);
-
       //Return a cached item if possible:
-      PyGlomRelated::type_map_relatedrecords::iterator iterCacheFind = self_related->m_pMap_relatedrecords->find(key);
-      if(iterCacheFind != self_related->m_pMap_relatedrecords->end())
+      PyGlomRelated::type_map_relatedrecords::iterator iterCacheFind = m_map_relatedrecords.find(key);
+      if(iterCacheFind != m_map_relatedrecords.end())
       {
         //Return a reference to the cached item:
-        PyGlomRelatedRecord* pyRelatedRecord = iterCacheFind->second;
-        Py_INCREF((PyObject*)pyRelatedRecord);
-        return (PyObject*)pyRelatedRecord;
+        boost::python::object objectRelatedRecord = iterCacheFind->second;
+        return objectRelatedRecord;
       }
       else
       {
         //If the relationship exists:
-        PyGlomRelated::type_map_relationships::const_iterator iterFind = self_related->m_pMap_relationships->find(key);
-        if(iterFind != self_related->m_pMap_relationships->end())
+        PyGlomRelated::type_map_relationships::const_iterator iterFind = m_map_relationships.find(key);
+        if(iterFind != m_map_relationships.end())
         {
           //Return a new RelatedRecord:
-          PyObject* new_args = PyTuple_New(0);
-          PyGlomRelatedRecord* pyRelatedRecord = (PyGlomRelatedRecord*)PyObject_Call((PyObject*)PyGlomRelatedRecord_GetPyType(), new_args, 0);
-          Py_DECREF(new_args);
+          PyGlomRelatedRecord* pyRelatedRecord = new PyGlomRelatedRecord();
 
           //Fill it.
 
           //Get the value of the from_key in the parent record.
           sharedptr<Relationship> relationship = iterFind->second;
           const Glib::ustring from_key = relationship->get_from_field();
-          PyGlomRecord::type_map_field_values::const_iterator iterFromKey = self_related->m_record->m_pMap_field_values->find(from_key);
-          if(iterFromKey != self_related->m_record->m_pMap_field_values->end())
+          
+          boost::python::extract<PyGlomRecord*> extractor(m_record);
+          if(extractor.check())
           {
-            const Gnome::Gda::Value from_key_value = iterFromKey->second;
-
-            //TODO_Performance:
-            //Get the full field details so we can sqlize its value:
-            sharedptr<Field> from_key_field;
-            from_key_field = self_related->m_record->m_document->get_field(*(self_related->m_record->m_table_name), from_key);
-            if(from_key_field)
+            PyGlomRecord* record = extractor;
+            PyGlomRecord::type_map_field_values::const_iterator iterFromKey = record->m_map_field_values.find(from_key);
+            if(iterFromKey != record->m_map_field_values.end())
             {
-              Glib::ustring key_value_sqlized;
-              //std::cout << "from_key_field=" << from_key_field->get_name() << ", from_key_value=" << from_key_value.to_string() << std::endl;
+              const Gnome::Gda::Value from_key_value = iterFromKey->second;
+
+              //TODO_Performance:
+              //Get the full field details so we can sqlize its value:
+              sharedptr<Field> from_key_field;
+              from_key_field = record->m_document->get_field(record->m_table_name, from_key);
+              if(from_key_field)
+              {
+                Glib::ustring key_value_sqlized;
+                //std::cout << "from_key_field=" << from_key_field->get_name() << ", from_key_value=" << from_key_value.to_string() << std::endl;
 
-              if(!Conversions::value_is_empty(from_key_value)) //Do not link on null-values. That would cause us to link on 0, or "0".
-                key_value_sqlized = from_key_field->sql(from_key_value);
+                if(!Conversions::value_is_empty(from_key_value)) //Do not link on null-values. That would cause us to link on 0, or "0".
+                  key_value_sqlized = from_key_field->sql(from_key_value);
 
-              PyGlomRelatedRecord_SetRelationship(pyRelatedRecord, iterFind->second, key_value_sqlized, self_related->m_record->m_document);
+                PyGlomRelatedRecord_SetRelationship(pyRelatedRecord, iterFind->second, key_value_sqlized, record->m_document);
 
-              //Store it in the cache:
-              Py_INCREF((PyObject*)pyRelatedRecord); //Dereferenced in _dealloc().
-              (*(self_related->m_pMap_relatedrecords))[key] = pyRelatedRecord;
+                //Store it in the cache:
+                boost::python::object objectRelatedRecord(pyRelatedRecord);
+                m_map_relatedrecords[key] = objectRelatedRecord;
 
-              return (PyObject*)pyRelatedRecord; //TODO: pygda_value_as_pyobject(iterFind->second.gobj(), true /* copy */);
+                return objectRelatedRecord;
+              }
             }
           }
         }
@@ -192,72 +112,10 @@ Related_tp_as_mapping_getitem(PyObject *self, PyObject *item)
   }
 
   PyErr_SetString(PyExc_IndexError, "relationship not found");
-  return 0;
+  boost::python::throw_error_already_set(); //TODO: Find a simpler way to throw a python exception/error.
+  return boost::python::object();
 }
 
-/*
-static int
-Related_tp_as_mapping_setitem(PyGObject *self, PyObject *item, PyObject *value)
-{
-  Py_INCREF(Py_None);
-  return Py_None;
-}
-*/
-
-static PyMappingMethods Related_tp_as_mapping = {
-    Related_tp_as_mapping_length,
-    Related_tp_as_mapping_getitem,
-    (objobjargproc)0 /* Related_tp_as_mapping_setitem */
-};
-
-
-static PyTypeObject pyglom_RelatedType = {
-    PyObject_HEAD_INIT(0)
-    0,                         /*ob_size*/
-    (char*)"glom.Related",             /*tp_name*/
-    sizeof(PyGlomRelated), /*tp_basicsize*/
-    0,                         /*tp_itemsize*/
-    (destructor)Related_dealloc, /*tp_dealloc*/
-    0,                         /*tp_print*/
-    0,                         /*tp_getattr*/
-    0,                         /*tp_setattr*/
-    0,                         /*tp_compare*/
-    0,                         /*tp_repr*/
-    0,                         /*tp_as_number*/
-    0,                         /*tp_as_sequence*/
-    &Related_tp_as_mapping,                         /*tp_as_mapping*/
-    0,                         /*tp_hash */
-    0,                         /*tp_call*/
-    0,                         /*tp_str*/
-    0,                         /*tp_getattro*/
-    0,                         /*tp_setattro*/
-    0,                         /*tp_as_buffer*/
-    Py_TPFLAGS_DEFAULT,        /*tp_flags*/
-    (char*)"Glom objects",           /* tp_doc */
-    0,                  /* tp_traverse */
-    0,                   /* tp_clear */
-    0,                   /* tp_richcompare */
-    0,                   /* tp_weaklistoffset */
-    0,                   /* tp_iter */
-    0,                   /* tp_iternext */
-    0 /* Related_methods */,             /* tp_methods */
-    0 /* Related_members */,             /* tp_members */
-    0,                   /* tp_getset */
-    0,                         /* tp_base */
-    0,                         /* tp_dict */
-    0,                         /* tp_descr_get */
-    0,                         /* tp_descr_set */
-    0,                         /* tp_dictoffset */
-    (initproc)Related_init,      /* tp_init */
-    0,                         /* tp_alloc */
-    Related_new,                 /* tp_new */
-    0, 0, 0, 0, 0, 0, 0, 0, //TODO: or one more 0 if using a newer Python.
-};
-
-PyTypeObject* PyGlomRelated_GetPyType()
-{
-  return &pyglom_RelatedType;
-}
 
 /*
 static void Related_HandlePythonError()
@@ -270,7 +128,7 @@ static void Related_HandlePythonError()
 
 void PyGlomRelated_SetRelationships(PyGlomRelated* self, const PyGlomRelated::type_map_relationships& relationships)
 {
-  *(self->m_pMap_relationships) = relationships;
+  self->m_map_relationships = relationships;
 }
 
 } //namespace Glom
diff --git a/glom/libglom/python_embed/py_glom_related.h b/glom/libglom/python_embed/py_glom_related.h
index 1bbaf9f..65f8dde 100644
--- a/glom/libglom/python_embed/py_glom_related.h
+++ b/glom/libglom/python_embed/py_glom_related.h
@@ -28,23 +28,31 @@ namespace Glom
 {
 
 class PyGlomRelatedRecord;
+class PyGlomRecord;
 
 class PyGlomRelated
 {
 public:
-  PyObject_HEAD
+  PyGlomRelated();
+  ~PyGlomRelated();
 
-  PyGlomRecord* m_record; //A reference to the parent record.
+  //[] notation:
+  long len() const;
+  boost::python::object getitem(boost::python::object item);
+
+  friend class PyGlomRecord;
 
   typedef std::map<Glib::ustring, sharedptr<Relationship> > type_map_relationships;
-  type_map_relationships* m_pMap_relationships;
+  typedef std::map<Glib::ustring, boost::python::object /* Actually PyGlomRelatedRecord* */> type_map_relatedrecords;
 
-  typedef std::map<Glib::ustring, PyGlomRelatedRecord*> type_map_relatedrecords;
-  type_map_relatedrecords* m_pMap_relatedrecords;
-};
+//TODO: protected:
+  boost::python::object m_record; //Actually PyGlomRecord. A reference to the parent record.
 
-PyTypeObject* PyGlomRelated_GetPyType();
+ 
+  type_map_relationships m_map_relationships;
 
+  type_map_relatedrecords m_map_relatedrecords;
+};
 
 void PyGlomRelated_SetRelationships(PyGlomRelated* self, const PyGlomRelated::type_map_relationships& relationships);
 
diff --git a/glom/libglom/python_embed/py_glom_relatedrecord.cc b/glom/libglom/python_embed/py_glom_relatedrecord.cc
index d8edc81..4f2c9da 100644
--- a/glom/libglom/python_embed/py_glom_relatedrecord.cc
+++ b/glom/libglom/python_embed/py_glom_relatedrecord.cc
@@ -18,12 +18,6 @@
  * Boston, MA 02111-1307, USA.
  */
 
-//We need to include this before anything else, to avoid redefinitions:
-#include <Python.h>
-#include <compile.h> /* for the PyCodeObject */
-#include <eval.h> /* for PyEval_EvalCode */
-#include <objimpl.h> /* for PyObject_New() */
-
 #include <libglom/python_embed/py_glom_relatedrecord.h>
 #include <libglom/python_embed/py_glom_record.h>
 #include <libglom/python_embed/pygdavalue_conversions.h> //For pygda_value_as_pyobject().
@@ -37,125 +31,14 @@
 namespace Glom
 {
 
-//Allocate a new object:
-//TODO: Why not parse the args here as well as in RelatedRecord_init()?
-static PyObject *
-RelatedRecord_new(PyTypeObject *type, PyObject * /* args */, PyObject * /* kwds */)
+PyGlomRelatedRecord::PyGlomRelatedRecord()
 {
-  PyGlomRelatedRecord *self  = (PyGlomRelatedRecord*)type->tp_alloc(type, 0);
-  if(self)
-  {
-    self->m_py_gda_connection = 0;
-    self->m_document = 0;
-    self->m_relationship = 0;
-    self->m_from_key_value_sqlized = 0;
-
-    //self->m_record_parent = 0;
-
-    self->m_pMap_field_values = new PyGlomRelatedRecord::type_map_field_values();
-  }
-
-  return (PyObject*)self;
 }
 
-//Set the object's member data, from the parameters supplied when creating the object:
-static int
-RelatedRecord_init(PyGlomRelatedRecord *self, PyObject * /* args */, PyObject * /* kwds */)
+PyGlomRelatedRecord::~PyGlomRelatedRecord()
 {
-  //static char *kwlist[] = {"test", 0};
-
-  //if(!PyArg_ParseTupleAndKeywords(args, kwds, "|i", kwlist,
-   //                                 &self->m_test))
-   // return -1;
-
-  if(self)
-  {
-    self->m_py_gda_connection = 0;
-    self->m_document = 0;
-    self->m_relationship = 0;
-    self->m_from_key_value_sqlized = 0;
-
-    //self->m_record_parent = 0;
-
-    if(self->m_pMap_field_values == 0)
-      self->m_pMap_field_values = new PyGlomRelatedRecord::type_map_field_values();
-  }
-
-  return 0;
 }
 
-static void
-RelatedRecord_dealloc(PyGlomRelatedRecord* self)
-{
-  if(self->m_pMap_field_values)
-  {
-    delete self->m_pMap_field_values;
-    self->m_pMap_field_values = 0;
-  }
-
-  if(self->m_relationship)
-  {
-    delete self->m_relationship;
-    self->m_relationship = 0;
-  }
-
-  if(self->m_from_key_value_sqlized)
-  {
-    delete self->m_from_key_value_sqlized;
-    self->m_from_key_value_sqlized = 0;
-  }
-
-  if(self->m_py_gda_connection)
-  {
-    Py_XDECREF( (PyObject*)(self->m_py_gda_connection));
-    self->m_py_gda_connection = 0;
-  }
-
-  self->ob_type->tp_free((PyObject*)self);
-}
-
-/*
-static PyObject *
-RelatedRecord__get_fields(PyGlomRelatedRecord *self, void * closure )
-{
-  if(self->m_fields_dict)
-  {
-    Py_INCREF(self->m_fields_dict); //TODO: Should we do this?
-    return self->m_fields_dict;
-  }
-  else
-  {
-    Py_INCREF(Py_None);
-    return Py_None;
-  }
-}
-*/
-
-/*
-static PyGetSetDef RelatedRecord_getseters[] = {
-    {"fields",
-     (getter)RelatedRecord__get_fields, (setter)0, 0, 0
-    },
-    {0, 0, 0, 0, 0, }  // Sentinel
-};
-*/
-
-//Adapt to API changes in Python 2.5:
-#if defined(PY_VERSION_HEX) && (PY_VERSION_HEX >= 0x02050000) /* Python 2.5 */
-static Py_ssize_t
-RelatedRecord_tp_as_mapping_length(PyObject *self)
-{
-  PyGlomRelatedRecord* self_derived = (PyGlomRelatedRecord*)self;
-  return self_derived->m_pMap_field_values->size();
-}
-#else
-static int
-RelatedRecord_tp_as_mapping_length(PyObject *self)
-{
-  PyGlomRelatedRecord* self_derived = (PyGlomRelatedRecord*)self;
-  return (int)(self_derived->m_pMap_field_values->size());
-}
-#endif
 
 static void RelatedRecord_HandlePythonError()
 {
@@ -163,152 +46,30 @@ static void RelatedRecord_HandlePythonError()
     PyErr_Print();
 }
 
-static PyObject *
-RelatedRecord_tp_as_mapping_getitem(PyObject *self, PyObject *item)
-{
-  PyGlomRelatedRecord* self_derived = (PyGlomRelatedRecord*)self;
-
-  if(PyString_Check(item))
-  {
-    const char* pchKey = PyString_AsString(item);
-    if(pchKey)
-    {
-      const Glib::ustring field_name(pchKey);
-      PyGlomRelatedRecord::type_map_field_values::const_iterator iterFind = self_derived->m_pMap_field_values->find(field_name);
-      if(iterFind != self_derived->m_pMap_field_values->end())
-      {
-        //If the value has already been stored, then just return it again:
-        return glom_pygda_value_as_pyobject(iterFind->second.gobj(), true /* copy */);
-      }
-      else
-      {
-         const Glib::ustring related_table = (*(self_derived->m_relationship))->get_to_table();
-
-        //Check whether the field exists in the table.
-        //TODO_Performance: Do this without the useless Field information?
-        sharedptr<Field> field = self_derived->m_document->get_field((*(self_derived->m_relationship))->get_to_table(), field_name);
-        if(!field)
-          g_warning("RelatedRecord_tp_as_mapping_getitem: field %s not found in table %s", field_name.c_str(), (*(self_derived->m_relationship))->get_to_table().c_str());
-        else
-        {
-          //Try to get the value from the database:
-          //const Glib::ustring parent_key_name;
-#ifdef GLIBMM_EXCEPTIONS_ENABLED
-          sharedptr<SharedConnection> sharedconnection = ConnectionPool::get_instance()->connect();
-#else
-          std::auto_ptr<ExceptionConnection> conn_error;
-          sharedptr<SharedConnection> sharedconnection = ConnectionPool::get_instance()->connect(conn_error);
-          // Ignore error, sharedconnection presence is checked below
-#endif
-          if(sharedconnection)
-          {
-            Glib::RefPtr<Gnome::Gda::Connection> gda_connection = sharedconnection->get_gda_connection();
-
-            const Glib::ustring related_key_name = (*(self_derived->m_relationship))->get_to_field();
-
-            //Do not try to get a value based on a null key value:
-            if(!(self_derived->m_from_key_value_sqlized))
-              return Py_None;
-       
-            //Get the single value from the related records:
-            Glib::ustring sql_query = "SELECT \"" + related_table + "\".\"" + field_name + "\" FROM \"" + related_table + "\""
-              + " WHERE \"" + related_table + "\".\"" + related_key_name + "\" = " + *(self_derived->m_from_key_value_sqlized);
-
-            /* TODO: Fix linking problems
-            const App_Glom* app = App_Glom::get_application();
-            if(app && app->get_show_sql_debug())
-            {
-              try
-              {
-                std::cout << "Debug: RelatedRecord_tp_as_mapping_getitem():  " << sql_query << std::endl;
-              }
-              catch(const Glib::Exception& ex)
-              {
-                std::cout << "Debug: query string could not be converted to std::cout: " << ex.what() << std::endl;
-              }
-            }*/
-
-#ifdef GLIBMM_EXCEPTIONS_ENABLED
-            // TODO: Does this behave well if this throws an exception?
-            Glib::RefPtr<Gnome::Gda::DataModel> datamodel = gda_connection->statement_execute_select(sql_query);
-#else
-            std::auto_ptr<Glib::Error> error;
-            Glib::RefPtr<Gnome::Gda::DataModel> datamodel =  gda_connection->statement_execute_select(sql_query, Gnome::Gda::STATEMENT_MODEL_RANDOM_ACCESS, error);
-            // Ignore error, datamodel return value is checked below
-#endif
-            if(datamodel && datamodel->get_n_rows())
-            {
-#ifdef GLIBMM_EXCEPTIONS_ENABLED            
-              Gnome::Gda::Value value = datamodel->get_value_at(0, 0);
-#else
-              Gnome::Gda::Value value = datamodel->get_value_at(0, 0, error);
-#endif                            
-              //g_warning("RelatedRecord_tp_as_mapping_getitem(): value from datamodel = %s", value.to_string().c_str());
-
-              //Cache it, in case it's asked-for again.
-              (*(self_derived->m_pMap_field_values))[field_name] = value;
-              return glom_pygda_value_as_pyobject(value.gobj(), true /* copy */);
-            }
-            else if(!datamodel)
-            {
-              g_warning("RelatedRecord_tp_as_mapping_getitem(): The datamodel was null.");
-              ConnectionPool::handle_error_cerr_only();
-              RelatedRecord_HandlePythonError();
-            }
-            else
-            {
-              g_warning("RelatedRecord_tp_as_mapping_getitem(): No related records exist yet for relationship %s.",  (*(self_derived->m_relationship))->get_name().c_str());
-            }
-          }
-        }
-      }
-    }
-  }
-
-  g_warning("RelatedRecord_tp_as_mapping_getitem(): return null.");
-  PyErr_SetString(PyExc_IndexError, "field not found");
-  return 0;
-}
-
-/*
-static int
-RelatedRecord_tp_as_mapping_setitem(PyGObject *self, PyObject *item, PyObject *value)
+long PyGlomRelatedRecord::len() const
 {
-  Py_INCREF(Py_None);
-  return Py_None;
+  return m_map_field_values.size();
 }
-*/
-
-static PyMappingMethods RelatedRecord_tp_as_mapping = {
-    RelatedRecord_tp_as_mapping_length,
-    RelatedRecord_tp_as_mapping_getitem,
-    (objobjargproc)0 /* RelatedRecord_tp_as_mapping_setitem */
-};
 
-static PyObject *
-RelatedRecord_generic_aggregate(PyGlomRelatedRecord* self, PyObject *args, PyObject *kwargs, const Glib::ustring& aggregate)
+boost::python::object PyGlomRelatedRecord::getitem(boost::python::object cppitem)
 {
-  typedef const char* type_pch;
-  static type_pch kwlist[] = { "field_name", 0 };
-  PyObject* py_field_name = 0;
-
-  if(!PyArg_ParseTupleAndKeywords(args, kwargs, (char*)"O:RelatedRecord.sum", (char**)kwlist, &py_field_name))
-    return 0;
-
-  if(!(PyString_Check(py_field_name)))
-    return 0;
-
-  const char* pchKey = PyString_AsString(py_field_name);
-  if(pchKey)
+  const std::string field_name = boost::python::extract<std::string>(cppitem);
+ 
+  PyGlomRelatedRecord::type_map_field_values::const_iterator iterFind = m_map_field_values.find(field_name);
+  if(iterFind != m_map_field_values.end())
+  {
+    //If the value has already been stored, then just return it again:
+    return glom_pygda_value_as_boost_pyobject(iterFind->second);
+  }
+  else
   {
-    const Glib::ustring field_name(pchKey);
-    const Glib::ustring related_table = (*(self->m_relationship))->get_to_table();
+    const Glib::ustring related_table = m_relationship->get_to_table();
 
     //Check whether the field exists in the table.
     //TODO_Performance: Do this without the useless Field information?
-    sharedptr<Field> field = self->m_document->get_field((*(self->m_relationship))->get_to_table(), field_name);
+    sharedptr<Field> field = m_document->get_field(m_relationship->get_to_table(), field_name);
     if(!field)
-      g_warning("RelatedRecord_sum: field %s not found in table %s", field_name.c_str(), (*(self->m_relationship))->get_to_table().c_str());
+      g_warning("RelatedRecord_tp_as_mapping_getitem: field %s not found in table %s", field_name.c_str(), m_relationship->get_to_table().c_str());
     else
     {
       //Try to get the value from the database:
@@ -324,161 +85,174 @@ RelatedRecord_generic_aggregate(PyGlomRelatedRecord* self, PyObject *args, PyObj
       {
         Glib::RefPtr<Gnome::Gda::Connection> gda_connection = sharedconnection->get_gda_connection();
 
-        const Glib::ustring related_key_name = (*(self->m_relationship))->get_to_field();
+        const Glib::ustring related_key_name = m_relationship->get_to_field();
 
         //Do not try to get a value based on a null key value:
-        if(!(self->m_from_key_value_sqlized))
-          return Py_None;
+        if(m_from_key_value_sqlized.empty())
+          return boost::python::object();
        
-        //Get the aggregate value from the related records:
-        const Glib::ustring sql_query = "SELECT " + aggregate + "(\"" + related_table + "\".\"" + field_name + "\") FROM \"" + related_table + "\""
-          + " WHERE \"" + related_table + "\".\"" + related_key_name + "\" = " + *(self->m_from_key_value_sqlized);
-        
-        //std::cout << "PyGlomRelatedRecord: Executing:  " << sql_query << std::endl;
+        //Get the single value from the related records:
+        const Glib::ustring sql_query = "SELECT \"" + related_table + "\".\"" + field_name + "\" FROM \"" + related_table + "\""
+          + " WHERE \"" + related_table + "\".\"" + related_key_name + "\" = " + m_from_key_value_sqlized;
+
+        /* TODO: Fix linking problems
+        const App_Glom* app = App_Glom::get_application();
+        if(app && app->get_show_sql_debug())
+        {
+          try
+          {
+            std::cout << "Debug: RelatedRecord_tp_as_mapping_getitem():  " << sql_query << std::endl;
+          }
+          catch(const Glib::Exception& ex)
+          {
+            std::cout << "Debug: query string could not be converted to std::cout: " << ex.what() << std::endl;
+          }
+        }*/
+
 #ifdef GLIBMM_EXCEPTIONS_ENABLED
+        // TODO: Does this behave well if this throws an exception?
         Glib::RefPtr<Gnome::Gda::DataModel> datamodel = gda_connection->statement_execute_select(sql_query);
 #else
         std::auto_ptr<Glib::Error> error;
-        Glib::RefPtr<Gnome::Gda::DataModel> datamodel = gda_connection->statement_execute_select(sql_query, Gnome::Gda::STATEMENT_MODEL_RANDOM_ACCESS, error);
-
-        // Ignore the error: The case that the command execution didn't return
-        // a datamodel is handled below.
+        Glib::RefPtr<Gnome::Gda::DataModel> datamodel =  gda_connection->statement_execute_select(sql_query, Gnome::Gda::STATEMENT_MODEL_RANDOM_ACCESS, error);
+        // Ignore error, datamodel return value is checked below
 #endif
         if(datamodel && datamodel->get_n_rows())
         {
-#ifdef GLIBMM_EXCEPTIONS_ENABLED        
+#ifdef GLIBMM_EXCEPTIONS_ENABLED            
           Gnome::Gda::Value value = datamodel->get_value_at(0, 0);
 #else
           Gnome::Gda::Value value = datamodel->get_value_at(0, 0, error);
-#endif          
-          //g_warning("RelatedRecord_generic_aggregate(): value from datamodel = %s", value.to_string().c_str());
+#endif                            
+          //g_warning("RelatedRecord_tp_as_mapping_getitem(): value from datamodel = %s", value.to_string().c_str());
 
           //Cache it, in case it's asked-for again.
-          (*(self->m_pMap_field_values))[field_name] = value;
-          return glom_pygda_value_as_pyobject(value.gobj(), true /* copy */);
+          m_map_field_values[field_name] = value;
+          return glom_pygda_value_as_boost_pyobject(value);
         }
         else if(!datamodel)
         {
-          g_warning("RelatedRecord_generic_aggregate(): The datamodel was null.");
+          g_warning("RelatedRecord_tp_as_mapping_getitem(): The datamodel was null.");
           ConnectionPool::handle_error_cerr_only();
           RelatedRecord_HandlePythonError();
         }
         else
         {
-          g_warning("RelatedRecord_generic_aggregate(): No related records exist yet for relationship %s.",  (*(self->m_relationship))->get_name().c_str());
+          g_warning("RelatedRecord_tp_as_mapping_getitem(): No related records exist yet for relationship %s.",  m_relationship->get_name().c_str());
         }
       }
     }
   }
 
-  Py_INCREF(Py_None);
-  return Py_None;
+  g_warning("RelatedRecord_tp_as_mapping_getitem(): return null.");
+  PyErr_SetString(PyExc_IndexError, "field not found");
+  return boost::python::object();
 }
 
-static PyObject *
-RelatedRecord_sum(PyObject* self, PyObject *args, PyObject *kwargs)
+boost::python::object PyGlomRelatedRecord::generic_aggregate(const std::string& field_name, const std::string& aggregate) const
 {
-  PyGlomRelatedRecord* self_derived = (PyGlomRelatedRecord*)self;
-  return RelatedRecord_generic_aggregate(self_derived, args, kwargs, "sum");
+  const Glib::ustring related_table = m_relationship->get_to_table();
+
+  //Check whether the field exists in the table.
+  //TODO_Performance: Do this without the useless Field information?
+  sharedptr<Field> field = m_document->get_field(m_relationship->get_to_table(), field_name);
+  if(!field)
+  {
+    g_warning("RelatedRecord_sum: field %s not found in table %s", field_name.c_str(), m_relationship->get_to_table().c_str());
+    return boost::python::object();
+  }
+
+  //Try to get the value from the database:
+  //const Glib::ustring parent_key_name;
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+  sharedptr<SharedConnection> sharedconnection = ConnectionPool::get_instance()->connect();
+#else
+  std::auto_ptr<ExceptionConnection> conn_error;
+  sharedptr<SharedConnection> sharedconnection = ConnectionPool::get_instance()->connect(conn_error);
+  // Ignore error, sharedconnection presence is checked below
+#endif
+  if(!sharedconnection)
+  {
+    g_warning("RelatedRecord_sum: no connection.");
+    return boost::python::object();
+  }
+  
+  Glib::RefPtr<Gnome::Gda::Connection> gda_connection = sharedconnection->get_gda_connection();
+
+  const Glib::ustring related_key_name = m_relationship->get_to_field();
+
+  //Do not try to get a value based on a null key value:
+  if(m_from_key_value_sqlized.empty())
+  {
+    return boost::python::object();
+  }
+       
+  //Get the aggregate value from the related records:
+  const Glib::ustring sql_query = "SELECT " + aggregate + "(\"" + related_table + "\".\"" + field_name + "\") FROM \"" + related_table + "\""
+    + " WHERE \"" + related_table + "\".\"" + related_key_name + "\" = " + m_from_key_value_sqlized;
+        
+  //std::cout << "PyGlomRelatedRecord: Executing:  " << sql_query << std::endl;
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+  Glib::RefPtr<Gnome::Gda::DataModel> datamodel = gda_connection->statement_execute_select(sql_query);
+#else
+  std::auto_ptr<Glib::Error> error;
+  Glib::RefPtr<Gnome::Gda::DataModel> datamodel = gda_connection->statement_execute_select(sql_query, Gnome::Gda::STATEMENT_MODEL_RANDOM_ACCESS, error);
+
+  // Ignore the error: The case that the command execution didn't return
+  // a datamodel is handled below.
+#endif
+  if(datamodel && datamodel->get_n_rows())
+  {
+#ifdef GLIBMM_EXCEPTIONS_ENABLED        
+    Gnome::Gda::Value value = datamodel->get_value_at(0, 0);
+#else
+    Gnome::Gda::Value value = datamodel->get_value_at(0, 0, error);
+#endif          
+    //g_warning("RelatedRecord_generic_aggregate(): value from datamodel = %s", value.to_string().c_str());
+
+    //Cache it, in case it's asked-for again.
+    m_map_field_values[field_name] = value;
+    return glom_pygda_value_as_boost_pyobject(value);
+  }
+  else if(!datamodel)
+  {
+    g_warning("RelatedRecord_generic_aggregate(): The datamodel was null.");
+    ConnectionPool::handle_error_cerr_only();
+    RelatedRecord_HandlePythonError();
+  }
+  else
+  {
+    g_warning("RelatedRecord_generic_aggregate(): No related records exist yet for relationship %s.",  m_relationship->get_name().c_str());
+  }
+
+  return boost::python::object();
 }
 
-static PyObject *
-RelatedRecord_count(PyObject* self, PyObject *args, PyObject *kwargs)
+boost::python::object PyGlomRelatedRecord::sum(const std::string& field_name) const
 {
-  PyGlomRelatedRecord* self_derived = (PyGlomRelatedRecord*)self;
-  return RelatedRecord_generic_aggregate(self_derived, args, kwargs, "count");
+  return generic_aggregate(field_name, "sum");
 }
 
-static PyObject *
-RelatedRecord_min(PyObject* self, PyObject *args, PyObject *kwargs)
+boost::python::object PyGlomRelatedRecord::count(const std::string& field_name) const
 {
-  PyGlomRelatedRecord* self_derived = (PyGlomRelatedRecord*)self;
-  return RelatedRecord_generic_aggregate(self_derived, args, kwargs, "min");
+  return generic_aggregate(field_name, "count");
 }
 
-static PyObject *
-RelatedRecord_max(PyObject* self, PyObject *args, PyObject *kwargs)
+boost::python::object PyGlomRelatedRecord::min(const std::string& field_name) const
 {
-  PyGlomRelatedRecord* self_derived = (PyGlomRelatedRecord*)self;
-  return RelatedRecord_generic_aggregate(self_derived, args, kwargs, "max");
+  return generic_aggregate(field_name, "min");
 }
 
-static PyMethodDef RelatedRecord_methods[] = {
-    {(char*)"sum", (PyCFunction)RelatedRecord_sum, METH_VARARGS | METH_KEYWORDS,
-     (char*)"Add all values of the field in the related records."
-    },
-    {(char*)"count", (PyCFunction)RelatedRecord_count, METH_VARARGS | METH_KEYWORDS,
-     (char*)"Count all values in the field in the related records."
-    },
-    {(char*)"min", (PyCFunction)RelatedRecord_min, METH_VARARGS | METH_KEYWORDS,
-     (char*)"Minimum of all values of the field in the related records."
-    },
-    {(char*)"max", (PyCFunction)RelatedRecord_max, METH_VARARGS | METH_KEYWORDS,
-     (char*)"Maximum of all values of the field in the related records."
-    },
-    {0, 0, 0, 0}  /* Sentinel */
-};
-
-
-
-
-static PyTypeObject pyglom_RelatedRecordType = {
-    PyObject_HEAD_INIT(0)
-    0,                         /*ob_size*/
-    (char*)"glom.RelatedRecord",             /*tp_name*/
-    sizeof(PyGlomRelatedRecord), /*tp_basicsize*/
-    0,                         /*tp_itemsize*/
-    (destructor)RelatedRecord_dealloc, /*tp_dealloc*/
-    0,                         /*tp_print*/
-    0,                         /*tp_getattr*/
-    0,                         /*tp_setattr*/
-    0,                         /*tp_compare*/
-    0,                         /*tp_repr*/
-    0,                         /*tp_as_number*/
-    0,                         /*tp_as_sequence*/
-    &RelatedRecord_tp_as_mapping,      /*tp_as_mapping*/
-    0,                         /*tp_hash */
-    0,                         /*tp_call*/
-    0,                         /*tp_str*/
-    0,                         /*tp_getattro*/
-    0,                         /*tp_setattro*/
-    0,                         /*tp_as_buffer*/
-    Py_TPFLAGS_DEFAULT,        /*tp_flags*/
-    (char*)"Glom objects",           /* tp_doc */
-    0,                  /* tp_traverse */
-    0,                   /* tp_clear */
-    0,                   /* tp_richcompare */
-    0,                   /* tp_weaklistoffset */
-    0,                   /* tp_iter */
-    0,                   /* tp_iternext */
-    RelatedRecord_methods,             /* tp_methods */
-    0 /* RelatedRecord_members */,             /* tp_members */
-    0, /* RelatedRecord_getseters, */                   /* tp_getset */
-    0,                         /* tp_base */
-    0,                         /* tp_dict */
-    0,                         /* tp_descr_get */
-    0,                         /* tp_descr_set */
-    0,                         /* tp_dictoffset */
-    (initproc)RelatedRecord_init,      /* tp_init */
-    0,                         /* tp_alloc */
-    RelatedRecord_new,                 /* tp_new */
-    0, 0, 0, 0, 0, 0, 0, 0, //TODO: or one more 0 if using a newer Python.
-};
-
-PyTypeObject* PyGlomRelatedRecord_GetPyType()
+boost::python::object PyGlomRelatedRecord::max(const std::string& field_name) const
 {
-  return &pyglom_RelatedRecordType;
+  return generic_aggregate(field_name, "max");
 }
 
-
 void PyGlomRelatedRecord_SetRelationship(PyGlomRelatedRecord* self, const sharedptr<const Relationship>& relationship, const Glib::ustring& from_key_value_sqlized,  Document* document)
 {
-  self->m_relationship = new sharedptr<const Relationship>(relationship);
+  self->m_relationship = relationship;
 
-  if(!from_key_value_sqlized.empty())
-    self->m_from_key_value_sqlized = new Glib::ustring(from_key_value_sqlized);
-  else
-    self->m_from_key_value_sqlized = 0;
+  self->m_from_key_value_sqlized = from_key_value_sqlized;
 
   self->m_document = document;
 }
diff --git a/glom/libglom/python_embed/py_glom_relatedrecord.h b/glom/libglom/python_embed/py_glom_relatedrecord.h
index 3de3bc9..caee847 100644
--- a/glom/libglom/python_embed/py_glom_relatedrecord.h
+++ b/glom/libglom/python_embed/py_glom_relatedrecord.h
@@ -21,8 +21,7 @@
 #ifndef GLOM_PYTHON_GLOM_RELATEDRECORD_H
 #define GLOM_PYTHON_GLOM_RELATEDRECORD_H
 
-#define NO_IMPORT_PYGOBJECT //To avoid a multiple definition in pygtk.
-#include <pygobject.h> //For the PyGObject and PyGBoxed struct definitions.
+#include <boost/python.hpp>
 
 #include <libglom/document/document.h>
 #include <libglom/data_structure/field.h>
@@ -33,28 +32,37 @@ namespace Glom
 
 class PyGlomRecord;
 
-struct PyGlomRelatedRecord
+class PyGlomRelatedRecord
 {
 public:
-  PyObject_HEAD
+  PyGlomRelatedRecord();
+  ~PyGlomRelatedRecord();
+
+  boost::python::object sum(const std::string& field_name) const;
+  boost::python::object count(const std::string& field_name) const;
+  boost::python::object min(const std::string& field_name) const;
+  boost::python::object max(const std::string& field_name) const;
+
+  //[] notation:
+  long len() const;
+  boost::python::object getitem(boost::python::object item);
+
+//TODO: protected:
+
+  boost::python::object generic_aggregate(const std::string& field_name, const std::string& aggregate) const;
 
   //PyObject* m_fields_dict; //Dictionary (map) of field names (string) to field values (Gnome::Gda::Value).
-  PyGObject* m_py_gda_connection; //"derived" from PyObject.
   //PyGlomRecord* m_record_parent;
   Document* m_document;
 
-  sharedptr<const Relationship>* m_relationship;
-  Glib::ustring* m_from_key_value_sqlized;
+  sharedptr<const Relationship> m_relationship;
+  Glib::ustring m_from_key_value_sqlized;
 
   //Available, for instance, in python via record["name_first"]
   typedef std::map<Glib::ustring, Gnome::Gda::Value> type_map_field_values;
-  //We use a pointer because python will not run the class/struct's default constructor.
-  type_map_field_values* m_pMap_field_values; 
+  mutable type_map_field_values m_map_field_values; //A cache.
 };
 
-PyTypeObject* PyGlomRelatedRecord_GetPyType();
-
-
 void PyGlomRelatedRecord_SetRelationship(PyGlomRelatedRecord* self, const sharedptr<const Relationship>& relationship, const Glib::ustring& from_key_value_sqlized, Document* document);
 
 /*
diff --git a/glom/libglom/python_embed/pygdavalue_conversions.h b/glom/libglom/python_embed/pygdavalue_conversions.h
index 8ce78bb..e2e2780 100644
--- a/glom/libglom/python_embed/pygdavalue_conversions.h
+++ b/glom/libglom/python_embed/pygdavalue_conversions.h
@@ -1,17 +1,19 @@
-#include <pygobject.h>
-#include <libgda/libgda.h>
-
 #ifndef GLOM_PYGDA_VALUE_CONVERSIONS_H
 #define GLOM_PYGDA_VALUE_CONVERSIONS_H
 
-G_BEGIN_DECLS
+//We need to include this before anything else, to avoid redefinitions:
+#include <Python.h>
+
+#include <glibmm/value.h>
+#include <libgda/libgda.h>
+#include <boost/python.hpp>
 
-int
-glom_pygda_value_from_pyobject(GValue *boxed, PyObject *input);
+bool
+glom_pygda_value_from_pyobject(GValue *boxed, const boost::python::object& input);
 
-PyObject *
-glom_pygda_value_as_pyobject(const GValue *value, gboolean copy_boxed);
+//PyObject *
+//glom_pygda_value_as_pyobject(const GValue *value, gboolean copy_boxed);
 
-G_END_DECLS
+boost::python::object glom_pygda_value_as_boost_pyobject(const Glib::ValueBase& value);
 
 #endif //GLOM_PYGDA_VALUE_CONVERSIONS_H
diff --git a/glom/main.cc b/glom/main.cc
index 52c2190..29bbd45 100644
--- a/glom/main.cc
+++ b/glom/main.cc
@@ -21,7 +21,8 @@
 #include <config.h>
 
 //We use Python for calculated fields.
-#include <Python.h> //Include it before anything else to avoid "_POSIX_C_SOURCE redefined".
+//#include <Python.h> //Include it before anything else to avoid "_POSIX_C_SOURCE redefined".
+#include <boost/python.hpp>
 
 //#include <gnome.h>
 #include <glom/libglom/init.h>
diff --git a/glom/python_embed/glom_python.cc b/glom/python_embed/glom_python.cc
index da6d98f..a3574a1 100644
--- a/glom/python_embed/glom_python.cc
+++ b/glom/python_embed/glom_python.cc
@@ -23,12 +23,10 @@
 #include <libglom/python_embed/py_glom_record.h>
 #include <libglom/python_embed/pygdavalue_conversions.h>
 
-#define NO_IMPORT_PYGOBJECT //To avoid a multiple definition in pygtk.
-#include <pygobject.h> //For the PyGObject and PyGBoxed struct definitions.
+#include <boost/python.hpp>
 
-#include <Python.h>
-#include <compile.h> /* for the PyCodeObject */
-#include <eval.h> /* for PyEval_EvalCode */
+//#define NO_IMPORT_PYGOBJECT //To avoid a multiple definition in pygtk.
+//#include <pygobject.h> //For the PyGObject and PyGBoxed struct definitions.
 
 #include "glom_python.h"
 #include <libglom/data_structure/glomconversions.h>
@@ -41,7 +39,7 @@
 namespace Glom
 {
 
-std::list<Glib::ustring> ustring_tokenize(const Glib::ustring& msg, const Glib::ustring& separators, int maxParts)
+static std::list<Glib::ustring> ustring_tokenize(const Glib::ustring& msg, const Glib::ustring& separators, int maxParts)
 {
   std::list<Glib::ustring> result;
   Glib::ustring str = msg;
@@ -102,18 +100,17 @@ void ShowTrace()
       
       if(!tbList)
       {
-        std::cerr << "format_exception failed" << std::endl;
+        std::cerr << "Glom: format_exception failed while trying to show Python TraceBack." << std::endl;
         return;
       }
 
-      PyObject* emptyString = PyString_FromString("");
-      PyObject* strRetval = PyObject_CallMethod(emptyString, (char*)"join",
+      boost::python::str emptyString("");
+      PyObject* strRetval = PyObject_CallMethod(emptyString.ptr(), (char*)"join",
             (char*)"O", tbList);
       if(strRetval)
         chrRetval = g_strdup(PyString_AsString(strRetval));
     
       Py_DECREF(tbList);
-      Py_DECREF(emptyString);
       Py_DECREF(strRetval);
       Py_DECREF(tracebackModule);
     }
@@ -129,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);
 }
@@ -140,7 +140,7 @@ void ShowTrace()
 bool glom_python_module_is_available()
 {
   const gchar* name = "glom_" GLOM_ABI_VERSION_UNDERLINED;
-  PyObject* module_glom = PyImport_ImportModule((char*)name); //TODO: unref this?
+  boost::python::object module_glom = boost::python::import(name);
 
   if(!module_glom)
   {
@@ -153,7 +153,7 @@ bool glom_python_module_is_available()
 bool gda_python_module_is_available()
 {
   const gchar* name = "gda";
-  PyObject* module_glom = PyImport_ImportModule((char*)name); //TODO: unref this?
+  boost::python::object module_glom = boost::python::import(name); //TODO: unref this?
 
   if(!module_glom)
   {
@@ -201,9 +201,16 @@ Gnome::Gda::Value glom_evaluate_python_function_implementation(Field::glom_field
   func_def = "def " + func_name + "(record):\n  import glom_" GLOM_ABI_VERSION_UNDERLINED "\n  import gda\n" + func_def;
   //We did this in main(): Py_Initialize();
 
-  PyObject* pMain = PyImport_AddModule((char*)"__main__");
-  PyObject* pDict = PyModule_GetDict(pMain);
-
+  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()?
+  //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)
+  {
+     std::cerr << "glom_evaluate_python_function_implementation(): pDict is null" << std::endl;
+     ShowTrace();
+     return valueResult;
+  }
 
   //Allow the function to import from our script library:
   if(pDocument)
@@ -215,78 +222,100 @@ Gnome::Gda::Value glom_evaluate_python_function_implementation(Field::glom_field
       const Glib::ustring script = pDocument->get_library_module(name);
       if(!name.empty() && !script.empty())
       {
-        PyObject* objectCompiled = Py_CompileString(script.c_str(), name.c_str() /* "filename", for debugging info */,  Py_file_input /* "start token" for multiple lines of code. */); //Returns a reference.
-  
+        //TODO: Is there a boost::python equivalent for Py_CompileString()?
+        PyObject* cObject = Py_CompileString(script.c_str(), name.c_str() /* "filename", for debugging info */,  Py_file_input /* "start token" for multiple lines of code. */); //Returns a reference.
+        boost::python::handle<> objectCompiled(cObject); //Takes the reference.
+
         if(!objectCompiled)
           HandlePythonError();
+          
+      
+        cObject = PyImport_ExecCodeModule(const_cast<char*>(name.c_str()), cObject); //Returns a reference. //This should make it importable.
+        boost::python::handle<> object(cObject); //Takes the reference.
 
-        PyObject* pObject = PyImport_ExecCodeModule(const_cast<char*>(name.c_str()), objectCompiled); //Returns a reference. //This should make it importable.
-
-        if(!pObject)
-         HandlePythonError();
+        if(!object)
+          HandlePythonError();
 
-        Py_DECREF(pObject);
-        Py_DECREF(objectCompiled);
         //TODO: When do these stop being importable? Should we unload them somehow later?
       }
     }
   }
 
-
-  PyObject* module_glom = PyImport_ImportModule((char*)"glom_" GLOM_ABI_VERSION_UNDERLINED);
+  //TODO: Is this necessary?
+  boost::python::object module_glom = boost::python::import("glom_" GLOM_ABI_VERSION_UNDERLINED);
   if(!module_glom)
   {
     g_warning("Could not import python glom module.");
     return valueResult; // don't crash
   }
-
-  PyObject* module_glom_dict = PyModule_GetDict(module_glom);
-  //This seems to be different to PyGlomRecord_GetPyType() - we can PyObject_Call() this one to instantiate it.
-  PyObject* pyTypeGlomRecord = PyDict_GetItemString(module_glom_dict, (char*)"Record"); //TODO: Unref this?
-  if(!pyTypeGlomRecord || !PyType_Check(pyTypeGlomRecord))
-  {
-    g_warning("Could not get glom.Record from glom_module.");
-    return valueResult; // don't crash
-  }
-
-
-  PyObject* module_gda = PyImport_ImportModule((char*)"gda");
+  
+  //TODO: Is this necessary?
+  boost::python::object module_gda = boost::python::import("gda");
   if(!module_gda)
   {
     g_warning("Could not import python gda module.");
     return valueResult;
   }
 
-  // Gda.Value does not exist anymore in pygda-3.0
-#if 0
-  PyObject* module_gda_dict = PyModule_GetDict(module_gda);
-  PyObject* pyTypeGdaValue = PyDict_GetItemString(module_gda_dict, "Value"); //TODO: Unref this?
-  if(!pyTypeGdaValue || !PyType_Check(pyTypeGdaValue))
-    g_warning("Could not get gda.Value from gda_module.");
-#endif
-
-
   //Create the function definition:
-  PyObject* pyValue = PyRun_String(func_def.c_str(), Py_file_input, pDict, pDict);
-  if(pyValue)
+  /*
+  boost::python::object pyValue;
+  try
   {
-    Py_DECREF(pyValue);
-    pyValue = 0;
+    pyValue = boost::python::eval(func_def.c_str(), pDict, pDict); //TODO: This throws.
   }
-  else
+  catch(const boost::python::error_already_set& ex)
   {
+    std::cerr << "Glom: boost::python::eval() threw boost::python_error_already_set." << std::endl;
+    HandlePythonError();
+  }
+  */
+  
+  //TODO: Complain that exec(std::string(something), pMain) doesn't work.
+  boost::python::object pyValue;
+  try
+  {
+    //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;
+  }
+  
+  if(!pyValue.ptr())
+  {
+    std::cerr << "glom_evaluate_python_function_implementation(): boost::python::exec failed." << std::endl;
+    ShowTrace();
+    return valueResult;
   }
 
   //Call the function:
   {
-    PyObject* pFunc = PyDict_GetItemString(pDict, 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.");
@@ -297,27 +326,26 @@ Gnome::Gda::Value glom_evaluate_python_function_implementation(Field::glom_field
   
     //PyObject* pParam = PyString_FromString("test value"); //This test did not need the extra ref.
 
-    PyObject* new_args = PyTuple_New(0);
-    PyGlomRecord* pParam = (PyGlomRecord*)PyObject_Call((PyObject*)pyTypeGlomRecord, new_args, 0);
-    //PyGlomRecord* pParam = (PyGlomRecord*)PyObject_Call((PyObject*)PyGlomRecord_GetPyType(), new_args, 0);
-    Py_DECREF(new_args);
+    boost::python::object objRecord(new PyGlomRecord);
+    boost::python::extract<PyGlomRecord*> extractor(objRecord);
+    if(!extractor.check())
+    {
+      std::cerr << ("extract<PyGlomRecord*> failed.") << std::endl;
+      return valueResult;
+    }
+    
+    PyGlomRecord* pParam = extractor;
     if(pParam)
     {
-      Py_INCREF(pParam); //TODO: As I understand it, PyObject_New() should return a new reference, so this should not be necessary.
-
       //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
@@ -326,9 +354,9 @@ Gnome::Gda::Value glom_evaluate_python_function_implementation(Field::glom_field
         bool object_is_gda_value = false;
 
         GValue value = {0, {{0}}};
-        const int test = glom_pygda_value_from_pyobject(&value, pyResult);
+        const bool test = glom_pygda_value_from_pyobject(&value, pyResultCpp);
 
-        if(test == 0) //-1 means error.
+        if(test)
           object_is_gda_value = true;
 
         if(object_is_gda_value && G_IS_VALUE(&value))
@@ -346,9 +374,8 @@ 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);
             //std::cout << "DEBUG: glom_evaluate_python_function_implementation(): empty value Gda type=" << g_type_name(valueResult.get_value_type()) << std::endl;
@@ -359,26 +386,28 @@ Gnome::Gda::Value glom_evaluate_python_function_implementation(Field::glom_field
             //(though I don't think this code is ever reached. murrayc)
 
             //Treat this as a string or something that can be converted to a string:
-            PyObject* pyObjectResult = PyObject_Str(pyResult);
-            if(PyString_Check(pyObjectResult))
+            const char* pchResult = 0;
+            try
             {
-              const char* pchResult = PyString_AsString(pyObjectResult);
-              if(pchResult)
-              {
-                bool success = false;
-                valueResult = Conversions::parse_value(result_type, pchResult, success, true /* iso_format */);
-                std::cout << "DEBUG: glom_evaluate_python_function_implementation(): parsed value Gda type=" << g_type_name(valueResult.get_value_type()) << std::endl;
- 
-              }
-              else
-                HandlePythonError();
+              pchResult = boost::python::extract<const char*>(pyResultCpp);
+            }
+            catch(const boost::python::error_already_set& ex)
+            {
+              std::cerr << "Glom: Exception caught from boost::python::extract() while converting result to a const char*." << std::endl << func_name << std::endl;
+              ShowTrace();
+              return valueResult;
+            }
+
+            if(pchResult)
+            {
+              bool success = false;
+              valueResult = Conversions::parse_value(result_type, pchResult, success, true /* iso_format */);
+              std::cout << "DEBUG: glom_evaluate_python_function_implementation(): parsed value Gda type=" << g_type_name(valueResult.get_value_type()) << std::endl;
             }
             else
               HandlePythonError();
           }
         }
-
-        Py_DECREF(pyResult);
       }
     }
   }
diff --git a/glom/python_embed/python_module/py_glom_module.cc b/glom/python_embed/python_module/py_glom_module.cc
index 790bafa..4013997 100644
--- a/glom/python_embed/python_module/py_glom_module.cc
+++ b/glom/python_embed/python_module/py_glom_module.cc
@@ -20,55 +20,44 @@
 
 #include <config.h>
 //We need to include this before anything else, to avoid redefinitions:
-#include <Python.h>
-#include <compile.h> /* for the PyCodeObject */
-#include <eval.h> /* for PyEval_EvalCode */
-#include <objimpl.h> /* for PyObject_New() */
+#include <boost/python.hpp>
+//#include <compile.h> /* for the PyCodeObject */
+//#include <eval.h> /* for PyEval_EvalCode */
 
 #include <libglom/python_embed/py_glom_record.h>
 #include <libglom/python_embed/py_glom_related.h>
 #include <libglom/python_embed/py_glom_relatedrecord.h>
 
-static PyMethodDef pyglom_methods[] = {
-    {0, 0, 0, 0}  /* Sentinel */
-};
+using namespace Glom;
 
-PyMODINIT_FUNC
-initglom_1_14(void) 
+BOOST_PYTHON_MODULE(glom_1_14)
 {
-  PyObject* m;
-
-  //pyglom_RecordType.tp_new = PyType_GenericNew;
-
-  if(PyType_Ready(Glom::PyGlomRecord_GetPyType()) < 0)
-    return;
-
-  if(PyType_Ready(Glom::PyGlomRelated_GetPyType()) < 0)
-    return;
-
-  if(PyType_Ready(Glom::PyGlomRelatedRecord_GetPyType()) < 0)
-    return;
-
-
-  m = Py_InitModule3((char*)"glom_" GLOM_ABI_VERSION_UNDERLINED, pyglom_methods,
-                      (char*)"Python module for Glom caluclated fields.");
-
-
-  Py_INCREF(Glom::PyGlomRecord_GetPyType());
-  PyModule_AddObject(m, (char*)"Record", (PyObject *)Glom::PyGlomRecord_GetPyType());
-
-  Py_INCREF(Glom::PyGlomRelated_GetPyType());
-  PyModule_AddObject(m, (char*)"Related", (PyObject *)Glom::PyGlomRelated_GetPyType());
-
-  Py_INCREF(Glom::PyGlomRelated_GetPyType());
-  PyModule_AddObject(m, (char*)"RelatedRecord", (PyObject *)Glom::PyGlomRelated_GetPyType());
-
-
-  if(PyErr_Occurred())
-    Py_FatalError((char*)"Can't initialise glom module");
+  boost::python::class_<PyGlomRecord>("Record")
+    .add_property("table_name", &PyGlomRecord::get_table_name)
+    .add_property("connection", &PyGlomRecord::get_connection)
+    .add_property("related", &PyGlomRecord::get_related)
+
+        /* TODO: python still says "TypeError: 'Boost.Python.class' object is unsubscriptable" */
+	        /* This suggests that it should work: http://lists.boost.org/boost-users/2003/08/4750.php */
+    .def("__getitem__", &PyGlomRecord::getitem)
+    .def("__len__", &PyGlomRecord::len)
+  ;
+
+  boost::python::class_<PyGlomRelated>("Related")
+    .def("__getitem__", &PyGlomRelated::getitem)
+    .def("__len__", &PyGlomRelated::len)
+  ;
+
+  boost::python::class_<PyGlomRelatedRecord>("RelatedRecord")
+    .def("sum", &PyGlomRelatedRecord::sum, boost::python::args("field_name"), "Add all values of the field in the related records.")
+    .def("count", &PyGlomRelatedRecord::sum, boost::python::args("field_name"), "Count all values in the field in the related records.")
+    .def("min", &PyGlomRelatedRecord::sum, boost::python::args("field_name"), "Minimum of all values of the field in the related recordss.")
+    .def("max", &PyGlomRelatedRecord::sum, boost::python::args("field_name"), "Maximum of all values of the field in the related records.")
+    .def("__getitem__", &PyGlomRecord::getitem)
+    .def("__len__", &PyGlomRecord::len)
+  ;
 }
 
 
 
 
-
diff --git a/glom/python_embed/python_module/py_glom_module.h b/glom/python_embed/python_module/py_glom_module.h
index 1faa97e..aca23aa 100644
--- a/glom/python_embed/python_module/py_glom_module.h
+++ b/glom/python_embed/python_module/py_glom_module.h
@@ -37,7 +37,6 @@
  * - field values: for instance, name = record.related["contacts"]["name_first"];
  * - Summary functions: for instance, total = record.related["invoice_lines"].sum("price");
  */
-PyMODINIT_FUNC initglom_1_14(void);
 
 
 
diff --git a/macros/ax_boost_python.m4 b/macros/ax_boost_python.m4
new file mode 100644
index 0000000..8ccd765
--- /dev/null
+++ b/macros/ax_boost_python.m4
@@ -0,0 +1,121 @@
+# ===========================================================================
+#         http://www.nongnu.org/autoconf-archive/ax_boost_python.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_BOOST_PYTHON
+#
+# DESCRIPTION
+#
+#   This macro checks to see if the Boost.Python library is installed. It
+#   also attempts to guess the currect library name using several attempts.
+#   It tries to build the library name using a user supplied name or suffix
+#   and then just the raw library.
+#
+#   If the library is found, HAVE_BOOST_PYTHON is defined and
+#   BOOST_PYTHON_LIBS is set to the name of the library.
+#
+#   This macro calls AC_SUBST(BOOST_PYTHON_LIBS).
+#
+#   In order to ensure that the Python headers are specified on the include
+#   path, this macro requires AX_PYTHON to be called.
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Michael Tindal
+#
+#   This program is free software; you can redistribute it and/or modify it
+#   under the terms of the GNU General Public License as published by the
+#   Free Software Foundation; either version 2 of the License, or (at your
+#   option) any later version.
+#
+#   This program is distributed in the hope that it will be useful, but
+#   WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+#   Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License along
+#   with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+#   As a special exception, the respective Autoconf Macro's copyright owner
+#   gives unlimited permission to copy, distribute and modify the configure
+#   scripts that are the output of Autoconf when processing the Macro. You
+#   need not follow the terms of the GNU General Public License when using
+#   or distributing such scripts, even though portions of the text of the
+#   Macro appear in them. The GNU General Public License (GPL) does govern
+#   all other use of the material that constitutes the Autoconf Macro.
+#
+#   This special exception to the GPL applies to versions of the Autoconf
+#   Macro released by the Autoconf Archive. When you make and distribute a
+#   modified version of the Autoconf Macro, you may extend this special
+#   exception to the GPL to apply to your modified version as well.
+
+#serial 7
+#With large changes by murrayc
+
+#Note that this previously said it was checking for the library, but it's techically the both the headers and library that it looks for. murrayc
+AC_DEFUN([AX_BOOST_PYTHON],
+[AC_REQUIRE([AX_PYTHON])dnl
+AC_CACHE_CHECK(whether the Boost::Python headers are available,
+ac_cv_boost_python,
+[AC_LANG_SAVE
+ AC_LANG_CPLUSPLUS
+ CPPFLAGS_SAVE=$CPPFLAGS
+ # Hacked to use the output of MM_CHECK_MODULE_PYTHON() instead of the outout of AX_PYTHON, which we don't use. murrayc
+ # if test x$PYTHON_INCLUDE_DIR != x; then
+ #  CPPFLAGS=-I$PYTHON_INCLUDE_DIR $CPPFLAGS
+ #fi
+ if test x$PYTHON_CPPFLAGS != x; then
+   #Note that this expects boost/ to be at some top-level such as /usr/include/
+   #We couldn't check for anything else anyway because there's no pkg-config file to tell us where it is 
+   CPPFLAGS=$PYTHON_CPPFLAGS $CPPFLAGS
+ fi
+ AC_COMPILE_IFELSE(AC_LANG_PROGRAM([[
+ #include <boost/python/module.hpp>
+ using namespace boost::python;
+ BOOST_PYTHON_MODULE(test) { throw "Boost::Python test."; }]],
+ 			   [[return 0;]]),
+  			   ac_cv_boost_python=yes, ac_cv_boost_python=no)
+ AC_LANG_RESTORE
+ CPPFLAGS=$CPPFLAGS_SAVE
+])
+if test "$ac_cv_boost_python" = "yes"; then
+  AC_DEFINE(HAVE_BOOST_PYTHON,,[define if the Boost::Python headers and library are available])
+  ax_python_lib=boost_python
+  AC_ARG_WITH([boost-python],AS_HELP_STRING([--with-boost-python],[specify the boost python shared library to use. For instance, --with-boost-python=boost_python-py25. Defaults to boost-python. If you use this then you should probably set PYTHON too, to avoid using multiple python versions.]),
+  [if test "x$with_boost_python" != "xno"; then
+     ax_python_lib=$with_boost_python
+     ax_boost_python_lib=boost_python-$with_boost_python
+   fi])
+  for ax_lib in $ax_python_lib $ax_boost_python_lib boost_python; do
+    #This previously checked for an exit() function in the boost::python library.
+    #That was strange and apparently no longer works. murrayc.
+    #AC_CHECK_LIB($ax_lib, exit, [BOOST_PYTHON_LIB=$ax_lib break])
+    AC_LANG_SAVE
+    AC_LANG_CPLUSPLUS
+    #Note that this requires PYTHON_LIBS from MM_CHECK_MODULE_PYTHON()
+    SAVED_CPPFLAGS=$CPPFLAGS
+    CPPFLAGS="-I$PYTHON_INCLUDE_DIR"
+    SAVED_LDFLAGS=$LDFLAGS
+    LDFLAGS="$LDFLAGS $PYTHON_LIBS -l$ax_lib"
+    AC_LINK_IFELSE(
+      [AC_LANG_PROGRAM([#include <boost/python.hpp>],
+        [boost::python::object test_object])],
+      #Note that this was previously BOOST_PYTHON_LIB, but I renamed it because people expect *LIBS. murrayc.
+      [BOOST_PYTHON_LIBS="-l$ax_lib"])
+    LDFLAGS=$SAVED_LDFLAGS
+    CPPFLAGS=$SAVED_CPPFLAGS
+    AC_LANG_RESTORE
+  done
+  if test x$BOOST_PYTHON_LIBS != x; then
+    AC_MSG_RESULT([boost::python shared library found: $BOOST_PYTHON_LIBS])
+  fi
+  #TODO: Figure out how to do a simple if else:
+  if test x$BOOST_PYTHON_LIBS == x; then
+    AC_MSG_ERROR([boost::python shared library not found])
+  fi
+  AC_SUBST(BOOST_PYTHON_LIBS)
+fi
+])dnl
+
diff --git a/macros/mm-python.m4 b/macros/mm-python.m4
index 77dbce5..9265507 100644
--- a/macros/mm-python.m4
+++ b/macros/mm-python.m4
@@ -34,6 +34,9 @@ AS_IF([test "[$]?" -eq 0 && test "x$mm_val" != x], [$2], [$3])[]dnl
 ## The resulting configuration is stored in the PYTHON_CPPFLAGS and
 ## PYTHON_LIBS substitution variables.
 ##
+## Note: We use this in preference to AX_PYTHON() because it seems to behave better, preferring python and PYTHON.
+## For instance, it uses python2.5 if python is symlinked to python2.5, instead of preferring the newest one.
+##
 AC_DEFUN([MM_CHECK_MODULE_PYTHON],
 [dnl
 AC_REQUIRE([AM_PATH_PYTHON])[]dnl
diff --git a/tests/test_python_execute_func.cc b/tests/test_python_execute_func.cc
index 1cb8269..e4a4437 100644
--- a/tests/test_python_execute_func.cc
+++ b/tests/test_python_execute_func.cc
@@ -2,6 +2,8 @@
 #include <glom/python_embed/glom_python.h>
 #include <libglom/data_structure/glomconversions.h>
 
+#include <boost/python.hpp>
+
 int main()
 {
   Glom::libglom_init(); //Also initializes python.
@@ -11,9 +13,24 @@ int main()
   Glib::RefPtr<Gnome::Gda::Connection> connection;
 
   //Execute a python function:
-  const Gnome::Gda::Value value = Glom::glom_evaluate_python_function_implementation(
-    Glom::Field::TYPE_TEXT, calculation, field_values, 
-    0 /* document */, "" /* table name */, connection);
+  Gnome::Gda::Value value;
+  try
+  {
+    value = Glom::glom_evaluate_python_function_implementation(
+      Glom::Field::TYPE_TEXT, calculation, field_values, 
+      0 /* document */, "" /* table name */, connection);
+  }
+  catch(const std::exception& ex)
+  {
+    std::cerr << "Exception: " << ex.what() << std::endl;
+    return EXIT_FAILURE;
+  }
+  catch(const boost::python::error_already_set& ex)
+  {
+    std::cerr << "Exception: boost::python::error_already_set" << std::endl;
+    return EXIT_FAILURE;
+  }
+
 
   //std::cout << "type=" << g_type_name(value.get_value_type()) << std::endl;
 



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