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 notmuch.globals import (
29 from .messages import Messages
30 from notmuch.tag import Tags
31 from datetime import date
34 """Represents a single message thread."""
36 """notmuch_thread_get_thread_id"""
37 _get_thread_id = nmlib.notmuch_thread_get_thread_id
38 _get_thread_id.argtypes = [NotmuchThreadP]
39 _get_thread_id.restype = c_char_p
41 """notmuch_thread_get_authors"""
42 _get_authors = nmlib.notmuch_thread_get_authors
43 _get_authors.argtypes = [NotmuchThreadP]
44 _get_authors.restype = c_char_p
46 """notmuch_thread_get_subject"""
47 _get_subject = nmlib.notmuch_thread_get_subject
48 _get_subject.argtypes = [NotmuchThreadP]
49 _get_subject.restype = c_char_p
51 """notmuch_thread_get_toplevel_messages"""
52 _get_toplevel_messages = nmlib.notmuch_thread_get_toplevel_messages
53 _get_toplevel_messages.argtypes = [NotmuchThreadP]
54 _get_toplevel_messages.restype = NotmuchMessagesP
56 _get_newest_date = nmlib.notmuch_thread_get_newest_date
57 _get_newest_date.argtypes = [NotmuchThreadP]
58 _get_newest_date.restype = c_long
60 _get_oldest_date = nmlib.notmuch_thread_get_oldest_date
61 _get_oldest_date.argtypes = [NotmuchThreadP]
62 _get_oldest_date.restype = c_long
64 """notmuch_thread_get_tags"""
65 _get_tags = nmlib.notmuch_thread_get_tags
66 _get_tags.argtypes = [NotmuchThreadP]
67 _get_tags.restype = NotmuchTagsP
69 def __init__(self, thread_p, parent=None):
71 :param thread_p: A pointer to an internal notmuch_thread_t
72 Structure. These are not publically exposed, so a user
73 will almost never instantiate a :class:`Thread` object
74 herself. They are usually handed back as a result,
75 e.g. when iterating through :class:`Threads`. *thread_p*
76 must be valid, we will raise an :exc:`NullPointerError`
79 :param parent: A 'parent' object is passed which this message is
80 derived from. We save a reference to it, so we can
81 automatically delete the parent object once all derived
85 raise NullPointerError()
86 self._thread = thread_p
87 #keep reference to parent, so we keep it alive
90 def get_thread_id(self):
91 """Get the thread ID of 'thread'
93 The returned string belongs to 'thread' and will only be valid
94 for as long as the thread is valid.
96 :returns: String with a message ID
97 :raises: :exc:`NotInitializedError` if the thread
101 raise NotInitializedError()
102 return Thread._get_thread_id(self._thread).decode('utf-8', 'ignore')
104 _get_total_messages = nmlib.notmuch_thread_get_total_messages
105 _get_total_messages.argtypes = [NotmuchThreadP]
106 _get_total_messages.restype = c_int
108 def get_total_messages(self):
109 """Get the total number of messages in 'thread'
111 :returns: The number of all messages in the database
112 belonging to this thread. Contrast with
113 :meth:`get_matched_messages`.
114 :raises: :exc:`NotInitializedError` if the thread
118 raise NotInitializedError()
119 return self._get_total_messages(self._thread)
121 def get_toplevel_messages(self):
122 """Returns a :class:`Messages` iterator for the top-level messages in
125 This iterator will not necessarily iterate over all of the messages
126 in the thread. It will only iterate over the messages in the thread
127 which are not replies to other messages in the thread.
129 To iterate over all messages in the thread, the caller will need to
130 iterate over the result of :meth:`Message.get_replies` for each
131 top-level message (and do that recursively for the resulting
134 :returns: :class:`Messages`
135 :raises: :exc:`NotInitializedError` if query is not initialized
136 :raises: :exc:`NullPointerError` if search_messages failed
139 raise NotInitializedError()
141 msgs_p = Thread._get_toplevel_messages(self._thread)
144 raise NullPointerError()
146 return Messages(msgs_p, self)
148 _get_matched_messages = nmlib.notmuch_thread_get_matched_messages
149 _get_matched_messages.argtypes = [NotmuchThreadP]
150 _get_matched_messages.restype = c_int
152 def get_matched_messages(self):
153 """Returns the number of messages in 'thread' that matched the query
155 :returns: The number of all messages belonging to this thread that
156 matched the :class:`Query`from which this thread was created.
157 Contrast with :meth:`get_total_messages`.
158 :raises: :exc:`NotInitializedError` if the thread
162 raise NotInitializedError()
163 return self._get_matched_messages(self._thread)
165 def get_authors(self):
166 """Returns the authors of 'thread'
168 The returned string is a comma-separated list of the names of the
169 authors of mail messages in the query results that belong to this
172 The returned string belongs to 'thread' and will only be valid for
173 as long as this Thread() is not deleted.
176 raise NotInitializedError()
177 authors = Thread._get_authors(self._thread)
180 return authors.decode('UTF-8', 'ignore')
182 def get_subject(self):
183 """Returns the Subject of 'thread'
185 The returned string belongs to 'thread' and will only be valid for
186 as long as this Thread() is not deleted.
189 raise NotInitializedError()
190 subject = Thread._get_subject(self._thread)
193 return subject.decode('UTF-8', 'ignore')
195 def get_newest_date(self):
196 """Returns time_t of the newest message date
198 :returns: A time_t timestamp.
200 :raises: :exc:`NotInitializedError` if the message
204 raise NotInitializedError()
205 return Thread._get_newest_date(self._thread)
207 def get_oldest_date(self):
208 """Returns time_t of the oldest message date
210 :returns: A time_t timestamp.
212 :raises: :exc:`NotInitializedError` if the message
216 raise NotInitializedError()
217 return Thread._get_oldest_date(self._thread)
220 """ Returns the message tags
222 In the Notmuch database, tags are stored on individual
223 messages, not on threads. So the tags returned here will be all
224 tags of the messages which matched the search and which belong to
227 The :class:`Tags` object is owned by the thread and as such, will only
228 be valid for as long as this :class:`Thread` is valid (e.g. until the
229 query from which it derived is explicitely deleted).
231 :returns: A :class:`Tags` iterator.
232 :raises: :exc:`NotInitializedError` if query is not initialized
233 :raises: :exc:`NullPointerError` if search_messages failed
236 raise NotInitializedError()
238 tags_p = Thread._get_tags(self._thread)
240 raise NullPointerError()
241 return Tags(tags_p, self)
243 def __unicode__(self):
244 frm = "thread:%s %12s [%d/%d] %s; %s (%s)"
246 return frm % (self.get_thread_id(),
247 date.fromtimestamp(self.get_newest_date()),
248 self.get_matched_messages(),
249 self.get_total_messages(),
255 _destroy = nmlib.notmuch_thread_destroy
256 _destroy.argtypes = [NotmuchThreadP]
257 _destroy.restype = None
260 """Close and free the notmuch Thread"""
261 if self._thread is not None:
262 self._destroy(self._thread)