From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mp1 ([2001:41d0:2:4a6f::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by ms11 with LMTPS id eKhzNH/u5156IgAA0tVLHw (envelope-from ) for ; Mon, 15 Jun 2020 21:56:15 +0000 Received: from aspmx1.migadu.com ([2001:41d0:2:4a6f::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by mp1 with LMTPS id YDM3MH/u517PXwAAbx9fmQ (envelope-from ) for ; Mon, 15 Jun 2020 21:56:15 +0000 Received: from arlo.cworth.org (arlo.cworth.org [50.126.95.6]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) server-signature RSA-PSS (4096 bits)) (No client certificate requested) by aspmx1.migadu.com (Postfix) with ESMTPS id 4BFC19408E7 for ; Mon, 15 Jun 2020 21:56:15 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by arlo.cworth.org (Postfix) with ESMTP id 3C3616DE1030; Mon, 15 Jun 2020 14:56:13 -0700 (PDT) X-Virus-Scanned: Debian amavisd-new at cworth.org Received: from arlo.cworth.org ([127.0.0.1]) by localhost (arlo.cworth.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 4BxNzOzxhIfw; Mon, 15 Jun 2020 14:56:12 -0700 (PDT) Received: from arlo.cworth.org (localhost [IPv6:::1]) by arlo.cworth.org (Postfix) with ESMTP id C838F6DE0F62; Mon, 15 Jun 2020 14:56:11 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by arlo.cworth.org (Postfix) with ESMTP id C46496DE0F76 for ; Mon, 15 Jun 2020 14:56:09 -0700 (PDT) X-Virus-Scanned: Debian amavisd-new at cworth.org Received: from arlo.cworth.org ([127.0.0.1]) by localhost (arlo.cworth.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id xmIDJDFpKKJB for ; Mon, 15 Jun 2020 14:56:09 -0700 (PDT) Received: from mail-ej1-f53.google.com (mail-ej1-f53.google.com [209.85.218.53]) by arlo.cworth.org (Postfix) with ESMTPS id 2D73F6DE0F62 for ; Mon, 15 Jun 2020 14:56:07 -0700 (PDT) Received: by mail-ej1-f53.google.com with SMTP id k11so19115265ejr.9 for ; Mon, 15 Jun 2020 14:56:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=4uJDW7RIwVbEkywhLXUSgv2sFoc1KkGP1kwkIvtPTRw=; b=dv5PwDXVK0swR2eIDJNGN5XFOaJectqbIQian4beGXcpsFpX1c5zslDxxxFRke8mkr nsdlxe8nUKYtwye7GoQz4bFUYYGZA6WJOSG9O6DvORZD0bi2qCSw+hEFObN1VHagTdo8 jDhcvduogVmRpObyAom99bP0PDpWJYabQKsyc0UqOBLFHoZySeRwVO/6vpvTiGiw7RLG zi161T2Ge4jWJxe6ae0MUCt+AS4a/gfIRoKa5+U+sU3Oy9zWDJco9tetv2zK6KjXGSUl jkFhM7uCACYcf+kkyBztU5XR2OYPdsTBxLFF5IGupGmE+GxS5BT72OhJovNFbtRBRkbW LuQQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id :in-reply-to:references:mime-version:content-transfer-encoding; bh=4uJDW7RIwVbEkywhLXUSgv2sFoc1KkGP1kwkIvtPTRw=; b=hlEeLDBw3IvXM9ky0LRHLTZXTrltnnVZf5fTDLBgw4dqaWdXhYD3P77g49/oUl0vll buouzWbbB0LQUMkFB1MBKo/Br6dHiUMPiKZmZziAVKbENkpEqWratK3XNl7X+C3yQ4OD +XEUHpM/E3O2+w2GcfpqSr5Bx70PK3TUK5RF1MYwZzUgjhvYWfWV6F+Df4ydjRon2fmK D9vZTSx6m7gfhsibFXoW/sG2HvrV5MO4mxiJ7zxNXhMeij5ei944eV0iIS0UsUR7Kr35 jaOXFRbdP21+V7BirkyloKJt9KrMfjZorulwagArK4gqwKLUMe5eiycLXe0ISVsX6Nwt gGxg== X-Gm-Message-State: AOAM533HD/1AZa/qphC23qAAc27gOTK4KMe0HRsV0H/xOczlD/yMFwhk 1w8iR5ZpkosI6rewo4IW61yHNOyR X-Google-Smtp-Source: ABdhPJwascd9d6EQvdYI9v5vbrCDYWOISt97Dz1BVo0trQS0v/DIrbOaWQaFuTJ1vf6Wn+cog2yOtg== X-Received: by 2002:a17:906:d216:: with SMTP id w22mr26612046ejz.420.1592258165455; Mon, 15 Jun 2020 14:56:05 -0700 (PDT) Received: from powell.devork.be (2a02-8388-8480-1180-4c18-fc69-8d8c-22b5.cable.dynamic.v6.surfer.at. [2a02:8388:8480:1180:4c18:fc69:8d8c:22b5]) by smtp.gmail.com with ESMTPSA id l8sm9934602ejz.52.2020.06.15.14.56.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 15 Jun 2020 14:56:04 -0700 (PDT) Received: (nullmailer pid 220674 invoked by uid 1000); Mon, 15 Jun 2020 21:56:03 -0000 From: Floris Bruynooghe To: notmuch@notmuchmail.org Subject: [PATCH 1/2] python/notmuch2: add bindings for the database config strings Date: Mon, 15 Jun 2020 23:55:52 +0200 Message-Id: <20200615215553.220529-2-flub@devork.be> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20200615215553.220529-1-flub@devork.be> References: <20200615215553.220529-1-flub@devork.be> MIME-Version: 1.0 X-BeenThere: notmuch@notmuchmail.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Use and development of the notmuch mail system." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: notmuch-bounces@notmuchmail.org Sender: "notmuch" X-Scanner: scn0 Authentication-Results: aspmx1.migadu.com; dkim=fail (body hash did not verify) header.d=gmail.com header.s=20161025 header.b=dv5PwDXV; dmarc=none; spf=pass (aspmx1.migadu.com: domain of notmuch-bounces@notmuchmail.org designates 50.126.95.6 as permitted sender) smtp.mailfrom=notmuch-bounces@notmuchmail.org X-Spam-Score: 0.99 X-TUID: Mef4EfhhTuRZ 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 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 -- 2.27.0