python: refactor the error handling machinery

Raise specific error classes instead of a generic NotmuchError with an
magic status value (e.g. NotmuchError(STATUS.NULL_POINTER) ->
NullPointerError()), update the documentation accordingly.

Signed-off-by: Justus Winter <4winter@informatik.uni-hamburg.de>
This commit is contained in:
Justus Winter 2012-02-20 23:49:07 +01:00
parent 4bb9f59ff6
commit a1442952d4
5 changed files with 165 additions and 161 deletions

View file

@ -23,7 +23,9 @@ from ctypes import c_char_p, c_void_p, c_uint, c_long, byref, POINTER
from notmuch.globals import ( from notmuch.globals import (
nmlib, nmlib,
STATUS, STATUS,
FileError,
NotmuchError, NotmuchError,
NullPointerError,
NotInitializedError, NotInitializedError,
Enum, Enum,
_str, _str,
@ -355,9 +357,8 @@ 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(STATUS.FILE_ERROR, raise FileError('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
else: else:
#we got a relative path, make it absolute #we got a relative path, make it absolute
@ -542,7 +543,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(STATUS.NULL_POINTER) raise NullPointerError()
return Tags(tags_p, self) return Tags(tags_p, self)
def create_query(self, querystring): def create_query(self, querystring):
@ -636,7 +637,7 @@ class Directory(object):
"""Raises a NotmuchError(:attr:`STATUS`.NOT_INITIALIZED) """Raises a NotmuchError(:attr:`STATUS`.NOT_INITIALIZED)
if dir_p is None""" if dir_p is None"""
if not self._dir_p: if not self._dir_p:
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotInitializedError()
def __init__(self, path, dir_p, parent): def __init__(self, path, dir_p, parent):
""" """
@ -797,7 +798,7 @@ class Filenames(object):
def __next__(self): def __next__(self):
if not self._files_p: if not self._files_p:
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotInitializedError()
if not self._valid(self._files_p): if not self._valid(self._files_p):
self._files_p = None self._files_p = None
@ -824,7 +825,7 @@ class Filenames(object):
for file in files: print file for file in files: print file
""" """
if not self._files_p: if not self._files_p:
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotInitializedError()
i = 0 i = 0
while self._valid(self._files_p): while self._valid(self._files_p):

View file

@ -17,8 +17,14 @@ along with notmuch. If not, see <http://www.gnu.org/licenses/>.
Copyright 2010 Sebastian Spaeth <Sebastian@SSpaeth.de>' Copyright 2010 Sebastian Spaeth <Sebastian@SSpaeth.de>'
""" """
from ctypes import c_char_p from ctypes import c_char_p
from notmuch.globals import (nmlib, STATUS, NotmuchError, from notmuch.globals import (
NotmuchFilenamesP, NotmuchMessageP, Python3StringMixIn) nmlib,
NullPointerError,
NotInitializedError,
NotmuchMessageP,
NotmuchFilenamesP,
Python3StringMixIn,
)
class Filenames(Python3StringMixIn): class Filenames(Python3StringMixIn):
@ -29,9 +35,9 @@ class Filenames(Python3StringMixIn):
iterator over a list of notmuch filenames. Do note that the underlying iterator over a list of notmuch filenames. Do note that the underlying
library only provides a one-time iterator (it cannot reset the iterator to library only provides a one-time iterator (it cannot reset the iterator to
the start). Thus iterating over the function will "exhaust" the list of the start). Thus iterating over the function will "exhaust" the list of
tags, and a subsequent iteration attempt will raise a :exc:`NotmuchError` tags, and a subsequent iteration attempt will raise a
STATUS.NOT_INITIALIZED. Also note, that any function that uses iteration :exc:`NotInitializedError`. Also note, that any function that uses
(nearly all) will also exhaust the tags. So both:: iteration (nearly all) will also exhaust the tags. So both::
for name in filenames: print name for name in filenames: print name
@ -61,8 +67,8 @@ class Filenames(Python3StringMixIn):
will almost never instantiate a :class:`Tags` object will almost never instantiate a :class:`Tags` object
herself. They are usually handed back as a result, herself. They are usually handed back as a result,
e.g. in :meth:`Database.get_all_tags`. *tags_p* must be e.g. in :meth:`Database.get_all_tags`. *tags_p* must be
valid, we will raise an :exc:`NotmuchError` valid, we will raise an :exc:`NullPointerError`
(STATUS.NULL_POINTER) if it is `None`. if it is `None`.
:type files_p: :class:`ctypes.c_void_p` :type files_p: :class:`ctypes.c_void_p`
:param parent: The parent object (ie :class:`Message` these :param parent: The parent object (ie :class:`Message` these
filenames are derived from, and saves a filenames are derived from, and saves a
@ -70,7 +76,7 @@ class Filenames(Python3StringMixIn):
once all derived objects are dead. once all derived objects are dead.
""" """
if not files_p: if not files_p:
raise NotmuchError(STATUS.NULL_POINTER) raise NullPointerError()
self._files = files_p self._files = files_p
#save reference to parent object so we keep it alive #save reference to parent object so we keep it alive
@ -90,7 +96,7 @@ class Filenames(Python3StringMixIn):
This is the main function that will usually be used by the This is the main function that will usually be used by the
user.""" user."""
if not self._files: if not self._files:
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotInitializedError()
while self._valid(self._files): while self._valid(self._files):
yield Filenames._get(self._files).decode('utf-8', 'ignore') yield Filenames._get(self._files).decode('utf-8', 'ignore')
@ -104,7 +110,7 @@ class Filenames(Python3StringMixIn):
.. note:: As this iterates over the filenames, we will not be .. note:: As this iterates over the filenames, we will not be
able to iterate over them again (as in retrieve them)! If able to iterate over them again (as in retrieve them)! If
the tags have been exhausted already, this will raise a the tags have been exhausted already, this will raise a
:exc:`NotmuchError` STATUS.NOT_INITIALIZED on subsequent :exc:`NotInitializedError` on subsequent
attempts. However, you can use attempts. However, you can use
:meth:`Message.get_filenames` repeatedly to perform :meth:`Message.get_filenames` repeatedly to perform
various actions on filenames. various actions on filenames.

View file

@ -22,8 +22,19 @@ Copyright 2010 Sebastian Spaeth <Sebastian@SSpaeth.de>'
from ctypes import c_char_p, c_long, c_uint, c_int from ctypes import c_char_p, c_long, c_uint, c_int
from datetime import date from datetime import date
from notmuch.globals import ( from notmuch.globals import (
nmlib, STATUS, NotmuchError, Enum, _str, Python3StringMixIn, nmlib,
NotmuchTagsP, NotmuchMessagesP, NotmuchMessageP, NotmuchFilenamesP) Enum,
_str,
Python3StringMixIn,
STATUS,
NotmuchError,
NullPointerError,
NotInitializedError,
NotmuchTagsP,
NotmuchMessageP,
NotmuchMessagesP,
NotmuchFilenamesP,
)
from notmuch.tag import Tags from notmuch.tag import Tags
from notmuch.filename import Filenames from notmuch.filename import Filenames
import sys import sys
@ -43,7 +54,7 @@ class Messages(object):
only provides a one-time iterator (it cannot reset the iterator to only provides a one-time iterator (it cannot reset the iterator to
the start). Thus iterating over the function will "exhaust" the list the start). Thus iterating over the function will "exhaust" the list
of messages, and a subsequent iteration attempt will raise a of messages, and a subsequent iteration attempt will raise a
:exc:`NotmuchError` STATUS.NOT_INITIALIZED. If you need to :exc:`NotInitializedError`. If you need to
re-iterate over a list of messages you will need to retrieve a new re-iterate over a list of messages you will need to retrieve a new
:class:`Messages` object or cache your :class:`Message`\s in a list :class:`Messages` object or cache your :class:`Message`\s in a list
via:: via::
@ -107,8 +118,8 @@ class Messages(object):
will almost never instantiate a :class:`Messages` object will almost never instantiate a :class:`Messages` object
herself. They are usually handed back as a result, herself. They are usually handed back as a result,
e.g. in :meth:`Query.search_messages`. *msgs_p* must be e.g. in :meth:`Query.search_messages`. *msgs_p* must be
valid, we will raise an :exc:`NotmuchError` valid, we will raise an :exc:`NullPointerError` if it is
(STATUS.NULL_POINTER) if it is `None`. `None`.
:type msgs_p: :class:`ctypes.c_void_p` :type msgs_p: :class:`ctypes.c_void_p`
:param parent: The parent object :param parent: The parent object
(ie :class:`Query`) these tags are derived from. It saves (ie :class:`Query`) these tags are derived from. It saves
@ -118,7 +129,7 @@ class Messages(object):
the Python object.(?) the Python object.(?)
""" """
if not msgs_p: if not msgs_p:
raise NotmuchError(STATUS.NULL_POINTER) raise NullPointerError()
self._msgs = msgs_p self._msgs = msgs_p
#store parent, so we keep them alive as long as self is alive #store parent, so we keep them alive as long as self is alive
@ -128,7 +139,7 @@ class Messages(object):
"""Return the unique :class:`Tags` in the contained messages """Return the unique :class:`Tags` in the contained messages
:returns: :class:`Tags` :returns: :class:`Tags`
:exceptions: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if not init'ed :exceptions: :exc:`NotInitializedError` if not init'ed
.. note:: .. note::
@ -136,7 +147,7 @@ class Messages(object):
will not allow further iterations. will not allow further iterations.
""" """
if not self._msgs: if not self._msgs:
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotInitializedError()
# collect all tags (returns NULL on error) # collect all tags (returns NULL on error)
tags_p = Messages._collect_tags(self._msgs) tags_p = Messages._collect_tags(self._msgs)
@ -144,7 +155,7 @@ class Messages(object):
self._msgs = None self._msgs = None
if tags_p == None: if tags_p == None:
raise NotmuchError(STATUS.NULL_POINTER) raise NullPointerError()
return Tags(tags_p, self) return Tags(tags_p, self)
def __iter__(self): def __iter__(self):
@ -161,7 +172,7 @@ class Messages(object):
def __next__(self): def __next__(self):
if not self._msgs: if not self._msgs:
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotInitializedError()
if not self._valid(self._msgs): if not self._valid(self._msgs):
self._msgs = None self._msgs = None
@ -341,8 +352,8 @@ class Message(Python3StringMixIn):
def __init__(self, msg_p, parent=None): def __init__(self, msg_p, parent=None):
""" """
:param msg_p: A pointer to an internal notmuch_message_t :param msg_p: A pointer to an internal notmuch_message_t
Structure. If it is `None`, we will raise an :exc:`NotmuchError` Structure. If it is `None`, we will raise an
STATUS.NULL_POINTER. :exc:`NullPointerError`.
:param parent: A 'parent' object is passed which this message is :param parent: A 'parent' object is passed which this message is
derived from. We save a reference to it, so we can derived from. We save a reference to it, so we can
@ -350,7 +361,7 @@ class Message(Python3StringMixIn):
objects are dead. objects are dead.
""" """
if not msg_p: if not msg_p:
raise NotmuchError(STATUS.NULL_POINTER) raise NullPointerError()
self._msg = msg_p self._msg = msg_p
#keep reference to parent, so we keep it alive #keep reference to parent, so we keep it alive
self._parent = parent self._parent = parent
@ -359,11 +370,11 @@ class Message(Python3StringMixIn):
"""Returns the message ID """Returns the message ID
:returns: String with a message ID :returns: String with a message ID
:exception: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if the message :exception: :exc:`NotInitializedError` if the message
is not initialized. is not initialized.
""" """
if not self._msg: if not self._msg:
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotInitializedError()
return Message._get_message_id(self._msg).decode('utf-8', 'ignore') return Message._get_message_id(self._msg).decode('utf-8', 'ignore')
def get_thread_id(self): def get_thread_id(self):
@ -376,11 +387,11 @@ class Message(Python3StringMixIn):
message belongs to a single thread. message belongs to a single thread.
:returns: String with a thread ID :returns: String with a thread ID
:exception: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if the message :exception: :exc:`NotInitializedError` if the message
is not initialized. is not initialized.
""" """
if not self._msg: if not self._msg:
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotInitializedError()
return Message._get_thread_id(self._msg).decode('utf-8', 'ignore') return Message._get_thread_id(self._msg).decode('utf-8', 'ignore')
@ -399,11 +410,11 @@ class Message(Python3StringMixIn):
an empty Messages iterator. an empty Messages iterator.
:returns: :class:`Messages`. :returns: :class:`Messages`.
:exception: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if the message :exception: :exc:`NotInitializedError` if the message
is not initialized. is not initialized.
""" """
if not self._msg: if not self._msg:
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotInitializedError()
msgs_p = Message._get_replies(self._msg) msgs_p = Message._get_replies(self._msg)
@ -421,11 +432,11 @@ class Message(Python3StringMixIn):
:returns: A time_t timestamp. :returns: A time_t timestamp.
:rtype: c_unit64 :rtype: c_unit64
:exception: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if the message :exception: :exc:`NotInitializedError` if the message
is not initialized. is not initialized.
""" """
if not self._msg: if not self._msg:
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotInitializedError()
return Message._get_date(self._msg) return Message._get_date(self._msg)
def get_header(self, header): def get_header(self, header):
@ -441,30 +452,28 @@ class Message(Python3StringMixIn):
It is not case-sensitive. It is not case-sensitive.
:type header: str :type header: str
:returns: The header value as string :returns: The header value as string
:exception: :exc:`NotmuchError` :raises: :exc:`NotInitializedError` if the message is not
initialized
* STATUS.NOT_INITIALIZED if the message :raises: :exc:`NullPointerError` if any error occured
is not initialized.
* STATUS.NULL_POINTER if any error occured.
""" """
if not self._msg: if not self._msg:
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotInitializedError()
#Returns NULL if any error occurs. #Returns NULL if any error occurs.
header = Message._get_header(self._msg, _str(header)) header = Message._get_header(self._msg, _str(header))
if header == None: if header == None:
raise NotmuchError(STATUS.NULL_POINTER) raise NullPointerError()
return header.decode('UTF-8', 'ignore') return header.decode('UTF-8', 'ignore')
def get_filename(self): def get_filename(self):
"""Returns the file path of the message file """Returns the file path of the message file
:returns: Absolute file path & name of the message file :returns: Absolute file path & name of the message file
:exception: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if the message :exception: :exc:`NotInitializedError` if the message
is not initialized. is not initialized.
""" """
if not self._msg: if not self._msg:
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotInitializedError()
return Message._get_filename(self._msg).decode('utf-8', 'ignore') return Message._get_filename(self._msg).decode('utf-8', 'ignore')
def get_filenames(self): def get_filenames(self):
@ -474,7 +483,7 @@ class Message(Python3StringMixIn):
messages recorded to have the same Message-ID. These files must messages recorded to have the same Message-ID. These files must
not necessarily have identical content.""" not necessarily have identical content."""
if not self._msg: if not self._msg:
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotInitializedError()
files_p = Message._get_filenames(self._msg) files_p = Message._get_filenames(self._msg)
@ -490,11 +499,11 @@ class Message(Python3StringMixIn):
:param flag: One of the :attr:`Message.FLAG` values (currently only :param flag: One of the :attr:`Message.FLAG` values (currently only
*Message.FLAG.MATCH* *Message.FLAG.MATCH*
:returns: An unsigned int (0/1), indicating whether the flag is set. :returns: An unsigned int (0/1), indicating whether the flag is set.
:exception: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if the message :exception: :exc:`NotInitializedError` if the message
is not initialized. is not initialized.
""" """
if not self._msg: if not self._msg:
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotInitializedError()
return Message._get_flag(self._msg, flag) return Message._get_flag(self._msg, flag)
def set_flag(self, flag, value): def set_flag(self, flag, value):
@ -505,29 +514,27 @@ class Message(Python3StringMixIn):
:param value: A bool indicating whether to set or unset the flag. :param value: A bool indicating whether to set or unset the flag.
:returns: Nothing :returns: Nothing
:exception: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if the message :exception: :exc:`NotInitializedError` if the message
is not initialized. is not initialized.
""" """
if not self._msg: if not self._msg:
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotInitializedError()
self._set_flag(self._msg, flag, value) self._set_flag(self._msg, flag, value)
def get_tags(self): def get_tags(self):
"""Returns the message tags """Returns the message tags
:returns: A :class:`Tags` iterator. :returns: A :class:`Tags` iterator.
:exception: :exc:`NotmuchError` :raises: :exc:`NotInitializedError` if the message is not
initialized
* STATUS.NOT_INITIALIZED if the message :raises: :exc:`NullPointerError` if any error occured
is not initialized.
* STATUS.NULL_POINTER, on error
""" """
if not self._msg: if not self._msg:
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotInitializedError()
tags_p = Message._get_tags(self._msg) tags_p = Message._get_tags(self._msg)
if tags_p == None: if tags_p == None:
raise NotmuchError(STATUS.NULL_POINTER) raise NullPointerError()
return Tags(tags_p, self) return Tags(tags_p, self)
_add_tag = nmlib.notmuch_message_add_tag _add_tag = nmlib.notmuch_message_add_tag
@ -552,21 +559,16 @@ class Message(Python3StringMixIn):
:returns: STATUS.SUCCESS if the tag was successfully added. :returns: STATUS.SUCCESS if the tag was successfully added.
Raises an exception otherwise. Raises an exception otherwise.
:exception: :exc:`NotmuchError`. They have the following meaning: :raises: :exc:`NullPointerError` if the `tag` argument is NULL
:raises: :exc:`TagTooLongError` if the length of `tag` exceeds
STATUS.NULL_POINTER Message.NOTMUCH_TAG_MAX)
The 'tag' argument is NULL :raises: :exc:`ReadOnlyDatabaseError` if the database was opened
STATUS.TAG_TOO_LONG in read-only mode so message cannot be modified
The length of 'tag' is too long :raises: :exc:`NotInitializedError` if message has not been
(exceeds Message.NOTMUCH_TAG_MAX) initialized
STATUS.READ_ONLY_DATABASE """
Database was opened in read-only mode so message cannot be
modified.
STATUS.NOT_INITIALIZED
The message has not been initialized.
"""
if not self._msg: if not self._msg:
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotInitializedError()
status = self._add_tag(self._msg, _str(tag)) status = self._add_tag(self._msg, _str(tag))
@ -600,21 +602,16 @@ class Message(Python3StringMixIn):
:returns: STATUS.SUCCESS if the tag was successfully removed or if :returns: STATUS.SUCCESS if the tag was successfully removed or if
the message had no such tag. the message had no such tag.
Raises an exception otherwise. Raises an exception otherwise.
:exception: :exc:`NotmuchError`. They have the following meaning: :raises: :exc:`NullPointerError` if the `tag` argument is NULL
:raises: :exc:`TagTooLongError` if the length of `tag` exceeds
STATUS.NULL_POINTER Message.NOTMUCH_TAG_MAX)
The 'tag' argument is NULL :raises: :exc:`ReadOnlyDatabaseError` if the database was opened
STATUS.TAG_TOO_LONG in read-only mode so message cannot be modified
The length of 'tag' is too long :raises: :exc:`NotInitializedError` if message has not been
(exceeds NOTMUCH_TAG_MAX) initialized
STATUS.READ_ONLY_DATABASE
Database was opened in read-only mode so message cannot
be modified.
STATUS.NOT_INITIALIZED
The message has not been initialized.
""" """
if not self._msg: if not self._msg:
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotInitializedError()
status = self._remove_tag(self._msg, _str(tag)) status = self._remove_tag(self._msg, _str(tag))
# bail out on error # bail out on error
@ -646,16 +643,13 @@ class Message(Python3StringMixIn):
:returns: STATUS.SUCCESS if the tags were successfully removed. :returns: STATUS.SUCCESS if the tags were successfully removed.
Raises an exception otherwise. Raises an exception otherwise.
:exception: :exc:`NotmuchError`. They have the following meaning: :raises: :exc:`ReadOnlyDatabaseError` if the database was opened
in read-only mode so message cannot be modified
STATUS.READ_ONLY_DATABASE :raises: :exc:`NotInitializedError` if message has not been
Database was opened in read-only mode so message cannot initialized
be modified.
STATUS.NOT_INITIALIZED
The message has not been initialized.
""" """
if not self._msg: if not self._msg:
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotInitializedError()
status = self._remove_all_tags(self._msg) status = self._remove_all_tags(self._msg)
@ -704,16 +698,13 @@ class Message(Python3StringMixIn):
:returns: STATUS.SUCCESS if the message was successfully frozen. :returns: STATUS.SUCCESS if the message was successfully frozen.
Raises an exception otherwise. Raises an exception otherwise.
:exception: :exc:`NotmuchError`. They have the following meaning: :raises: :exc:`ReadOnlyDatabaseError` if the database was opened
in read-only mode so message cannot be modified
STATUS.READ_ONLY_DATABASE :raises: :exc:`NotInitializedError` if message has not been
Database was opened in read-only mode so message cannot initialized
be modified.
STATUS.NOT_INITIALIZED
The message has not been initialized.
""" """
if not self._msg: if not self._msg:
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotInitializedError()
status = self._freeze(self._msg) status = self._freeze(self._msg)
@ -742,17 +733,15 @@ class Message(Python3StringMixIn):
:returns: STATUS.SUCCESS if the message was successfully frozen. :returns: STATUS.SUCCESS if the message was successfully frozen.
Raises an exception otherwise. Raises an exception otherwise.
:exception: :exc:`NotmuchError`. They have the following meaning: :raises: :exc:`UnbalancedFreezeThawError` if an attempt was made
to thaw an unfrozen message. That is, there have been
STATUS.UNBALANCED_FREEZE_THAW an unbalanced number of calls to :meth:`freeze` and
An attempt was made to thaw an unfrozen message. :meth:`thaw`.
That is, there have been an unbalanced number of calls :raises: :exc:`NotInitializedError` if message has not been
to :meth:`freeze` and :meth:`thaw`. initialized
STATUS.NOT_INITIALIZED
The message has not been initialized.
""" """
if not self._msg: if not self._msg:
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotInitializedError()
status = self._thaw(self._msg) status = self._thaw(self._msg)
@ -788,7 +777,7 @@ class Message(Python3StringMixIn):
:returns: a :class:`STATUS` value. In short, you want to see :returns: a :class:`STATUS` value. In short, you want to see
notmuch.STATUS.SUCCESS here. See there for details.""" notmuch.STATUS.SUCCESS here. See there for details."""
if not self._msg: if not self._msg:
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotInitializedError()
return Message._tags_to_maildir_flags(self._msg) return Message._tags_to_maildir_flags(self._msg)
def maildir_flags_to_tags(self): def maildir_flags_to_tags(self):
@ -815,7 +804,7 @@ class Message(Python3StringMixIn):
:returns: a :class:`STATUS`. In short, you want to see :returns: a :class:`STATUS`. In short, you want to see
notmuch.STATUS.SUCCESS here. See there for details.""" notmuch.STATUS.SUCCESS here. See there for details."""
if not self._msg: if not self._msg:
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotInitializedError()
return Message._tags_to_maildir_flags(self._msg) return Message._tags_to_maildir_flags(self._msg)
def __repr__(self): def __repr__(self):

View file

@ -17,7 +17,13 @@ along with notmuch. If not, see <http://www.gnu.org/licenses/>.
Copyright 2010 Sebastian Spaeth <Sebastian@SSpaeth.de>' Copyright 2010 Sebastian Spaeth <Sebastian@SSpaeth.de>'
""" """
from ctypes import c_char_p from ctypes import c_char_p
from notmuch.globals import nmlib, STATUS, NotmuchError, NotmuchTagsP, Python3StringMixIn from notmuch.globals import (
nmlib,
Python3StringMixIn,
NullPointerError,
NotInitializedError,
NotmuchTagsP,
)
class Tags(Python3StringMixIn): class Tags(Python3StringMixIn):
@ -29,9 +35,9 @@ class Tags(Python3StringMixIn):
Do note that the underlying library only provides a one-time Do note that the underlying library only provides a one-time
iterator (it cannot reset the iterator to the start). Thus iterating iterator (it cannot reset the iterator to the start). Thus iterating
over the function will "exhaust" the list of tags, and a subsequent over the function will "exhaust" the list of tags, and a subsequent
iteration attempt will raise a :exc:`NotmuchError` iteration attempt will raise a :exc:`NotInitializedError`.
STATUS.NOT_INITIALIZED. Also note, that any function that uses Also note, that any function that uses iteration (nearly all) will
iteration (nearly all) will also exhaust the tags. So both:: also exhaust the tags. So both::
for tag in tags: print tag for tag in tags: print tag
@ -60,8 +66,8 @@ class Tags(Python3StringMixIn):
will almost never instantiate a :class:`Tags` object will almost never instantiate a :class:`Tags` object
herself. They are usually handed back as a result, herself. They are usually handed back as a result,
e.g. in :meth:`Database.get_all_tags`. *tags_p* must be e.g. in :meth:`Database.get_all_tags`. *tags_p* must be
valid, we will raise an :exc:`NotmuchError` valid, we will raise an :exc:`NullPointerError` if it is
(STATUS.NULL_POINTER) if it is `None`. `None`.
:type tags_p: :class:`ctypes.c_void_p` :type tags_p: :class:`ctypes.c_void_p`
:param parent: The parent object (ie :class:`Database` or :param parent: The parent object (ie :class:`Database` or
:class:`Message` these tags are derived from, and saves a :class:`Message` these tags are derived from, and saves a
@ -71,7 +77,7 @@ class Tags(Python3StringMixIn):
cache the tags in the Python object(?) cache the tags in the Python object(?)
""" """
if not tags_p: if not tags_p:
raise NotmuchError(STATUS.NULL_POINTER) raise NullPointerError()
self._tags = tags_p self._tags = tags_p
#save reference to parent object so we keep it alive #save reference to parent object so we keep it alive
@ -91,7 +97,7 @@ class Tags(Python3StringMixIn):
def __next__(self): def __next__(self):
if not self._tags: if not self._tags:
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotInitializedError()
if not self._valid(self._tags): if not self._valid(self._tags):
self._tags = None self._tags = None
raise StopIteration raise StopIteration
@ -118,8 +124,8 @@ class Tags(Python3StringMixIn):
As this iterates over the tags, we will not be able to iterate over As this iterates over the tags, we will not be able to iterate over
them again (as in retrieve them)! If the tags have been exhausted them again (as in retrieve them)! If the tags have been exhausted
already, this will raise a :exc:`NotmuchError` already, this will raise a :exc:`NotInitializedError`on subsequent
STATUS.NOT_INITIALIZED on subsequent attempts. attempts.
""" """
return " ".join(self) return " ".join(self)

View file

@ -18,9 +18,16 @@ Copyright 2010 Sebastian Spaeth <Sebastian@SSpaeth.de>'
""" """
from ctypes import c_char_p, c_long, c_int from ctypes import c_char_p, c_long, c_int
from notmuch.globals import (nmlib, STATUS, from notmuch.globals import (
NotmuchError, NotmuchThreadP, NotmuchThreadsP, NotmuchMessagesP, nmlib,
NotmuchTagsP, Python3StringMixIn) Python3StringMixIn,
NullPointerError,
NotInitializedError,
NotmuchThreadP,
NotmuchThreadsP,
NotmuchMessagesP,
NotmuchTagsP,
)
from notmuch.message import Messages from notmuch.message import Messages
from notmuch.tag import Tags from notmuch.tag import Tags
from datetime import date from datetime import date
@ -35,7 +42,7 @@ class Threads(Python3StringMixIn):
library only provides a one-time iterator (it cannot reset the library only provides a one-time iterator (it cannot reset the
iterator to the start). Thus iterating over the function will iterator to the start). Thus iterating over the function will
"exhaust" the list of threads, and a subsequent iteration attempt "exhaust" the list of threads, and a subsequent iteration attempt
will raise a :exc:`NotmuchError` STATUS.NOT_INITIALIZED. Also will raise a :exc:`NotInitializedError`. Also
note, that any function that uses iteration will also note, that any function that uses iteration will also
exhaust the messages. So both:: exhaust the messages. So both::
@ -87,8 +94,8 @@ class Threads(Python3StringMixIn):
will almost never instantiate a :class:`Threads` object will almost never instantiate a :class:`Threads` object
herself. They are usually handed back as a result, herself. They are usually handed back as a result,
e.g. in :meth:`Query.search_threads`. *threads_p* must be e.g. in :meth:`Query.search_threads`. *threads_p* must be
valid, we will raise an :exc:`NotmuchError` valid, we will raise an :exc:`NullPointerError` if it is
(STATUS.NULL_POINTER) if it is `None`. `None`.
:type threads_p: :class:`ctypes.c_void_p` :type threads_p: :class:`ctypes.c_void_p`
:param parent: The parent object :param parent: The parent object
(ie :class:`Query`) these tags are derived from. It saves (ie :class:`Query`) these tags are derived from. It saves
@ -98,7 +105,7 @@ class Threads(Python3StringMixIn):
the Python object.(?) the Python object.(?)
""" """
if not threads_p: if not threads_p:
raise NotmuchError(STATUS.NULL_POINTER) raise NullPointerError()
self._threads = threads_p self._threads = threads_p
#store parent, so we keep them alive as long as self is alive #store parent, so we keep them alive as long as self is alive
@ -118,7 +125,7 @@ class Threads(Python3StringMixIn):
def __next__(self): def __next__(self):
if not self._threads: if not self._threads:
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotInitializedError()
if not self._valid(self._threads): if not self._valid(self._threads):
self._threads = None self._threads = None
@ -138,11 +145,11 @@ class Threads(Python3StringMixIn):
#THIS FAILS #THIS FAILS
threads = Database().create_query('').search_threads() threads = Database().create_query('').search_threads()
if len(threads) > 0: #this 'exhausts' threads if len(threads) > 0: #this 'exhausts' threads
# next line raises NotmuchError(STATUS.NOT_INITIALIZED)!!! # next line raises :exc:`NotInitializedError`!!!
for thread in threads: print thread for thread in threads: print thread
""" """
if not self._threads: if not self._threads:
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotInitializedError()
i = 0 i = 0
# returns 'bool'. On out-of-memory it returns None # returns 'bool'. On out-of-memory it returns None
@ -220,8 +227,8 @@ class Thread(object):
will almost never instantiate a :class:`Thread` object will almost never instantiate a :class:`Thread` object
herself. They are usually handed back as a result, herself. They are usually handed back as a result,
e.g. when iterating through :class:`Threads`. *thread_p* e.g. when iterating through :class:`Threads`. *thread_p*
must be valid, we will raise an :exc:`NotmuchError` must be valid, we will raise an :exc:`NullPointerError`
(STATUS.NULL_POINTER) if it is `None`. if it is `None`.
:param parent: A 'parent' object is passed which this message is :param parent: A 'parent' object is passed which this message is
derived from. We save a reference to it, so we can derived from. We save a reference to it, so we can
@ -229,7 +236,7 @@ class Thread(object):
objects are dead. objects are dead.
""" """
if not thread_p: if not thread_p:
raise NotmuchError(STATUS.NULL_POINTER) raise NullPointerError()
self._thread = thread_p self._thread = thread_p
#keep reference to parent, so we keep it alive #keep reference to parent, so we keep it alive
self._parent = parent self._parent = parent
@ -241,11 +248,11 @@ class Thread(object):
for as long as the thread is valid. for as long as the thread is valid.
:returns: String with a message ID :returns: String with a message ID
:exception: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if the thread :exception: :exc:`NotInitializedError` if the thread
is not initialized. is not initialized.
""" """
if not self._thread: if not self._thread:
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotInitializedError()
return Thread._get_thread_id(self._thread).decode('utf-8', 'ignore') return Thread._get_thread_id(self._thread).decode('utf-8', 'ignore')
_get_total_messages = nmlib.notmuch_thread_get_total_messages _get_total_messages = nmlib.notmuch_thread_get_total_messages
@ -258,11 +265,11 @@ class Thread(object):
:returns: The number of all messages in the database :returns: The number of all messages in the database
belonging to this thread. Contrast with belonging to this thread. Contrast with
:meth:`get_matched_messages`. :meth:`get_matched_messages`.
:exception: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if the thread :exception: :exc:`NotInitializedError` if the thread
is not initialized. is not initialized.
""" """
if not self._thread: if not self._thread:
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotInitializedError()
return self._get_total_messages(self._thread) return self._get_total_messages(self._thread)
def get_toplevel_messages(self): def get_toplevel_messages(self):
@ -279,18 +286,16 @@ class Thread(object):
messages, etc.). messages, etc.).
:returns: :class:`Messages` :returns: :class:`Messages`
:exception: :exc:`NotmuchError` :raises: :exc:`NotInitializedError` if query is not initialized
:raises: :exc:`NullPointerError` if search_messages failed
* STATUS.NOT_INITIALIZED if query is not inited
* STATUS.NULL_POINTER if search_messages failed
""" """
if not self._thread: if not self._thread:
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotInitializedError()
msgs_p = Thread._get_toplevel_messages(self._thread) msgs_p = Thread._get_toplevel_messages(self._thread)
if not msgs_p: if not msgs_p:
raise NotmuchError(STATUS.NULL_POINTER) raise NullPointerError()
return Messages(msgs_p, self) return Messages(msgs_p, self)
@ -304,11 +309,11 @@ class Thread(object):
:returns: The number of all messages belonging to this thread that :returns: The number of all messages belonging to this thread that
matched the :class:`Query`from which this thread was created. matched the :class:`Query`from which this thread was created.
Contrast with :meth:`get_total_messages`. Contrast with :meth:`get_total_messages`.
:exception: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if the thread :exception: :exc:`NotInitializedError` if the thread
is not initialized. is not initialized.
""" """
if not self._thread: if not self._thread:
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotInitializedError()
return self._get_matched_messages(self._thread) return self._get_matched_messages(self._thread)
def get_authors(self): def get_authors(self):
@ -322,7 +327,7 @@ class Thread(object):
as long as this Thread() is not deleted. as long as this Thread() is not deleted.
""" """
if not self._thread: if not self._thread:
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotInitializedError()
authors = Thread._get_authors(self._thread) authors = Thread._get_authors(self._thread)
if not authors: if not authors:
return None return None
@ -335,7 +340,7 @@ class Thread(object):
as long as this Thread() is not deleted. as long as this Thread() is not deleted.
""" """
if not self._thread: if not self._thread:
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotInitializedError()
subject = Thread._get_subject(self._thread) subject = Thread._get_subject(self._thread)
if not subject: if not subject:
return None return None
@ -346,11 +351,11 @@ class Thread(object):
:returns: A time_t timestamp. :returns: A time_t timestamp.
:rtype: c_unit64 :rtype: c_unit64
:exception: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if the message :exception: :exc:`NotInitializedError` if the message
is not initialized. is not initialized.
""" """
if not self._thread: if not self._thread:
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotInitializedError()
return Thread._get_newest_date(self._thread) return Thread._get_newest_date(self._thread)
def get_oldest_date(self): def get_oldest_date(self):
@ -358,11 +363,11 @@ class Thread(object):
:returns: A time_t timestamp. :returns: A time_t timestamp.
:rtype: c_unit64 :rtype: c_unit64
:exception: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if the message :exception: :exc:`NotInitializedError` if the message
is not initialized. is not initialized.
""" """
if not self._thread: if not self._thread:
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotInitializedError()
return Thread._get_oldest_date(self._thread) return Thread._get_oldest_date(self._thread)
def get_tags(self): def get_tags(self):
@ -378,18 +383,15 @@ class Thread(object):
query from which it derived is explicitely deleted). query from which it derived is explicitely deleted).
:returns: A :class:`Tags` iterator. :returns: A :class:`Tags` iterator.
:exception: :exc:`NotmuchError` :raises: :exc:`NotInitializedError` if query is not initialized
:raises: :exc:`NullPointerError` if search_messages failed
* STATUS.NOT_INITIALIZED if the thread
is not initialized.
* STATUS.NULL_POINTER, on error
""" """
if not self._thread: if not self._thread:
raise NotmuchError(STATUS.NOT_INITIALIZED) raise NotInitializedError()
tags_p = Thread._get_tags(self._thread) tags_p = Thread._get_tags(self._thread)
if tags_p == None: if tags_p == None:
raise NotmuchError(STATUS.NULL_POINTER) raise NullPointerError()
return Tags(tags_p, self) return Tags(tags_p, self)
def __unicode__(self): def __unicode__(self):