from ctypes import c_char_p, c_long, c_uint, c_int
from datetime import date
-from notmuch.globals import (nmlib, STATUS, NotmuchError, Enum, _str,
+from notmuch.globals import (
+ nmlib, STATUS, NotmuchError, Enum, _str, Python3StringMixIn,
NotmuchTagsP, NotmuchMessagesP, NotmuchMessageP, NotmuchFilenamesP)
from notmuch.tag import Tags
from notmuch.filename import Filenames
_move_to_next.argtypes = [NotmuchMessagesP]
_move_to_next.restype = None
- def next(self):
+ def __next__(self):
if self._msgs is None:
raise NotmuchError(STATUS.NOT_INITIALIZED)
msg = Message(Messages._get(self._msgs), self)
self._move_to_next(self._msgs)
return msg
+ next = __next__ # python2.x iterator protocol compatibility
def __nonzero__(self):
"""
if self._msgs is not None:
self._destroy(self._msgs)
- def print_messages(self, format, indent=0, entire_thread=False):
- """Outputs messages as needed for 'notmuch show' to sys.stdout
+ def format_messages(self, format, indent=0, entire_thread=False):
+ """Formats messages as needed for 'notmuch show'.
:param format: A string of either 'text' or 'json'.
:param indent: A number indicating the reply depth of these messages.
:param entire_thread: A bool, indicating whether we want to output
whole threads or only the matching messages.
+ :return: a list of lines
"""
+ result = list()
+
if format.lower() == "text":
set_start = ""
set_end = ""
first_set = True
- sys.stdout.write(set_start)
+ result.append(set_start)
# iterate through all toplevel messages in this thread
for msg in self:
# if not msg:
# break
if not first_set:
- sys.stdout.write(set_sep)
+ result.append(set_sep)
first_set = False
- sys.stdout.write(set_start)
+ result.append(set_start)
match = msg.is_match()
next_indent = indent
if (match or entire_thread):
if format.lower() == "text":
- sys.stdout.write(msg.format_message_as_text(indent))
+ result.append(msg.format_message_as_text(indent))
else:
- sys.stdout.write(msg.format_message_as_json(indent))
+ result.append(msg.format_message_as_json(indent))
next_indent = indent + 1
# get replies and print them also out (if there are any)
- replies = msg.get_replies()
- if not replies is None:
- sys.stdout.write(set_sep)
- replies.print_messages(format, next_indent, entire_thread)
+ replies = msg.get_replies().format_messages(format, next_indent, entire_thread)
+ if replies:
+ result.append(set_sep)
+ result.extend(replies)
+
+ result.append(set_end)
+ result.append(set_end)
- sys.stdout.write(set_end)
- sys.stdout.write(set_end)
+ return result
+ def print_messages(self, format, indent=0, entire_thread=False, handle=sys.stdout):
+ """Outputs messages as needed for 'notmuch show' to a file like object.
+
+ :param format: A string of either 'text' or 'json'.
+ :param handle: A file like object to print to (default is sys.stdout).
+ :param indent: A number indicating the reply depth of these messages.
+ :param entire_thread: A bool, indicating whether we want to output
+ whole threads or only the matching messages.
+ """
+ handle.write(''.join(self.format_messages(format, indent, entire_thread)))
-class Message(object):
+
+class EmptyMessagesResult(Messages):
+ def __init__(self, parent):
+ self._msgs = None
+ self._parent = parent
+
+ def __next__(self):
+ raise StopIteration()
+ next = __next__
+
+
+class Message(Python3StringMixIn):
"""Represents a single Email message
Technically, this wraps the underlying *notmuch_message_t*
"""
if self._msg is None:
raise NotmuchError(STATUS.NOT_INITIALIZED)
- return Message._get_message_id(self._msg)
+ return Message._get_message_id(self._msg).decode('utf-8', errors='ignore')
def get_thread_id(self):
"""Returns the thread ID
if self._msg is None:
raise NotmuchError(STATUS.NOT_INITIALIZED)
- return Message._get_thread_id(self._msg)
+ return Message._get_thread_id(self._msg).decode('utf-8', errors='ignore')
def get_replies(self):
"""Gets all direct replies to this message as :class:`Messages`
number of subsequent calls to :meth:`get_replies`). If this message
was obtained through some non-thread means, (such as by a call to
:meth:`Query.search_messages`), then this function will return
- `None`.
+ an empty Messages iterator.
- :returns: :class:`Messages` or `None` if there are no replies to
- this message.
+ :returns: :class:`Messages`.
:exception: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if the message
is not initialized.
"""
msgs_p = Message._get_replies(self._msg)
if msgs_p is None:
- return None
+ return EmptyMessagesResult(self)
return Messages(msgs_p, self)
raise NotmuchError(STATUS.NOT_INITIALIZED)
#Returns NULL if any error occurs.
- header = Message._get_header(self._msg, header)
+ header = Message._get_header(self._msg, _str(header))
if header == None:
raise NotmuchError(STATUS.NULL_POINTER)
- return header.decode('UTF-8')
+ return header.decode('UTF-8', errors='ignore')
def get_filename(self):
"""Returns the file path of the message file
"""
if self._msg is None:
raise NotmuchError(STATUS.NOT_INITIALIZED)
- return Message._get_filename(self._msg)
+ return Message._get_filename(self._msg).decode('utf-8', errors='ignore')
def get_filenames(self):
"""Get all filenames for the email corresponding to 'message'
"""Represent a Message() object by str()"""
return self.__str__()
- def __str__(self):
- """A message() is represented by a 1-line summary"""
- msg = {}
- msg['from'] = self.get_header('from')
- msg['tags'] = self.get_tags()
- msg['date'] = date.fromtimestamp(self.get_date())
- return "%(from)s (%(date)s) (%(tags)s)" % (msg)
+ def __unicode__(self):
+ format = "%s (%s) (%s)"
+ return format % (self.get_header('from'),
+ self.get_tags(),
+ date.fromtimestamp(self.get_date()),
+ )
def get_message_parts(self):
"""Output like notmuch show"""