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>
20 from ctypes import c_char_p, c_long, c_int
21 from .globals import (
31 from .messages import Messages
33 from datetime import date
36 """Represents a single message thread."""
38 """notmuch_thread_get_thread_id"""
39 _get_thread_id = nmlib.notmuch_thread_get_thread_id
40 _get_thread_id.argtypes = [NotmuchThreadP]
41 _get_thread_id.restype = c_char_p
43 """notmuch_thread_get_authors"""
44 _get_authors = nmlib.notmuch_thread_get_authors
45 _get_authors.argtypes = [NotmuchThreadP]
46 _get_authors.restype = c_char_p
48 """notmuch_thread_get_subject"""
49 _get_subject = nmlib.notmuch_thread_get_subject
50 _get_subject.argtypes = [NotmuchThreadP]
51 _get_subject.restype = c_char_p
53 """notmuch_thread_get_toplevel_messages"""
54 _get_toplevel_messages = nmlib.notmuch_thread_get_toplevel_messages
55 _get_toplevel_messages.argtypes = [NotmuchThreadP]
56 _get_toplevel_messages.restype = NotmuchMessagesP
58 _get_newest_date = nmlib.notmuch_thread_get_newest_date
59 _get_newest_date.argtypes = [NotmuchThreadP]
60 _get_newest_date.restype = c_long
62 _get_oldest_date = nmlib.notmuch_thread_get_oldest_date
63 _get_oldest_date.argtypes = [NotmuchThreadP]
64 _get_oldest_date.restype = c_long
66 """notmuch_thread_get_tags"""
67 _get_tags = nmlib.notmuch_thread_get_tags
68 _get_tags.argtypes = [NotmuchThreadP]
69 _get_tags.restype = NotmuchTagsP
71 def __init__(self, thread_p, parent=None):
73 :param thread_p: A pointer to an internal notmuch_thread_t
74 Structure. These are not publically exposed, so a user
75 will almost never instantiate a :class:`Thread` object
76 herself. They are usually handed back as a result,
77 e.g. when iterating through :class:`Threads`. *thread_p*
78 must be valid, we will raise an :exc:`NullPointerError`
81 :param parent: A 'parent' object is passed which this message is
82 derived from. We save a reference to it, so we can
83 automatically delete the parent object once all derived
87 raise NullPointerError()
88 self._thread = thread_p
89 #keep reference to parent, so we keep it alive
92 def get_thread_id(self):
93 """Get the thread ID of 'thread'
95 The returned string belongs to 'thread' and will only be valid
96 for as long as the thread is valid.
98 :returns: String with a message ID
99 :raises: :exc:`NotInitializedError` if the thread
103 raise NotInitializedError()
104 return Thread._get_thread_id(self._thread).decode('utf-8', 'ignore')
106 _get_total_messages = nmlib.notmuch_thread_get_total_messages
107 _get_total_messages.argtypes = [NotmuchThreadP]
108 _get_total_messages.restype = c_int
110 def get_total_messages(self):
111 """Get the total number of messages in 'thread'
113 :returns: The number of all messages in the database
114 belonging to this thread. Contrast with
115 :meth:`get_matched_messages`.
116 :raises: :exc:`NotInitializedError` if the thread
120 raise NotInitializedError()
121 return self._get_total_messages(self._thread)
123 def get_toplevel_messages(self):
124 """Returns a :class:`Messages` iterator for the top-level messages in
127 This iterator will not necessarily iterate over all of the messages
128 in the thread. It will only iterate over the messages in the thread
129 which are not replies to other messages in the thread.
131 :returns: :class:`Messages`
132 :raises: :exc:`NotInitializedError` if query is not initialized
133 :raises: :exc:`NullPointerError` if search_messages failed
136 raise NotInitializedError()
138 msgs_p = Thread._get_toplevel_messages(self._thread)
141 raise NullPointerError()
143 return Messages(msgs_p, self)
145 """notmuch_thread_get_messages"""
146 _get_messages = nmlib.notmuch_thread_get_messages
147 _get_messages.argtypes = [NotmuchThreadP]
148 _get_messages.restype = NotmuchMessagesP
150 def get_messages(self):
151 """Returns a :class:`Messages` iterator for all messages in 'thread'
153 :returns: :class:`Messages`
154 :raises: :exc:`NotInitializedError` if query is not initialized
155 :raises: :exc:`NullPointerError` if get_messages failed
158 raise NotInitializedError()
160 msgs_p = Thread._get_messages(self._thread)
163 raise NullPointerError()
165 return Messages(msgs_p, self)
167 _get_matched_messages = nmlib.notmuch_thread_get_matched_messages
168 _get_matched_messages.argtypes = [NotmuchThreadP]
169 _get_matched_messages.restype = c_int
171 def get_matched_messages(self):
172 """Returns the number of messages in 'thread' that matched the query
174 :returns: The number of all messages belonging to this thread that
175 matched the :class:`Query`from which this thread was created.
176 Contrast with :meth:`get_total_messages`.
177 :raises: :exc:`NotInitializedError` if the thread
181 raise NotInitializedError()
182 return self._get_matched_messages(self._thread)
184 def get_authors(self):
185 """Returns the authors of 'thread'
187 The returned string is a comma-separated list of the names of the
188 authors of mail messages in the query results that belong to this
191 The returned string belongs to 'thread' and will only be valid for
192 as long as this Thread() is not deleted.
195 raise NotInitializedError()
196 authors = Thread._get_authors(self._thread)
199 return authors.decode('UTF-8', 'ignore')
201 def get_subject(self):
202 """Returns the Subject of 'thread'
204 The returned string belongs to 'thread' and will only be valid for
205 as long as this Thread() is not deleted.
208 raise NotInitializedError()
209 subject = Thread._get_subject(self._thread)
212 return subject.decode('UTF-8', 'ignore')
214 def get_newest_date(self):
215 """Returns time_t of the newest message date
217 :returns: A time_t timestamp.
219 :raises: :exc:`NotInitializedError` if the message
223 raise NotInitializedError()
224 return Thread._get_newest_date(self._thread)
226 def get_oldest_date(self):
227 """Returns time_t of the oldest message date
229 :returns: A time_t timestamp.
231 :raises: :exc:`NotInitializedError` if the message
235 raise NotInitializedError()
236 return Thread._get_oldest_date(self._thread)
239 """ Returns the message tags
241 In the Notmuch database, tags are stored on individual
242 messages, not on threads. So the tags returned here will be all
243 tags of the messages which matched the search and which belong to
246 The :class:`Tags` object is owned by the thread and as such, will only
247 be valid for as long as this :class:`Thread` is valid (e.g. until the
248 query from which it derived is explicitely deleted).
250 :returns: A :class:`Tags` iterator.
251 :raises: :exc:`NotInitializedError` if query is not initialized
252 :raises: :exc:`NullPointerError` if search_messages failed
255 raise NotInitializedError()
257 tags_p = Thread._get_tags(self._thread)
259 raise NullPointerError()
260 return Tags(tags_p, self)
262 def __unicode__(self):
263 frm = "thread:%s %12s [%d/%d] %s; %s (%s)"
265 return frm % (self.get_thread_id(),
266 date.fromtimestamp(self.get_newest_date()),
267 self.get_matched_messages(),
268 self.get_total_messages(),
274 _destroy = nmlib.notmuch_thread_destroy
275 _destroy.argtypes = [NotmuchThreadP]
276 _destroy.restype = None
279 """Close and free the notmuch Thread"""
281 self._destroy(self._thread)