X-Git-Url: https://git.notmuchmail.org/git?a=blobdiff_plain;f=bindings%2Fpython%2Fnotmuch%2Fmessage.py;h=e71dbe3e9933011d334d3ed18990d6fa2844693d;hb=f90e8e6a5c3731c8f7caf6a8c36ba78560913a7a;hp=0e65694e0c63e978754e5e6137a0475a33d3d3b8;hpb=e9bcbe7e70d0811f11e58466735b2bf7c8f0a3fc;p=notmuch
diff --git a/bindings/python/notmuch/message.py b/bindings/python/notmuch/message.py
index 0e65694e..e71dbe3e 100644
--- a/bindings/python/notmuch/message.py
+++ b/bindings/python/notmuch/message.py
@@ -12,14 +12,14 @@ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
-along with notmuch. If not, see .
+along with notmuch. If not, see .
Copyright 2010 Sebastian Spaeth
Jesse Rosenthal
"""
-from ctypes import c_char_p, c_long, c_uint, c_int
+from ctypes import c_char_p, c_long, c_uint, c_int, POINTER, byref
from datetime import date
from .globals import (
nmlib,
@@ -29,6 +29,7 @@ from .globals import (
NotmuchTagsP,
NotmuchMessageP,
NotmuchMessagesP,
+ NotmuchMessagePropertiesP,
NotmuchFilenamesP,
)
from .errors import (
@@ -41,14 +42,11 @@ from .tag import Tags
from .filenames import Filenames
import email
-try:
- import simplejson as json
-except ImportError:
- import json
+import sys
class Message(Python3StringMixIn):
- """Represents a single Email message
+ r"""Represents a single Email message
Technically, this wraps the underlying *notmuch_message_t*
structure. A user will usually not create these objects themselves
@@ -116,6 +114,36 @@ class Message(Python3StringMixIn):
_maildir_flags_to_tags.argtypes = [NotmuchMessageP]
_maildir_flags_to_tags.restype = c_int
+ """notmuch_message_get_property"""
+ _get_property = nmlib.notmuch_message_get_property
+ _get_property.argtypes = [NotmuchMessageP, c_char_p, POINTER(c_char_p)]
+ _get_property.restype = c_int
+
+ """notmuch_message_get_properties"""
+ _get_properties = nmlib.notmuch_message_get_properties
+ _get_properties.argtypes = [NotmuchMessageP, c_char_p, c_int]
+ _get_properties.restype = NotmuchMessagePropertiesP
+
+ """notmuch_message_properties_valid"""
+ _properties_valid = nmlib.notmuch_message_properties_valid
+ _properties_valid.argtypes = [NotmuchMessagePropertiesP]
+ _properties_valid.restype = bool
+
+ """notmuch_message_properties_value"""
+ _properties_value = nmlib.notmuch_message_properties_value
+ _properties_value.argtypes = [NotmuchMessagePropertiesP]
+ _properties_value.restype = c_char_p
+
+ """notmuch_message_properties_key"""
+ _properties_key = nmlib.notmuch_message_properties_key
+ _properties_key.argtypes = [NotmuchMessagePropertiesP]
+ _properties_key.restype = c_char_p
+
+ """notmuch_message_properties_move_to_next"""
+ _properties_move_to_next = nmlib.notmuch_message_properties_move_to_next
+ _properties_move_to_next.argtypes = [NotmuchMessagePropertiesP]
+ _properties_move_to_next.restype = None
+
#Constants: Flags that can be set/get with set_flag
FLAG = Enum(['MATCH'])
@@ -226,7 +254,7 @@ class Message(Python3StringMixIn):
:returns: The header value as string
:raises: :exc:`NotInitializedError` if the message is not
initialized
- :raises: :exc:`NullPointerError` if any error occured
+ :raises: :exc:`NullPointerError` if any error occurred
"""
if not self._msg:
raise NotInitializedError()
@@ -298,13 +326,13 @@ class Message(Python3StringMixIn):
:returns: A :class:`Tags` iterator.
:raises: :exc:`NotInitializedError` if the message is not
initialized
- :raises: :exc:`NullPointerError` if any error occured
+ :raises: :exc:`NullPointerError` if any error occurred
"""
if not self._msg:
raise NotInitializedError()
tags_p = Message._get_tags(self._msg)
- if tags_p == None:
+ if not tags_p:
raise NullPointerError()
return Tags(tags_p, self)
@@ -436,6 +464,55 @@ class Message(Python3StringMixIn):
_freeze.argtypes = [NotmuchMessageP]
_freeze.restype = c_uint
+ def get_property(self, prop):
+ """ Retrieve the value for a single property key
+
+ :param prop: The name of the property to get.
+ :returns: String with the property value or None if there is no such
+ key. In the case of multiple values for the given key, the
+ first one is retrieved.
+ :raises: :exc:`NotInitializedError` if message has not been
+ initialized
+ """
+ if not self._msg:
+ raise NotInitializedError()
+
+ value = c_char_p()
+ status = Message._get_property(self._msg, _str(prop), byref(value))
+ if status != 0:
+ raise NotmuchError(status)
+
+ if value is None or value.value is None:
+ return None
+ return value.value.decode('utf-8')
+
+ def get_properties(self, prop="", exact=False):
+ """ Get the properties of the message, returning a generator of
+ name, value pairs.
+
+ The generator will yield once per value. There might be more than one
+ value on each name, so the generator might yield the same name several
+ times.
+
+ :param prop: The name of the property to get. Otherwise it will return
+ the full list of properties of the message.
+ :param exact: if True, require exact match with key. Otherwise
+ treat as prefix.
+ :yields: Each property values as a pair of `name, value`
+ :ytype: pairs of str
+ :raises: :exc:`NotInitializedError` if message has not been
+ initialized
+ """
+ if not self._msg:
+ raise NotInitializedError()
+
+ properties = Message._get_properties(self._msg, _str(prop), exact)
+ while Message._properties_valid(properties):
+ key = Message._properties_key(properties)
+ value = Message._properties_value(properties)
+ yield key.decode("utf-8"), value.decode("utf-8")
+ Message._properties_move_to_next(properties)
+
def freeze(self):
"""Freezes the current state of 'message' within the database
@@ -570,13 +647,13 @@ class Message(Python3StringMixIn):
logical OR operator.)
As a convenience, you can set the sync_maildir_flags parameter in
- :meth:`Database.add_message` to implicitly call this.
+ :meth:`Database.index_file` to implicitly call this.
:returns: a :class:`STATUS`. In short, you want to see
notmuch.STATUS.SUCCESS here. See there for details."""
if not self._msg:
raise NotInitializedError()
- return Message._tags_to_maildir_flags(self._msg)
+ return Message._maildir_flags_to_tags(self._msg)
def __repr__(self):
"""Represent a Message() object by str()"""
@@ -591,8 +668,11 @@ class Message(Python3StringMixIn):
def get_message_parts(self):
"""Output like notmuch show"""
- fp = open(self.get_filename())
- email_msg = email.message_from_file(fp)
+ fp = open(self.get_filename(), 'rb')
+ if sys.version_info[0] < 3:
+ email_msg = email.message_from_file(fp)
+ else:
+ email_msg = email.message_from_binary_file(fp)
fp.close()
out = []
@@ -610,135 +690,6 @@ class Message(Python3StringMixIn):
out_part = parts[(num - 1)]
return out_part.get_payload(decode=True)
- def format_message_internal(self):
- """Create an internal representation of the message parts,
- which can easily be output to json, text, or another output
- format. The argument match tells whether this matched a
- query.
-
- .. deprecated:: 0.13
- This code adds functionality at the python
- level that is unlikely to be useful for
- anyone. Furthermore the python bindings strive
- to be a thin wrapper around libnotmuch, so
- this code will be removed in notmuch 0.14.
- """
- output = {}
- output["id"] = self.get_message_id()
- output["match"] = self.is_match()
- output["filename"] = self.get_filename()
- output["tags"] = list(self.get_tags())
-
- headers = {}
- for h in ["Subject", "From", "To", "Cc", "Bcc", "Date"]:
- headers[h] = self.get_header(h)
- output["headers"] = headers
-
- body = []
- parts = self.get_message_parts()
- for i in xrange(len(parts)):
- msg = parts[i]
- part_dict = {}
- part_dict["id"] = i + 1
- # We'll be using this is a lot, so let's just get it once.
- cont_type = msg.get_content_type()
- part_dict["content-type"] = cont_type
- # NOTE:
- # Now we emulate the current behaviour, where it ignores
- # the html if there's a text representation.
- #
- # This is being worked on, but it will be easier to fix
- # here in the future than to end up with another
- # incompatible solution.
- disposition = msg["Content-Disposition"]
- if disposition and disposition.lower().startswith("attachment"):
- part_dict["filename"] = msg.get_filename()
- else:
- if cont_type.lower() == "text/plain":
- part_dict["content"] = msg.get_payload()
- elif (cont_type.lower() == "text/html" and
- i == 0):
- part_dict["content"] = msg.get_payload()
- body.append(part_dict)
-
- output["body"] = body
-
- return output
-
- def format_message_as_json(self, indent=0):
- """Outputs the message as json. This is essentially the same
- as python's dict format, but we run it through, just so we
- don't have to worry about the details.
-
- .. deprecated:: 0.13
- This code adds functionality at the python
- level that is unlikely to be useful for
- anyone. Furthermore the python bindings strive
- to be a thin wrapper around libnotmuch, so
- this code will be removed in notmuch 0.14.
- """
- return json.dumps(self.format_message_internal())
-
- def format_message_as_text(self, indent=0):
- """Outputs it in the old-fashioned notmuch text form. Will be
- easy to change to a new format when the format changes.
-
- .. deprecated:: 0.13
- This code adds functionality at the python
- level that is unlikely to be useful for
- anyone. Furthermore the python bindings strive
- to be a thin wrapper around libnotmuch, so
- this code will be removed in notmuch 0.14.
- """
-
-
- format = self.format_message_internal()
- output = "\fmessage{ id:%s depth:%d match:%d filename:%s" \
- % (format['id'], indent, format['match'], format['filename'])
- output += "\n\fheader{"
-
- #Todo: this date is supposed to be prettified, as in the index.
- output += "\n%s (%s) (" % (format["headers"]["From"],
- format["headers"]["Date"])
- output += ", ".join(format["tags"])
- output += ")"
-
- output += "\nSubject: %s" % format["headers"]["Subject"]
- output += "\nFrom: %s" % format["headers"]["From"]
- output += "\nTo: %s" % format["headers"]["To"]
- if format["headers"]["Cc"]:
- output += "\nCc: %s" % format["headers"]["Cc"]
- if format["headers"]["Bcc"]:
- output += "\nBcc: %s" % format["headers"]["Bcc"]
- output += "\nDate: %s" % format["headers"]["Date"]
- output += "\n\fheader}"
-
- output += "\n\fbody{"
-
- parts = format["body"]
- parts.sort(key=lambda x: x['id'])
- for p in parts:
- if not "filename" in p:
- output += "\n\fpart{ "
- output += "ID: %d, Content-type: %s\n" % (p["id"],
- p["content-type"])
- if "content" in p:
- output += "\n%s\n" % p["content"]
- else:
- output += "Non-text part: %s\n" % p["content-type"]
- output += "\n\fpart}"
- else:
- output += "\n\fattachment{ "
- output += "ID: %d, Content-type:%s\n" % (p["id"],
- p["content-type"])
- output += "Attachment: %s\n" % p["filename"]
- output += "\n\fattachment}\n"
-
- output += "\n\fbody}\n"
- output += "\n\fmessage}"
-
- return output
-
def __hash__(self):
"""Implement hash(), so we can use Message() sets"""
file = self.get_filename()