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 <http://www.gnu.org/licenses/>.
17 Copyright 2010 Sebastian Spaeth <Sebastian@SSpaeth.de>'
20 from notmuch.globals import (
28 from .thread import Thread
30 class Threads(Python3StringMixIn):
31 """Represents a list of notmuch threads
33 This object provides an iterator over a list of notmuch threads
34 (Technically, it provides a wrapper for the underlying
35 *notmuch_threads_t* structure). Do note that the underlying
36 library only provides a one-time iterator (it cannot reset the
37 iterator to the start). Thus iterating over the function will
38 "exhaust" the list of threads, and a subsequent iteration attempt
39 will raise a :exc:`NotInitializedError`. Also
40 note, that any function that uses iteration will also
41 exhaust the messages. So both::
43 for thread in threads: print thread
47 number_of_msgs = len(threads)
49 will "exhaust" the threads. If you need to re-iterate over a list of
50 messages you will need to retrieve a new :class:`Threads` object.
52 Things are not as bad as it seems though, you can store and reuse
53 the single Thread objects as often as you want as long as you
54 keep the parent Threads object around. (Recall that due to
55 hierarchical memory allocation, all derived Threads objects will
56 be invalid when we delete the parent Threads() object, even if it
57 was already "exhausted".) So this works::
60 threads = Query(db,'').search_threads() #get a Threads() object
62 for thread in threads:
63 threadlist.append(thread)
65 # threads is "exhausted" now and even len(threads) will raise an
67 # However it will be kept around until all retrieved Thread() objects are
68 # also deleted. If you did e.g. an explicit del(threads) here, the
69 # following lines would fail.
71 # You can reiterate over *threadlist* however as often as you want.
72 # It is simply a list with Thread objects.
74 print (threadlist[0].get_thread_id())
75 print (threadlist[1].get_thread_id())
76 print (threadlist[0].get_total_messages())
80 _get = nmlib.notmuch_threads_get
81 _get.argtypes = [NotmuchThreadsP]
82 _get.restype = NotmuchThreadP
84 def __init__(self, threads_p, parent=None):
86 :param threads_p: A pointer to an underlying *notmuch_threads_t*
87 structure. These are not publically exposed, so a user
88 will almost never instantiate a :class:`Threads` object
89 herself. They are usually handed back as a result,
90 e.g. in :meth:`Query.search_threads`. *threads_p* must be
91 valid, we will raise an :exc:`NullPointerError` if it is
93 :type threads_p: :class:`ctypes.c_void_p`
94 :param parent: The parent object
95 (ie :class:`Query`) these tags are derived from. It saves
96 a reference to it, so we can automatically delete the db
97 object once all derived objects are dead.
98 :TODO: Make the iterator work more than once and cache the tags in
102 raise NullPointerError()
104 self._threads = threads_p
105 #store parent, so we keep them alive as long as self is alive
106 self._parent = parent
109 """ Make Threads an iterator """
112 _valid = nmlib.notmuch_threads_valid
113 _valid.argtypes = [NotmuchThreadsP]
114 _valid.restype = bool
116 _move_to_next = nmlib.notmuch_threads_move_to_next
117 _move_to_next.argtypes = [NotmuchThreadsP]
118 _move_to_next.restype = None
121 if not self._threads:
122 raise NotInitializedError()
124 if not self._valid(self._threads):
128 thread = Thread(Threads._get(self._threads), self)
129 self._move_to_next(self._threads)
131 next = __next__ # python2.x iterator protocol compatibility
134 """len(:class:`Threads`) returns the number of contained Threads
136 .. note:: As this iterates over the threads, we will not be able to
137 iterate over them again! So this will fail::
140 threads = Database().create_query('').search_threads()
141 if len(threads) > 0: #this 'exhausts' threads
142 # next line raises :exc:`NotInitializedError`!!!
143 for thread in threads: print thread
145 if not self._threads:
146 raise NotInitializedError()
149 # returns 'bool'. On out-of-memory it returns None
150 while self._valid(self._threads):
151 self._move_to_next(self._threads)
153 # reset self._threads to mark as "exhausted"
157 def __nonzero__(self):
158 """Check if :class:`Threads` contains at least one more valid thread
160 The existence of this function makes 'if Threads: foo' work, as
161 that will implicitely call len() exhausting the iterator if
162 __nonzero__ does not exist. This function makes `bool(Threads())`
165 :return: True if there is at least one more thread in the
166 Iterator, False if not. None on a "Out-of-memory" error.
168 return self._threads is not None and \
169 self._valid(self._threads) > 0
171 _destroy = nmlib.notmuch_threads_destroy
172 _destroy.argtypes = [NotmuchThreadsP]
173 _destroy.argtypes = None
176 """Close and free the notmuch Threads"""
177 if self._threads is not None:
178 self._destroy(self._threads)