]> git.notmuchmail.org Git - notmuch/blobdiff - bindings/python/notmuch/globals.py
python: improve Query.count_messages docstring
[notmuch] / bindings / python / notmuch / globals.py
index e5399beda0d47043a47c715db3b03b80ca406542..41384604588d16ef70f5eb47f84f62aca129695f 100644 (file)
@@ -16,18 +16,49 @@ along with notmuch.  If not, see <http://www.gnu.org/licenses/>.
 
 Copyright 2010 Sebastian Spaeth <Sebastian@SSpaeth.de>'
 """
-
-from ctypes import CDLL, c_char_p, c_int
-from ctypes.util import find_library
+import sys
+from ctypes import CDLL, c_char_p, c_int, Structure, POINTER
 
 #-----------------------------------------------------------------------------
 #package-global instance of the notmuch library
 try:
-    nmlib = CDLL("libnotmuch.so.1")
+    nmlib = CDLL("libnotmuch.so.2")
 except:
     raise ImportError("Could not find shared 'notmuch' library.")
 
 
+if sys.version_info[0] == 2:
+    class Python3StringMixIn(object):
+        def __str__(self):
+            return unicode(self).encode('utf-8')
+
+
+    def _str(value):
+        """Ensure a nicely utf-8 encoded string to pass to libnotmuch
+
+        C++ code expects strings to be well formatted and
+        unicode strings to have no null bytes."""
+        if not isinstance(value, basestring):
+            raise TypeError("Expected str or unicode, got %s" % type(value))
+        if isinstance(value, unicode):
+            return value.encode('UTF-8')
+        return value
+else:
+    class Python3StringMixIn(object):
+        def __str__(self):
+            return self.__unicode__()
+
+
+    def _str(value):
+        """Ensure a nicely utf-8 encoded string to pass to libnotmuch
+
+        C++ code expects strings to be well formatted and
+        unicode strings to have no null bytes."""
+        if not isinstance(value, str):
+            raise TypeError("Expected str, got %s" % type(value))
+        return value.encode('UTF-8')
+
+
 class Enum(object):
     """Provides ENUMS as "code=Enum(['a','b','c'])" where code.a=0 etc..."""
     def __init__(self, names):
@@ -49,11 +80,11 @@ class Status(Enum):
 
     @classmethod
     def status2str(self, status):
-        """Get a string representation of a notmuch_status_t value."""
+        """Get a (unicode) string representation of a notmuch_status_t value."""
         # define strings for custom error messages
         if status == STATUS.NOT_INITIALIZED:
             return "Operation on uninitialized object impossible."
-        return str(Status._status2str(status))
+        return unicode(Status._status2str(status))
 
 STATUS = Status(['SUCCESS',
   'OUT_OF_MEMORY',
@@ -90,25 +121,149 @@ argument to receive a human readable string"""
 STATUS.__name__ = 'STATUS'
 
 
-class NotmuchError(Exception):
+class NotmuchError(Exception, Python3StringMixIn):
+    """Is initiated with a (notmuch.STATUS[, message=None]). It will not
+    return an instance of the class NotmuchError, but a derived instance
+    of a more specific Error Message, e.g. OutOfMemoryError. Each status
+    but SUCCESS has a corresponding subclassed Exception."""
+
+    @classmethod
+    def get_exc_subclass(cls, status):
+        """Returns a fine grained Exception() type,
+        detailing the error status"""
+        subclasses = {
+            STATUS.OUT_OF_MEMORY: OutOfMemoryError,
+            STATUS.READ_ONLY_DATABASE: ReadOnlyDatabaseError,
+            STATUS.XAPIAN_EXCEPTION: XapianError,
+            STATUS.FILE_ERROR: FileError,
+            STATUS.FILE_NOT_EMAIL: FileNotEmailError,
+            STATUS.DUPLICATE_MESSAGE_ID: DuplicateMessageIdError,
+            STATUS.NULL_POINTER: NullPointerError,
+            STATUS.TAG_TOO_LONG: TagTooLongError,
+            STATUS.UNBALANCED_FREEZE_THAW: UnbalancedFreezeThawError,
+            STATUS.UNBALANCED_ATOMIC: UnbalancedAtomicError,
+            STATUS.NOT_INITIALIZED: NotInitializedError,
+        }
+        assert 0 < status <= len(subclasses)
+        return subclasses[status]
+
+    def __new__(cls, *args, **kwargs):
+        """Return a correct subclass of NotmuchError if needed
+
+        We return a NotmuchError instance if status is None (or 0) and a
+        subclass that inherits from NotmuchError depending on the
+        'status' parameter otherwise."""
+        # get 'status'. Passed in as arg or kwarg?
+        status = args[0] if len(args) else kwargs.get('status', None)
+        # no 'status' or cls is subclass already, return 'cls' instance
+        if not status or cls != NotmuchError:
+            return super(NotmuchError, cls).__new__(cls)
+        subclass = cls.get_exc_subclass(status)  # which class to use?
+        return subclass.__new__(subclass, *args, **kwargs)
+
     def __init__(self, status=None, message=None):
-        """Is initiated with a (notmuch.STATUS[,message=None])"""
-        super(NotmuchError, self).__init__(message, status)
+        self.status = status
+        self.message = message
 
-    def __str__(self):
-        if self.args[0] is not None:
-            return self.args[0]
+    def __unicode__(self):
+        if self.message is not None:
+            return self.message
+        elif self.status is not None:
+            return STATUS.status2str(self.status)
         else:
-            return STATUS.status2str(self.args[1])
+            return 'Unknown error'
 
-def _str(value):
-    """Ensure a nicely utf-8 encoded string to pass to libnotmuch
 
-    C++ code expects strings to be well formatted and
-    unicode strings to have no null bytes."""
-    if not isinstance(value, basestring):
-        raise TypeError("Expected str or unicode, got %s" % str(type(value)))
-    if isinstance(value, unicode):
-        return value.encode('UTF-8')
-    return value
+# List of Subclassed exceptions that correspond to STATUS values and are
+# subclasses of NotmuchError.
+class OutOfMemoryError(NotmuchError):
+    status = STATUS.OUT_OF_MEMORY
+
+
+class ReadOnlyDatabaseError(NotmuchError):
+    status = STATUS.READ_ONLY_DATABASE
+
+
+class XapianError(NotmuchError):
+    status = STATUS.XAPIAN_EXCEPTION
+
+
+class FileError(NotmuchError):
+    status = STATUS.FILE_ERROR
+
+
+class FileNotEmailError(NotmuchError):
+    status = STATUS.FILE_NOT_EMAIL
+
+
+class DuplicateMessageIdError(NotmuchError):
+    status = STATUS.DUPLICATE_MESSAGE_ID
+
+
+class NullPointerError(NotmuchError):
+    status = STATUS.NULL_POINTER
+
+
+class TagTooLongError(NotmuchError):
+    status = STATUS.TAG_TOO_LONG
+
+
+class UnbalancedFreezeThawError(NotmuchError):
+    status = STATUS.UNBALANCED_FREEZE_THAW
+
+
+class UnbalancedAtomicError(NotmuchError):
+    status = STATUS.UNBALANCED_ATOMIC
+
+
+class NotInitializedError(NotmuchError):
+    """Derived from NotmuchError, this occurs if the underlying data
+    structure (e.g. database is not initialized (yet) or an iterator has
+    been exhausted. You can test for NotmuchError with .status =
+    STATUS.NOT_INITIALIZED"""
+    status = STATUS.NOT_INITIALIZED
+
+
+class NotmuchDatabaseS(Structure):
+    pass
+NotmuchDatabaseP = POINTER(NotmuchDatabaseS)
+
+
+class NotmuchQueryS(Structure):
+    pass
+NotmuchQueryP = POINTER(NotmuchQueryS)
+
+
+class NotmuchThreadsS(Structure):
+    pass
+NotmuchThreadsP = POINTER(NotmuchThreadsS)
+
+
+class NotmuchThreadS(Structure):
+    pass
+NotmuchThreadP = POINTER(NotmuchThreadS)
+
+
+class NotmuchMessagesS(Structure):
+    pass
+NotmuchMessagesP = POINTER(NotmuchMessagesS)
+
+
+class NotmuchMessageS(Structure):
+    pass
+NotmuchMessageP = POINTER(NotmuchMessageS)
+
+
+class NotmuchTagsS(Structure):
+    pass
+NotmuchTagsP = POINTER(NotmuchTagsS)
+
+
+class NotmuchDirectoryS(Structure):
+    pass
+NotmuchDirectoryP = POINTER(NotmuchDirectoryS)
+
 
+class NotmuchFilenamesS(Structure):
+    pass
+NotmuchFilenamesP = POINTER(NotmuchFilenamesS)