From 80ca9b3ccb2a64f22ab3039b9ee2ecbae2c43e10 Mon Sep 17 00:00:00 2001 From: Diane Trout Date: Wed, 29 Aug 2007 05:43:49 +0000 Subject: [PATCH] Start wrapping seq_span 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 | 4 ++ py/alphabet.cpp | 23 ++++++++ py/module.cpp | 7 +++ py/seq_span.cpp | 52 +++++++++++++++++ py/stl_container_adapter.cpp | 12 ++++ py/stl_container_adapter.hpp | 108 ++++++++++++++++++++++------------- py/test/TestSeqSpan.py | 29 ++++++++++ 7 files changed, 194 insertions(+), 41 deletions(-) create mode 100644 py/alphabet.cpp create mode 100644 py/seq_span.cpp create mode 100644 py/stl_container_adapter.cpp create mode 100644 py/test/TestSeqSpan.py diff --git a/py/CMakeLists.txt b/py/CMakeLists.txt index 0a7fdc2..c5a30d6 100644 --- a/py/CMakeLists.txt +++ b/py/CMakeLists.txt @@ -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 index 0000000..bc473ea --- /dev/null +++ b/py/alphabet.cpp @@ -0,0 +1,23 @@ +#include + +#include + +#include +using namespace boost::python; + +#include "alg/alphabet.hpp" + +void export_alphabet() +{ + enum_("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) + ; + +} + diff --git a/py/module.cpp b/py/module.cpp index 9b5d54f..8456427 100644 --- a/py/module.cpp +++ b/py/module.cpp @@ -1,6 +1,8 @@ #include +#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(&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 index 0000000..aa484c9 --- /dev/null +++ b/py/seq_span.cpp @@ -0,0 +1,52 @@ +#include + +#include +#include + +#include +using namespace boost::python; + +#include "alg/seq_span.hpp" + +#include "stl_container_adapter.hpp" + +void export_seq_span() +{ + enum_("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", + init + >() ) + .def("__len__", &SeqSpan::size) + .def("__getitem__", &std_item::get_const, + return_value_policy()) + .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 >(); +} diff --git a/py/stl_container_adapter.cpp b/py/stl_container_adapter.cpp new file mode 100644 index 0000000..5135b44 --- /dev/null +++ b/py/stl_container_adapter.cpp @@ -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"); +} + diff --git a/py/stl_container_adapter.hpp b/py/stl_container_adapter.hpp index 8758247..a298909 100644 --- a/py/stl_container_adapter.hpp +++ b/py/stl_container_adapter.hpp @@ -2,55 +2,81 @@ #define STL_CONTAINER_ADAPTER_HPP_ #include +#include + #include // 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 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=0 && i=0 && i=0 && i=0 && i=0 && i=0 && i 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 index 0000000..09747de --- /dev/null +++ b/py/test/TestSeqSpan.py @@ -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')) -- 2.30.2