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