1""" 2This file is part of notmuch. 3 4Notmuch is free software: you can redistribute it and/or modify it 5under the terms of the GNU General Public License as published by the 6Free Software Foundation, either version 3 of the License, or (at your 7option) any later version. 8 9Notmuch is distributed in the hope that it will be useful, but WITHOUT 10ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12for more details. 13 14You should have received a copy of the GNU General Public License 15along with notmuch. If not, see <https://www.gnu.org/licenses/>. 16 17Copyright 2010 Sebastian Spaeth <Sebastian@SSpaeth.de> 18""" 19 20from ctypes import c_char_p, c_int 21 22from .globals import ( 23 nmlib, 24 Enum, 25 Python3StringMixIn, 26) 27 28class Status(Enum): 29 """Enum with a string representation of a notmuch_status_t value.""" 30 _status2str = nmlib.notmuch_status_to_string 31 _status2str.restype = c_char_p 32 _status2str.argtypes = [c_int] 33 34 def __init__(self, statuslist): 35 """It is initialized with a list of strings that are available as 36 Status().string1 - Status().stringn attributes. 37 """ 38 super(Status, self).__init__(statuslist) 39 40 @classmethod 41 def status2str(self, status): 42 """Get a (unicode) string representation of a notmuch_status_t value.""" 43 # define strings for custom error messages 44 if status == STATUS.NOT_INITIALIZED: 45 return "Operation on uninitialized object impossible." 46 return unicode(Status._status2str(status)) 47 48STATUS = Status(['SUCCESS', 49 'OUT_OF_MEMORY', 50 'READ_ONLY_DATABASE', 51 'XAPIAN_EXCEPTION', 52 'FILE_ERROR', 53 'FILE_NOT_EMAIL', 54 'DUPLICATE_MESSAGE_ID', 55 'NULL_POINTER', 56 'TAG_TOO_LONG', 57 'UNBALANCED_FREEZE_THAW', 58 'UNBALANCED_ATOMIC', 59 'UNSUPPORTED_OPERATION', 60 'UPGRADE_REQUIRED', 61 'PATH_ERROR', 62 'NOT_INITIALIZED']) 63"""STATUS is a class, whose attributes provide constants that serve as return 64indicators for notmuch functions. Currently the following ones are defined. For 65possible return values and specific meaning for each method, see the method 66description. 67 68 * SUCCESS 69 * OUT_OF_MEMORY 70 * READ_ONLY_DATABASE 71 * XAPIAN_EXCEPTION 72 * FILE_ERROR 73 * FILE_NOT_EMAIL 74 * DUPLICATE_MESSAGE_ID 75 * NULL_POINTER 76 * TAG_TOO_LONG 77 * UNBALANCED_FREEZE_THAW 78 * UNBALANCED_ATOMIC 79 * UNSUPPORTED_OPERATION 80 * UPGRADE_REQUIRED 81 * PATH_ERROR 82 * NOT_INITIALIZED 83 84Invoke the class method `notmuch.STATUS.status2str` with a status value as 85argument to receive a human readable string""" 86STATUS.__name__ = 'STATUS' 87 88 89class NotmuchError(Exception, Python3StringMixIn): 90 """Is initiated with a (notmuch.STATUS[, message=None]). It will not 91 return an instance of the class NotmuchError, but a derived instance 92 of a more specific Error Message, e.g. OutOfMemoryError. Each status 93 but SUCCESS has a corresponding subclassed Exception.""" 94 95 @classmethod 96 def get_exc_subclass(cls, status): 97 """Returns a fine grained Exception() type, 98 detailing the error status""" 99 subclasses = { 100 STATUS.OUT_OF_MEMORY: OutOfMemoryError, 101 STATUS.READ_ONLY_DATABASE: ReadOnlyDatabaseError, 102 STATUS.XAPIAN_EXCEPTION: XapianError, 103 STATUS.FILE_ERROR: FileError, 104 STATUS.FILE_NOT_EMAIL: FileNotEmailError, 105 STATUS.DUPLICATE_MESSAGE_ID: DuplicateMessageIdError, 106 STATUS.NULL_POINTER: NullPointerError, 107 STATUS.TAG_TOO_LONG: TagTooLongError, 108 STATUS.UNBALANCED_FREEZE_THAW: UnbalancedFreezeThawError, 109 STATUS.UNBALANCED_ATOMIC: UnbalancedAtomicError, 110 STATUS.UNSUPPORTED_OPERATION: UnsupportedOperationError, 111 STATUS.UPGRADE_REQUIRED: UpgradeRequiredError, 112 STATUS.PATH_ERROR: PathError, 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 __unicode__(self): 137 if self.message is not None: 138 return self.message 139 elif self.status is not None: 140 return STATUS.status2str(self.status) 141 else: 142 return 'Unknown error' 143 144 145# List of Subclassed exceptions that correspond to STATUS values and are 146# subclasses of NotmuchError. 147class OutOfMemoryError(NotmuchError): 148 status = STATUS.OUT_OF_MEMORY 149 150 151class ReadOnlyDatabaseError(NotmuchError): 152 status = STATUS.READ_ONLY_DATABASE 153 154 155class XapianError(NotmuchError): 156 status = STATUS.XAPIAN_EXCEPTION 157 158 159class FileError(NotmuchError): 160 status = STATUS.FILE_ERROR 161 162 163class FileNotEmailError(NotmuchError): 164 status = STATUS.FILE_NOT_EMAIL 165 166 167class DuplicateMessageIdError(NotmuchError): 168 status = STATUS.DUPLICATE_MESSAGE_ID 169 170 171class NullPointerError(NotmuchError): 172 status = STATUS.NULL_POINTER 173 174 175class TagTooLongError(NotmuchError): 176 status = STATUS.TAG_TOO_LONG 177 178 179class UnbalancedFreezeThawError(NotmuchError): 180 status = STATUS.UNBALANCED_FREEZE_THAW 181 182 183class UnbalancedAtomicError(NotmuchError): 184 status = STATUS.UNBALANCED_ATOMIC 185 186 187class UnsupportedOperationError(NotmuchError): 188 status = STATUS.UNSUPPORTED_OPERATION 189 190 191class UpgradeRequiredError(NotmuchError): 192 status = STATUS.UPGRADE_REQUIRED 193 194 195class PathError(NotmuchError): 196 status = STATUS.PATH_ERROR 197 198 199class NotInitializedError(NotmuchError): 200 """Derived from NotmuchError, this occurs if the underlying data 201 structure (e.g. database is not initialized (yet) or an iterator has 202 been exhausted. You can test for NotmuchError with .status = 203 STATUS.NOT_INITIALIZED""" 204 status = STATUS.NOT_INITIALIZED 205