From c79c50d729b6eb27367ff58136f1b589682b4065 Mon Sep 17 00:00:00 2001 From: Pietro Albini Date: Fri, 1 Sep 2023 10:15:55 +0200 Subject: [PATCH] Fix ordering inside searchindex.js not being deterministic Fixes #11622 --- CHANGES.rst | 1 + sphinx/search/__init__.py | 2 +- tests/test_search.py | 20 ++++++++++++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 10abb27b452..2c3961ec42b 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -18,6 +18,7 @@ Bugs fixed * #11668: Raise a useful error when ``theme.conf`` is missing. Patch by Vinay Sajip. +* #11622: Fix ordering inside searchindex.js not being deterministic. Testing ------- diff --git a/sphinx/search/__init__.py b/sphinx/search/__init__.py index 21758d3f424..114d7fe9072 100644 --- a/sphinx/search/__init__.py +++ b/sphinx/search/__init__.py @@ -162,7 +162,7 @@ class _JavaScriptIndex: SUFFIX = ')' def dumps(self, data: Any) -> str: - return self.PREFIX + json.dumps(data) + self.SUFFIX + return self.PREFIX + json.dumps(data, sort_keys=True) + self.SUFFIX def loads(self, s: str) -> Any: data = s[len(self.PREFIX):-len(self.SUFFIX)] diff --git a/tests/test_search.py b/tests/test_search.py index 68a7b01aa55..0fdc0642343 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -304,3 +304,23 @@ def test_parallel(app): app.build() index = load_searchindex(app.outdir / 'searchindex.js') assert index['docnames'] == ['index', 'nosearch', 'tocitem'] + + +@pytest.mark.sphinx(testroot='search') +def test_search_index_is_deterministic(app): + app.builder.build_all() + index = load_searchindex(app.outdir / 'searchindex.js') + + def check_if_dicts_are_sorted(item): + if isinstance(item, dict): + assert list(item.keys()) == sorted(item.keys()) + for child in item.values(): + check_if_dicts_are_sorted(child) + # Lists in the search index cannot be sorted: for some lists, their + # position inside the list is referenced elsewhere in the index, so if + # we were to sort lists, the search index would break. + elif isinstance(item, list): + for child in item: + check_if_dicts_are_sorted(child) + + check_if_dicts_are_sorted(index)