python: rework creating of Subclasses

Add some smart magic, so that when we invoke a
NotmuchError(STATUSVALUE), a nicely derived subclass is created, e.g. a
OutOfMemoryError. This way users can easily distinguish between error
types, while still catching NotmuchError.

I have tested this, and hope it works for others too.

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
This commit is contained in:
Sebastian Spaeth 2011-09-30 16:04:42 +02:00
parent b6a01735d2
commit 8c51525e82
2 changed files with 35 additions and 21 deletions

View file

@ -112,7 +112,7 @@ class Database(object):
def _assert_db_is_initialized(self): def _assert_db_is_initialized(self):
"""Raises a NotmuchError in case self._db is still None""" """Raises a NotmuchError in case self._db is still None"""
if self._db is None: if self._db is None:
raise NotmuchError.get_subclass_exc(STATUS.NOT_INITIALIZED) raise NotmuchError(STATUS.NOT_INITIALIZED)
def create(self, path): def create(self, path):
"""Creates a new notmuch database """Creates a new notmuch database
@ -231,7 +231,7 @@ class Database(object):
self._assert_db_is_initialized() self._assert_db_is_initialized()
status = nmlib.notmuch_database_begin_atomic(self._db) status = nmlib.notmuch_database_begin_atomic(self._db)
if status != STATUS.SUCCESS: if status != STATUS.SUCCESS:
raise NotmuchError.get_subclass_exc(status) raise NotmuchError(status)
return status return status
def end_atomic(self): def end_atomic(self):
@ -253,7 +253,7 @@ class Database(object):
self._assert_db_is_initialized() self._assert_db_is_initialized()
status = nmlib.notmuch_database_end_atomic(self._db) status = nmlib.notmuch_database_end_atomic(self._db)
if status != STATUS.SUCCESS: if status != STATUS.SUCCESS:
raise NotmuchError.get_subclass_exc(status) raise NotmuchError(status)
return status return status
def get_directory(self, path): def get_directory(self, path):
@ -284,7 +284,7 @@ class Database(object):
# we got an absolute path # we got an absolute path
if not path.startswith(self.get_path()): if not path.startswith(self.get_path()):
# but its initial components are not equal to the db path # but its initial components are not equal to the db path
raise NotmuchError.get_subclass_exc(STATUS.FILE_ERROR, raise NotmuchError(STATUS.FILE_ERROR,
message="Database().get_directory() called " message="Database().get_directory() called "
"with a wrong absolute path.") "with a wrong absolute path.")
abs_dirpath = path abs_dirpath = path
@ -355,7 +355,7 @@ class Database(object):
byref(msg_p)) byref(msg_p))
if not status in [STATUS.SUCCESS, STATUS.DUPLICATE_MESSAGE_ID]: if not status in [STATUS.SUCCESS, STATUS.DUPLICATE_MESSAGE_ID]:
raise NotmuchError.get_subclass_exc(status) raise NotmuchError(status)
#construct Message() and return #construct Message() and return
msg = Message(msg_p, self) msg = Message(msg_p, self)
@ -449,7 +449,7 @@ class Database(object):
self._assert_db_is_initialized() self._assert_db_is_initialized()
tags_p = Database._get_all_tags(self._db) tags_p = Database._get_all_tags(self._db)
if tags_p == None: if tags_p == None:
raise NotmuchError.get_subclass_exc(STATUS.NULL_POINTER) raise NotmuchError(STATUS.NULL_POINTER)
return Tags(tags_p, self) return Tags(tags_p, self)
def create_query(self, querystring): def create_query(self, querystring):
@ -573,13 +573,13 @@ class Query(object):
(too little memory) (too little memory)
""" """
if db.db_p is None: if db.db_p is None:
raise NotmuchError.get_subclass_exc(STATUS.NOT_INITIALIZED) raise NotmuchError(STATUS.NOT_INITIALIZED)
# create reference to parent db to keep it alive # create reference to parent db to keep it alive
self._db = db self._db = db
# create query, return None if too little mem available # create query, return None if too little mem available
query_p = Query._create(db.db_p, _str(querystr)) query_p = Query._create(db.db_p, _str(querystr))
if query_p is None: if query_p is None:
raise NotmuchError.get_subclass_exc(STATUS.NULL_POINTER) raise NotmuchError(STATUS.NULL_POINTER)
self._query = query_p self._query = query_p
def set_sort(self, sort): def set_sort(self, sort):
@ -593,7 +593,7 @@ class Query(object):
been initialized. been initialized.
""" """
if self._query is None: if self._query is None:
raise NotmuchError.get_subclass_exc(STATUS.NOT_INITIALIZED) raise NotmuchError(STATUS.NOT_INITIALIZED)
self.sort = sort self.sort = sort
nmlib.notmuch_query_set_sort(self._query, sort) nmlib.notmuch_query_set_sort(self._query, sort)
@ -619,7 +619,7 @@ class Query(object):
* :attr:`STATUS`.NULL_POINTER if search_threads failed * :attr:`STATUS`.NULL_POINTER if search_threads failed
""" """
if self._query is None: if self._query is None:
raise NotmuchError.get_subclass_exc(STATUS.NOT_INITIALIZED) raise NotmuchError(STATUS.NOT_INITIALIZED)
threads_p = Query._search_threads(self._query) threads_p = Query._search_threads(self._query)
@ -642,12 +642,12 @@ class Query(object):
* :attr:`STATUS`.NULL_POINTER if search_messages failed * :attr:`STATUS`.NULL_POINTER if search_messages failed
""" """
if self._query is None: if self._query is None:
raise NotmuchError.get_subclass_exc(STATUS.NOT_INITIALIZED) raise NotmuchError(STATUS.NOT_INITIALIZED)
msgs_p = Query._search_messages(self._query) msgs_p = Query._search_messages(self._query)
if msgs_p is None: if msgs_p is None:
raise NotmuchError.get_subclass_exc(STATUS.NULL_POINTER) raise NotmuchError(STATUS.NULL_POINTER)
return Messages(msgs_p, self) return Messages(msgs_p, self)
@ -667,7 +667,7 @@ class Query(object):
* :attr:`STATUS`.NOT_INITIALIZED if query is not inited * :attr:`STATUS`.NOT_INITIALIZED if query is not inited
""" """
if self._query is None: if self._query is None:
raise NotmuchError.get_subclass_exc(STATUS.NOT_INITIALIZED) raise NotmuchError(STATUS.NOT_INITIALIZED)
return Query._count_messages(self._query) return Query._count_messages(self._query)
@ -710,7 +710,7 @@ class Directory(object):
def _assert_dir_is_initialized(self): def _assert_dir_is_initialized(self):
"""Raises a NotmuchError(:attr:`STATUS`.NOT_INITIALIZED) if dir_p is None""" """Raises a NotmuchError(:attr:`STATUS`.NOT_INITIALIZED) if dir_p is None"""
if self._dir_p is None: if self._dir_p is None:
raise NotmuchError.get_subclass_exc(STATUS.NOT_INITIALIZED) raise NotmuchError(STATUS.NOT_INITIALIZED)
def __init__(self, path, dir_p, parent): def __init__(self, path, dir_p, parent):
""" """
@ -770,7 +770,7 @@ class Directory(object):
if status == STATUS.SUCCESS: if status == STATUS.SUCCESS:
return return
#fail with Exception otherwise #fail with Exception otherwise
raise NotmuchError.get_subclass_exc(status) raise NotmuchError(status)
def get_mtime(self): def get_mtime(self):
"""Gets the mtime value of this directory in the database """Gets the mtime value of this directory in the database
@ -856,7 +856,7 @@ class Filenames(object):
def next(self): def next(self):
if self._files_p is None: if self._files_p is None:
raise NotmuchError.get_subclass_exc(STATUS.NOT_INITIALIZED) raise NotmuchError(STATUS.NOT_INITIALIZED)
if not nmlib.notmuch_filenames_valid(self._files_p): if not nmlib.notmuch_filenames_valid(self._files_p):
self._files_p = None self._files_p = None
@ -879,7 +879,7 @@ class Filenames(object):
for file in files: print file for file in files: print file
""" """
if self._files_p is None: if self._files_p is None:
raise NotmuchError.get_subclass_exc(STATUS.NOT_INITIALIZED) raise NotmuchError(STATUS.NOT_INITIALIZED)
i = 0 i = 0
while nmlib.notmuch_filenames_valid(self._files_p): while nmlib.notmuch_filenames_valid(self._files_p):

View file

@ -96,7 +96,7 @@ class NotmuchError(Exception):
but SUCCESS has a corresponding subclassed Exception.""" but SUCCESS has a corresponding subclassed Exception."""
@classmethod @classmethod
def get_subclass_exc(cls, status, message=None): def get_exc_subclass(cls, status):
"""Returns a fine grained Exception() type,detailing the error status""" """Returns a fine grained Exception() type,detailing the error status"""
subclasses = { subclasses = {
STATUS.OUT_OF_MEMORY: OutOfMemoryError, STATUS.OUT_OF_MEMORY: OutOfMemoryError,
@ -112,9 +112,23 @@ class NotmuchError(Exception):
STATUS.NOT_INITIALIZED: NotInitializedError STATUS.NOT_INITIALIZED: NotInitializedError
} }
assert 0 < status <= len(subclasses) assert 0 < status <= len(subclasses)
return subclasses[status](status, message) return subclasses[status]
def __init__(self, status, message=None): def __new__(cls, *args, **kwargs):
"""Return a correct subclass of NotmuchError if needed
We return a NotmuchError instance if status is None (or 0) and a
subclass that inherits from NotmuchError depending on the
'status' parameter otherwise."""
# get 'status'. Passed in as arg or kwarg?
status = args[0] if len(args) else kwargs.get('status', None)
# no 'status' or cls is subclass already, return 'cls' instance
if not status or cls != NotmuchError:
return super(NotmuchError, cls).__new__(cls)
subclass = cls.get_exc_subclass(status) # which class to use?
return subclass.__new__(subclass, *args, **kwargs)
def __init__(self, status=None, message=None):
self.status = status self.status = status
self.message = message self.message = message
@ -127,7 +141,7 @@ class NotmuchError(Exception):
return 'Unknown error' return 'Unknown error'
# List of Subclassed exceptions that correspond to STATUS values and are # List of Subclassed exceptions that correspond to STATUS values and are
# subclasses of NotmuchError: # subclasses of NotmuchError.
class OutOfMemoryError(NotmuchError): class OutOfMemoryError(NotmuchError):
pass pass
class ReadOnlyDatabaseError(NotmuchError): class ReadOnlyDatabaseError(NotmuchError):