2 This file is part of notmuch.
4 Notmuch is free software: you can redistribute it and/or modify it
5 under the terms of the GNU General Public License as published by the
6 Free Software Foundation, either version 3 of the License, or (at your
7 option) any later version.
9 Notmuch is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 You should have received a copy of the GNU General Public License
15 along with notmuch. If not, see <http://www.gnu.org/licenses/>.
17 Copyright 2010 Sebastian Spaeth <Sebastian@SSpaeth.de>'
20 from ctypes import CDLL, c_char_p, c_int, Structure, POINTER
22 #-----------------------------------------------------------------------------
23 #package-global instance of the notmuch library
25 nmlib = CDLL("libnotmuch.so.2")
27 raise ImportError("Could not find shared 'notmuch' library.")
30 if sys.version_info[0] == 2:
31 class Python3StringMixIn(object):
33 return unicode(self).encode('utf-8')
37 """Ensure a nicely utf-8 encoded string to pass to libnotmuch
39 C++ code expects strings to be well formatted and
40 unicode strings to have no null bytes."""
41 if not isinstance(value, basestring):
42 raise TypeError("Expected str or unicode, got %s" % type(value))
43 if isinstance(value, unicode):
44 return value.encode('UTF-8')
47 class Python3StringMixIn(object):
49 return self.__unicode__()
53 """Ensure a nicely utf-8 encoded string to pass to libnotmuch
55 C++ code expects strings to be well formatted and
56 unicode strings to have no null bytes."""
57 if not isinstance(value, str):
58 raise TypeError("Expected str, got %s" % type(value))
59 return value.encode('UTF-8')
63 """Provides ENUMS as "code=Enum(['a','b','c'])" where code.a=0 etc..."""
64 def __init__(self, names):
65 for number, name in enumerate(names):
66 setattr(self, name, number)
70 """Enum with a string representation of a notmuch_status_t value."""
71 _status2str = nmlib.notmuch_status_to_string
72 _status2str.restype = c_char_p
73 _status2str.argtypes = [c_int]
75 def __init__(self, statuslist):
76 """It is initialized with a list of strings that are available as
77 Status().string1 - Status().stringn attributes.
79 super(Status, self).__init__(statuslist)
82 def status2str(self, status):
83 """Get a (unicode) string representation of a notmuch_status_t value."""
84 # define strings for custom error messages
85 if status == STATUS.NOT_INITIALIZED:
86 return "Operation on uninitialized object impossible."
87 return unicode(Status._status2str(status))
89 STATUS = Status(['SUCCESS',
95 'DUPLICATE_MESSAGE_ID',
98 'UNBALANCED_FREEZE_THAW',
101 """STATUS is a class, whose attributes provide constants that serve as return
102 indicators for notmuch functions. Currently the following ones are defined. For
103 possible return values and specific meaning for each method, see the method
112 * DUPLICATE_MESSAGE_ID
115 * UNBALANCED_FREEZE_THAW
119 Invoke the class method `notmuch.STATUS.status2str` with a status value as
120 argument to receive a human readable string"""
121 STATUS.__name__ = 'STATUS'
124 class NotmuchError(Exception, Python3StringMixIn):
125 """Is initiated with a (notmuch.STATUS[, message=None]). It will not
126 return an instance of the class NotmuchError, but a derived instance
127 of a more specific Error Message, e.g. OutOfMemoryError. Each status
128 but SUCCESS has a corresponding subclassed Exception."""
131 def get_exc_subclass(cls, status):
132 """Returns a fine grained Exception() type,
133 detailing the error status"""
135 STATUS.OUT_OF_MEMORY: OutOfMemoryError,
136 STATUS.READ_ONLY_DATABASE: ReadOnlyDatabaseError,
137 STATUS.XAPIAN_EXCEPTION: XapianError,
138 STATUS.FILE_ERROR: FileError,
139 STATUS.FILE_NOT_EMAIL: FileNotEmailError,
140 STATUS.DUPLICATE_MESSAGE_ID: DuplicateMessageIdError,
141 STATUS.NULL_POINTER: NullPointerError,
142 STATUS.TAG_TOO_LONG: TagTooLongError,
143 STATUS.UNBALANCED_FREEZE_THAW: UnbalancedFreezeThawError,
144 STATUS.UNBALANCED_ATOMIC: UnbalancedAtomicError,
145 STATUS.NOT_INITIALIZED: NotInitializedError,
147 assert 0 < status <= len(subclasses)
148 return subclasses[status]
150 def __new__(cls, *args, **kwargs):
151 """Return a correct subclass of NotmuchError if needed
153 We return a NotmuchError instance if status is None (or 0) and a
154 subclass that inherits from NotmuchError depending on the
155 'status' parameter otherwise."""
156 # get 'status'. Passed in as arg or kwarg?
157 status = args[0] if len(args) else kwargs.get('status', None)
158 # no 'status' or cls is subclass already, return 'cls' instance
159 if not status or cls != NotmuchError:
160 return super(NotmuchError, cls).__new__(cls)
161 subclass = cls.get_exc_subclass(status) # which class to use?
162 return subclass.__new__(subclass, *args, **kwargs)
164 def __init__(self, status=None, message=None):
166 self.message = message
168 def __unicode__(self):
169 if self.message is not None:
171 elif self.status is not None:
172 return STATUS.status2str(self.status)
174 return 'Unknown error'
177 # List of Subclassed exceptions that correspond to STATUS values and are
178 # subclasses of NotmuchError.
179 class OutOfMemoryError(NotmuchError):
180 status = STATUS.OUT_OF_MEMORY
183 class ReadOnlyDatabaseError(NotmuchError):
184 status = STATUS.READ_ONLY_DATABASE
187 class XapianError(NotmuchError):
188 status = STATUS.XAPIAN_EXCEPTION
191 class FileError(NotmuchError):
192 status = STATUS.FILE_ERROR
195 class FileNotEmailError(NotmuchError):
196 status = STATUS.FILE_NOT_EMAIL
199 class DuplicateMessageIdError(NotmuchError):
200 status = STATUS.DUPLICATE_MESSAGE_ID
203 class NullPointerError(NotmuchError):
204 status = STATUS.NULL_POINTER
207 class TagTooLongError(NotmuchError):
208 status = STATUS.TAG_TOO_LONG
211 class UnbalancedFreezeThawError(NotmuchError):
212 status = STATUS.UNBALANCED_FREEZE_THAW
215 class UnbalancedAtomicError(NotmuchError):
216 status = STATUS.UNBALANCED_ATOMIC
219 class NotInitializedError(NotmuchError):
220 """Derived from NotmuchError, this occurs if the underlying data
221 structure (e.g. database is not initialized (yet) or an iterator has
222 been exhausted. You can test for NotmuchError with .status =
223 STATUS.NOT_INITIALIZED"""
224 status = STATUS.NOT_INITIALIZED
227 class NotmuchDatabaseS(Structure):
229 NotmuchDatabaseP = POINTER(NotmuchDatabaseS)
232 class NotmuchQueryS(Structure):
234 NotmuchQueryP = POINTER(NotmuchQueryS)
237 class NotmuchThreadsS(Structure):
239 NotmuchThreadsP = POINTER(NotmuchThreadsS)
242 class NotmuchThreadS(Structure):
244 NotmuchThreadP = POINTER(NotmuchThreadS)
247 class NotmuchMessagesS(Structure):
249 NotmuchMessagesP = POINTER(NotmuchMessagesS)
252 class NotmuchMessageS(Structure):
254 NotmuchMessageP = POINTER(NotmuchMessageS)
257 class NotmuchTagsS(Structure):
259 NotmuchTagsP = POINTER(NotmuchTagsS)
262 class NotmuchDirectoryS(Structure):
264 NotmuchDirectoryP = POINTER(NotmuchDirectoryS)
267 class NotmuchFilenamesS(Structure):
269 NotmuchFilenamesP = POINTER(NotmuchFilenamesS)