[PATCH 2/2] Make messages returned by Thread objects owned

2020-06-15 Thread Floris Bruynooghe
This reverses the logic of StandaloneMessage to instead create a
OwnedMessage.  Only the Thread class allows retrieving messages more
then once so it can explicitly create such messages.

The added test fails with SIGABRT without the fix for the message
re-use in threads being present.
---
 bindings/python-cffi/notmuch2/_database.py  |  6 +--
 bindings/python-cffi/notmuch2/_message.py   | 55 -
 bindings/python-cffi/notmuch2/_thread.py|  8 ++-
 bindings/python-cffi/tests/test_database.py | 11 +
 4 files changed, 51 insertions(+), 29 deletions(-)

diff --git a/bindings/python-cffi/notmuch2/_database.py 
b/bindings/python-cffi/notmuch2/_database.py
index f14eac78..95f59ca0 100644
--- a/bindings/python-cffi/notmuch2/_database.py
+++ b/bindings/python-cffi/notmuch2/_database.py
@@ -399,7 +399,7 @@ class Database(base.NotmuchObject):
   capi.lib.NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID]
 if ret not in ok:
 raise errors.NotmuchError(ret)
-msg = message.StandaloneMessage(self, msg_pp[0], db=self)
+msg = message.Message(self, msg_pp[0], db=self)
 if sync_flags:
 msg.tags.from_maildir_flags()
 return self.AddedMessage(
@@ -468,7 +468,7 @@ class Database(base.NotmuchObject):
 msg_p = msg_pp[0]
 if msg_p == capi.ffi.NULL:
 raise LookupError
-msg = message.StandaloneMessage(self, msg_p, db=self)
+msg = message.Message(self, msg_p, db=self)
 return msg
 
 def get(self, filename):
@@ -501,7 +501,7 @@ class Database(base.NotmuchObject):
 msg_p = msg_pp[0]
 if msg_p == capi.ffi.NULL:
 raise LookupError
-msg = message.StandaloneMessage(self, msg_p, db=self)
+msg = message.Message(self, msg_p, db=self)
 return msg
 
 @property
diff --git a/bindings/python-cffi/notmuch2/_message.py 
b/bindings/python-cffi/notmuch2/_message.py
index 416ce7ca..02de50ad 100644
--- a/bindings/python-cffi/notmuch2/_message.py
+++ b/bindings/python-cffi/notmuch2/_message.py
@@ -47,9 +47,7 @@ class Message(base.NotmuchObject):
 :type db: Database
 :param msg_p: The C pointer to the ``notmuch_message_t``.
 :type msg_p: 
-
 :param dup: Whether the message was a duplicate on insertion.
-
 :type dup: None or bool
 """
 _msg_p = base.MemoryPointer()
@@ -61,10 +59,22 @@ class Message(base.NotmuchObject):
 
 @property
 def alive(self):
-return self._parent.alive
+if not self._parent.alive:
+return False
+try:
+self._msg_p
+except errors.ObjectDestroyedError:
+return False
+else:
+return True
+
+def __del__(self):
+self._destroy()
 
 def _destroy(self):
-pass
+if self.alive:
+capi.lib.notmuch_message_destroy(self._msg_p)
+self._msg_p = None
 
 @property
 def messageid(self):
@@ -363,30 +373,26 @@ class Message(base.NotmuchObject):
 if isinstance(other, self.__class__):
 return self.messageid == other.messageid
 
-class StandaloneMessage(Message):
-"""An email message stored in the notmuch database.
 
-This subclass of Message is used for messages that are retrieved from the
-database directly and are not owned by a query.
+class OwnedMessage(Message):
+"""An email message owned by parent thread object.
+
+This subclass of Message is used for messages that are retrieved
+from the notmuch database via a parent :class:`notmuch2.Thread`
+object, which "owns" this message.  This means that when this
+message object is destroyed, by calling :func:`del` or
+:meth:`_destroy` directly or indirectly, the message is not freed
+in the notmuch API and the parent :class:`notmuch2.Thread` object
+can return the same object again when needed.
 """
+
 @property
 def alive(self):
-if not self._parent.alive:
-return False
-try:
-self._msg_p
-except errors.ObjectDestroyedError:
-return False
-else:
-return True
-
-def __del__(self):
-self._destroy()
+return self._parent.alive
 
 def _destroy(self):
-if self.alive:
-capi.lib.notmuch_message_destroy(self._msg_p)
-self._msg_p = None
+pass
+
 
 class FilenamesIter(base.NotmuchIter):
 """Iterator for binary filenames objects."""
@@ -690,8 +696,9 @@ collections.abc.ValuesView.register(PropertiesValuesView)
 
 class MessageIter(base.NotmuchIter):
 
-def __init__(self, parent, msgs_p, *, db):
+def __init__(self, parent, msgs_p, *, db, msg_cls=Message):
 self._db = db
+self._msg_cls = msg_cls
 super().__init__(parent, msgs_p,
  fn_destroy=capi.lib.notmuch_messages_destroy,
  fn_valid=capi.lib.notmuch_messages_valid,
@@ -700,4 +707,4 @@ class 

python: Continuing message re-use fix

2020-06-15 Thread Floris Bruynooghe
Hi,

This builds on the patch by Anton Khirnov to fix the message re-use
that is possible when accessing messages from a thread.  I started
with just addressing my own comments on this patch, but evolved it
into switching the logic around and leave the normal Message object
untouched.  Instead I created a new OwnedMessage which is used by
the Thread which does not free itself on __del__().  I think this
is preferable because the other iterators, mainly Database.messages(),
do not allow retrieving messages more than once since the query object
is hidden from the API.

I've left the original commit in this patch series to not alter any
contributions.

Cheers,
Floris


___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


[PATCH 1/2] python/notmuch2: add bindings for the database config strings

2020-06-15 Thread Floris Bruynooghe
From: Anton Khirnov 

---
 bindings/python-cffi/notmuch2/_build.py| 17 +
 bindings/python-cffi/notmuch2/_config.py   | 84 ++
 bindings/python-cffi/notmuch2/_database.py | 23 ++
 3 files changed, 124 insertions(+)
 create mode 100644 bindings/python-cffi/notmuch2/_config.py

diff --git a/bindings/python-cffi/notmuch2/_build.py 
b/bindings/python-cffi/notmuch2/_build.py
index 5e1fcac1..f269f2a1 100644
--- a/bindings/python-cffi/notmuch2/_build.py
+++ b/bindings/python-cffi/notmuch2/_build.py
@@ -314,6 +314,23 @@ ffibuilder.cdef(
 notmuch_indexopts_get_decrypt_policy (const notmuch_indexopts_t 
*indexopts);
 void
 notmuch_indexopts_destroy (notmuch_indexopts_t *options);
+
+notmuch_status_t
+notmuch_database_set_config (notmuch_database_t *db, const char *key, 
const char *value);
+notmuch_status_t
+notmuch_database_get_config (notmuch_database_t *db, const char *key, char 
**value);
+notmuch_status_t
+notmuch_database_get_config_list (notmuch_database_t *db, const char 
*prefix, notmuch_config_list_t **out);
+notmuch_bool_t
+notmuch_config_list_valid (notmuch_config_list_t *config_list);
+const char *
+notmuch_config_list_key (notmuch_config_list_t *config_list);
+const char *
+notmuch_config_list_value (notmuch_config_list_t *config_list);
+void
+notmuch_config_list_move_to_next (notmuch_config_list_t *config_list);
+void
+notmuch_config_list_destroy (notmuch_config_list_t *config_list);
 """
 )
 
diff --git a/bindings/python-cffi/notmuch2/_config.py 
b/bindings/python-cffi/notmuch2/_config.py
new file mode 100644
index ..58383c16
--- /dev/null
+++ b/bindings/python-cffi/notmuch2/_config.py
@@ -0,0 +1,84 @@
+import collections.abc
+
+import notmuch2._base as base
+import notmuch2._capi as capi
+import notmuch2._errors as errors
+
+__all__ = ['ConfigMapping']
+
+class ConfigIter(base.NotmuchIter):
+def __init__(self, parent, iter_p):
+super().__init__(
+parent, iter_p,
+fn_destroy=capi.lib.notmuch_config_list_destroy,
+fn_valid=capi.lib.notmuch_config_list_valid,
+fn_get=capi.lib.notmuch_config_list_key,
+fn_next=capi.lib.notmuch_config_list_move_to_next)
+
+def __next__(self):
+item = super().__next__()
+return base.BinString.from_cffi(item)
+
+class ConfigMapping(base.NotmuchObject, collections.abc.MutableMapping):
+"""The config key/value pairs stored in the database.
+
+The entries are exposed as a :class:`collections.abc.MutableMapping` 
object.
+Note that setting a value to an empty string is the same as deleting it.
+
+:param parent: the parent object
+:param ptr_name: the name of the attribute on the parent which will
+   return the memory pointer.  This allows this object to
+   access the pointer via the parent's descriptor and thus
+   trigger :class:`MemoryPointer`'s memory safety.
+"""
+
+def __init__(self, parent, ptr_name):
+self._parent = parent
+self._ptr = lambda: getattr(parent, ptr_name)
+
+@property
+def alive(self):
+return self._parent.alive
+
+def _destroy(self):
+pass
+
+def __getitem__(self, key):
+if isinstance(key, str):
+key = key.encode('utf-8')
+val_pp = capi.ffi.new('char**')
+ret = capi.lib.notmuch_database_get_config(self._ptr(), key, val_pp)
+if ret != capi.lib.NOTMUCH_STATUS_SUCCESS:
+raise errors.NotmuchError(ret)
+if val_pp[0] == "":
+capi.lib.free(val_pp[0])
+raise KeyError
+val = base.BinString.from_cffi(val_pp[0])
+capi.lib.free(val_pp[0])
+return val
+
+def __setitem__(self, key, val):
+if isinstance(key, str):
+key = key.encode('utf-8')
+if isinstance(val, str):
+val = val.encode('utf-8')
+ret = capi.lib.notmuch_database_set_config(self._ptr(), key, val)
+if ret != capi.lib.NOTMUCH_STATUS_SUCCESS:
+raise errors.NotmuchError(ret)
+
+def __delitem__(self, key):
+self[key] = ""
+
+def __iter__(self):
+"""Return an iterator over the config items.
+
+:raises NullPointerError: If the iterator can not be created.
+"""
+configlist_pp = capi.ffi.new('notmuch_config_list_t**')
+ret = capi.lib.notmuch_database_get_config_list(self._ptr(), b'', 
configlist_pp)
+if ret != capi.lib.NOTMUCH_STATUS_SUCCESS:
+raise errors.NotmuchError(ret)
+return ConfigIter(self._parent, configlist_pp[0])
+
+def __len__(self):
+return sum(1 for t in self)
diff --git a/bindings/python-cffi/notmuch2/_database.py 
b/bindings/python-cffi/notmuch2/_database.py
index 95f59ca0..3c06402d 100644
--- a/bindings/python-cffi/notmuch2/_database.py
+++ b/bindings/python-cffi/notmuch2/_database.py
@@ -7,6 +7,7 @@ import pathlib
 import weakref

[PATCH 2/2] python config access: fix style and KeyError bug

2020-06-15 Thread Floris Bruynooghe
This fixes some minor style/pep8 things and adds tests for the new
config support.  Also fixes a bug where KeyError was never raised
on a missing key.
---
 bindings/python-cffi/notmuch2/_config.py  |  9 ++--
 bindings/python-cffi/tests/test_config.py | 56 +++
 2 files changed, 62 insertions(+), 3 deletions(-)
 create mode 100644 bindings/python-cffi/tests/test_config.py

diff --git a/bindings/python-cffi/notmuch2/_config.py 
b/bindings/python-cffi/notmuch2/_config.py
index 58383c16..29de6495 100644
--- a/bindings/python-cffi/notmuch2/_config.py
+++ b/bindings/python-cffi/notmuch2/_config.py
@@ -4,9 +4,12 @@ import notmuch2._base as base
 import notmuch2._capi as capi
 import notmuch2._errors as errors
 
+
 __all__ = ['ConfigMapping']
 
+
 class ConfigIter(base.NotmuchIter):
+
 def __init__(self, parent, iter_p):
 super().__init__(
 parent, iter_p,
@@ -19,6 +22,7 @@ class ConfigIter(base.NotmuchIter):
 item = super().__next__()
 return base.BinString.from_cffi(item)
 
+
 class ConfigMapping(base.NotmuchObject, collections.abc.MutableMapping):
 """The config key/value pairs stored in the database.
 
@@ -50,11 +54,10 @@ class ConfigMapping(base.NotmuchObject, 
collections.abc.MutableMapping):
 ret = capi.lib.notmuch_database_get_config(self._ptr(), key, val_pp)
 if ret != capi.lib.NOTMUCH_STATUS_SUCCESS:
 raise errors.NotmuchError(ret)
-if val_pp[0] == "":
-capi.lib.free(val_pp[0])
-raise KeyError
 val = base.BinString.from_cffi(val_pp[0])
 capi.lib.free(val_pp[0])
+if val == '':
+raise KeyError
 return val
 
 def __setitem__(self, key, val):
diff --git a/bindings/python-cffi/tests/test_config.py 
b/bindings/python-cffi/tests/test_config.py
new file mode 100644
index ..1b2695f5
--- /dev/null
+++ b/bindings/python-cffi/tests/test_config.py
@@ -0,0 +1,56 @@
+import collections.abc
+
+import pytest
+
+import notmuch2._database as dbmod
+
+import notmuch2._config as config
+
+
+class TestIter:
+
+@pytest.fixture
+def db(self, maildir):
+with dbmod.Database.create(maildir.path) as db:
+yield db
+
+def test_type(self, db):
+assert isinstance(db.config, collections.abc.MutableMapping)
+assert isinstance(db.config, config.ConfigMapping)
+
+def test_alive(self, db):
+assert db.config.alive
+
+def test_set_get(self, maildir):
+# Ensure get-set works from different db objects
+with dbmod.Database.create(maildir.path) as db0:
+db0.config['spam'] = 'ham'
+with dbmod.Database(maildir.path) as db1:
+assert db1.config['spam'] == 'ham'
+
+def test_get_keyerror(self, db):
+with pytest.raises(KeyError):
+val = db.config['not-a-key']
+print(repr(val))
+
+def test_iter(self, db):
+assert list(db.config) == []
+db.config['spam'] = 'ham'
+db.config['eggs'] = 'bacon'
+assert set(db.config) == {'spam', 'eggs'}
+assert set(db.config.keys()) == {'spam', 'eggs'}
+assert set(db.config.values()) == {'ham', 'bacon'}
+assert set(db.config.items()) == {('spam', 'ham'), ('eggs', 'bacon')}
+
+def test_len(self, db):
+assert len(db.config) == 0
+db.config['spam'] = 'ham'
+assert len(db.config) == 1
+db.config['eggs'] = 'bacon'
+assert len(db.config) == 2
+
+def test_del(self, db):
+db.config['spam'] = 'ham'
+assert db.config.get('spam') == 'ham'
+del db.config['spam']
+assert db.config.get('spam') is None
-- 
2.27.0

___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


python: config API

2020-06-15 Thread Floris Bruynooghe
This is a followup on the patch from Anton Khirnov adding config
API support to the python bindings.

I can not help myself but point out that I did not spot the bug
until not only I had written tests, but until I looked at the
test coverage to see what was not yet executed and added more
tests.  Tests and test coverage is good!


___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH] Update tox.ini for python3.8 and fix pypy3.6

2020-06-15 Thread Floris Bruynooghe
On Mon 15 Jun 2020 at 07:06 -0300, David Bremner wrote:

> Floris Bruynooghe  writes:
>
>>  [testenv]
>>  deps =
>> @@ -14,3 +14,6 @@ commands = pytest --cov={envsitepackagesdir}/notmuch2 
>> {posargs}
>>  
>>  [testenv:pypy35]
>>  basepython = pypy3.5
>> +
>> +[testenv:pypy36]
>> +basepython = pypy3.6
>
> I'm not an expert, but should python 3.7 and python 3.8 have similar
> clauses? Or do they work by default?

They do work by default.  But to be honest these two pypy ones are not
fully standardised and rely on me manually ensuring I have both a
pypy3.5 and pypy3.6 binary on my path.  The default thing that works is
just a "pypy3" which doesn't give you any guarantees over the version
you get (other than not pypy2).
___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


[PATCH 1/2] python/notmuch2: do not destroy messages owned by a query

2020-06-15 Thread Floris Bruynooghe
From: Anton Khirnov 

Any messages retrieved from a query - either directly via
search_messages() or indirectly via thread objects - are owned by that
query. Retrieving the same message (i.e. corresponding to the same
message ID / database object) several times will always yield the same
C object.

The caller is allowed to destroy message objects owned by a query before
the query itself - which can save memory for long-lived queries.
However, that message must then never be retrieved again from that
query.

The python-notmuch2 bindings will currently destroy every message object
in Message._destroy(), which will lead to an invalid free if the same
message is then retrieved again. E.g. the following python program leads
to libtalloc abort()ing:

import notmuch2
db   = notmuch2.Database(mode = notmuch2.Database.MODE.READ_ONLY)
t= next(db.threads('*'))
msgs = list(zip(t.toplevel(), t.toplevel()))
msgs = list(zip(t.toplevel(), t.toplevel()))

Fix this issue by creating a subclass of Message, which is used for
"standalone" message which have to be freed by the caller. Message class
is then used only for messages descended from a query, which do not need
to be freed by the caller.
---
 bindings/python-cffi/notmuch2/_database.py |  6 ++--
 bindings/python-cffi/notmuch2/_message.py  | 42 ++
 2 files changed, 30 insertions(+), 18 deletions(-)

diff --git a/bindings/python-cffi/notmuch2/_database.py 
b/bindings/python-cffi/notmuch2/_database.py
index 95f59ca0..f14eac78 100644
--- a/bindings/python-cffi/notmuch2/_database.py
+++ b/bindings/python-cffi/notmuch2/_database.py
@@ -399,7 +399,7 @@ class Database(base.NotmuchObject):
   capi.lib.NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID]
 if ret not in ok:
 raise errors.NotmuchError(ret)
-msg = message.Message(self, msg_pp[0], db=self)
+msg = message.StandaloneMessage(self, msg_pp[0], db=self)
 if sync_flags:
 msg.tags.from_maildir_flags()
 return self.AddedMessage(
@@ -468,7 +468,7 @@ class Database(base.NotmuchObject):
 msg_p = msg_pp[0]
 if msg_p == capi.ffi.NULL:
 raise LookupError
-msg = message.Message(self, msg_p, db=self)
+msg = message.StandaloneMessage(self, msg_p, db=self)
 return msg
 
 def get(self, filename):
@@ -501,7 +501,7 @@ class Database(base.NotmuchObject):
 msg_p = msg_pp[0]
 if msg_p == capi.ffi.NULL:
 raise LookupError
-msg = message.Message(self, msg_p, db=self)
+msg = message.StandaloneMessage(self, msg_p, db=self)
 return msg
 
 @property
diff --git a/bindings/python-cffi/notmuch2/_message.py 
b/bindings/python-cffi/notmuch2/_message.py
index c5fdbf6d..416ce7ca 100644
--- a/bindings/python-cffi/notmuch2/_message.py
+++ b/bindings/python-cffi/notmuch2/_message.py
@@ -14,7 +14,7 @@ __all__ = ['Message']
 
 
 class Message(base.NotmuchObject):
-"""An email message stored in the notmuch database.
+"""An email message stored in the notmuch database retrieved via a query.
 
 This should not be directly created, instead it will be returned
 by calling methods on :class:`Database`.  A message keeps a
@@ -61,22 +61,10 @@ class Message(base.NotmuchObject):
 
 @property
 def alive(self):
-if not self._parent.alive:
-return False
-try:
-self._msg_p
-except errors.ObjectDestroyedError:
-return False
-else:
-return True
-
-def __del__(self):
-self._destroy()
+return self._parent.alive
 
 def _destroy(self):
-if self.alive:
-capi.lib.notmuch_message_destroy(self._msg_p)
-self._msg_p = None
+pass
 
 @property
 def messageid(self):
@@ -375,6 +363,30 @@ class Message(base.NotmuchObject):
 if isinstance(other, self.__class__):
 return self.messageid == other.messageid
 
+class StandaloneMessage(Message):
+"""An email message stored in the notmuch database.
+
+This subclass of Message is used for messages that are retrieved from the
+database directly and are not owned by a query.
+"""
+@property
+def alive(self):
+if not self._parent.alive:
+return False
+try:
+self._msg_p
+except errors.ObjectDestroyedError:
+return False
+else:
+return True
+
+def __del__(self):
+self._destroy()
+
+def _destroy(self):
+if self.alive:
+capi.lib.notmuch_message_destroy(self._msg_p)
+self._msg_p = None
 
 class FilenamesIter(base.NotmuchIter):
 """Iterator for binary filenames objects."""
-- 
2.27.0

___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


Re: difficulties with notmuch2 python bindings for alot

2020-06-15 Thread Floris Bruynooghe
On Sun 14 Jun 2020 at 19:44 -0300, David Bremner wrote:

> Floris Bruynooghe  writes:
>
>> One thing that they encountered and don't yet understand is that they
>> reported issues with leaking filedescriptors.  They used the bindings in
>> a way where I expect it to only call notmuch_database_destroy() when
>> they are done with it.  From reading notmuch.h I think that's correct
>> and there's no need to call notmuch_database_close() first.  Yet someone
>> reported that explicitly calling close helped.  Is the assumption I made
>> of only calling destroy correct?
>
> The first thing destroy does is call close. My read of the
> notmuch_database_close code is that it is idempotent (calling multiple
> times does not change anything).

Thanks for confirming, so that should be fine.

Cheers,
Floris
___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH] Support aborting the atomic context

2020-06-15 Thread Floris Bruynooghe
On Mon 15 Jun 2020 at 07:35 -0300, David Bremner wrote:

> Floris Bruynooghe  writes:
>
>> This is an implementation of what was suggested in
>> id:87v9k96xtl@powell.devork.be It closes the database as that is
>> the only safe way to do this afaik.
>>
>> Currently when the database is closed there are still a bunch of
>> operations which can result in segfaults.

I realise that this is perhaps not a very helpful comment.  I'll see if
I can narrow this down into a proper bug report.

>> Yet the API also promises
>> that some operations are still valid, basically those which only
>> access already previously retrieved information.  It would be nice to
>> find a good solution for this in the python bindings, but I don't yet
>> know what this would be.
>
> There is a Xapian method WritableDatabase::cancel_transaction(), but it
> is not currently exposed by notmuch.  I think it could be added, but
> getting the testing working right is not something I want to tackle at
> this point in the release cycle.  I guess this should be upwardly
> compatible, as all code correct for your current implementation should
> still work if we replace notmuch_database_close with
> notmuch_cancel_atomic. Thoughts?

I agree with your reasoning here that such a change would be upwardly
compatible.  Though I can think of some really convoluted scenarios
where this could be false, e.g. after aborting a transaction code could
rely on handling NOTMUCH_STATUS_XAPIAN_EXCEPTION it gets from a the
closed database.  This seems so much a corner case though, that I'd be
willing to ignore it.
___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


Re: python: config API

2020-06-15 Thread David Bremner
Floris Bruynooghe  writes:

> This is a followup on the patch from Anton Khirnov adding config
> API support to the python bindings.
>
> I can not help myself but point out that I did not spot the bug
> until not only I had written tests, but until I looked at the
> test coverage to see what was not yet executed and added more
> tests.  Tests and test coverage is good!
>

I've merged the revised series to release and master.
___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH] Support aborting the atomic context

2020-06-15 Thread David Bremner
Floris Bruynooghe  writes:

> This is an implementation of what was suggested in
> id:87v9k96xtl@powell.devork.be It closes the database as that is
> the only safe way to do this afaik.
>
> Currently when the database is closed there are still a bunch of
> operations which can result in segfaults.  Yet the API also promises
> that some operations are still valid, basically those which only
> access already previously retrieved information.  It would be nice to
> find a good solution for this in the python bindings, but I don't yet
> know what this would be.

There is a Xapian method WritableDatabase::cancel_transaction(), but it
is not currently exposed by notmuch.  I think it could be added, but
getting the testing working right is not something I want to tackle at
this point in the release cycle.  I guess this should be upwardly
compatible, as all code correct for your current implementation should
still work if we replace notmuch_database_close with
notmuch_cancel_atomic. Thoughts?

d


___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH] Update tox.ini for python3.8 and fix pypy3.6

2020-06-15 Thread David Bremner
Floris Bruynooghe  writes:

>  [testenv]
>  deps =
> @@ -14,3 +14,6 @@ commands = pytest --cov={envsitepackagesdir}/notmuch2 
> {posargs}
>  
>  [testenv:pypy35]
>  basepython = pypy3.5
> +
> +[testenv:pypy36]
> +basepython = pypy3.6

I'm not an expert, but should python 3.7 and python 3.8 have similar
clauses? Or do they work by default?

d



___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH] Update tox.ini for python3.8 and fix pypy3.6

2020-06-15 Thread Tomi Ollila
On Mon, Jun 15 2020, David Bremner wrote:

> Floris Bruynooghe  writes:
>
>>  [testenv]
>>  deps =
>> @@ -14,3 +14,6 @@ commands = pytest --cov={envsitepackagesdir}/notmuch2 
>> {posargs}
>>  
>>  [testenv:pypy35]
>>  basepython = pypy3.5
>> +
>> +[testenv:pypy36]
>> +basepython = pypy3.6
>
> I'm not an expert, but should python 3.7 and python 3.8 have similar
> clauses? Or do they work by default?

"Python 3.6 compatible PyPy3.6 v7.3.1" in https://www.pypy.org/download.html
hints there are not (yet) pypy3.7 nor pypy3.8

>
> d

Tomi
___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH] Update tox.ini for python3.8 and fix pypy3.6

2020-06-15 Thread David Bremner
Tomi Ollila  writes:

> On Mon, Jun 15 2020, David Bremner wrote:
>
>> Floris Bruynooghe  writes:
>>
>>>  [testenv]
>>>  deps =
>>> @@ -14,3 +14,6 @@ commands = pytest --cov={envsitepackagesdir}/notmuch2 
>>> {posargs}
>>>  
>>>  [testenv:pypy35]
>>>  basepython = pypy3.5
>>> +
>>> +[testenv:pypy36]
>>> +basepython = pypy3.6
>>
>> I'm not an expert, but should python 3.7 and python 3.8 have similar
>> clauses? Or do they work by default?
>
> "Python 3.6 compatible PyPy3.6 v7.3.1" in https://www.pypy.org/download.html
> hints there are not (yet) pypy3.7 nor pypy3.8

Oh sorry. I really should read better. I even knew that about pypy. OK,
no objections to the patch as is.

d
___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH] Update tox.ini for python3.8 and fix pypy3.6

2020-06-15 Thread David Bremner
Floris Bruynooghe  writes:

> Python 3.8 has been released for a while now, make sure we keep
> supporting it correctly.
>
> PyPy 3.6 wasn not configured correctly.

merged to origin and master

d
___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH] Add missing set methods to tagsets

2020-06-15 Thread David Bremner
Floris Bruynooghe  writes:

> Even though we use collections.abc.Set which implements all these
> methods under their operator names, the actual named variations of
> these methods are shockingly missing.  So let's add them manually.

merged to release and master.

d
___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch