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 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 To iterate over all messages in the thread, the caller will need to
132 iterate over the result of :meth:`Message.get_replies` for each
133 top-level message (and do that recursively for the resulting
136 :returns: :class:`Messages`
137 :raises: :exc:`NotInitializedError` if query is not initialized
138 :raises: :exc:`NullPointerError` if search_messages failed
141 raise NotInitializedError()
143 msgs_p = Thread._get_toplevel_messages(self._thread)
146 raise NullPointerError()
148 return Messages(msgs_p, self)
150 _get_matched_messages = nmlib.notmuch_thread_get_matched_messages
151 _get_matched_messages.argtypes = [NotmuchThreadP]
152 _get_matched_messages.restype = c_int
154 def get_matched_messages(self):
155 """Returns the number of messages in 'thread' that matched the query
157 :returns: The number of all messages belonging to this thread that
158 matched the :class:`Query`from which this thread was created.
159 Contrast with :meth:`get_total_messages`.
160 :raises: :exc:`NotInitializedError` if the thread
164 raise NotInitializedError()
165 return self._get_matched_messages(self._thread)
167 def get_authors(self):
168 """Returns the authors of 'thread'
170 The returned string is a comma-separated list of the names of the
171 authors of mail messages in the query results that belong to this
174 The returned string belongs to 'thread' and will only be valid for
175 as long as this Thread() is not deleted.
178 raise NotInitializedError()
179 authors = Thread._get_authors(self._thread)
182 return authors.decode('UTF-8', 'ignore')
184 def get_subject(self):
185 """Returns the Subject of 'thread'
187 The returned string belongs to 'thread' and will only be valid for
188 as long as this Thread() is not deleted.
191 raise NotInitializedError()
192 subject = Thread._get_subject(self._thread)
195 return subject.decode('UTF-8', 'ignore')
197 def get_newest_date(self):
198 """Returns time_t of the newest message date
200 :returns: A time_t timestamp.
202 :raises: :exc:`NotInitializedError` if the message
206 raise NotInitializedError()
207 return Thread._get_newest_date(self._thread)
209 def get_oldest_date(self):
210 """Returns time_t of the oldest message date
212 :returns: A time_t timestamp.
214 :raises: :exc:`NotInitializedError` if the message
218 raise NotInitializedError()
219 return Thread._get_oldest_date(self._thread)
222 """ Returns the message tags
224 In the Notmuch database, tags are stored on individual
225 messages, not on threads. So the tags returned here will be all
226 tags of the messages which matched the search and which belong to
229 The :class:`Tags` object is owned by the thread and as such, will only
230 be valid for as long as this :class:`Thread` is valid (e.g. until the
231 query from which it derived is explicitely deleted).
233 :returns: A :class:`Tags` iterator.
234 :raises: :exc:`NotInitializedError` if query is not initialized
235 :raises: :exc:`NullPointerError` if search_messages failed
238 raise NotInitializedError()
240 tags_p = Thread._get_tags(self._thread)
242 raise NullPointerError()
243 return Tags(tags_p, self)
245 def __unicode__(self):
246 frm = "thread:%s %12s [%d/%d] %s; %s (%s)"
248 return frm % (self.get_thread_id(),
249 date.fromtimestamp(self.get_newest_date()),
250 self.get_matched_messages(),
251 self.get_total_messages(),
257 _destroy = nmlib.notmuch_thread_destroy
258 _destroy.argtypes = [NotmuchThreadP]
259 _destroy.restype = None
262 """Close and free the notmuch Thread"""
264 self._destroy(self._thread)