]> git.notmuchmail.org Git - notmuch/blobdiff - bindings/python/notmuch/database.py
python: fix error handling
[notmuch] / bindings / python / notmuch / database.py
index ca172738d273d52cf113fba1d2e127771d96b45b..6238b2891e066cf76a9361fc573509a287851124 100644 (file)
@@ -18,9 +18,9 @@ Copyright 2010 Sebastian Spaeth <Sebastian@SSpaeth.de>'
 """
 
 import os
 """
 
 import os
-from ctypes import c_int, c_char_p, c_void_p, c_uint, c_long, byref, POINTER
+from ctypes import c_char_p, c_void_p, c_uint, c_long, byref, POINTER
 from notmuch.globals import (nmlib, STATUS, NotmuchError, NotInitializedError,
 from notmuch.globals import (nmlib, STATUS, NotmuchError, NotInitializedError,
-     NullPointerError, OutOfMemoryError, XapianError, Enum, _str,
+     NullPointerError, Enum, _str,
      NotmuchDatabaseP, NotmuchDirectoryP, NotmuchMessageP, NotmuchTagsP,
      NotmuchQueryP, NotmuchMessagesP, NotmuchThreadsP, NotmuchFilenamesP)
 from notmuch.thread import Threads
      NotmuchDatabaseP, NotmuchDirectoryP, NotmuchMessageP, NotmuchTagsP,
      NotmuchQueryP, NotmuchMessagesP, NotmuchThreadsP, NotmuchFilenamesP)
 from notmuch.thread import Threads
@@ -40,16 +40,19 @@ class Database(object):
     :exc:`XapianError` as the underlying database has been
     modified. Close and reopen the database to continue working with it.
 
     :exc:`XapianError` as the underlying database has been
     modified. Close and reopen the database to continue working with it.
 
-    .. note:: Any function in this class can and will throw an
-           :exc:`NotInitializedError` if the database was not
-           intitialized properly.
+    .. note::
 
 
-    .. note:: Do remember that as soon as we tear down (e.g. via `del
-           db`) this object, all underlying derived objects such as
-           queries, threads, messages, tags etc will be freed by the
-           underlying library as well. Accessing these objects will lead
-           to segfaults and other unexpected behavior. See above for
-           more details.
+        Any function in this class can and will throw an
+        :exc:`NotInitializedError` if the database was not intitialized
+        properly.
+
+    .. note::
+
+        Do remember that as soon as we tear down (e.g. via `del db`) this
+        object, all underlying derived objects such as queries, threads,
+        messages, tags etc will be freed by the underlying library as well.
+        Accessing these objects will lead to segfaults and other unexpected
+        behavior. See above for more details.
     """
     _std_db_path = None
     """Class attribute to cache user's default database"""
     """
     _std_db_path = None
     """Class attribute to cache user's default database"""
@@ -165,7 +168,7 @@ class Database(object):
 
         res = Database._create(_str(path), Database.MODE.READ_WRITE)
 
 
         res = Database._create(_str(path), Database.MODE.READ_WRITE)
 
-        if res is None:
+        if not res:
             raise NotmuchError(
                 message="Could not create the specified database")
         self._db = res
             raise NotmuchError(
                 message="Could not create the specified database")
         self._db = res
@@ -185,7 +188,7 @@ class Database(object):
         """
         res = Database._open(_str(path), mode)
 
         """
         res = Database._open(_str(path), mode)
 
-        if res is None:
+        if not res:
             raise NotmuchError(message="Could not open the specified database")
         self._db = res
 
             raise NotmuchError(message="Could not open the specified database")
         self._db = res
 
@@ -253,10 +256,8 @@ class Database(object):
         neither begin nor end necessarily flush modifications to disk.
 
         :returns: :attr:`STATUS`.SUCCESS or raises
         neither begin nor end necessarily flush modifications to disk.
 
         :returns: :attr:`STATUS`.SUCCESS or raises
-
-        :exception: :exc:`NotmuchError`:
-            :attr:`STATUS`.XAPIAN_EXCEPTION
-                Xapian exception occurred; atomic section not entered.
+        :exception: :exc:`NotmuchError`: :attr:`STATUS`.XAPIAN_EXCEPTION
+                    Xapian exception occurred; atomic section not entered.
 
         *Added in notmuch 0.9*"""
         self._assert_db_is_initialized()
 
         *Added in notmuch 0.9*"""
         self._assert_db_is_initialized()
@@ -295,9 +296,11 @@ class Database(object):
         """Returns a :class:`Directory` of path,
         (creating it if it does not exist(?))
 
         """Returns a :class:`Directory` of path,
         (creating it if it does not exist(?))
 
-        .. warning:: This call needs a writeable database in
-           :attr:`Database.MODE`.READ_WRITE mode. The underlying library will
-           exit the program if this method is used on a read-only database!
+        .. warning::
+
+            This call needs a writeable database in
+            :attr:`Database.MODE`.READ_WRITE mode. The underlying library will
+            exit the program if this method is used on a read-only database!
 
         :param path: An unicode string containing the path relative to the path
               of database (see :meth:`get_path`), or else should be an absolute
 
         :param path: An unicode string containing the path relative to the path
               of database (see :meth:`get_path`), or else should be an absolute
@@ -382,7 +385,7 @@ class Database(object):
                       be added.
         """
         self._assert_db_is_initialized()
                       be added.
         """
         self._assert_db_is_initialized()
-        msg_p = c_void_p()
+        msg_p = NotmuchMessageP()
         status = self._add_message(self._db, _str(filename), byref(msg_p))
 
         if not status in [STATUS.SUCCESS, STATUS.DUPLICATE_MESSAGE_ID]:
         status = self._add_message(self._db, _str(filename), byref(msg_p))
 
         if not status in [STATUS.SUCCESS, STATUS.DUPLICATE_MESSAGE_ID]:
@@ -427,7 +430,7 @@ class Database(object):
                removed.
         """
         self._assert_db_is_initialized()
                removed.
         """
         self._assert_db_is_initialized()
-        return self._remove_message(self._db, filename)
+        return self._remove_message(self._db, _str(filename))
 
     def find_message(self, msgid):
         """Returns a :class:`Message` as identified by its message ID
 
     def find_message(self, msgid):
         """Returns a :class:`Message` as identified by its message ID
@@ -450,7 +453,7 @@ class Database(object):
                     the database was not intitialized.
         """
         self._assert_db_is_initialized()
                     the database was not intitialized.
         """
         self._assert_db_is_initialized()
-        msg_p = c_void_p()
+        msg_p = NotmuchMessageP()
         status = Database._find_message(self._db, _str(msgid), byref(msg_p))
         if status != STATUS.SUCCESS:
             raise NotmuchError(status)
         status = Database._find_message(self._db, _str(msgid), byref(msg_p))
         if status != STATUS.SUCCESS:
             raise NotmuchError(status)
@@ -459,10 +462,11 @@ class Database(object):
     def find_message_by_filename(self, filename):
         """Find a message with the given filename
 
     def find_message_by_filename(self, filename):
         """Find a message with the given filename
 
-        .. warning:: This call needs a writeable database in
-           :attr:`Database.MODE`.READ_WRITE mode. The underlying library will
-           exit the program if this method is used on a read-only
-           database!
+        .. warning::
+
+            This call needs a writeable database in
+            :attr:`Database.MODE`.READ_WRITE mode. The underlying library will
+            exit the program if this method is used on a read-only database!
 
         :returns: If the database contains a message with the given
             filename, then a class:`Message:` is returned.  This
 
         :returns: If the database contains a message with the given
             filename, then a class:`Message:` is returned.  This
@@ -483,7 +487,7 @@ class Database(object):
 
         *Added in notmuch 0.9*"""
         self._assert_db_is_initialized()
 
         *Added in notmuch 0.9*"""
         self._assert_db_is_initialized()
-        msg_p = c_void_p()
+        msg_p = NotmuchMessageP()
         status = Database._find_message_by_filename(self._db, _str(filename),
                                                     byref(msg_p))
         if status != STATUS.SUCCESS:
         status = Database._find_message_by_filename(self._db, _str(filename),
                                                     byref(msg_p))
         if status != STATUS.SUCCESS:
@@ -539,7 +543,13 @@ class Database(object):
         """ Reads a user's notmuch config and returns his db location
 
         Throws a NotmuchError if it cannot find it"""
         """ Reads a user's notmuch config and returns his db location
 
         Throws a NotmuchError if it cannot find it"""
-        from ConfigParser import SafeConfigParser
+        try:
+            # python3.x
+            from configparser import SafeConfigParser
+        except ImportError:
+            # python2.x
+            from ConfigParser import SafeConfigParser
+
         config = SafeConfigParser()
         conf_f = os.getenv('NOTMUCH_CONFIG',
                            os.path.expanduser('~/.notmuch-config'))
         config = SafeConfigParser()
         conf_f = os.getenv('NOTMUCH_CONFIG',
                            os.path.expanduser('~/.notmuch-config'))
@@ -641,7 +651,7 @@ class Query(object):
         self._db = db
         # create query, return None if too little mem available
         query_p = Query._create(db.db_p, _str(querystr))
         self._db = db
         # create query, return None if too little mem available
         query_p = Query._create(db.db_p, _str(querystr))
-        if query_p is None:
+        if not query_p:
             raise NullPointerError
         self._query = query_p
 
             raise NullPointerError
         self._query = query_p
 
@@ -675,7 +685,7 @@ class Query(object):
         self._assert_query_is_initialized()
         threads_p = Query._search_threads(self._query)
 
         self._assert_query_is_initialized()
         threads_p = Query._search_threads(self._query)
 
-        if threads_p is None:
+        if not threads_p:
             raise NullPointerError
         return Threads(threads_p, self)
 
             raise NullPointerError
         return Threads(threads_p, self)
 
@@ -689,7 +699,7 @@ class Query(object):
         self._assert_query_is_initialized()
         msgs_p = Query._search_messages(self._query)
 
         self._assert_query_is_initialized()
         msgs_p = Query._search_messages(self._query)
 
-        if msgs_p is None:
+        if not msgs_p:
             raise NullPointerError
         return Messages(msgs_p, self)
 
             raise NullPointerError
         return Messages(msgs_p, self)
 
@@ -755,7 +765,7 @@ class Directory(object):
     def _assert_dir_is_initialized(self):
         """Raises a NotmuchError(:attr:`STATUS`.NOT_INITIALIZED)
         if dir_p is None"""
     def _assert_dir_is_initialized(self):
         """Raises a NotmuchError(:attr:`STATUS`.NOT_INITIALIZED)
         if dir_p is None"""
-        if self._dir_p is None:
+        if not self._dir_p:
             raise NotmuchError(STATUS.NOT_INITIALIZED)
 
     def __init__(self, path, dir_p, parent):
             raise NotmuchError(STATUS.NOT_INITIALIZED)
 
     def __init__(self, path, dir_p, parent):
@@ -791,10 +801,12 @@ class Directory(object):
           and know that it only needs to add files if the mtime of the
           directory and files are newer than the stored timestamp.
 
           and know that it only needs to add files if the mtime of the
           directory and files are newer than the stored timestamp.
 
-          .. note:: :meth:`get_mtime` function does not allow the caller
-                 to distinguish a timestamp of 0 from a non-existent
-                 timestamp. So don't store a timestamp of 0 unless you are
-                 comfortable with that.
+          .. note::
+
+                :meth:`get_mtime` function does not allow the caller to
+                distinguish a timestamp of 0 from a non-existent timestamp. So
+                don't store a timestamp of 0 unless you are comfortable with
+                that.
 
           :param mtime: A (time_t) timestamp
           :returns: Nothing on success, raising an exception on failure.
 
           :param mtime: A (time_t) timestamp
           :returns: Nothing on success, raising an exception on failure.
@@ -913,31 +925,35 @@ class Filenames(object):
     _move_to_next.argtypes = [NotmuchFilenamesP]
     _move_to_next.restype = None
 
     _move_to_next.argtypes = [NotmuchFilenamesP]
     _move_to_next.restype = None
 
-    def next(self):
-        if self._files_p is None:
+    def __next__(self):
+        if not self._files_p:
             raise NotmuchError(STATUS.NOT_INITIALIZED)
 
         if not self._valid(self._files_p):
             self._files_p = None
             raise StopIteration
 
             raise NotmuchError(STATUS.NOT_INITIALIZED)
 
         if not self._valid(self._files_p):
             self._files_p = None
             raise StopIteration
 
-        file = Filenames._get(self._files_p)
+        file_ = Filenames._get(self._files_p)
         self._move_to_next(self._files_p)
         self._move_to_next(self._files_p)
-        return file
+        return file_.decode('utf-8', 'ignore')
+    next = __next__ # python2.x iterator protocol compatibility
 
     def __len__(self):
         """len(:class:`Filenames`) returns the number of contained files
 
 
     def __len__(self):
         """len(:class:`Filenames`) returns the number of contained files
 
-        .. note:: As this iterates over the files, we will not be able to
-               iterate over them again! So this will fail::
+        .. note::
+
+            As this iterates over the files, we will not be able to
+            iterate over them again! So this will fail::
 
                  #THIS FAILS
                  files = Database().get_directory('').get_child_files()
 
                  #THIS FAILS
                  files = Database().get_directory('').get_child_files()
-                 if len(files) > 0:              #this 'exhausts' msgs
-                     # next line raises NotmuchError(:attr:`STATUS`.NOT_INITIALIZED)!!!
+                 if len(files) > 0:  # this 'exhausts' msgs
+                     # next line raises
+                     # NotmuchError(:attr:`STATUS`.NOT_INITIALIZED)
                      for file in files: print file
         """
                      for file in files: print file
         """
-        if self._files_p is None:
+        if not self._files_p:
             raise NotmuchError(STATUS.NOT_INITIALIZED)
 
         i = 0
             raise NotmuchError(STATUS.NOT_INITIALIZED)
 
         i = 0