File 12870.patch of Package python-Sphinx

From bd2cafa2746280dd501981c7e132a3ebb26fbafc Mon Sep 17 00:00:00 2001
From: James Addison <jay@jp-hosting.net>
Date: Sun, 8 Sep 2024 15:04:07 +0100
Subject: [PATCH] Environment.file_to_rebuild: pickle to sorted `list` instead
 of `set`

---
 pyproject.toml                                   |  1 +
 sphinx/environment/__init__.py                   |  6 +++---
 sphinx/environment/adapters/toctree.py           |  5 ++++-
 sphinx/environment/collectors/toctree.py         | 16 +++++++++++-----
 tests/js/fixtures/cpp/searchindex.js             |  2 +-
 tests/js/fixtures/multiterm/searchindex.js       |  2 +-
 tests/js/fixtures/partial/searchindex.js         |  2 +-
 tests/js/fixtures/titles/searchindex.js          |  2 +-
 .../test_environment/test_environment_toctree.py |  6 +++---
 9 files changed, 26 insertions(+), 16 deletions(-)

diff --git a/pyproject.toml b/pyproject.toml
index 93a3dd405ea..cf5693285cd 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -70,6 +70,7 @@ dependencies = [
     "imagesize>=1.3",
     "requests>=2.30.0",
     "packaging>=23.0",
+    "sortedcontainers>=2.4.0",
     "tomli>=2; python_version < '3.11'",
     "colorama>=0.4.6; sys_platform == 'win32'",
 ]
diff --git a/sphinx/environment/__init__.py b/sphinx/environment/__init__.py
index 67087e61829..c19d6fb3fac 100644
--- a/sphinx/environment/__init__.py
+++ b/sphinx/environment/__init__.py
@@ -66,7 +66,7 @@
 
 # This is increased every time an environment attribute is added
 # or changed to properly invalidate pickle files.
-ENV_VERSION = 63
+ENV_VERSION = 64
 
 # config status
 CONFIG_UNSET = -1
@@ -232,7 +232,7 @@ def __init__(self, app: Sphinx) -> None:
         # docname -> list of toctree includefiles
         self.toctree_includes: dict[str, list[str]] = {}
         # docname -> set of files (containing its TOCs) to rebuild too
-        self.files_to_rebuild: dict[str, set[str]] = {}
+        self.files_to_rebuild: dict[str, list[str]] = {}
         # docnames that have :glob: toctrees
         self.glob_toctrees: set[str] = set()
         # docnames that have :numbered: toctrees
@@ -747,7 +747,7 @@ def check_consistency(self) -> None:
         """Do consistency checks."""
         included = set().union(*self.included.values())
         for docname in sorted(self.all_docs):
-            if docname not in self.files_to_rebuild:
+            if docname not in self.files_to_rebuild:  # TODO: SortedList (?)
                 if docname == self.config.root_doc:
                     # the master file is not included anywhere ;)
                     continue
diff --git a/sphinx/environment/adapters/toctree.py b/sphinx/environment/adapters/toctree.py
index 217cb0d41ef..e7c532e5bbf 100644
--- a/sphinx/environment/adapters/toctree.py
+++ b/sphinx/environment/adapters/toctree.py
@@ -6,6 +6,7 @@
 
 from docutils import nodes
 from docutils.nodes import Element, Node
+from sortedcontainers import SortedList
 
 from sphinx import addnodes
 from sphinx.locale import __
@@ -36,7 +37,9 @@ def note_toctree(env: BuildEnvironment, docname: str, toctreenode: addnodes.toct
     for include_file in include_files:
         # note that if the included file is rebuilt, this one must be
         # too (since the TOC of the included file could have changed)
-        env.files_to_rebuild.setdefault(include_file, set()).add(docname)
+        fnlist = SortedList(env.files_to_rebuild.get(include_file, []))
+        fnlist.add(docname)
+        env.files_to_rebuild[include_file] = list(fnlist)
     env.toctree_includes.setdefault(docname, []).extend(include_files)
 
 
diff --git a/sphinx/environment/collectors/toctree.py b/sphinx/environment/collectors/toctree.py
index e3f80f25155..5f71ce0c66c 100644
--- a/sphinx/environment/collectors/toctree.py
+++ b/sphinx/environment/collectors/toctree.py
@@ -5,6 +5,7 @@
 from typing import TYPE_CHECKING, TypeVar, cast
 
 from docutils import nodes
+from sortedcontainers import SortedList
 
 from sphinx import addnodes
 from sphinx.environment.adapters.toctree import note_toctree
@@ -37,9 +38,12 @@ def clear_doc(self, app: Sphinx, env: BuildEnvironment, docname: str) -> None:
         env.glob_toctrees.discard(docname)
         env.numbered_toctrees.discard(docname)
 
-        for subfn, fnset in list(env.files_to_rebuild.items()):
-            fnset.discard(docname)
-            if not fnset:
+        for subfn, fnlist in list(env.files_to_rebuild.items()):
+            try:
+                fnlist.remove(docname)
+            except ValueError:
+                pass
+            if not fnlist:
                 del env.files_to_rebuild[subfn]
 
     def merge_other(self, app: Sphinx, env: BuildEnvironment, docnames: set[str],
@@ -54,8 +58,10 @@ def merge_other(self, app: Sphinx, env: BuildEnvironment, docnames: set[str],
             if docname in other.numbered_toctrees:
                 env.numbered_toctrees.add(docname)
 
-        for subfn, fnset in other.files_to_rebuild.items():
-            env.files_to_rebuild.setdefault(subfn, set()).update(fnset & set(docnames))
+        for subfn, other_fnlist in other.files_to_rebuild.items():
+            fnlist = SortedList(env.files_to_rebuild.get(subfn, []))
+            fnlist.update(frozenset(other_fnlist) & docnames)
+            env.files_to_rebuild[subfn] = list(fnlist)
 
     def process_doc(self, app: Sphinx, doctree: nodes.document) -> None:
         """Build a TOC from the doctree and store it in the inventory."""
+Search.setIndex({"alltitles": {"Main Page": [[0, null]], "Relevance": [[0, "relevance"], [1, null]], "Result Scoring": [[0, "result-scoring"]]}, "docnames": ["index", "relevance"], "envversion": {"sphinx": 64, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["index.rst", "relevance.rst"], "indexentries": {"example (class in relevance)": [[0, "relevance.Example", false]], "module": [[0, "module-relevance", false]], "relevance": [[0, "index-1", false], [0, "module-relevance", false]], "relevance (relevance.example attribute)": [[0, "relevance.Example.relevance", false]], "scoring": [[0, "index-0", true]]}, "objects": {"": [[0, 0, 0, "-", "relevance"]], "relevance": [[0, 1, 1, "", "Example"]], "relevance.Example": [[0, 2, 1, "", "relevance"]]}, "objnames": {"0": ["py", "module", "Python module"], "1": ["py", "class", "Python class"], "2": ["py", "attribute", "Python attribute"]}, "objtypes": {"0": "py:module", "1": "py:class", "2": "py:attribute"}, "terms": {"": [0, 1], "A": 1, "By": 0, "For": [0, 1], "In": [0, 1], "against": 0, "align": 0, "also": 1, "an": 0, "answer": 0, "appear": 1, "ar": 1, "area": 0, "ask": 0, "assign": 0, "attempt": 0, "attribut": 0, "both": 0, "built": 1, "can": [0, 1], "class": 0, "code": [0, 1], "collect": 0, "consid": 1, "contain": 0, "context": 0, "corpu": 1, "could": 1, "demonstr": 0, "describ": 1, "detail": 1, "determin": [0, 1], "docstr": 0, "document": [0, 1], "domain": 1, "dure": 0, "engin": 0, "evalu": 0, "exampl": [0, 1], "extract": 0, "feedback": 0, "find": 0, "found": 0, "from": 0, "function": 1, "ha": 1, "handl": 0, "happen": 1, "head": 0, "help": 0, "highli": [0, 1], "how": 0, "i": [0, 1], "improv": 0, "inform": 0, "intend": 0, "issu": [0, 1], "itself": 1, "knowledg": 0, "languag": 1, "less": 1, "like": [0, 1], "mani": 0, "match": 0, "mention": 1, "more": 0, "name": [0, 1], "numer": 0, "object": 0, "often": 0, "one": [0, 1], "onli": [0, 1], "order": 0, "other": 0, "over": 0, "page": 1, "part": 1, "particular": 0, "present": 0, "printf": 1, "program": 1, "project": 0, "queri": [0, 1], "question": 0, "re": 0, "rel": 0, "research": 0, "result": 1, "retriev": 0, "sai": 0, "same": 1, "search": [0, 1], "seem": 0, "softwar": 1, "some": 1, "sphinx": 0, "straightforward": 1, "subject": 0, "subsect": 0, "term": [0, 1], "test": 0, "text": 0, "than": [0, 1], "thei": 0, "them": 0, "thi": 0, "time": 0, "titl": 0, "two": 0, "typic": 0, "us": 0, "user": [0, 1], "we": [0, 1], "when": 0, "whether": 1, "which": 0, "within": 0, "word": 0, "would": [0, 1]}, "titles": ["Main Page", "Relevance"], "titleterms": {"main": 0, "page": 0, "relev": [0, 1], "result": 0, "score": 0}})
\ No newline at end of file
diff --git a/tests/test_environment/test_environment_toctree.py b/tests/test_environment/test_environment_toctree.py
index 7f283c42231..49825b58be1 100644
--- a/tests/test_environment/test_environment_toctree.py
+++ b/tests/test_environment/test_environment_toctree.py
@@ -144,9 +144,9 @@ def test_process_doc(app):
     # other collections
     assert app.env.toc_num_entries['index'] == 6
     assert app.env.toctree_includes['index'] == ['foo', 'bar', 'baz']
-    assert app.env.files_to_rebuild['foo'] == {'index'}
-    assert app.env.files_to_rebuild['bar'] == {'index'}
-    assert app.env.files_to_rebuild['baz'] == {'index'}
+    assert app.env.files_to_rebuild['foo'] == ['index']
+    assert app.env.files_to_rebuild['bar'] == ['index']
+    assert app.env.files_to_rebuild['baz'] == ['index']
     assert app.env.glob_toctrees == set()
     assert app.env.numbered_toctrees == {'index'}
 
openSUSE Build Service is sponsored by