From 5a58754841f4d3e62d104ad338c8ca2c481dc32e Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Mon, 15 Jun 2020 23:55:52 +0200 Subject: [PATCH] python/notmuch2: add bindings for the database config strings --- 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 00000000..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 import notmuch2._base as base +import notmuch2._config as config import notmuch2._capi as capi import notmuch2._errors as errors import notmuch2._message as message @@ -536,6 +537,28 @@ class Database(base.NotmuchObject): self._cached_tagset = weakref.ref(tagset) return tagset + @property + def config(self): + """Return a mutable mapping with the settings stored in this database. + + This returns an mutable dict-like object implementing the + collections.abc.MutableMapping Abstract Base Class. + + :rtype: Config + + :raises ObjectDestroyedError: if used after destroyed. + """ + try: + ref = self._cached_config + except AttributeError: + config_mapping = None + else: + config_mapping = ref() + if config_mapping is None: + config_mapping = config.ConfigMapping(self, '_db_p') + self._cached_config = weakref.ref(config_mapping) + return config_mapping + def _create_query(self, query, *, omit_excluded=EXCLUDE.TRUE, sort=SORT.UNSORTED, # Check this default