import ctypes, os
-from ctypes import c_int, c_char_p, c_void_p, c_uint, c_uint64, c_bool
+from ctypes import c_int, c_char_p, c_void_p, c_uint, c_uint64, c_bool, byref
from cnotmuch.globals import nmlib, STATUS, NotmuchError, Enum
import logging
from datetime import date
return notmuch_database_needs_upgrade(self.db)
+ def add_message(self, filename):
+ """Adds a new message to the database
+
+ `filename` should be a path relative to the path of the open
+ database (see :meth:`get_path`), or else should be an absolute
+ filename with initial components that match the path of the
+ database.
+
+ The file should be a single mail message (not a multi-message mbox)
+ that is expected to remain at its current location, since the
+ notmuch database will reference the filename, and will not copy the
+ entire contents of the file.
+
+ :returns: On success, we return
+
+ 1) a :class:`Message` object that can be used for things
+ such as adding tags to the just-added message.
+ 2) one of the following STATUS values:
+
+ STATUS.SUCCESS
+ Message successfully added to database.
+ STATUS.DUPLICATE_MESSAGE_ID
+ Message has the same message ID as another message already
+ in the database. The new filename was successfully added
+ to the message in the database.
+
+ :rtype: 2-tuple(:class:`Message`, STATUS)
+
+ :exception: Raises a :exc:`NotmuchError` with the following meaning.
+ If such an exception occurs, nothing was added to the database.
+
+ STATUS.FILE_ERROR
+ An error occurred trying to open the file, (such as
+ permission denied, or file not found, etc.).
+ STATUS.FILE_NOT_EMAIL
+ The contents of filename don't look like an email message.
+ STATUS.READ_ONLY_DATABASE
+ Database was opened in read-only mode so no message can
+ be added.
+ STATUS.NOT_INITIALIZED
+ The database has not been initialized.
+ """
+ # Raise a NotmuchError if not initialized
+ self._verify_initialized_db()
+
+ msg_p = c_void_p()
+ status = nmlib.notmuch_database_add_message(self._db,
+ filename,
+ byref(msg_p))
+
+ if not status in [STATUS.SUCCESS,STATUS.DUPLICATE_MESSAGE_ID]:
+ raise NotmuchError(status)
+
+ #construct Message() and return
+ msg = Message(msg_p, self)
+ return (msg, status)
+
+ def remove_message(self, filename):
+ """Removes a message from the given notmuch database
+
+ Note that only this particular filename association is removed from
+ the database. If the same message (as determined by the message ID)
+ is still available via other filenames, then the message will
+ persist in the database for those filenames. When the last filename
+ is removed for a particular message, the database content for that
+ message will be entirely removed.
+
+ :returns: A STATUS.* value with the following meaning:
+
+ STATUS.SUCCESS
+ The last filename was removed and the message was removed
+ from the database.
+ STATUS.DUPLICATE_MESSAGE_ID
+ This filename was removed but the message persists in the
+ database with at least one other filename.
+
+ :exception: Raises a :exc:`NotmuchError` with the following meaning.
+ If such an exception occurs, nothing was removed from the database.
+
+ STATUS.READ_ONLY_DATABASE
+ Database was opened in read-only mode so no message can be
+ removed.
+ STATUS.NOT_INITIALIZED
+ The database has not been initialized.
+ """
+ # Raise a NotmuchError if not initialized
+ self._verify_initialized_db()
+
+ status = nmlib.notmuch_database_remove_message(self._db,
+ filename)
+
def find_message(self, msgid):
"""Returns a :class:`Message` as identified by its message ID
raise NotmuchError(STATUS.NULL_POINTER)
return Tags(tags_p, self)
+ def create_query(self, querystring):
+ """Returns a :class:`Query` derived from this database
+
+ This is a shorthand method for doing::
+
+ # short version
+ # Automatically frees the Database() when 'q' is deleted
+
+ q = Database(dbpath).create_query('from:"Biene Maja"')
+
+ # long version, which is functionally equivalent but will keep the
+ # Database in the 'db' variable around after we delete 'q':
+
+ db = Database(dbpath)
+ q = Query(db,'from:"Biene Maja"')
+
+ This function is a python extension and not in the underlying C API.
+ """
+ # Raise a NotmuchError if not initialized
+ self._verify_initialized_db()
+
+ return Query(self, querystring)
+
def __repr__(self):
return "'Notmuch DB " + self.get_path() + "'"
"""len(:class:`Messages`) returns the number of contained messages
.. note:: As this iterates over the messages, we will not be able to
- iterate over them again (as in retrieve them)!
+ iterate over them again! So this will fail::
+
+ #THIS FAILS
+ msgs = Database().create_query('').search_message()
+ if len(msgs) > 0: #this 'exhausts' msgs
+ # next line raises NotmuchError(STATUS.NOT_INITIALIZED)!!!
+ for msg in msgs: print msg
"""
if self._msgs is None:
raise NotmuchError(STATUS.NOT_INITIALIZED)
_get_thread_id = nmlib.notmuch_message_get_thread_id
_get_thread_id.restype = c_char_p
+ """notmuch_message_get_replies"""
+ _get_replies = nmlib.notmuch_message_get_replies
+ _get_replies.restype = c_void_p
+
"""notmuch_message_get_tags (notmuch_message_t *message)"""
_get_tags = nmlib.notmuch_message_get_tags
_get_tags.restype = c_void_p
return Message._get_thread_id (self._msg);
+ def get_replies(self):
+ """Gets all direct replies to this message as :class:`Messages` iterator
+
+ .. note:: This call only makes sense if 'message' was
+ ultimately obtained from a :class:`Thread` object, (such as
+ by coming directly from the result of calling
+ :meth:`Thread.get_toplevel_messages` or by any number of
+ subsequent calls to :meth:`get_replies`). If this message was
+ obtained through some non-thread means, (such as by a call
+ to :meth:`Query.search_messages`), then this function will
+ return `None`.
+
+ :returns: :class:`Messages` or `None` if there are no replies to
+ this message.
+ :exception: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if the message
+ is not initialized.
+ """
+ if self._msg is None:
+ raise NotmuchError(STATUS.NOT_INITIALIZED)
+
+ msgs_p = Message._get_replies(self._msg);
+
+ if msgs_p is None:
+ return None
+
+ return Messages(msgs_p,self)
+
def get_date(self):
"""Returns time_t of the message date
msg['from'] = self.get_header('from')
msg['tags'] = str(self.get_tags())
msg['date'] = date.fromtimestamp(self.get_date())
- return "%(from)s (%(date)s) (%(tags)s)" % (msg)
+ replies = self.get_replies()
+ msg['replies'] = len(replies) if replies is not None else -1
+ return "%(from)s (%(date)s) (%(tags)s) (%(replies)d) replies" % (msg)
def format_as_text(self):
"""Output like notmuch show (Not implemented)"""