]> git.notmuchmail.org Git - notmuch/blob - bindings/python/notmuch/globals.py
32ed9ae40181ece3632fa2175a5569c89dd69e2c
[notmuch] / bindings / python / notmuch / globals.py
1 """
2 This file is part of notmuch.
3
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.
8
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
12 for more details.
13
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/>.
16
17 Copyright 2010 Sebastian Spaeth <Sebastian@SSpaeth.de>'
18 """
19 import sys
20 from ctypes import CDLL, c_char_p, c_int, Structure, POINTER
21
22 #-----------------------------------------------------------------------------
23 #package-global instance of the notmuch library
24 try:
25     nmlib = CDLL("libnotmuch.so.2")
26 except:
27     raise ImportError("Could not find shared 'notmuch' library.")
28
29
30 if sys.version_info[0] == 2:
31     class Python3StringMixIn(object):
32         def __str__(self):
33             return unicode(self).encode('utf-8')
34 else:
35     class Python3StringMixIn(object):
36         def __str__(self):
37             return self.__unicode__()
38
39
40 class Enum(object):
41     """Provides ENUMS as "code=Enum(['a','b','c'])" where code.a=0 etc..."""
42     def __init__(self, names):
43         for number, name in enumerate(names):
44             setattr(self, name, number)
45
46
47 class Status(Enum):
48     """Enum with a string representation of a notmuch_status_t value."""
49     _status2str = nmlib.notmuch_status_to_string
50     _status2str.restype = c_char_p
51     _status2str.argtypes = [c_int]
52
53     def __init__(self, statuslist):
54         """It is initialized with a list of strings that are available as
55         Status().string1 - Status().stringn attributes.
56         """
57         super(Status, self).__init__(statuslist)
58
59     @classmethod
60     def status2str(self, status):
61         """Get a (unicode) string representation of a notmuch_status_t value."""
62         # define strings for custom error messages
63         if status == STATUS.NOT_INITIALIZED:
64             return "Operation on uninitialized object impossible."
65         return unicode(Status._status2str(status))
66
67 STATUS = Status(['SUCCESS',
68   'OUT_OF_MEMORY',
69   'READ_ONLY_DATABASE',
70   'XAPIAN_EXCEPTION',
71   'FILE_ERROR',
72   'FILE_NOT_EMAIL',
73   'DUPLICATE_MESSAGE_ID',
74   'NULL_POINTER',
75   'TAG_TOO_LONG',
76   'UNBALANCED_FREEZE_THAW',
77   'UNBALANCED_ATOMIC',
78   'NOT_INITIALIZED'])
79 """STATUS is a class, whose attributes provide constants that serve as return
80 indicators for notmuch functions. Currently the following ones are defined. For
81 possible return values and specific meaning for each method, see the method
82 description.
83
84   * SUCCESS
85   * OUT_OF_MEMORY
86   * READ_ONLY_DATABASE
87   * XAPIAN_EXCEPTION
88   * FILE_ERROR
89   * FILE_NOT_EMAIL
90   * DUPLICATE_MESSAGE_ID
91   * NULL_POINTER
92   * TAG_TOO_LONG
93   * UNBALANCED_FREEZE_THAW
94   * UNBALANCED_ATOMIC
95   * NOT_INITIALIZED
96
97 Invoke the class method `notmuch.STATUS.status2str` with a status value as
98 argument to receive a human readable string"""
99 STATUS.__name__ = 'STATUS'
100
101
102 class NotmuchError(Exception, Python3StringMixIn):
103     """Is initiated with a (notmuch.STATUS[, message=None]). It will not
104     return an instance of the class NotmuchError, but a derived instance
105     of a more specific Error Message, e.g. OutOfMemoryError. Each status
106     but SUCCESS has a corresponding subclassed Exception."""
107
108     @classmethod
109     def get_exc_subclass(cls, status):
110         """Returns a fine grained Exception() type,
111         detailing the error status"""
112         subclasses = {
113             STATUS.OUT_OF_MEMORY: OutOfMemoryError,
114             STATUS.READ_ONLY_DATABASE: ReadOnlyDatabaseError,
115             STATUS.XAPIAN_EXCEPTION: XapianError,
116             STATUS.FILE_ERROR: FileError,
117             STATUS.FILE_NOT_EMAIL: FileNotEmailError,
118             STATUS.DUPLICATE_MESSAGE_ID: DuplicateMessageIdError,
119             STATUS.NULL_POINTER: NullPointerError,
120             STATUS.TAG_TOO_LONG: TagTooLongError,
121             STATUS.UNBALANCED_FREEZE_THAW: UnbalancedFreezeThawError,
122             STATUS.UNBALANCED_ATOMIC: UnbalancedAtomicError,
123             STATUS.NOT_INITIALIZED: NotInitializedError,
124         }
125         assert 0 < status <= len(subclasses)
126         return subclasses[status]
127
128     def __new__(cls, *args, **kwargs):
129         """Return a correct subclass of NotmuchError if needed
130
131         We return a NotmuchError instance if status is None (or 0) and a
132         subclass that inherits from NotmuchError depending on the
133         'status' parameter otherwise."""
134         # get 'status'. Passed in as arg or kwarg?
135         status = args[0] if len(args) else kwargs.get('status', None)
136         # no 'status' or cls is subclass already, return 'cls' instance
137         if not status or cls != NotmuchError:
138             return super(NotmuchError, cls).__new__(cls)
139         subclass = cls.get_exc_subclass(status)  # which class to use?
140         return subclass.__new__(subclass, *args, **kwargs)
141
142     def __init__(self, status=None, message=None):
143         self.status = status
144         self.message = message
145
146     def __unicode__(self):
147         if self.message is not None:
148             return self.message
149         elif self.status is not None:
150             return STATUS.status2str(self.status)
151         else:
152             return 'Unknown error'
153
154
155 # List of Subclassed exceptions that correspond to STATUS values and are
156 # subclasses of NotmuchError.
157 class OutOfMemoryError(NotmuchError):
158     status = STATUS.OUT_OF_MEMORY
159
160
161 class ReadOnlyDatabaseError(NotmuchError):
162     status = STATUS.READ_ONLY_DATABASE
163
164
165 class XapianError(NotmuchError):
166     status = STATUS.XAPIAN_EXCEPTION
167
168
169 class FileError(NotmuchError):
170     status = STATUS.FILE_ERROR
171
172
173 class FileNotEmailError(NotmuchError):
174     status = STATUS.FILE_NOT_EMAIL
175
176
177 class DuplicateMessageIdError(NotmuchError):
178     status = STATUS.DUPLICATE_MESSAGE_ID
179
180
181 class NullPointerError(NotmuchError):
182     status = STATUS.NULL_POINTER
183
184
185 class TagTooLongError(NotmuchError):
186     status = STATUS.TAG_TOO_LONG
187
188
189 class UnbalancedFreezeThawError(NotmuchError):
190     status = STATUS.UNBALANCED_FREEZE_THAW
191
192
193 class UnbalancedAtomicError(NotmuchError):
194     status = STATUS.UNBALANCED_ATOMIC
195
196
197 class NotInitializedError(NotmuchError):
198     """Derived from NotmuchError, this occurs if the underlying data
199     structure (e.g. database is not initialized (yet) or an iterator has
200     been exhausted. You can test for NotmuchError with .status =
201     STATUS.NOT_INITIALIZED"""
202     status = STATUS.NOT_INITIALIZED
203
204
205 def _str(value):
206     """Ensure a nicely utf-8 encoded string to pass to libnotmuch
207
208     C++ code expects strings to be well formatted and
209     unicode strings to have no null bytes."""
210     if not isinstance(value, basestring):
211         raise TypeError("Expected str or unicode, got %s" % str(type(value)))
212     if isinstance(value, unicode):
213         return value.encode('UTF-8')
214     return value
215
216
217 class NotmuchDatabaseS(Structure):
218     pass
219 NotmuchDatabaseP = POINTER(NotmuchDatabaseS)
220
221
222 class NotmuchQueryS(Structure):
223     pass
224 NotmuchQueryP = POINTER(NotmuchQueryS)
225
226
227 class NotmuchThreadsS(Structure):
228     pass
229 NotmuchThreadsP = POINTER(NotmuchThreadsS)
230
231
232 class NotmuchThreadS(Structure):
233     pass
234 NotmuchThreadP = POINTER(NotmuchThreadS)
235
236
237 class NotmuchMessagesS(Structure):
238     pass
239 NotmuchMessagesP = POINTER(NotmuchMessagesS)
240
241
242 class NotmuchMessageS(Structure):
243     pass
244 NotmuchMessageP = POINTER(NotmuchMessageS)
245
246
247 class NotmuchTagsS(Structure):
248     pass
249 NotmuchTagsP = POINTER(NotmuchTagsS)
250
251
252 class NotmuchDirectoryS(Structure):
253     pass
254 NotmuchDirectoryP = POINTER(NotmuchDirectoryS)
255
256
257 class NotmuchFilenamesS(Structure):
258     pass
259 NotmuchFilenamesP = POINTER(NotmuchFilenamesS)