unofficial mirror of notmuch@notmuchmail.org
 help / color / mirror / code / Atom feed
* [PATCH] SWIG bindings
@ 2010-01-26  3:53 Ben Gamari
  2010-01-26  3:53 ` [PATCH] Add SWIG interface file Ben Gamari
  0 siblings, 1 reply; 4+ messages in thread
From: Ben Gamari @ 2010-01-26  3:53 UTC (permalink / raw)
  To: notmuch


Hey all,

Here's the latest version of my patch adding SWIG interface generation to
notmuch. It has been rebased on the shared-library patches I sent over earlier
this week, so you'll need those as well. Unfortunately, SWIG has effectively no
support for exposing notmuch's C-style object-oriented interface in a
reasonable manner. Therefore, I use SWIG to generate a set of low-level
bindings which is then wrapped with a light-weight module properly exposing the
functions in their intended class structure. This seems like a fairly good
solution, given the wide variety of capabilities possessed by modern high-level
languages.

As you can see, it all works quite nicely,

    [2252 ben@ben-laptop swig(swig)] $ python 
    Python 2.6.4 (r264:75706, Dec  7 2009, 18:43:55) 
    [GCC 4.4.1] on linux2
    Type "help", "copyright", "credits" or "license" for more information.
    >>> from notmuch import Database
    >>> db=Database.open('/home/ben/.mail', Database.MODE_READ_ONLY)
    >>> for tag in db.get_all_tags(): print tag
    ... 
    attachment
    inbox
    unread
    
Let me know what you think. Thanks!

- Ben

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH] Add SWIG interface file
  2010-01-26  3:53 [PATCH] SWIG bindings Ben Gamari
@ 2010-01-26  3:53 ` Ben Gamari
  2010-01-26  5:38   ` Ben Gamari
  0 siblings, 1 reply; 4+ messages in thread
From: Ben Gamari @ 2010-01-26  3:53 UTC (permalink / raw)
  To: notmuch

---
 swig/Makefile        |   18 ++++
 swig/notmuch.py      |  222 ++++++++++++++++++++++++++++++++++++++++++++++++++
 swig/notmuch_funcs.i |    9 ++
 3 files changed, 249 insertions(+), 0 deletions(-)
 create mode 100644 swig/Makefile
 create mode 100644 swig/notmuch.py
 create mode 100644 swig/notmuch_funcs.i

diff --git a/swig/Makefile b/swig/Makefile
new file mode 100644
index 0000000..7b10ea7
--- /dev/null
+++ b/swig/Makefile
@@ -0,0 +1,18 @@
+include ../Makefile.config
+
+INCLUDES=-I../lib -I/usr/include/python2.6
+CFLAGS=${INCLUDES}
+
+all : python
+
+python : _notmuch_funcs.so notmuch.py
+
+_notmuch_funcs.so : notmuch_funcs_wrap.o
+	g++ -shared -lnotmuch ${XAPIAN_LDFLAGS} ${GMIME_LDFLAGS} ${TALLOC_LDFLAGS} -o $@ $<
+
+%_wrap.o : %_wrap.c
+	gcc ${CFLAGS} -fPIC -c -o $@ $<
+
+%_wrap.c %.py : %.i 
+	swig -python ${INCLUDES} $<
+
diff --git a/swig/notmuch.py b/swig/notmuch.py
new file mode 100644
index 0000000..e17f71f
--- /dev/null
+++ b/swig/notmuch.py
@@ -0,0 +1,222 @@
+import notmuch_funcs as nm
+from datetime import datetime
+
+class Exception(Exception):
+        def get_message():
+                errors = {
+                        nm.NOTMUCH_STATUS_OUT_OF_MEMORY: 'out of memory',
+                        nm.NOTMUCH_STATUS_READONLY_DATABASE: 'database opened as read-only',
+                        nm.NOTMUCH_STATUS_XAPIAN_EXCEPTION: 'xapian error',
+                        nm.NOTMUCH_STATUS_FILE_ERROR: 'file error',
+                        nm.NOTMUCH_STATUS_FILE_NOT_EMAIL: 'file not email message',
+                        nm.NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID: 'duplicate message id',
+                        nm.NOTMUCH_STATUS_NULL_POINTER: 'null pointer',
+                        nm.NOTMUCH_STATUS_TAG_TOO_LONG: 'tag name too long',
+                        nm.NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW: 'unbalanced freeze/thaw',
+                }
+                return errors.get(self.status, 'unknown error')
+
+        def __init__(self, status):
+                self.status = status
+
+def _handle_status(status):
+        if (status != nm.NOTMUCH_STATUS_SUCCESS):
+                raise Exception(status)
+
+class Database(object):
+        MODE_READ_ONLY = nm.NOTMUCH_DATABASE_MODE_READ_ONLY
+        MODE_READ_WRITE = nm.NOTMUCH_DATABASE_MODE_READ_WRITE
+
+        def __init__(self, db):
+                if not db:
+                        raise Exception("Failed to open database")
+                self.db = db
+
+        @staticmethod
+        def create(path):
+                return Database(nm.notmuch_database_create(path))
+
+        @staticmethod
+        def open(path, mode):
+                return Database(nm.notmuch_database_open(path, mode))
+
+        def close(self):
+                nm.notmuch_database_close(self.db)
+
+        def get_path(self):
+                return nm.notmuch_database_get_path(self.db)
+
+        def set_timestamp(self, key, timestamp):
+                _handle_status(nm.notmuch_database_set_timestamp(self.db, key, timestamp))
+
+        def get_timestamp(self, key):
+                return datetime.fromtimestamp(nm.notmuch_database_get_timestamp(self.db, key))
+
+        def add_message(self, filename, message):
+                _handle_status(nm.notmuch_database_add_message(self.db, filename, message.message))
+
+        def find_message(self, message_id):
+                return Message(nm.notmuch_database_find_message(self.db, message_id))
+
+        def get_all_tags(self):
+                return Tags(nm.notmuch_database_get_all_tags(self.db))
+
+        def __destroy__(self):
+                self.close()
+        
+class Query(object):
+        def __init__(self, db, query_string):
+                self.query = nm.notmuch_query_create(db, query_string)
+                if not self.query: # This might not work
+                        raise "Failed to create query"
+
+        def set_sort(self, sort):
+                nm.notmuch_query_set_sort(self.query, sort)
+                
+        def search_threads(self):
+                return Threads(nm.notmuch_query_search_threads(self.query))
+
+        def search_messages(self):
+                return Messages(nm.notmuch_query_search_messages(self.query))
+
+        def count_message(self):
+                return nm.notmuch_query_count_messages(self.query)
+
+        def __destroy__(self):
+                nm.notmuch_query_destroy(self.query)
+
+class Tags(object):
+        def __init__(self, tags):
+                self.tags = tags
+
+        def __iter__(self):
+                return self
+
+        def next(self):
+                if not nm.notmuch_tags_has_more(self.tags):
+                        raise StopIteration
+                else:
+                        h = nm.notmuch_tags_get(self.tags)
+                        nm.notmuch_tags_advance(self.tags)
+                        return h
+
+        def __destroy__(self):
+                nm.notmuch_messages_destroy(self.tags)
+
+class Thread(object):
+        def __init__(self, thread):
+                self.thread = thread
+
+        def get_thread_id(self):
+                return nm.notmuch_thread_get_thread_id(self.thread)
+
+        def get_total_messages(self):
+                return nm.notmuch_thread_total_messages(self.thread)
+        
+        def get_toplevel_messages(self):
+                return Messages(nm.notmuch_thread_get_toplevel_messages(self.thread))
+
+        def get_matched_messages(self):
+                return nm.notmuch_thread_get_matched_messages(self.thread)
+
+        def get_authors(self):
+                return nm.notmuch_thread_get_authors(self.thread)
+
+        def get_subject(self):
+                return nm.notmuch_thread_get_subject(self.thread)
+
+        def get_oldest_date(self):
+                return nm.notmuch_thread_get_oldest_date(self.thread)
+
+        def get_newest_date(self):
+                return nm.notmuch_thread_get_newest_date(self.thread)
+
+        def get_tags(self):
+                return Tags(nm.notmuch_thread_get_tags(self.thread))
+
+        def __destroy__(self):
+                nm.notmuch_thread_destroy(self.thread)
+
+class Threads(object):
+        def __init__(self, threads):
+                self.threads = threads
+
+        def __iter__(self):
+                return self
+
+        def next(self):
+                if not nm.notmuch_threads_has_more(self.threads):
+                        raise StopIteration
+                else:
+                        h = Thread(nm.notmuch_threads_get(self.threads))
+                        nm.notmuch_threads_advance(self.threads)
+                        return h
+
+        def __destroy__(self):
+                nm.notmuch_threads_destroy(self.threads)
+
+class Messages(object):
+        def __init__(self, messages):
+                self.messages = messages
+
+        def __iter__(self):
+                return self
+
+        def next(self):
+                if not nm.notmuch_messages_has_more(self.messages):
+                        raise StopIteration
+                else:
+                        h = Message(nm.notmuch_messages_get(self.messages))
+                        nm.notmuch_messages_advance(self.messages)
+                        return h
+
+        def __destroy__(self):
+                nm.notmuch_messages_destroy(self.messages)
+
+        def collect_tags(self):
+                return Tags(nm.notmuch_messages_collect_tags(self.messages))
+
+class Message(object):
+        def __init__(self, message):
+                self.message = message
+
+        def get_replies(self):
+                return Messages(nm.notmuch_message_get_replies(self.message))
+
+        def get_filename(self):
+                return nm.notmuch_message_get_filename(self.message)
+
+        def get_flag(self, flag):
+                return bool(nm.notmuch_message_get_flag(self.message, flag))
+
+        def set_flag(self, flag, value):
+                return nm.notmuch_message_set_flag(self.message, flag, value)
+
+        def get_date(self):
+                return datetime.fromtimestamp(nm.notmuch_message_get_date(self.message))
+
+        def get_header(self, header):
+                return nm.notmuch_message_get_header(self.message, header)
+
+        def get_tags(self):
+                return Tags(nm.notmuch_message_get_tags(self.message))
+
+        def add_tag(self, tag):
+                _handle_status(nm.notmuch_message_add_tag(self.message, tag))
+
+        def remove_tag(self, tag):
+                _handle_status(nm.notmuch_message_remove_tag(self.message, tag))
+
+        def remove_all_tags(self):
+                nm.notmuch_message_remove_all_tags(self.message)
+
+        def freeze(self):
+                nm.notmuch_message_freeze(self.message)
+
+        def thaw(self):
+                nm.notmuch_message_thaw(self.message)
+
+        def __destroy__(self):
+                nm.notmuch_message_destroy(self.message)
+
+
diff --git a/swig/notmuch_funcs.i b/swig/notmuch_funcs.i
new file mode 100644
index 0000000..cc1826e
--- /dev/null
+++ b/swig/notmuch_funcs.i
@@ -0,0 +1,9 @@
+%module notmuch_funcs
+
+%{
+#define SWIG_FILE_WITH_INIT
+#include "notmuch.h"
+%}
+
+%include "notmuch.h"
+
-- 
1.6.3.3

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH] Add SWIG interface file
  2010-01-26  3:53 ` [PATCH] Add SWIG interface file Ben Gamari
@ 2010-01-26  5:38   ` Ben Gamari
  2010-01-26  6:15     ` [PATCH] Build and link against notmuch shared library Ben Gamari
  0 siblings, 1 reply; 4+ messages in thread
From: Ben Gamari @ 2010-01-26  5:38 UTC (permalink / raw)
  To: notmuch


For the record, both the swig branch and the shared-library branch are
available from,
  anonymous: git://goldnerlab.physics.umass.edu/notmuch
  gitweb: http://goldnerlab.physics.umass.edu/git?p=notmuch.git;a=summary

Hope this helps,

- Ben

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH] Build and link against notmuch shared library
  2010-01-26  5:38   ` Ben Gamari
@ 2010-01-26  6:15     ` Ben Gamari
  0 siblings, 0 replies; 4+ messages in thread
From: Ben Gamari @ 2010-01-26  6:15 UTC (permalink / raw)
  To: notmuch

Inger in #notmuch brought to light some build issues that will occur when the
notmuch binary is being built before libnotmuch is installed. Here is an
updated patch that resolves these issues.

---
 .gitignore         |    1 +
 Makefile           |    1 +
 Makefile.local     |    6 ++++--
 lib/Makefile.local |    9 +++++----
 4 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/.gitignore b/.gitignore
index efa98fb..daf8094 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,7 @@ tags
 .deps
 notmuch
 notmuch.1.gz
+libnotmuch.so*
 *.[ao]
 *~
 .*.swp
diff --git a/Makefile b/Makefile
index 64b9d4a..6f296bb 100644
--- a/Makefile
+++ b/Makefile
@@ -1,3 +1,4 @@
+SONAME = libnotmuch.so.1
 WARN_CXXFLAGS=-Wall -Wextra -Wwrite-strings -Wswitch-enum
 WARN_CFLAGS=$(WARN_CXXFLAGS) -Wmissing-declarations
 
diff --git a/Makefile.local b/Makefile.local
index 04bac83..5b6af0a 100644
--- a/Makefile.local
+++ b/Makefile.local
@@ -21,8 +21,8 @@ notmuch_client_srcs =		\
 	show-message.c
 
 notmuch_client_modules = $(notmuch_client_srcs:.c=.o)
-notmuch: $(notmuch_client_modules) lib/notmuch.a
-	$(call quiet,CXX,$(LDFLAGS)) $^ $(FINAL_LDFLAGS) -o $@
+notmuch: $(notmuch_client_modules) lib/libnotmuch.so
+	$(call quiet,CC,$(LDFLAGS)) -Llib -lnotmuch $(filter-out lib/libnotmuch.so,$^) $(FINAL_LDFLAGS) -o $@
 
 notmuch.1.gz: notmuch.1
 	$(call quiet,gzip) --stdout $^ > $@
@@ -33,6 +33,8 @@ install: all notmuch.1.gz
 		install -d $$d ; \
 	done ;
 	install notmuch $(DESTDIR)$(prefix)/bin/
+	install lib/$(SONAME) $(DESTDIR)$(prefix)/lib/
+	ln -sf $(DESTDIR)$(prefix)/lib/$(SONAME) $(DESTDIR)$(prefix)/lib/libnotmuch.so
 	install -m0644 notmuch.1.gz $(DESTDIR)$(prefix)/share/man/man1/
 
 install-emacs: install emacs
diff --git a/lib/Makefile.local b/lib/Makefile.local
index 70489e1..a6462ae 100644
--- a/lib/Makefile.local
+++ b/lib/Makefile.local
@@ -1,5 +1,5 @@
 dir=lib
-extra_cflags += -I$(dir)
+extra_cflags += -I$(dir) -fPIC
 
 libnotmuch_c_srcs =		\
 	$(dir)/libsha1.c	\
@@ -18,8 +18,9 @@ libnotmuch_cxx_srcs =		\
 	$(dir)/thread.cc
 
 libnotmuch_modules = $(libnotmuch_c_srcs:.c=.o) $(libnotmuch_cxx_srcs:.cc=.o)
-$(dir)/notmuch.a: $(libnotmuch_modules)
-	$(call quiet,AR) rcs $@ $^
+$(dir)/libnotmuch.so : $(libnotmuch_modules)
+	$(call quiet,CXX,$(LDFLAGS)) $^ $(FINAL_LDFLAGS) -shared -Wl,-soname=$(SONAME) -o $@
+	ln -sf $(SONAME) $@
 
 SRCS  := $(SRCS) $(libnotmuch_c_srcs) $(libnotmuch_cxx_srcs)
-CLEAN := $(CLEAN) $(libnotmuch_modules) $(dir)/notmuch.a
+CLEAN := $(CLEAN) $(libnotmuch_modules) $(dir)/libnotmuch.so *.so
-- 
1.6.3.3

^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2010-01-26  6:16 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-01-26  3:53 [PATCH] SWIG bindings Ben Gamari
2010-01-26  3:53 ` [PATCH] Add SWIG interface file Ben Gamari
2010-01-26  5:38   ` Ben Gamari
2010-01-26  6:15     ` [PATCH] Build and link against notmuch shared library Ben Gamari

Code repositories for project(s) associated with this public inbox

	https://yhetil.org/notmuch.git/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).