From 48b5263646bae2ce40e3ec17e0cfae26a7abc91f Mon Sep 17 00:00:00 2001 From: David Bremner Date: Fri, 29 Oct 2021 11:54:59 -0300 Subject: [PATCH 1/9] doc/python-cffi: import from built bindings, not installed module Previously the python-cffi bindings either failed to build, or built for the wrong module by using the installed module. The fix requires correction the module path, building the bindings before docs, and helping python find the built libnotmuch. Based on patch / discussion from Micheal Gruber [1] [1]: id:cover.1634808719.git.git@grubix.eu --- doc/Makefile.local | 12 ++++++++---- doc/conf.py | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/doc/Makefile.local b/doc/Makefile.local index f476d1da..730ad4fb 100644 --- a/doc/Makefile.local +++ b/doc/Makefile.local @@ -4,7 +4,7 @@ dir := doc # You can set these variables from the command line. SPHINXOPTS := -q -SPHINXBUILD = sphinx-build +SPHINXBUILD = env LD_LIBRARY_PATH=${NOTMUCH_BUILDDIR}/lib sphinx-build DOCBUILDDIR := $(dir)/_build # Internal variables. @@ -35,7 +35,7 @@ endif INFO_INFO_FILES := $(INFO_TEXI_FILES:.texi=.info) -.PHONY: sphinx-html sphinx-texinfo sphinx-info +.PHONY: sphinx-html sphinx-texinfo sphinx-info doc-prereqs .PHONY: install-man build-man apidocs install-apidocs @@ -46,15 +46,19 @@ ifeq ($(WITH_EMACS),1) $(DOCBUILDDIR)/.roff.stamp $(DOCBUILDDIR)/.html.stamp $(DOCBUILDDIR)/.texi.stamp : docstring.stamp endif +ifeq ($(HAVE_PYTHON3_CFFI),1) +doc-prereqs: python-cffi-bindings +endif + sphinx-html: $(DOCBUILDDIR)/.html.stamp -$(DOCBUILDDIR)/.html.stamp: $(ALL_RST_FILES) +$(DOCBUILDDIR)/.html.stamp: $(ALL_RST_FILES) doc-prereqs $(SPHINXBUILD) -b html -d $(DOCBUILDDIR)/html_doctrees $(ALLSPHINXOPTS) $(DOCBUILDDIR)/html touch $@ sphinx-texinfo: $(DOCBUILDDIR)/.texi.stamp -$(DOCBUILDDIR)/.texi.stamp: $(ALL_RST_FILES) +$(DOCBUILDDIR)/.texi.stamp: $(ALL_RST_FILES) doc-prereqs $(SPHINXBUILD) -b texinfo -d $(DOCBUILDDIR)/texinfo_doctrees $(ALLSPHINXOPTS) $(DOCBUILDDIR)/texinfo touch $@ diff --git a/doc/conf.py b/doc/conf.py index 1fbd102b..c7fd8f5a 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -25,7 +25,7 @@ for pathdir in ['.', '..']: version=infile.read().replace('\n','') # for autodoc -sys.path.insert(0, os.path.join(location, '..', 'bindings', 'python-cffi', 'notmuch2')) +sys.path.insert(0, os.path.join(location, '..', 'bindings', 'python-cffi', 'build', 'stage')) # read generated config for pathdir in ['.', '..']: From 59a778ae4bae182101c43f1acde08452a6542299 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Fri, 3 Dec 2021 20:47:05 -0400 Subject: [PATCH 2/9] test/libconfig: use 'export' for remaining sets of NOTMUCH_CONFIG It makes the tests easier to understand if we always use export for environment variables. --- test/T590-libconfig.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/T590-libconfig.sh b/test/T590-libconfig.sh index 8db51ed0..891ffb4f 100755 --- a/test/T590-libconfig.sh +++ b/test/T590-libconfig.sh @@ -876,13 +876,13 @@ test_expect_equal_file EXPECTED OUTPUT test_begin_subtest "open: database set to null on missing config (env)" old_NOTMUCH_CONFIG=${NOTMUCH_CONFIG} -NOTMUCH_CONFIG="/nonexistent" +export NOTMUCH_CONFIG="/nonexistent" cat c_head3 - c_tail3 <<'EOF' | test_C ${MAIL_DIR} notmuch_status_t st = notmuch_database_open_with_config(argv[1], NOTMUCH_DATABASE_MODE_READ_ONLY, NULL, NULL, &db, NULL); EOF -NOTMUCH_CONFIG=${old_NOTMUCH_CONFIG} +export NOTMUCH_CONFIG=${old_NOTMUCH_CONFIG} cat < EXPECTED == stdout == db == NULL: 1 @@ -903,12 +903,12 @@ test_expect_equal_file EXPECTED OUTPUT test_begin_subtest "create: database set to null on missing config (env)" old_NOTMUCH_CONFIG=${NOTMUCH_CONFIG} -NOTMUCH_CONFIG="/nonexistent" +export NOTMUCH_CONFIG="/nonexistent" cat c_head3 - c_tail3 <<'EOF' | test_C ${MAIL_DIR} notmuch_status_t st = notmuch_database_create_with_config(argv[1], NULL, NULL, &db, NULL); EOF -NOTMUCH_CONFIG=${old_NOTMUCH_CONFIG} +export NOTMUCH_CONFIG=${old_NOTMUCH_CONFIG} cat < EXPECTED == stdout == db == NULL: 1 @@ -929,11 +929,11 @@ test_expect_equal_file EXPECTED OUTPUT test_begin_subtest "load_config: database non-null on missing config (env)" old_NOTMUCH_CONFIG=${NOTMUCH_CONFIG} -NOTMUCH_CONFIG="/nonexistent" +export NOTMUCH_CONFIG="/nonexistent" cat c_head3 - c_tail3 <<'EOF' | test_C ${MAIL_DIR} notmuch_status_t st = notmuch_database_load_config(argv[1], NULL, NULL, &db, NULL); EOF -NOTMUCH_CONFIG=${old_NOTMUCH_CONFIG} +export NOTMUCH_CONFIG=${old_NOTMUCH_CONFIG} cat < EXPECTED == stdout == db == NULL: 0 From 378415a55762ff0222e3de6a2e20a9823ce1d07e Mon Sep 17 00:00:00 2001 From: David Bremner Date: Wed, 27 Oct 2021 22:34:17 -0300 Subject: [PATCH 3/9] test: add known broken test for conflict with database parameter This is arguably user error: having configuration file with bad settings in it (and/or having a bad NOTMUCH_CONFIG environment variable). On the other hand returning a different path than was actually opened is definitely a bug. --- test/T590-libconfig.sh | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/test/T590-libconfig.sh b/test/T590-libconfig.sh index 891ffb4f..0b3e54f3 100755 --- a/test/T590-libconfig.sh +++ b/test/T590-libconfig.sh @@ -952,4 +952,26 @@ db == NULL: 1 EOF test_expect_equal_file EXPECTED OUTPUT +test_begin_subtest "open: database parameter overrides implicit config" +test_subtest_known_broken +notmuch config set database.path ${MAIL_DIR}/nonexistent +cat c_head3 - c_tail3 <<'EOF' | test_C ${MAIL_DIR} + const char *path = NULL; + notmuch_status_t st = notmuch_database_open_with_config(argv[1], + NOTMUCH_DATABASE_MODE_READ_ONLY, + NULL, NULL, &db, NULL); + printf ("status: %d\n", st); + path = notmuch_database_get_path (db); + printf ("path: %s\n", path ? path : "(null)"); +EOF +cat < EXPECTED +== stdout == +status: 0 +path: MAIL_DIR +db == NULL: 0 +== stderr == +EOF +notmuch_dir_sanitize < OUTPUT > OUTPUT.clean +test_expect_equal_file EXPECTED OUTPUT.clean + test_done From d467444085d96229172ce29ef8654c3c459b6ce2 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Wed, 27 Oct 2021 22:34:18 -0300 Subject: [PATCH 4/9] lib/open: track which parameters are passed This will be used to fine tune the loading of configuration for certain special configuration items (initially just "database.path"). --- lib/database-private.h | 36 ++++++++++++++++++++++++++++++++++++ lib/open.cc | 17 +++++++++++++---- 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/lib/database-private.h b/lib/database-private.h index 8b9d67fe..8dd77281 100644 --- a/lib/database-private.h +++ b/lib/database-private.h @@ -190,6 +190,39 @@ operator& (notmuch_field_flag_t a, notmuch_field_flag_t b) Xapian::QueryParser::FLAG_WILDCARD | \ Xapian::QueryParser::FLAG_PURE_NOT) +/* + * Which parameters were explicit when the database was opened */ +typedef enum { + NOTMUCH_PARAM_NONE = 0, + NOTMUCH_PARAM_DATABASE = 1 << 0, + NOTMUCH_PARAM_CONFIG = 1 << 1, + NOTMUCH_PARAM_PROFILE = 1 << 2, +} notmuch_open_param_t; + +/* + * define bitwise operators to hide casts */ + +inline notmuch_open_param_t +operator| (notmuch_open_param_t a, notmuch_open_param_t b) +{ + return static_cast( + static_cast(a) | static_cast(b)); +} + +inline notmuch_open_param_t& +operator|= (notmuch_open_param_t &a, notmuch_open_param_t b) +{ + a = a | b; + return a; +} + +inline notmuch_open_param_t +operator& (notmuch_open_param_t a, notmuch_open_param_t b) +{ + return static_cast( + static_cast(a) & static_cast(b)); +} + struct _notmuch_database { bool exception_reported; @@ -249,6 +282,9 @@ struct _notmuch_database { /* Cached and possibly overridden configuration */ notmuch_string_map_t *config; + + /* Track what parameters were specified when opening */ + notmuch_open_param_t params; }; /* Prior to database version 3, features were implied by the database diff --git a/lib/open.cc b/lib/open.cc index ba32c2f1..a942383b 100644 --- a/lib/open.cc +++ b/lib/open.cc @@ -247,7 +247,7 @@ _choose_database_path (void *ctx, } static notmuch_database_t * -_alloc_notmuch () +_alloc_notmuch (const char *database_path, const char *config_path, const char *profile) { notmuch_database_t *notmuch; @@ -263,6 +263,15 @@ _alloc_notmuch () notmuch->transaction_count = 0; notmuch->transaction_threshold = 0; notmuch->view = 1; + + notmuch->params = NOTMUCH_PARAM_NONE; + if (database_path) + notmuch->params |= NOTMUCH_PARAM_DATABASE; + if (config_path) + notmuch->params |= NOTMUCH_PARAM_CONFIG; + if (profile) + notmuch->params |= NOTMUCH_PARAM_PROFILE; + return notmuch; } @@ -510,7 +519,7 @@ notmuch_database_open_with_config (const char *database_path, _notmuch_init (); - notmuch = _alloc_notmuch (); + notmuch = _alloc_notmuch (database_path, config_path, profile); if (! notmuch) { status = NOTMUCH_STATUS_OUT_OF_MEMORY; goto DONE; @@ -610,7 +619,7 @@ notmuch_database_create_with_config (const char *database_path, _notmuch_init (); - notmuch = _alloc_notmuch (); + notmuch = _alloc_notmuch (database_path, config_path, profile); if (! notmuch) { status = NOTMUCH_STATUS_OUT_OF_MEMORY; goto DONE; @@ -812,7 +821,7 @@ notmuch_database_load_config (const char *database_path, _notmuch_init (); - notmuch = _alloc_notmuch (); + notmuch = _alloc_notmuch (database_path, config_path, profile); if (! notmuch) { status = NOTMUCH_STATUS_OUT_OF_MEMORY; goto DONE; From 59aac9cef37a9937bdf2265078d063673111f80b Mon Sep 17 00:00:00 2001 From: David Bremner Date: Wed, 27 Oct 2021 22:34:19 -0300 Subject: [PATCH 5/9] lib/config: don't overwrite database.path if the caller passed it If the user passed a path, and we opened it, then we consider that definitive definition of "database.path". This makes libnotmuch respond more gracefully to certain erroneous combinations of NOTMUCH_CONFIG settings and config file contents. --- lib/config.cc | 17 ++++++++++++++++- test/T590-libconfig.sh | 1 - 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/config.cc b/lib/config.cc index 8775b00a..e502858d 100644 --- a/lib/config.cc +++ b/lib/config.cc @@ -259,7 +259,15 @@ _notmuch_config_load_from_database (notmuch_database_t *notmuch) for (; notmuch_config_list_valid (list); notmuch_config_list_move_to_next (list)) { const char *key = notmuch_config_list_key (list); - char *normalized_val = _expand_path (list, key, notmuch_config_list_value (list)); + char *normalized_val = NULL; + + /* If we opened from a given path, do not overwrite it */ + if (strcmp (key, "database.path") == 0 && + (notmuch->params & NOTMUCH_PARAM_DATABASE) && + notmuch->xapian_db) + continue; + + normalized_val = _expand_path (list, key, notmuch_config_list_value (list)); _notmuch_string_map_append (notmuch->config, key, normalized_val); talloc_free (normalized_val); } @@ -432,6 +440,13 @@ _notmuch_config_load_from_file (notmuch_database_t *notmuch, status = NOTMUCH_STATUS_FILE_ERROR; goto DONE; } + + /* If we opened from a given path, do not overwrite it */ + if (strcmp (absolute_key, "database.path") == 0 && + (notmuch->params & NOTMUCH_PARAM_DATABASE) && + notmuch->xapian_db) + continue; + normalized_val = _expand_path (notmuch, absolute_key, val); _notmuch_string_map_set (notmuch->config, absolute_key, normalized_val); g_free (val); diff --git a/test/T590-libconfig.sh b/test/T590-libconfig.sh index 0b3e54f3..9fa51fc0 100755 --- a/test/T590-libconfig.sh +++ b/test/T590-libconfig.sh @@ -953,7 +953,6 @@ EOF test_expect_equal_file EXPECTED OUTPUT test_begin_subtest "open: database parameter overrides implicit config" -test_subtest_known_broken notmuch config set database.path ${MAIL_DIR}/nonexistent cat c_head3 - c_tail3 <<'EOF' | test_C ${MAIL_DIR} const char *path = NULL; From 4b361f4d35df92bb2c679143adedf4b88288648c Mon Sep 17 00:00:00 2001 From: David Bremner Date: Sat, 30 Oct 2021 13:22:32 -0300 Subject: [PATCH 6/9] python-cffi: fix typos in docstring for Database.default_path These generate warnings from sphinx doc, which makes it harder to debug documentation changes. They also corrupt the output. --- bindings/python-cffi/notmuch2/_database.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bindings/python-cffi/notmuch2/_database.py b/bindings/python-cffi/notmuch2/_database.py index 868f4408..c1fb88eb 100644 --- a/bindings/python-cffi/notmuch2/_database.py +++ b/bindings/python-cffi/notmuch2/_database.py @@ -187,8 +187,8 @@ class Database(base.NotmuchObject): :param cfg_path: The pathname of the notmuch configuration file. If not specified tries to use the pathname provided in the - :env:`NOTMUCH_CONFIG` environment variable and falls back - to :file:`~/.notmuch-config. + :envvar:`NOTMUCH_CONFIG` environment variable and falls back + to :file:`~/.notmuch-config`. :type cfg_path: str, bytes, os.PathLike or pathlib.Path. :returns: The path of the database, which does not necessarily @@ -198,7 +198,7 @@ class Database(base.NotmuchObject): be opened. :raises configparser.Error: or subclasses if the configuration file can not be parsed. - :raises NotmuchError if the config file does not have the + :raises NotmuchError: if the config file does not have the database.path setting. """ if not cfg_path: From 5f077bef37462267258f95f7400596a157878b14 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Sat, 30 Oct 2021 13:22:33 -0300 Subject: [PATCH 7/9] test: add python-cffi bindings to path for test_python This will allow testing the new python bindings using test_python, in addition to the current invocation of pytest. --- test/test-lib.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test-lib.sh b/test/test-lib.sh index 32f710a5..e476a69b 100644 --- a/test/test-lib.sh +++ b/test/test-lib.sh @@ -909,7 +909,7 @@ test_done () { test_python () { # Note: if there is need to print debug information from python program, # use stdout = os.fdopen(6, 'w') or stderr = os.fdopen(7, 'w') - PYTHONPATH="$NOTMUCH_SRCDIR/bindings/python${PYTHONPATH:+:$PYTHONPATH}" \ + PYTHONPATH="$NOTMUCH_BUILDDIR/bindings/python-cffi/build/stage:$NOTMUCH_SRCDIR/bindings/python${PYTHONPATH:+:$PYTHONPATH}" \ $NOTMUCH_PYTHON -B - > OUTPUT } From caafab01a4409fd2a1a5d31b3d05815bf587d8e0 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Sat, 30 Oct 2021 13:22:34 -0300 Subject: [PATCH 8/9] test: add known broken tests for python bindings in split configs This reproduces the bug(s) reported in id:87h7d4wp6b.fsf@tethera.net --- test/T055-path-config.sh | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/test/T055-path-config.sh b/test/T055-path-config.sh index ef22e964..d6494b92 100755 --- a/test/T055-path-config.sh +++ b/test/T055-path-config.sh @@ -306,7 +306,24 @@ EOF output2=$(notmuch --config='' config get ${key}) notmuch config set ${key} test_expect_equal "${output}+${output2}" "${value}+" - ;; + ;& + split) + test_begin_subtest "'to' header does not crash (python-cffi) ($config)" + test_subtest_known_broken + echo 'notmuch@notmuchmail.org' > EXPECTED + test_python < Date: Sat, 30 Oct 2021 13:22:35 -0300 Subject: [PATCH 9/9] python-cffi: switch to notmuch_database_{open,create}_with_config Since release 0.32, libnotmuch provides searching for database and configuration paths. This commit changes the python module notmuch2 to use those facilities. This fixes the bug reported in [1], along with a couple of the deprecation warnings in the python bindings. Database.default_path is deprecated, since it no longer faithfully reflects what libnotmuch is doing, and it is also no longer used in the bindings themselves. This commit choose the default of config=CONFIG.EMPTY (equivalent to passing "" to notmuch_database_open_with_config). This makes the change upward compatible API-wise (at least as far as the test suite verifies), but changing the default to CONFIG.SEARCH would probably be more convenient for bindings users. [1]: id:87h7d4wp6b.fsf@tethera.net --- bindings/python-cffi/notmuch2/_build.py | 24 +++---- bindings/python-cffi/notmuch2/_database.py | 78 ++++++++++++++++------ doc/man1/notmuch-config.rst | 2 + test/T055-path-config.sh | 5 +- test/T391-python-cffi.sh | 8 ++- 5 files changed, 78 insertions(+), 39 deletions(-) diff --git a/bindings/python-cffi/notmuch2/_build.py b/bindings/python-cffi/notmuch2/_build.py index 24df939e..f6184b97 100644 --- a/bindings/python-cffi/notmuch2/_build.py +++ b/bindings/python-cffi/notmuch2/_build.py @@ -103,20 +103,18 @@ ffibuilder.cdef( notmuch_status_to_string (notmuch_status_t status); notmuch_status_t - notmuch_database_create_verbose (const char *path, - notmuch_database_t **database, - char **error_message); + notmuch_database_create_with_config (const char *database_path, + const char *config_path, + const char *profile, + notmuch_database_t **database, + char **error_message); notmuch_status_t - notmuch_database_create (const char *path, notmuch_database_t **database); - notmuch_status_t - notmuch_database_open_verbose (const char *path, - notmuch_database_mode_t mode, - notmuch_database_t **database, - char **error_message); - notmuch_status_t - notmuch_database_open (const char *path, - notmuch_database_mode_t mode, - notmuch_database_t **database); + notmuch_database_open_with_config (const char *database_path, + notmuch_database_mode_t mode, + const char *config_path, + const char *profile, + notmuch_database_t **database, + char **error_message); notmuch_status_t notmuch_database_close (notmuch_database_t *database); notmuch_status_t diff --git a/bindings/python-cffi/notmuch2/_database.py b/bindings/python-cffi/notmuch2/_database.py index c1fb88eb..14a8f15c 100644 --- a/bindings/python-cffi/notmuch2/_database.py +++ b/bindings/python-cffi/notmuch2/_database.py @@ -31,6 +31,9 @@ class Mode(enum.Enum): READ_ONLY = capi.lib.NOTMUCH_DATABASE_MODE_READ_ONLY READ_WRITE = capi.lib.NOTMUCH_DATABASE_MODE_READ_WRITE +class ConfigFile(enum.Enum): + EMPTY = b'' + SEARCH = capi.ffi.NULL class QuerySortOrder(enum.Enum): OLDEST_FIRST = capi.lib.NOTMUCH_SORT_OLDEST_FIRST @@ -71,6 +74,9 @@ class Database(base.NotmuchObject): :cvar EXCLUDE: Which messages to exclude from queries, ``TRUE``, ``FLAG``, ``FALSE`` or ``ALL``. See the query documentation for details. + :cvar CONFIG: Control loading of config file. Enumeration of + ``EMPTY`` (don't load a config file), and ``SEARCH`` (search as + in :ref:`config_search`) :cvar AddedMessage: A namedtuple ``(msg, dup)`` used by :meth:`add` as return value. :cvar STR_MODE_MAP: A map mapping strings to :attr:`MODE` items. @@ -81,9 +87,8 @@ class Database(base.NotmuchObject): still open. :param path: The directory of where the database is stored. If - ``None`` the location will be read from the user's - configuration file, respecting the ``NOTMUCH_CONFIG`` - environment variable if set. + ``None`` the location will be searched according to + :ref:`database` :type path: str, bytes, os.PathLike or pathlib.Path :param mode: The mode to open the database in. One of :attr:`MODE.READ_ONLY` OR :attr:`MODE.READ_WRITE`. For @@ -91,6 +96,8 @@ class Database(base.NotmuchObject): :attr:`MODE.READ_ONLY` and ``rw`` for :attr:`MODE.READ_WRITE`. :type mode: :attr:`MODE` or str. + :param config: Where to load the configuration from, if any. + :type config: :attr:`CONFIG.EMPTY`, :attr:`CONFIG.SEARCH`, str, bytes, os.PathLike, pathlib.Path :raises KeyError: if an unknown mode string is used. :raises OSError: or subclasses if the configuration file can not be opened. @@ -102,6 +109,7 @@ class Database(base.NotmuchObject): MODE = Mode SORT = QuerySortOrder EXCLUDE = QueryExclude + CONFIG = ConfigFile AddedMessage = collections.namedtuple('AddedMessage', ['msg', 'dup']) _db_p = base.MemoryPointer() STR_MODE_MAP = { @@ -109,18 +117,40 @@ class Database(base.NotmuchObject): 'rw': MODE.READ_WRITE, } - def __init__(self, path=None, mode=MODE.READ_ONLY): + @staticmethod + def _cfg_path_encode(path): + if isinstance(path,ConfigFile): + path = path.value + elif path is None: + path = capi.ffi.NULL + elif not hasattr(os, 'PathLike') and isinstance(path, pathlib.Path): + path = bytes(path) + else: + path = os.fsencode(path) + return path + + @staticmethod + def _db_path_encode(path): + if path is None: + path = capi.ffi.NULL + elif not hasattr(os, 'PathLike') and isinstance(path, pathlib.Path): + path = bytes(path) + else: + path = os.fsencode(path) + return path + + def __init__(self, path=None, mode=MODE.READ_ONLY, config=CONFIG.EMPTY): if isinstance(mode, str): mode = self.STR_MODE_MAP[mode] self.mode = mode - if path is None: - path = self.default_path() - if not hasattr(os, 'PathLike') and isinstance(path, pathlib.Path): - path = bytes(path) + db_pp = capi.ffi.new('notmuch_database_t **') cmsg = capi.ffi.new('char**') - ret = capi.lib.notmuch_database_open_verbose(os.fsencode(path), - mode.value, db_pp, cmsg) + ret = capi.lib.notmuch_database_open_with_config(self._db_path_encode(path), + mode.value, + self._cfg_path_encode(config), + capi.ffi.NULL, + db_pp, cmsg) if cmsg[0]: msg = capi.ffi.string(cmsg[0]).decode(errors='replace') capi.lib.free(cmsg[0]) @@ -132,18 +162,20 @@ class Database(base.NotmuchObject): self.closed = False @classmethod - def create(cls, path=None): + def create(cls, path=None, config=ConfigFile.EMPTY): """Create and open database in READ_WRITE mode. This is creates a new notmuch database and returns an opened instance in :attr:`MODE.READ_WRITE` mode. - :param path: The directory of where the database is stored. If - ``None`` the location will be read from the user's - configuration file, respecting the ``NOTMUCH_CONFIG`` - environment variable if set. + :param path: The directory of where the database is stored. + If ``None`` the location will be read searched by the + notmuch library (see notmuch(3)::notmuch_open_with_config). :type path: str, bytes or os.PathLike + :param config: The pathname of the notmuch configuration file. + :type config: :attr:`CONFIG.EMPTY`, :attr:`CONFIG.SEARCH`, str, bytes, os.PathLike, pathlib.Path + :raises OSError: or subclasses if the configuration file can not be opened. :raises configparser.Error: or subclasses if the configuration @@ -154,14 +186,13 @@ class Database(base.NotmuchObject): :returns: The newly created instance. """ - if path is None: - path = cls.default_path() - if not hasattr(os, 'PathLike') and isinstance(path, pathlib.Path): - path = bytes(path) + db_pp = capi.ffi.new('notmuch_database_t **') cmsg = capi.ffi.new('char**') - ret = capi.lib.notmuch_database_create_verbose(os.fsencode(path), - db_pp, cmsg) + ret = capi.lib.notmuch_database_create_with_config(cls._db_path_encode(path), + cls._cfg_path_encode(config), + capi.ffi.NULL, + db_pp, cmsg) if cmsg[0]: msg = capi.ffi.string(cmsg[0]).decode(errors='replace') capi.lib.free(cmsg[0]) @@ -176,7 +207,7 @@ class Database(base.NotmuchObject): ret = capi.lib.notmuch_database_destroy(db_pp[0]) if ret != capi.lib.NOTMUCH_STATUS_SUCCESS: raise errors.NotmuchError(ret) - return cls(path, cls.MODE.READ_WRITE) + return cls(path, cls.MODE.READ_WRITE, config=config) @staticmethod def default_path(cfg_path=None): @@ -200,6 +231,9 @@ class Database(base.NotmuchObject): file can not be parsed. :raises NotmuchError: if the config file does not have the database.path setting. + + .. deprecated:: 0.35 + Use the ``config`` parameter to :meth:`__init__` or :meth:`__create__` instead. """ if not cfg_path: cfg_path = _config_pathname() diff --git a/doc/man1/notmuch-config.rst b/doc/man1/notmuch-config.rst index 7d901758..36e57ea6 100644 --- a/doc/man1/notmuch-config.rst +++ b/doc/man1/notmuch-config.rst @@ -259,6 +259,8 @@ paths are presumed relative to `$HOME` for items in section FILES ===== +.. _config_search: + CONFIGURATION ------------- diff --git a/test/T055-path-config.sh b/test/T055-path-config.sh index d6494b92..6d9fb402 100755 --- a/test/T055-path-config.sh +++ b/test/T055-path-config.sh @@ -309,11 +309,10 @@ EOF ;& split) test_begin_subtest "'to' header does not crash (python-cffi) ($config)" - test_subtest_known_broken echo 'notmuch@notmuchmail.org' > EXPECTED test_python < $pytest_dir/pytest.ini test_expect_success "(cd $pytest_dir && ${NOTMUCH_PYTHON} -m pytest --verbose --log-file=$TMP_DIRECTORY/test.output)" + +test_begin_subtest "python cffi tests (NOTMUCH_CONFIG unset)" +pytest_dir=$NOTMUCH_BUILDDIR/bindings/python-cffi/build/stage +printf "[pytest]\nminversion = 3.0\naddopts = -ra\n" > $pytest_dir/pytest.ini +unset NOTMUCH_CONFIG +test_expect_success "(cd $pytest_dir && ${NOTMUCH_PYTHON} -m pytest --verbose --log-file=$TMP_DIRECTORY/test.output)" test_done