]> git.notmuchmail.org Git - notmuch/blobdiff - cnotmuch/database.py
docs: Various typo fixes in docstrings
[notmuch] / cnotmuch / database.py
index 92afa0a047d8f773ec76b1459723bc9f0cdbc1b1..f611777cafbc869cbf3addc6f7e558f0f84c719a 100644 (file)
@@ -1,4 +1,4 @@
-import ctypes
+import ctypes, os
 from ctypes import c_int, c_char_p, c_void_p, c_uint, c_uint64, c_bool
 from cnotmuch.globals import nmlib, STATUS, NotmuchError, Enum
 import logging
@@ -13,12 +13,12 @@ class Database(object):
            as well. Accessing these objects will lead to segfaults and
            other unexpected behavior. See above for more details.
     """
-    MODE = Enum(['READ_ONLY','READ_WRITE'])
-    """Constants: Mode in which to open the database"""
-
     _std_db_path = None
     """Class attribute to cache user's default database"""
 
+    MODE = Enum(['READ_ONLY','READ_WRITE'])
+    """Constants: Mode in which to open the database"""
+
     """notmuch_database_get_path (notmuch_database_t *database)"""
     _get_path = nmlib.notmuch_database_get_path
     _get_path.restype = c_char_p
@@ -43,11 +43,14 @@ class Database(object):
     _create = nmlib.notmuch_database_create
     _create.restype = c_void_p
 
-    def __init__(self, path=None, create=False, mode= MODE.READ_ONLY):
-        """If *path* is *None*, we will try to read a users notmuch
-        configuration and use his default database. If *create* is `True`,
-        the database will always be created in
-        :attr:`MODE`.READ_WRITE mode.
+    def __init__(self, path=None, create=False, mode= 0):
+        """If *path* is *None*, we will try to read a users notmuch 
+        configuration and use his configured database. The location of the 
+        configuration file can be specified through the environment variable
+        *NOTMUCH_CONFIG*, falling back to the default `~/.notmuch-config`.
+
+        If *create* is `True`, the database will always be created in
+        :attr:`MODE`.READ_WRITE mode. Default mode for opening is READ_ONLY.
 
         :param path:   Directory to open/create the database in (see
                        above for behavior if `None`)
@@ -74,6 +77,11 @@ class Database(object):
         else:
             self.create(path)
 
+    def _verify_initialized_db(self):
+        """Raises a NotmuchError in case self._db is still None"""
+        if self._db is None:
+            raise NotmuchError(STATUS.NOT_INITIALIZED)            
+
     def create(self, path):
         """Creates a new notmuch database
 
@@ -94,14 +102,14 @@ class Database(object):
             raise NotmuchError(
             message="Cannot create db, this Database() already has an open one.")
 
-        res = Database._create(path, MODE.READ_WRITE)
+        res = Database._create(path, Database.MODE.READ_WRITE)
 
         if res is None:
             raise NotmuchError(
                 message="Could not create the specified database")
         self._db = res
 
-    def open(self, path, mode= MODE.READ_ONLY): 
+    def open(self, path, mode= 0): 
         """Opens an existing database
 
         This function is used by __init__() and usually does not need
@@ -126,6 +134,9 @@ class Database(object):
         """Returns the file path of an open database
 
         Wraps notmuch_database_get_path"""
+        # Raise a NotmuchError if not initialized
+        self._verify_initialized_db()
+
         return Database._get_path(self._db)
 
     def get_version(self):
@@ -135,8 +146,8 @@ class Database(object):
         :exception: :exc:`NotmuchError` with STATUS.NOT_INITIALIZED if
                     the database was not intitialized.
         """
-        if self._db is None:
-            raise NotmuchError(STATUS.NOT_INITIALIZED)
+        # Raise a NotmuchError if not initialized
+        self._verify_initialized_db()
 
         return Database._get_version (self._db)
 
@@ -152,8 +163,8 @@ class Database(object):
         :exception: :exc:`NotmuchError` with STATUS.NOT_INITIALIZED if
                     the database was not intitialized.
         """
-        if self._db is None:
-            raise NotmuchError(STATUS.NOT_INITIALIZED)
+        # Raise a NotmuchError if not initialized
+        self._verify_initialized_db()
 
         return notmuch_database_needs_upgrade(self.db) 
 
@@ -169,8 +180,9 @@ class Database(object):
         :exception: :exc:`NotmuchError` with STATUS.NOT_INITIALIZED if
                   the database was not intitialized.
         """
-        if self._db is None:
-            raise NotmuchError(STATUS.NOT_INITIALIZED)
+        # Raise a NotmuchError if not initialized
+        self._verify_initialized_db()
+
         msg_p = Database._find_message(self._db, msgid)
         if msg_p is None:
             return None
@@ -182,8 +194,8 @@ class Database(object):
         :returns: :class:`Tags`
         :execption: :exc:`NotmuchError` with STATUS.NULL_POINTER on error
         """
-        if self._db is None:
-            raise NotmuchError(STATUS.NOT_INITIALIZED)
+        # Raise a NotmuchError if not initialized
+        self._verify_initialized_db()
 
         tags_p = Database._get_all_tags (self._db)
         if tags_p == None:
@@ -204,9 +216,10 @@ class Database(object):
 
         Throws a NotmuchError if it cannot find it"""
         from ConfigParser import SafeConfigParser
-        import os.path
         config = SafeConfigParser()
-        config.read(os.path.expanduser('~/.notmuch-config'))
+        conf_f = os.getenv('NOTMUCH_CONFIG',
+                           os.path.expanduser('~/.notmuch-config'))
+        config.read(conf_f)
         if not config.has_option('database','path'):
             raise NotmuchError(message=
                                "No DB path specified and no user default found")
@@ -214,14 +227,16 @@ class Database(object):
 
     @property
     def db_p(self):
-        """Property returning a pointer to the notmuch_database_t or `None`
+        """Property returning a pointer to `notmuch_database_t` or `None`
 
-        This should normally not be needed by a user."""
+        This should normally not be needed by a user (and is not yet
+        guaranteed to remain stable in future versions).
+        """
         return self._db
 
 #------------------------------------------------------------------------------
 class Query(object):
-    """ Represents a search query on an opened :class:`Database`.
+    """Represents a search query on an opened :class:`Database`.
 
     A query selects and filters a subset of messages from the notmuch
     database we derive from.
@@ -246,6 +261,11 @@ class Query(object):
     _search_messages = nmlib.notmuch_query_search_messages
     _search_messages.restype = c_void_p
 
+
+    """notmuch_query_count_messages"""
+    _count_messages = nmlib.notmuch_query_count_messages
+    _count_messages.restype = c_uint
+
     def __init__(self, db, querystr):
         """
         :param db: An open database which we derive the Query from.
@@ -258,7 +278,7 @@ class Query(object):
         self.create(db, querystr)
 
     def create(self, db, querystr):
-        """Creates a new query derived from a Database.
+        """Creates a new query derived from a Database
 
         This function is utilized by __init__() and usually does not need to 
         be called directly.
@@ -323,6 +343,25 @@ class Query(object):
 
         return Messages(msgs_p,self)
 
+    def count_messages(self):
+        """Estimate the number of messages matching the query
+
+        This function performs a search and returns Xapian's best
+        guess as to the number of matching messages. It is much faster
+        than performing :meth:`search_messages` and counting the
+        result with `len()` (although it always returned the same
+        result in my tests). Technically, it wraps the underlying
+        *notmuch_query_count_messages* function.
+
+        :returns: :class:`Messages`
+        :exception: :exc:`NotmuchError`
+
+                      * STATUS.NOT_INITIALIZED if query is not inited
+        """
+        if self._query is None:
+            raise NotmuchError(STATUS.NOT_INITIALIZED)            
+
+        return Query._count_messages(self._query)
 
     def __del__(self):
         """Close and free the Query"""
@@ -592,10 +631,15 @@ class Message(object):
     """notmuch_message_get_filename (notmuch_message_t *message)"""
     _get_filename = nmlib.notmuch_message_get_filename
     _get_filename.restype = c_char_p 
+
     """notmuch_message_get_message_id (notmuch_message_t *message)"""
     _get_message_id = nmlib.notmuch_message_get_message_id
     _get_message_id.restype = c_char_p 
 
+    """notmuch_message_get_thread_id"""
+    _get_thread_id = nmlib.notmuch_message_get_thread_id
+    _get_thread_id.restype = c_char_p
+
     """notmuch_message_get_tags (notmuch_message_t *message)"""
     _get_tags = nmlib.notmuch_message_get_tags
     _get_tags.restype = c_void_p
@@ -625,7 +669,7 @@ class Message(object):
 
 
     def get_message_id(self):
-        """Return the message ID
+        """Returns the message ID
         
         :returns: String with a message ID
         :exception: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if the message 
@@ -635,6 +679,24 @@ class Message(object):
             raise NotmuchError(STATUS.NOT_INITIALIZED)
         return Message._get_message_id(self._msg)
 
+    def get_thread_id(self):
+        """Returns the thread ID
+
+        The returned string belongs to 'message' will only be valid for as 
+        long as the message is valid.
+
+        This function will not return None since Notmuch ensures that every
+        message belongs to a single thread.
+
+        :returns: String with a thread ID
+        :exception: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if the message 
+                    is not initialized.
+        """
+        if self._msg is None:
+            raise NotmuchError(STATUS.NOT_INITIALIZED)
+
+        return Message._get_thread_id (self._msg);
+
     def get_date(self):
         """Returns time_t of the message date