python: Remove Messages().__len__

Messages.__len__() exhausted the iterator and list() inherently calls
len(), so we could not invoke list(msgs) without getting errors. Fix
this by implementing __nonzero__ but removing __len__ on Messages.

Use Query.count_messages() or len(list(msgs)) if you need to know the
number.

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
This commit is contained in:
Sebastian Spaeth 2011-06-15 14:25:33 +02:00
parent b7db7ea420
commit 8866a89e3c
2 changed files with 28 additions and 52 deletions

View file

@ -127,7 +127,12 @@ More information on specific topics can be found on the following pages:
.. automethod:: collect_tags .. automethod:: collect_tags
.. automethod:: __len__ .. method:: __len__()
.. note:: :meth:`__len__` was removed in version 0.6 as it exhausted
the iterator and broke list(Messages()). Use the
:meth:`Query.count_messages` function or use
`len(list(msgs))`.
:class:`Message` -- A single message :class:`Message` -- A single message
---------------------------------------- ----------------------------------------

View file

@ -37,35 +37,27 @@ class Messages(object):
This object provides an iterator over a list of notmuch messages This object provides an iterator over a list of notmuch messages
(Technically, it provides a wrapper for the underlying (Technically, it provides a wrapper for the underlying
*notmuch_messages_t* structure). Do note that the underlying *notmuch_messages_t* structure). Do note that the underlying library
library only provides a one-time iterator (it cannot reset the only provides a one-time iterator (it cannot reset the iterator to
iterator to the start). Thus iterating over the function will the start). Thus iterating over the function will "exhaust" the list
"exhaust" the list of messages, and a subsequent iteration attempt of messages, and a subsequent iteration attempt will raise a
will raise a :exc:`NotmuchError` STATUS.NOT_INITIALIZED. Also :exc:`NotmuchError` STATUS.NOT_INITIALIZED. Also note, that any
note, that any function that uses iteration will also function that uses iteration will also exhaust the messages.If you
exhaust the messages. So both:: need to 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 via::
for msg in msgs: print msg msglist = list(msgs)
as well as:: You can store and reuse the single Message objects as often as you
want as long as you keep the parent Messages object around. (Recall
number_of_msgs = len(msgs) that due to hierarchical memory allocation, all derived Message
objects will be invalid when we delete the parent Messages() object,
will "exhaust" the Messages. If you need to re-iterate over a list of even if it was already "exhausted".) So this works::
messages you will need to retrieve a new :class:`Messages` object.
Things are not as bad as it seems though, you can store and reuse
the single Message objects as often as you want as long as you
keep the parent Messages object around. (Recall that due to
hierarchical memory allocation, all derived Message objects will
be invalid when we delete the parent Messages() object, even if it
was already "exhausted".) So this works::
db = Database() db = Database()
msgs = Query(db,'').search_messages() #get a Messages() object msgs = Query(db,'').search_messages() #get a Messages() object
msglist = [] msglist = list(msgs)
for m in msgs:
msglist.append(m)
# msgs is "exhausted" now and even len(msgs) will raise an exception. # msgs is "exhausted" now and even len(msgs) will raise an exception.
# However it will be kept around until all retrieved Message() objects are # However it will be kept around until all retrieved Message() objects are
@ -80,7 +72,7 @@ class Messages(object):
print (msglist[0].get_message_id()) print (msglist[0].get_message_id())
""" """
#notmuch_tags_get #notmuch_messages_get
_get = nmlib.notmuch_messages_get _get = nmlib.notmuch_messages_get
_get.restype = c_void_p _get.restype = c_void_p
@ -148,33 +140,12 @@ class Messages(object):
nmlib.notmuch_messages_move_to_next(self._msgs) nmlib.notmuch_messages_move_to_next(self._msgs)
return msg return msg
def __len__(self): def __nonzero__(self):
"""len(:class:`Messages`) returns the number of contained messages
.. note:: As this iterates over the messages, we will not be able to
iterate over them again! So this will fail::
#THIS FAILS
msgs = Database().create_query('').search_message()
if len(msgs) > 0: #this 'exhausts' msgs
# next line raises NotmuchError(STATUS.NOT_INITIALIZED)!!!
for msg in msgs: print msg
Most of the time, using the
:meth:`Query.count_messages` is therefore more
appropriate (and much faster). While not guaranteeing
that it will return the exact same number than len(),
in my tests it effectively always did so.
""" """
if self._msgs is None: :return: True if there is at least one more thread in the
raise NotmuchError(STATUS.NOT_INITIALIZED) Iterator, False if not."""
return self._msgs is not None and \
i=0 nmlib.notmuch_messages_valid(self._msgs) > 0
while nmlib.notmuch_messages_valid(self._msgs):
nmlib.notmuch_messages_move_to_next(self._msgs)
i += 1
self._msgs = None
return i
def __del__(self): def __del__(self):
"""Close and free the notmuch Messages""" """Close and free the notmuch Messages"""