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