]> git.notmuchmail.org Git - notmuch/blob - bindings/python/notmuch/globals.py
python: Set status in the class definitions
[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
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 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,detailing the error status"""
101         subclasses = {
102             STATUS.OUT_OF_MEMORY: OutOfMemoryError,
103             STATUS.READ_ONLY_DATABASE: ReadOnlyDatabaseError,
104             STATUS.XAPIAN_EXCEPTION: XapianError,
105             STATUS.FILE_ERROR: FileError,
106             STATUS.FILE_NOT_EMAIL: FileNotEmailError,
107             STATUS.DUPLICATE_MESSAGE_ID: DuplicateMessageIdError,
108             STATUS.NULL_POINTER: NullPointerError,
109             STATUS.TAG_TOO_LONG: TagTooLongError,
110             STATUS.UNBALANCED_FREEZE_THAW: UnbalancedFreezeThawError,
111             STATUS.UNBALANCED_ATOMIC: UnbalancedAtomicError,
112             STATUS.NOT_INITIALIZED: NotInitializedError
113         }
114         assert 0 < status <= len(subclasses)
115         return subclasses[status]
116
117     def __new__(cls, *args, **kwargs):
118         """Return a correct subclass of NotmuchError if needed
119
120         We return a NotmuchError instance if status is None (or 0) and a
121         subclass that inherits from NotmuchError depending on the
122         'status' parameter otherwise."""
123         # get 'status'. Passed in as arg or kwarg?
124         status = args[0] if len(args) else kwargs.get('status', None)
125         # no 'status' or cls is subclass already, return 'cls' instance
126         if not status or cls != NotmuchError:
127             return super(NotmuchError, cls).__new__(cls)
128         subclass = cls.get_exc_subclass(status) # which class to use?
129         return subclass.__new__(subclass, *args, **kwargs)
130
131     def __init__(self, status=None, message=None):
132         self.status = status
133         self.message = message
134
135     def __str__(self):
136         if self.message is not None:
137             return self.message
138         elif self.status is not None:
139             return STATUS.status2str(self.status)
140         else:
141             return 'Unknown error'
142
143 # List of Subclassed exceptions that correspond to STATUS values and are
144 # subclasses of NotmuchError.
145 class OutOfMemoryError(NotmuchError):
146     status = STATUS.OUT_OF_MEMORY
147 class ReadOnlyDatabaseError(NotmuchError):
148     status = STATUS.READ_ONLY_DATABASE
149 class XapianError(NotmuchError):
150     status = STATUS.XAPIAN_EXCEPTION
151 class FileError(NotmuchError):
152     status = STATUS.FILE_ERROR
153 class FileNotEmailError(NotmuchError):
154     status = STATUS.FILE_NOT_EMAIL
155 class DuplicateMessageIdError(NotmuchError):
156     status = STATUS.DUPLICATE_MESSAGE_ID
157 class NullPointerError(NotmuchError):
158     status = STATUS.NULL_POINTER
159 class TagTooLongError(NotmuchError):
160     status = STATUS.TAG_TOO_LONG
161 class UnbalancedFreezeThawError(NotmuchError):
162     status = STATUS.UNBALANCED_FREEZE_THAW
163 class UnbalancedAtomicError(NotmuchError):
164     status = STATUS.UNBALANCED_ATOMIC
165 class NotInitializedError(NotmuchError):
166     """Derived from NotmuchError, this occurs if the underlying data
167     structure (e.g. database is not initialized (yet) or an iterator has
168     been exhausted. You can test for NotmuchError with .status =
169     STATUS.NOT_INITIALIZED"""
170     status = STATUS.NOT_INITIALIZED
171
172
173
174 def _str(value):
175     """Ensure a nicely utf-8 encoded string to pass to libnotmuch
176
177     C++ code expects strings to be well formatted and
178     unicode strings to have no null bytes."""
179     if not isinstance(value, basestring):
180         raise TypeError("Expected str or unicode, got %s" % str(type(value)))
181     if isinstance(value, unicode):
182         return value.encode('UTF-8')
183     return value
184