[PATCH] python: add bindings for notmuch_message_get_property

classic Classic list List threaded Threaded
11 messages Options
Ruben Pollan Ruben Pollan
Reply | Threaded
Open this post in threaded view
|

[PATCH] python: add bindings for notmuch_message_get_property

Message.get_property (prop) returns a string with the value of the property.
---
 bindings/python/notmuch/message.py | 27 ++++++++++++++++++++++++++-
 1 file changed, 26 insertions(+), 1 deletion(-)

diff --git a/bindings/python/notmuch/message.py b/bindings/python/notmuch/message.py
index d5b98e4f..11263736 100644
--- a/bindings/python/notmuch/message.py
+++ b/bindings/python/notmuch/message.py
@@ -19,7 +19,7 @@ Copyright 2010 Sebastian Spaeth <[hidden email]>
 """
 
 
-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,
@@ -113,6 +113,11 @@ 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
+
     #Constants: Flags that can be set/get with set_flag
     FLAG = Enum(['MATCH'])
 
@@ -433,6 +438,26 @@ 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, prop, byref(value))
+        if status != 0:
+            raise NotmuchError(status)
+
+        return value.value
+
     def freeze(self):
         """Freezes the current state of 'message' within the database
 
--
2.15.0

_______________________________________________
notmuch mailing list
[hidden email]
https://notmuchmail.org/mailman/listinfo/notmuch
Ruben Pollan Ruben Pollan
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] python: add bindings for notmuch_message_get_property

Quoting Ruben Pollan (2017-11-15 23:29:54)
> Message.get_property (prop) returns a string with the value of the property.

I only implemented get_property as is the only one I need to add support for
session keys in alot (https://github.com/meskio/alot/tree/session-key). From the
point of view of the MUAs I don't see much interest in exporting all the other
functions to modify properties. But I was not sure if adding them to have the
whole set of message property functions in python.

--
meskio | http://meskio.net/
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 My contact info: http://meskio.net/crypto.txt
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
Nos vamos a Croatan.

_______________________________________________
notmuch mailing list
[hidden email]
https://notmuchmail.org/mailman/listinfo/notmuch

signature.asc (849 bytes) Download Attachment
Daniel Kahn Gillmor Daniel Kahn Gillmor
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] python: add bindings for notmuch_message_get_property

In reply to this post by Ruben Pollan
On Wed 2017-11-15 23:29:54 +0100, Ruben Pollan wrote:
> Message.get_property (prop) returns a string with the value of the property.

Upon review, this is actually insufficient for making robust use of the
session-key series :(

In particular, it only returns the first value for the session key
returned.

There are (at least) two situations where a message may have more than
one session key:

 * if two copies of the message are received by different channels, and
   each channel somehow obtains a different session key.

   For example: i send the message to an encrypted mailing list like
   schleuder that unwraps and then rewraps the encrypted message -- in
   this case, the version saved to sent-mail during sending has session
   key A, and the version received back from schleuder has session key B

 * if one encrypted message contains another encrypted message.  then
   the outer message has session key A, and the inner attachment has
   session key B.

of course there are more ways this can happen, as well as combinations
of these ways :/

it mostly won't happen!  so things will look like they're looking fine,
but then you'll get a message (or two copies of a single message) and at
some point you'll try to render one part or one version, but you'll only
have the other session key available.

In the session-key series, i work around this by simply trying each
session key against an encrypted part until i find one that works. It
would be "cleaner" (more principled) to somehow associate each session
key with the part(s) of the message file(s) that it is capable of
decrypting, but that's a lot of bookkeeping -- i think it's actually
"cleaner" (less code, less computation in the standard case) to just
take the current approach.

     --dkg
_______________________________________________
notmuch mailing list
[hidden email]
https://notmuchmail.org/mailman/listinfo/notmuch
Ruben Pollan Ruben Pollan
Reply | Threaded
Open this post in threaded view
|

[PATCH] python: add bindings for notmuch_message_get_propert(y/ies)

Message.get_property (prop) returns a string with the value of the property and
Message.get_properties (prop, exact=False) returns a list [(key, value)]
---
 bindings/python/notmuch/globals.py |  5 +++
 bindings/python/notmuch/message.py | 78 +++++++++++++++++++++++++++++++++++++-
 2 files changed, 82 insertions(+), 1 deletion(-)

diff --git a/bindings/python/notmuch/globals.py b/bindings/python/notmuch/globals.py
index 71426c84..801062dc 100644
--- a/bindings/python/notmuch/globals.py
+++ b/bindings/python/notmuch/globals.py
@@ -75,6 +75,11 @@ class NotmuchMessageS(Structure):
 NotmuchMessageP = POINTER(NotmuchMessageS)
 
 
+class NotmuchMessagePropertiesS(Structure):
+    pass
+NotmuchMessagePropertiesP = POINTER(NotmuchMessagePropertiesS)
+
+
 class NotmuchTagsS(Structure):
     pass
 NotmuchTagsP = POINTER(NotmuchTagsS)
diff --git a/bindings/python/notmuch/message.py b/bindings/python/notmuch/message.py
index d5b98e4f..7b737943 100644
--- a/bindings/python/notmuch/message.py
+++ b/bindings/python/notmuch/message.py
@@ -19,7 +19,7 @@ Copyright 2010 Sebastian Spaeth <[hidden email]>
 """
 
 
-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 (
@@ -113,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'])
 
@@ -433,6 +464,51 @@ 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, prop, byref(value))
+        if status != 0:
+            raise NotmuchError(status)
+
+        return value.value
+
+    def get_properties(self, prop, exact=False):
+        """ Get the properties for *message*, returning
+        notmuch_message_properties_t object which can be used to iterate
+        over all properties.
+
+        :param prop: The name of the property to get.
+        :param exact: if True, require exact match with key. Otherwise
+                      treat as prefix.
+        :returns: [(key, value)]
+        :raises: :exc:`NotInitializedError` if message has not been
+                 initialized
+        """
+        if not self._msg:
+            raise NotInitializedError()
+
+        properties_list = []
+        properties = Message._get_properties(self._msg, prop, exact)
+        while Message._properties_valid(properties):
+            key = Message._properties_key(properties)
+            value = Message._properties_value(properties)
+            properties_list.append((key, value))
+            Message._properties_move_to_next(properties)
+
+        return properties_list
+
     def freeze(self):
         """Freezes the current state of 'message' within the database
 
--
2.15.0

_______________________________________________
notmuch mailing list
[hidden email]
https://notmuchmail.org/mailman/listinfo/notmuch
Ruben Pollan Ruben Pollan
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] python: add bindings for notmuch_message_get_propert(y/ies)

Quoting Daniel Kahn Gillmor (2017-11-17 10:03:09)
> On Wed 2017-11-15 23:29:54 +0100, Ruben Pollan wrote:
> > Message.get_property (prop) returns a string with the value of the property.
>
> Upon review, this is actually insufficient for making robust use of the
> session-key series :(
>
> In particular, it only returns the first value for the session key
> returned.

See the last patch. I added the implementation of get_properties (and I use it
in my session-key branch of alot). Hopefully this solves the problem.

> In the session-key series, i work around this by simply trying each
> session key against an encrypted part until i find one that works. It
> would be "cleaner" (more principled) to somehow associate each session
> key with the part(s) of the message file(s) that it is capable of
> decrypting, but that's a lot of bookkeeping -- i think it's actually
> "cleaner" (less code, less computation in the standard case) to just
> take the current approach.

Fair enough for me, I'm copying this approach in alot as well.

--
meskio | http://meskio.net/
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 My contact info: http://meskio.net/crypto.txt
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
Nos vamos a Croatan.

_______________________________________________
notmuch mailing list
[hidden email]
https://notmuchmail.org/mailman/listinfo/notmuch

signature.asc (849 bytes) Download Attachment
Daniel Kahn Gillmor Daniel Kahn Gillmor
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] python: add bindings for notmuch_message_get_propert(y/ies)

In reply to this post by Ruben Pollan
On Tue 2017-11-28 23:46:11 +0100, Ruben Pollan wrote:
> Message.get_property (prop) returns a string with the value of the property and
> Message.get_properties (prop, exact=False) returns a list [(key, value)]

This looks like a sensible approach to me.  I'd be curious to hear what
others think of this.

In considering the API design space here, it occurs to me that it might
be more pythonic for get_properties to return a dict like:

   { key: [ value, … ], key: [ value, … ] }

Any reason you chose one over the other?  My python-fu is shallow, so
please don't take my aesthetic guesswork as authoritative; but i'm
imagining a user wanting to grab a bunch of properties and then easily
access them by key, and the dict seems like the simple way to do that.

Also, does get_properties() work with prop=None to fetch all properties?
if so, maybe that should be the default?

To be clear, I'd be fine with a response that disagrees with these
suggestions (especially if it explains why) and then adopting this
patch; i just want to make sure they've been considered before we lock
in the API.

Thanks for working on this, Meskio!

   --dkg

_______________________________________________
notmuch mailing list
[hidden email]
https://notmuchmail.org/mailman/listinfo/notmuch

signature.asc (847 bytes) Download Attachment
Ruben Pollan Ruben Pollan
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] python: add bindings for notmuch_message_get_propert(y/ies)

Quoting Daniel Kahn Gillmor (2017-11-29 02:57:24)

> On Tue 2017-11-28 23:46:11 +0100, Ruben Pollan wrote:
> > Message.get_property (prop) returns a string with the value of the property and
> > Message.get_properties (prop, exact=False) returns a list [(key, value)]
>
> This looks like a sensible approach to me.  I'd be curious to hear what
> others think of this.
>
> In considering the API design space here, it occurs to me that it might
> be more pythonic for get_properties to return a dict like:
>
>    { key: [ value, … ], key: [ value, … ] }
>
> Any reason you chose one over the other?  My python-fu is shallow, so
> please don't take my aesthetic guesswork as authoritative; but i'm
> imagining a user wanting to grab a bunch of properties and then easily
> access them by key, and the dict seems like the simple way to do that.
Yes, the dict is more pythonic. I thought about it, I went for the tuples it was
simpler to implement (and use in my use case). But giving a second thought it
makes more sense to do a dict.

> Also, does get_properties() work with prop=None to fetch all properties?
> if so, maybe that should be the default?

I didn't thought about that, but you are right, with prop="" you get the full
list of properties of the message. Nice, let's put it as default value.

--
meskio | http://meskio.net/
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 My contact info: http://meskio.net/crypto.txt
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
Nos vamos a Croatan.

_______________________________________________
notmuch mailing list
[hidden email]
https://notmuchmail.org/mailman/listinfo/notmuch

signature.asc (849 bytes) Download Attachment
Ruben Pollan Ruben Pollan
Reply | Threaded
Open this post in threaded view
|

[PATCH] python: add bindings for notmuch_message_get_propert(y/ies)

Message.get_property (prop) returns a string with the value of the property and
Message.get_properties (prop, exact=False) returns a list [(key, value)]
---
 bindings/python/notmuch/globals.py |  5 +++
 bindings/python/notmuch/message.py | 81 +++++++++++++++++++++++++++++++++++++-
 2 files changed, 85 insertions(+), 1 deletion(-)

diff --git a/bindings/python/notmuch/globals.py b/bindings/python/notmuch/globals.py
index 71426c84..801062dc 100644
--- a/bindings/python/notmuch/globals.py
+++ b/bindings/python/notmuch/globals.py
@@ -75,6 +75,11 @@ class NotmuchMessageS(Structure):
 NotmuchMessageP = POINTER(NotmuchMessageS)
 
 
+class NotmuchMessagePropertiesS(Structure):
+    pass
+NotmuchMessagePropertiesP = POINTER(NotmuchMessagePropertiesS)
+
+
 class NotmuchTagsS(Structure):
     pass
 NotmuchTagsP = POINTER(NotmuchTagsS)
diff --git a/bindings/python/notmuch/message.py b/bindings/python/notmuch/message.py
index d5b98e4f..2025f979 100644
--- a/bindings/python/notmuch/message.py
+++ b/bindings/python/notmuch/message.py
@@ -19,7 +19,7 @@ Copyright 2010 Sebastian Spaeth <[hidden email]>
 """
 
 
-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 (
@@ -113,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'])
 
@@ -433,6 +464,54 @@ 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, prop, byref(value))
+        if status != 0:
+            raise NotmuchError(status)
+
+        return value.value
+
+    def get_properties(self, prop="", exact=False):
+        """ Get the properties for *message*, returning
+        notmuch_message_properties_t object which can be used to iterate
+        over all properties.
+
+        :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.
+        :returns: A dictionary with the property names and values {key: value}
+        :raises: :exc:`NotInitializedError` if message has not been
+                 initialized
+        """
+        if not self._msg:
+            raise NotInitializedError()
+
+        properties_dict = {}
+        properties = Message._get_properties(self._msg, prop, exact)
+        while Message._properties_valid(properties):
+            key = Message._properties_key(properties)
+            value = Message._properties_value(properties)
+            if key not in properties_dict:
+                properties_dict[key] = []
+            properties_dict[key].append(value)
+            Message._properties_move_to_next(properties)
+
+        return properties_dict
+
     def freeze(self):
         """Freezes the current state of 'message' within the database
 
--
2.15.0

_______________________________________________
notmuch mailing list
[hidden email]
https://notmuchmail.org/mailman/listinfo/notmuch
Floris Bruynooghe Floris Bruynooghe
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] python: add bindings for notmuch_message_get_propert(y/ies)

In reply to this post by Daniel Kahn Gillmor
Daniel Kahn Gillmor <[hidden email]> writes:

> On Tue 2017-11-28 23:46:11 +0100, Ruben Pollan wrote:
>> Message.get_property (prop) returns a string with the value of the property and
>> Message.get_properties (prop, exact=False) returns a list [(key, value)]
>
> This looks like a sensible approach to me.  I'd be curious to hear what
> others think of this.
>
> In considering the API design space here, it occurs to me that it might
> be more pythonic for get_properties to return a dict like:

I would probably model properties as a dictionary in notdb, making this
a collections.abc.MutableMapping implementation with a .get_all(prop)
method inspired from the stdlib email.message package.  This kind of
also implies making properties access a property rather then a method
call:

msg.properties['prop'] = 'foo'
msg.properties['prop'] = 'bar'
msg.properties['prop'] == 'foo'  # pot luck
msg.properties.get_all('prop') == {'foo', 'bar'}  # properties are unsorted are they?

This also has the binary question problem, is this returned as bytes or
as str?  Current Python bindings seem to go for .decode('utf-8',
errors='ignore') afaik which is somewhat lossy.


Cheers,
Floris
_______________________________________________
notmuch mailing list
[hidden email]
https://notmuchmail.org/mailman/listinfo/notmuch
David Bremner-2 David Bremner-2
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] python: add bindings for notmuch_message_get_propert(y/ies)

Floris Bruynooghe <[hidden email]> writes:

> Daniel Kahn Gillmor <[hidden email]> writes:
>
> This also has the binary question problem, is this returned as bytes or
> as str?  Current Python bindings seem to go for .decode('utf-8',
> errors='ignore') afaik which is somewhat lossy.
>

IMHO there's no reason to support non-utf8 properties. We should
document this restriction now while adoption is relatively light.

d
_______________________________________________
notmuch mailing list
[hidden email]
https://notmuchmail.org/mailman/listinfo/notmuch
Daniel Kahn Gillmor Daniel Kahn Gillmor
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] python: add bindings for notmuch_message_get_propert(y/ies)

On Thu 2017-11-30 09:44:19 -0400, David Bremner wrote:
> Floris Bruynooghe <[hidden email]> writes:
>
>> This also has the binary question problem, is this returned as bytes or
>> as str?  Current Python bindings seem to go for .decode('utf-8',
>> errors='ignore') afaik which is somewhat lossy.
>>
>
> IMHO there's no reason to support non-utf8 properties. We should
> document this restriction now while adoption is relatively light.

Agreed, my reading of the source is that we expect strings for both
property names and property values.  If they're both strings, we should
not support anything other than UTF-8 in either case.

      --dkg
_______________________________________________
notmuch mailing list
[hidden email]
https://notmuchmail.org/mailman/listinfo/notmuch