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

2020-05-21 Thread Floris Bruynooghe
Thanks for adding more of the API!

This mostly is fine as well, again I'd mainly ask to add tests however.
At least the things which are implemented directly I guess: setitem,
getitem, iter and len.


On Sat 09 May 2020 at 07:05 +0200, Anton Khirnov wrote:

> ---
>  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']
> +

Same style thing about 2 blank lines

> +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 = 

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

2020-05-08 Thread 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 f14eac78..fc55fea8 100644
--- a/bindings/python-cffi/notmuch2/_database.py
+++ b/bindings/python-cffi/notmuch2/_database.py
@@ -7,6 +7,7 @@ import pathlib
 import weakref
 
 import