Start wrapping seq_span
authorDiane Trout <diane@caltech.edu>
Wed, 29 Aug 2007 05:43:49 +0000 (05:43 +0000)
committerDiane Trout <diane@caltech.edu>
Wed, 29 Aug 2007 05:43:49 +0000 (05:43 +0000)
there were a couple of challanges. First off the stl_container_adapter I
was using didn't handle returning a const_reference particularly well,
so I had to add a get_const function.

The other was more insideous, the example the wiki had showed just setting
the python exception, it didn't generate a C++ exception. So when
boost::python tried to copy an undefined reference when it fell of
the end of a container I got a bus error.

The solution was to add in a C++/Python exception translater so I threw
a C++ exception indicating an IndexError, but then the it was transformed
into the appropriate Python exception.

py/CMakeLists.txt
py/alphabet.cpp [new file with mode: 0644]
py/module.cpp
py/seq_span.cpp [new file with mode: 0644]
py/stl_container_adapter.cpp [new file with mode: 0644]
py/stl_container_adapter.hpp
py/test/TestSeqSpan.py [new file with mode: 0644]

index 0a7fdc2f6001e2be0fff90e392aeee0fa5b783a3..c5a30d609aa92b6f1cfb8e8c18452f17ecbc4c80 100644 (file)
@@ -10,6 +10,8 @@ INCLUDE( ${QT_USE_FILE} )
 IF(BOOST_PYTHON_LIBRARY)
   INCLUDE_DIRECTORIES(${PYTHON_INCLUDE_PATH} ${QT_INCLUDES})
   SET(SOURCES 
+        stl_container_adapter.cpp
+        alphabet.cpp
         annot.cpp
         annotation_colors.cpp
         conserved_path.cpp 
@@ -18,6 +20,7 @@ IF(BOOST_PYTHON_LIBRARY)
         module.cpp 
         mussa.cpp
         nway_paths.cpp
+        seq_span.cpp
         sequence.cpp
         )
   SET(QUI_SOURCES
@@ -93,6 +96,7 @@ IF(BOOST_PYTHON_LIBRARY)
 
   IF(PYTHON_EXECUTABLE)
     SET(PYTEST_DIR ${CMAKE_SOURCE_DIR}/py/test/)
+    ADD_TEST(TestSeqSpan ${PYTHON_EXECUTABLE} ${PYTEST_DIR}/TestSeqSpan.py)
     ADD_TEST(TestSequence ${PYTHON_EXECUTABLE} ${PYTEST_DIR}/TestSequence.py)
     ADD_TEST(TestFlp ${PYTHON_EXECUTABLE} ${PYTEST_DIR}/TestFlp.py)
     ADD_TEST(TestMussa ${PYTHON_EXECUTABLE} ${PYTEST_DIR}/TestMussa.py)
diff --git a/py/alphabet.cpp b/py/alphabet.cpp
new file mode 100644 (file)
index 0000000..bc473ea
--- /dev/null
@@ -0,0 +1,23 @@
+#include <boost/iterator.hpp>
+
+#include <boost/python.hpp>
+
+#include <boost/python/register_ptr_to_python.hpp>
+using namespace boost::python;
+
+#include "alg/alphabet.hpp"
+
+void export_alphabet()
+{
+  enum_<AlphabetRef>("alphabet")
+    .value("reduced_dna_alphabet", reduced_dna_alphabet)
+    .value("dna_alphabet", dna_alphabet)
+    .value("reduced_rna_alphabet", reduced_rna_alphabet)
+    .value("rna_alphabet", rna_alphabet)
+    .value("reduced_nucleic_alphabet", reduced_nucleic_alphabet)
+    .value("nucleic_alphabet", nucleic_alphabet)
+    .value("protein_alphabet", protein_alphabet)
+    ;
+
+}
+
index 9b5d54f5c063508df721409a16951c54ee33e066..84564273e81e6862f1865ec22e0cd33675579aa3 100644 (file)
@@ -1,6 +1,8 @@
 #include <boost/python.hpp>
+#include "stl_container_adapter.hpp"
 using namespace boost::python;
 
+void export_alphabet();
 void export_annot();
 void export_annotation_colors();
 void export_conserved_path();
@@ -8,15 +10,20 @@ void export_flps();
 void export_glsequence();
 void export_mussa();
 void export_nway_paths();
+void export_seq_span();
 void export_sequence();
 void export_mussa_window();
 
 BOOST_PYTHON_MODULE(mussa)
 {
+  boost::python::register_exception_translator<IndexError>(&translate);
+
+  export_alphabet();
   export_annot();
   export_annotation_colors();
   export_conserved_path();
   export_flps();
+  export_seq_span();
   export_sequence();
   export_glsequence();
   export_mussa();
diff --git a/py/seq_span.cpp b/py/seq_span.cpp
new file mode 100644 (file)
index 0000000..aa484c9
--- /dev/null
@@ -0,0 +1,52 @@
+#include <boost/iterator.hpp>
+
+#include <boost/python.hpp>
+#include <boost/python/return_value_policy.hpp>
+
+#include <boost/python/register_ptr_to_python.hpp>
+using namespace boost::python;
+
+#include "alg/seq_span.hpp"
+
+#include "stl_container_adapter.hpp"
+
+void export_seq_span()
+{
+  enum_<SeqSpan::strand_type>("strand_type")
+    .value("unknown", SeqSpan::UnknownStrand)
+    .value("minus", SeqSpan::MinusStrand)
+    .value("plus", SeqSpan::PlusStrand)
+    .value("both", SeqSpan::BothStrand)
+    .value("same", SeqSpan::SameStrand)
+    .value("opposite", SeqSpan::OppositeStrand)
+    .value("single", SeqSpan::SingleStrand)
+    ;
+
+
+  class_<SeqSpan>("SeqSpan", 
+                  init<std::string, optional<AlphabetRef, 
+                                             SeqSpan::strand_type> 
+                      >() )
+    .def("__len__", &SeqSpan::size)
+    .def("__getitem__", &std_item<SeqSpan>::get_const,
+        return_value_policy<copy_const_reference>())
+    .def("__str__", &SeqSpan::sequence)
+    .def("empty", &SeqSpan::empty)
+    .add_property("start", &SeqSpan::start, &SeqSpan::setStart,
+                  "start position relative to root sequence")
+    .add_property("stop", &SeqSpan::stop, &SeqSpan::setStop,
+                  "one past the last position relative to the root sequence.")
+    .add_property("strand", &SeqSpan::strand, 
+                  "describe which strand the span is on")
+
+    .add_property("parentStart", &SeqSpan::parentStart, &SeqSpan::setParentStart,
+                  "start position relative to parent sequence")
+    .add_property("parentStop", &SeqSpan::parentStop, &SeqSpan::setParentStop,
+                  "one past the last position relative to the parent sequence.")
+    .add_property("parent", &SeqSpan::parent)
+
+    .def("subseq", &SeqSpan::subseq)
+    ;
+  register_ptr_to_python< boost::shared_ptr<SeqSpan> >();
+}
diff --git a/py/stl_container_adapter.cpp b/py/stl_container_adapter.cpp
new file mode 100644 (file)
index 0000000..5135b44
--- /dev/null
@@ -0,0 +1,12 @@
+#include "stl_container_adapter.hpp"
+
+void translate(IndexError const& e)
+{ 
+  PyErr_SetString(PyExc_IndexError, "Index out of range"); 
+}
+
+void translate(KeyError const& e)
+{ 
+  PyErr_SetString(PyExc_KeyError, "Key not found"); 
+}
+
index 87582476eec0ef9ed47c5d10eb6fc78dc9412cf6..a2989094d234594fb8731f72ec0683f58c737d78 100644 (file)
@@ -2,55 +2,81 @@
 #define STL_CONTAINER_ADAPTER_HPP_
 
 #include <algorithm>
+#include <exception>
+
 #include <boost/python.hpp>
 
 // This template started off life at
 // http://wiki.python.org/moin/boost.python/StlContainers
 
-void IndexError() { PyErr_SetString(PyExc_IndexError, "Index out of range"); }
+struct IndexError : std::exception
+{
+public:
+  explicit IndexError(): std::exception() {};
+};
+
+void translate(IndexError const &e);
+
+
 template<typename T>
 struct std_item
 {
-    typedef typename T::value_type V;
-    static V& get(T& x, int i)
-    {
-        if( i<0 ) i+=x.size();
-        if( i>=0 && i<x.size() ) return x[i];
-        IndexError();
-    }
-    static void set(T& x, int i, V const& v)
-    {
-        if( i<0 ) i+=x.size();
-        if( i>=0 && i<x.size() ) x[i]=v;
-        else IndexError();
-    }
-    static void del(T& x, int i)
-    {
-        if( i<0 ) i+=x.size();
-        if( i>=0 && i<x.size() ) {
-          typename T::iterator itor = x.begin();
-          itor += i; 
-          x.erase(itor);
-        } else IndexError();
-    }
-    static void add(T& x, V& v)
-    {
-        x.push_back(v);
-    }
-    static bool in(T const& x, V const& v)
-    {
-        return std::find(x.begin(), x.end(), v) != x.end();
-    }    
-    static int index(T const& x, V const& v)
-    {
-        int i=0;
-        for(typename T::const_iterator it=x.begin; it!=x.end(); ++it,++i)
-          if( *it == v ) return i;
-        return -1;
-    }
+  typedef typename T::value_type V;
+  typedef typename T::reference R;
+  typedef typename T::const_reference CR;
+
+  static R get(T& x, int i)
+  {
+    if( i<0 ) i+=x.size();
+    if( i>=0 && i<x.size() ) return x[i];
+    throw IndexError();
+  }
+  static CR get_const(T& x, int i)
+  {
+    if( i<0 ) i+=x.size();
+    if( i>=0 && i<x.size() ) return x[i];
+    throw IndexError();
+  }
+  static void set(T& x, int i, V const& v)
+  {
+    if( i<0 ) i+=x.size();
+    if( i>=0 && i<x.size() ) x[i]=v;
+    else throw IndexError();
+  }
+  static void del(T& x, int i)
+  {
+    if( i<0 ) i+=x.size();
+    if( i>=0 && i<x.size() ) {
+      typename T::iterator itor = x.begin();
+      itor += i; 
+      x.erase(itor);
+    } else throw IndexError();
+  }
+  static void add(T& x, V& v)
+  {
+    x.push_back(v);
+  }
+  static bool in(T const& x, V const& v)
+  {
+    return std::find(x.begin(), x.end(), v) != x.end();
+  }    
+  static int index(T const& x, V const& v)
+  {
+    int i=0;
+    for(typename T::const_iterator it=x.begin; it!=x.end(); ++it,++i)
+      if( *it == v ) return i;
+    return -1;
+  }
+};
+
+struct KeyError : std::exception
+{
+public:
+  explicit KeyError(): std::exception() {};
 };
 
-void KeyError() { PyErr_SetString(PyExc_KeyError, "Key not found"); }
+void translate(KeyError const &e);
+
 template<typename T>
 struct map_item
 {
@@ -59,7 +85,7 @@ struct map_item
     static V& get(T const& x, K const& i)
     {
         if( x.find(i) != x.end() ) return x[i];
-        KeyError();
+        throw KeyError();
     }
     static void set(T const& x, K const& i, V const& v)
     {
@@ -68,7 +94,7 @@ struct map_item
     static void del(T const& x, K const& i)
     {
         if( x.find(i) != x.end() ) x.erase(i);
-        else KeyError();
+        else throw KeyError();
     }
     static bool in(T const& x, K const& i)
     {
diff --git a/py/test/TestSeqSpan.py b/py/test/TestSeqSpan.py
new file mode 100644 (file)
index 0000000..09747de
--- /dev/null
@@ -0,0 +1,29 @@
+import os
+import sys
+import unittest
+
+# kinda hackish but it makes it possible to runi under ctest 
+sys.path.append(os.getcwd())
+
+import mussa
+
+class TestSeqSpan(unittest.TestCase):
+  def testSimple(self):
+    seq_text = "AGCT"
+    s = mussa.SeqSpan(seq_text)
+    self.failUnlessEqual(str(s), seq_text)
+
+  def testIter(self):
+    seq_text = "AAAAAAGGGGGGG"
+    s = mussa.SeqSpan(seq_text)
+    new_seq_text = "".join( [ x for x in s ] )
+    print "len", len(s)
+    self.failUnlessEqual(len(seq_text), len(s))
+    self.failUnlessEqual(seq_text, new_seq_text)
+
+
+def suite():
+  return unittest.makeSuite(TestSeqSpan, 'test')
+
+if __name__ == "__main__":
+  sys.exit(unittest.main(defaultTest='suite'))