2 This file is part of notmuch.
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.
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
14 You should have received a copy of the GNU General Public License
15 along with notmuch. If not, see <https://www.gnu.org/licenses/>.
17 Copyright 2010 Sebastian Spaeth <Sebastian@SSpaeth.de>
18 Jesse Rosenthal <jrosenthal@jhu.edu>
21 from .globals import (
32 from .message import Message
34 class Messages(object):
35 """Represents a list of notmuch messages
37 This object provides an iterator over a list of notmuch messages
38 (Technically, it provides a wrapper for the underlying
39 *notmuch_messages_t* structure). Do note that the underlying library
40 only provides a one-time iterator (it cannot reset the iterator to
41 the start). Thus iterating over the function will "exhaust" the list
42 of messages, and a subsequent iteration attempt will raise a
43 :exc:`NotInitializedError`. If you need to
44 re-iterate over a list of messages you will need to retrieve a new
45 :class:`Messages` object or cache your :class:`Message`\s in a list
50 You can store and reuse the single :class:`Message` objects as often
51 as you want as long as you keep the parent :class:`Messages` object
52 around. (Due to hierarchical memory allocation, all derived
53 :class:`Message` objects will be invalid when we delete the parent
54 :class:`Messages` object, even if it was already exhausted.) So
58 msgs = Query(db,'').search_messages() #get a Messages() object
61 # msgs is "exhausted" now and msgs.next() will raise an exception.
62 # However it will be kept alive until all retrieved Message()
63 # objects are also deleted. If you do e.g. an explicit del(msgs)
64 # here, the following lines would fail.
66 # You can reiterate over *msglist* however as often as you want.
67 # It is simply a list with :class:`Message`s.
69 print (msglist[0].get_filename())
70 print (msglist[1].get_filename())
71 print (msglist[0].get_message_id())
74 As :class:`Message` implements both __hash__() and __cmp__(), it is
75 possible to make sets out of :class:`Messages` and use set
76 arithmetic (this happens in python and will of course be *much*
77 slower than redoing a proper query with the appropriate filters::
79 s1, s2 = set(msgs1), set(msgs2)
84 Be careful when using set arithmetic between message sets derived
85 from different Databases (ie the same database reopened after
86 messages have changed). If messages have added or removed associated
87 files in the meantime, it is possible that the same message would be
88 considered as a different object (as it points to a different file).
92 _get = nmlib.notmuch_messages_get
93 _get.argtypes = [NotmuchMessagesP]
94 _get.restype = NotmuchMessageP
96 _collect_tags = nmlib.notmuch_messages_collect_tags
97 _collect_tags.argtypes = [NotmuchMessagesP]
98 _collect_tags.restype = NotmuchTagsP
100 def __init__(self, msgs_p, parent=None):
102 :param msgs_p: A pointer to an underlying *notmuch_messages_t*
103 structure. These are not publicly exposed, so a user
104 will almost never instantiate a :class:`Messages` object
105 herself. They are usually handed back as a result,
106 e.g. in :meth:`Query.search_messages`. *msgs_p* must be
107 valid, we will raise an :exc:`NullPointerError` if it is
109 :type msgs_p: :class:`ctypes.c_void_p`
110 :param parent: The parent object
111 (ie :class:`Query`) these tags are derived from. It saves
112 a reference to it, so we can automatically delete the db
113 object once all derived objects are dead.
114 :TODO: Make the iterator work more than once and cache the tags in
115 the Python object.(?)
118 raise NullPointerError()
121 #store parent, so we keep them alive as long as self is alive
122 self._parent = parent
124 def collect_tags(self):
125 """Return the unique :class:`Tags` in the contained messages
127 :returns: :class:`Tags`
128 :exceptions: :exc:`NotInitializedError` if not init'ed
132 :meth:`collect_tags` will iterate over the messages and therefore
133 will not allow further iterations.
136 raise NotInitializedError()
138 # collect all tags (returns NULL on error)
139 tags_p = Messages._collect_tags(self._msgs)
140 #reset _msgs as we iterated over it and can do so only once
144 raise NullPointerError()
145 return Tags(tags_p, self)
148 """ Make Messages an iterator """
151 _valid = nmlib.notmuch_messages_valid
152 _valid.argtypes = [NotmuchMessagesP]
153 _valid.restype = bool
155 _move_to_next = nmlib.notmuch_messages_move_to_next
156 _move_to_next.argtypes = [NotmuchMessagesP]
157 _move_to_next.restype = None
161 raise NotInitializedError()
163 if not self._valid(self._msgs):
167 msg = Message(Messages._get(self._msgs), self)
168 self._move_to_next(self._msgs)
170 next = __next__ # python2.x iterator protocol compatibility
172 def __nonzero__(self):
174 Implement truth value testing. If __nonzero__ is not
175 implemented, the python runtime would fall back to `len(..) >
176 0` thus exhausting the iterator.
178 :returns: True if the wrapped iterator has at least one more object
181 return self._msgs and self._valid(self._msgs)
183 _destroy = nmlib.notmuch_messages_destroy
184 _destroy.argtypes = [NotmuchMessagesP]
185 _destroy.restype = None
188 """Close and free the notmuch Messages"""
190 self._destroy(self._msgs)
192 class EmptyMessagesResult(Messages):
193 def __init__(self, parent):
195 self._parent = parent
198 raise StopIteration()