From: Sebastian Spaeth Date: Mon, 15 Mar 2010 14:47:15 +0000 (+0100) Subject: initial revision. See README for what works X-Git-Tag: 0.3~121^2~104 X-Git-Url: https://git.notmuchmail.org/git?p=notmuch;a=commitdiff_plain;h=b4fdf0ae30e0847a759c01524898d0b9d803c43b;ds=sidebyside initial revision. See README for what works --HG-- extra : transplant_source : %FBQ%B3%C5%0E%85%E8%0B%154%F5t%19%2A%C6p%A5%1Dd%F1 --- diff --git a/.hgignore b/.hgignore new file mode 100644 index 00000000..c4aa446d --- /dev/null +++ b/.hgignore @@ -0,0 +1,2 @@ +.*\.pyc$ +~$ \ No newline at end of file diff --git a/README b/README new file mode 100644 index 00000000..3b96e648 --- /dev/null +++ b/README @@ -0,0 +1,18 @@ +>>>from cnotmuch import notmuch +>>>db = notmuch.Database("/home/spaetz/mail") +>>>db.get_path() +>>>tags = db.get_all_tags() +>>>for tag in tags: +>>> print tag + +#--------------------------------------------- + +>>>db = notmuch.Database("/home/spaetz/mailHAHA") +NotmuchError: Could not open the specified database + +#--------------------------------------------- + +>>>tags = notmuch.Database("/home/spaetz/mail").get_all_tags() +>>>del(tags) +Freeing the Tags now +Freeing the database now \ No newline at end of file diff --git a/cnotmuch/__init__.py b/cnotmuch/__init__.py new file mode 100644 index 00000000..e24dc966 --- /dev/null +++ b/cnotmuch/__init__.py @@ -0,0 +1,3 @@ +__version__=0.1 +__author__ ="Sebastian Spaeth " +#ctypes.util.find_library("notmuch") diff --git a/cnotmuch/database.py b/cnotmuch/database.py new file mode 100644 index 00000000..f3082e9a --- /dev/null +++ b/cnotmuch/database.py @@ -0,0 +1,147 @@ +import ctypes +from ctypes import c_int, c_char_p, c_void_p +from cnotmuch.globals import nmlib, STATUS, NotmuchError + + +class Database(object): + """ Wrapper around a notmuch_database_t + + Do note that as soon as we tear down this object, all derived queries, + threads, and messages will be freed as well. + """ + #constants + MODE_READ_ONLY = 0 + MODE_READ_WRITE = 1 + + _std_db_path = None + """Class attribute of users default database""" + + """notmuch_database_get_path (notmuch_database_t *database)""" + _get_path = nmlib.notmuch_database_get_path + _get_path.restype = c_char_p + + """notmuch_database_open (const char *path, notmuch_database_mode_t mode)""" + _open = nmlib.notmuch_database_open + _open.restype = c_void_p + + """notmuch_database_get_all_tags (notmuch_database_t *database)""" + _get_all_tags = nmlib.notmuch_database_get_all_tags + _get_all_tags.restype = c_void_p + + class notmuch_database_t(ctypes.Structure): + """the opaque database that is returned by functions.""" + pass + + def __init__(self, path=None, create=False, status= MODE_READ_ONLY): + """ Open or create a notmuch database""" + self._db = None + if create == False: + self.open(path, status) + else: + #TODO: implement + raise NotmuchError(message="Not implemented yet") + + #TODO: make a proper function + create=nmlib.notmuch_database_create + """ notmuch_database_create(const char *path):""" + + def open(self, path=None, status= MODE_READ_ONLY): + """calls notmuch_database_open + + If path is None, we will try to read a users notmuch configuration and + use his default database. + :returns: Raises :exc:`notmuch.NotmuchError` in case + of any failure (after printing an error message on stderr). + """ + if path is None: + if Database._std_db_path is None: + from ConfigParser import SafeConfigParser + import os.path + config = SafeConfigParser() + config.read(os.path.expanduser('~/.notmuch-config')) + if not config.has_option('database','path'): + raise NotmuchError(message= + "No DB path specified and no user default found") + Database._std_db_path=config.get('database','path') + path = Database._std_db_path + + res = Database._open(path, status) + + if res is None: + raise NotmuchError( + message="Could not open the specified database") + self._db = res + + def get_path(self): + """notmuch_database_get_path (notmuch_database_t *database); """ + return Database._get_path(self._db) + + #TODO:implement + #If no message is found with the given message_id or if an + #out-of-memory situation occurs, this function returns NULL. + #notmuch_message_t * + #notmuch_database_find_message (notmuch_database_t *database, + # const char *message_id); + + def get_all_tags(self): + """Return a Tags() object (list of all tags found in the database) + + :returns: Tags() object or raises :exc:`NotmuchError` with + STATUS.NULL_POINTER on error + """ + tags_p = Database._get_all_tags (self._db) + if tags_p == None: + raise NotmuchError(STATUS.NULL_POINTER) + return Tags(tags_p, self) + + def __repr__(self): + return "'Notmuch DB " + self.get_path() + "'" + + def __del__(self): + """Close and free the notmuch database if needed""" + if self._db is not None: + print("Freeing the database now") + nmlib.notmuch_database_close(self._db) + + @property + def db_p(self): + """Returns a pointer to the current notmuch_database_t or None""" + return self._db + + +#------------------------------------------------------------------------------ +class Tags(object): + """Wrapper around notmuch_tags_t""" + class notmuch_tags_t(ctypes.Structure): + """the opaque tags struct that is returned by functions.""" + pass + + #notmuch_tags_get + _get = nmlib.notmuch_tags_get + _get.restype = c_char_p + + def __init__(self, tags_p, db=None): + """ Is passed the db these tags are derived from, and saves a + reference to it, so we can automatically delete the db object + once all derived objects are dead.""" + self._tags = tags_p + self._db = db + print "inited tags with %d %s" %(tags_p, str(db)) + + def __iter__(self): + """ Make Tags an iterator """ + return self + + def next(self): + if self._tags is None: + raise StopIteration + nmlib.notmuch_tags_move_to_next(self._tags) + if not nmlib.notmuch_tags_valid(self._tags): + raise StopIteration + return Tags._get (self._tags) + + def __del__(self): + """Close and free the notmuch tags""" + if self._tags is not None: + print("Freeing the Tags now") + nmlib.notmuch_tags_destroy (self._tags) diff --git a/cnotmuch/globals.py b/cnotmuch/globals.py new file mode 100644 index 00000000..ff765167 --- /dev/null +++ b/cnotmuch/globals.py @@ -0,0 +1,38 @@ +from ctypes import CDLL, c_char_p, c_int +#package-global instance of the notmuch library +#TODO: lazy load this on first access? +nmlib = CDLL('/usr/local/lib/libnotmuch.so') + +class STATUS(object): + SUCCESS = 0 + OUT_OF_MEMORY = 1 + READ_ONLY_DATABASE = 2 + XAPIAN_EXCEPTION = 3 + FILE_ERROR = 4 + FILE_NOT_EMAIL = 5 + DUPLICATE_MESSAGE_ID = 6 + NULL_POINTER = 7 + TAG_TOO_LONG = 8 + UNBALANCED_FREEZE_THAW = 9 + LAST_STATUS = 10 + + """Get a string representation of a notmuch_status_t value.""" + status2str = nmlib.notmuch_status_to_string + status2str.restype = c_char_p + status2str.argtypes = [c_int] + + def __init__(self, status): + self._status = status + + def __str__(self): + """Get a string representation of a notmuch_status_t value.""" + return str(STATUS.status2str(self._status)) + +class NotmuchError(Exception): + def __init__(self, status=None, message=None): + """Is initiated with a (notmuch.STATUS[,message=None])""" + super(NotmuchError, self).__init__(message, status) + + def __str__(self): + if self.args[0] is not None: return self.args[0] + else: return str(STATUS(self.args[1])) diff --git a/cnotmuch/notmuch.py b/cnotmuch/notmuch.py new file mode 100644 index 00000000..8400f37c --- /dev/null +++ b/cnotmuch/notmuch.py @@ -0,0 +1,12 @@ +import ctypes +from ctypes import c_int, c_char_p +from database import Database,Tags +from cnotmuch.globals import nmlib,STATUS,NotmuchError + +# 114 typedef struct _notmuch_query notmuch_query_t; +# 115 typedef struct _notmuch_threads notmuch_threads_t; +# 116 typedef struct _notmuch_thread notmuch_thread_t; +# 117 typedef struct _notmuch_messages notmuch_messages_t; +# 118 typedef struct _notmuch_message notmuch_message_t; +# 120 typedef struct _notmuch_directory notmuch_directory_t; +# 121 typedef struct _notmuch_filenames notmuch_filenames_t;