From b75e6956314036811816cd6eb83a3dc1b970ead5 Mon Sep 17 00:00:00 2001 From: James Addison <55152140+jayaddison@users.noreply.github.com> Date: Thu, 24 Oct 2024 16:54:41 +0000 Subject: [PATCH 1/9] Write 'searchindex.js' using minimal JSON separators (#13062) Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- sphinx/search/__init__.py | 3 ++- 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 +- tests/test_search.py | 4 ++-- 6 files changed, 8 insertions(+), 7 deletions(-) diff --git a/sphinx/search/__init__.py b/sphinx/search/__init__.py index fce21edc482..82081cbe2e9 100644 --- a/sphinx/search/__init__.py +++ b/sphinx/search/__init__.py @@ -164,7 +164,8 @@ class _JavaScriptIndex: SUFFIX = ')' def dumps(self, data: Any) -> str: - return self.PREFIX + json.dumps(data, sort_keys=True) + self.SUFFIX + data_json = json.dumps(data, separators=(',', ':'), sort_keys=True) + return self.PREFIX + data_json + self.SUFFIX def loads(self, s: str) -> Any: data = s[len(self.PREFIX) : -len(self.SUFFIX)] diff --git a/tests/js/fixtures/cpp/searchindex.js b/tests/js/fixtures/cpp/searchindex.js index 989c877a8cc..e5837e65d56 100644 --- a/tests/js/fixtures/cpp/searchindex.js +++ b/tests/js/fixtures/cpp/searchindex.js @@ -1 +1 @@ -Search.setIndex({"alltitles": {}, "docnames": ["index"], "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"], "indexentries": {"sphinx (c++ class)": [[0, "_CPPv46Sphinx", false]]}, "objects": {"": [[0, 0, 1, "_CPPv46Sphinx", "Sphinx"]]}, "objnames": {"0": ["cpp", "class", "C++ class"]}, "objtypes": {"0": "cpp:class"}, "terms": {"The": 0, "becaus": 0, "c": 0, "can": 0, "cardin": 0, "challeng": 0, "charact": 0, "class": 0, "descript": 0, "drop": 0, "engin": 0, "fixtur": 0, "frequent": 0, "gener": 0, "i": 0, "index": 0, "inflat": 0, "mathemat": 0, "occur": 0, "often": 0, "project": 0, "punctuat": 0, "queri": 0, "relat": 0, "sampl": 0, "search": 0, "size": 0, "sphinx": 0, "term": 0, "thei": 0, "thi": 0, "token": 0, "us": 0, "web": 0, "would": 0}, "titles": ["<no title>"], "titleterms": {}}) \ No newline at end of file +Search.setIndex({"alltitles":{},"docnames":["index"],"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"],"indexentries":{"sphinx (c++ class)":[[0,"_CPPv46Sphinx",false]]},"objects":{"":[[0,0,1,"_CPPv46Sphinx","Sphinx"]]},"objnames":{"0":["cpp","class","C++ class"]},"objtypes":{"0":"cpp:class"},"terms":{"The":0,"becaus":0,"c":0,"can":0,"cardin":0,"challeng":0,"charact":0,"class":0,"descript":0,"drop":0,"engin":0,"fixtur":0,"frequent":0,"gener":0,"i":0,"index":0,"inflat":0,"mathemat":0,"occur":0,"often":0,"project":0,"punctuat":0,"queri":0,"relat":0,"sampl":0,"search":0,"size":0,"sphinx":0,"term":0,"thei":0,"thi":0,"token":0,"us":0,"web":0,"would":0},"titles":["<no title>"],"titleterms":{}}) \ No newline at end of file diff --git a/tests/js/fixtures/multiterm/searchindex.js b/tests/js/fixtures/multiterm/searchindex.js index c4e4a32e30a..b3e2977792c 100644 --- a/tests/js/fixtures/multiterm/searchindex.js +++ b/tests/js/fixtures/multiterm/searchindex.js @@ -1 +1 @@ -Search.setIndex({"alltitles": {"Main Page": [[0, null]]}, "docnames": ["index"], "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"], "indexentries": {}, "objects": {}, "objnames": {}, "objtypes": {}, "terms": {"At": 0, "adjac": 0, "all": 0, "an": 0, "appear": 0, "applic": 0, "ar": 0, "built": 0, "can": 0, "check": 0, "contain": 0, "do": 0, "document": 0, "doesn": 0, "each": 0, "fixtur": 0, "format": 0, "function": 0, "futur": 0, "html": 0, "i": 0, "includ": 0, "match": 0, "messag": 0, "multipl": 0, "multiterm": 0, "order": 0, "other": 0, "output": 0, "perform": 0, "perhap": 0, "phrase": 0, "project": 0, "queri": 0, "requir": 0, "same": 0, "search": 0, "successfulli": 0, "support": 0, "t": 0, "term": 0, "test": 0, "thi": 0, "time": 0, "us": 0, "when": 0, "write": 0}, "titles": ["Main Page"], "titleterms": {"main": 0, "page": 0}}) \ No newline at end of file +Search.setIndex({"alltitles":{"Main Page":[[0,null]]},"docnames":["index"],"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"],"indexentries":{},"objects":{},"objnames":{},"objtypes":{},"terms":{"At":0,"adjac":0,"all":0,"an":0,"appear":0,"applic":0,"ar":0,"built":0,"can":0,"check":0,"contain":0,"do":0,"document":0,"doesn":0,"each":0,"fixtur":0,"format":0,"function":0,"futur":0,"html":0,"i":0,"includ":0,"match":0,"messag":0,"multipl":0,"multiterm":0,"order":0,"other":0,"output":0,"perform":0,"perhap":0,"phrase":0,"project":0,"queri":0,"requir":0,"same":0,"search":0,"successfulli":0,"support":0,"t":0,"term":0,"test":0,"thi":0,"time":0,"us":0,"when":0,"write":0},"titles":["Main Page"],"titleterms":{"main":0,"page":0}}) \ No newline at end of file diff --git a/tests/js/fixtures/partial/searchindex.js b/tests/js/fixtures/partial/searchindex.js index 4925083169b..ac024bf0c6e 100644 --- a/tests/js/fixtures/partial/searchindex.js +++ b/tests/js/fixtures/partial/searchindex.js @@ -1 +1 @@ -Search.setIndex({"alltitles": {"sphinx_utils module": [[0, null]]}, "docnames": ["index"], "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"], "indexentries": {}, "objects": {}, "objnames": {}, "objtypes": {}, "terms": {"ar": 0, "both": 0, "built": 0, "confirm": 0, "document": 0, "function": 0, "html": 0, "i": 0, "includ": 0, "input": 0, "javascript": 0, "match": 0, "partial": 0, "possibl": 0, "project": 0, "provid": 0, "restructuredtext": 0, "sampl": 0, "search": 0, "should": 0, "term": 0, "thi": 0, "titl": 0, "us": 0, "when": 0}, "titles": ["sphinx_utils module"], "titleterms": {"modul": 0, "sphinx_util": 0}}) \ No newline at end of file +Search.setIndex({"alltitles":{"sphinx_utils module":[[0,null]]},"docnames":["index"],"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"],"indexentries":{},"objects":{},"objnames":{},"objtypes":{},"terms":{"ar":0,"both":0,"built":0,"confirm":0,"document":0,"function":0,"html":0,"i":0,"includ":0,"input":0,"javascript":0,"match":0,"partial":0,"possibl":0,"project":0,"provid":0,"restructuredtext":0,"sampl":0,"search":0,"should":0,"term":0,"thi":0,"titl":0,"us":0,"when":0},"titles":["sphinx_utils module"],"titleterms":{"modul":0,"sphinx_util":0}}) \ No newline at end of file diff --git a/tests/js/fixtures/titles/searchindex.js b/tests/js/fixtures/titles/searchindex.js index 293b087149b..987be77992a 100644 --- a/tests/js/fixtures/titles/searchindex.js +++ b/tests/js/fixtures/titles/searchindex.js @@ -1 +1 @@ -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 +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_search.py b/tests/test_search.py index 9f0e94f8311..f74755b3b5b 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -152,8 +152,8 @@ def test_term_in_heading_and_section(app): # if search term is in the title of one doc and in the text of another # both documents should be a hit in the search index as a title, # respectively text hit - assert '"textinhead": 2' in searchindex - assert '"textinhead": 0' in searchindex + assert '"textinhead":2' in searchindex + assert '"textinhead":0' in searchindex @pytest.mark.sphinx('html', testroot='search') From b409db1df20bf09829a46379e962ae9df0503844 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Oct 2024 17:55:26 +0100 Subject: [PATCH 2/9] Bump mypy to 1.13.0 (#13064) --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 446548de2f9..6f408c098b9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -82,7 +82,7 @@ docs = [ lint = [ "flake8>=6.0", "ruff==0.7.0", - "mypy==1.12.1", + "mypy==1.13.0", "sphinx-lint>=0.9", "types-colorama==0.4.15.20240311", "types-defusedxml==0.7.0.20240218", From 70c253e9683dee55ac3210051abb0441661d9225 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Oct 2024 17:56:16 +0100 Subject: [PATCH 3/9] Bump pyright to 1.1.386 (#13066) --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 6f408c098b9..2e97d5eee54 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -91,7 +91,7 @@ lint = [ "types-Pygments==2.18.0.20240506", "types-requests==2.32.0.20241016", # align with requests "types-urllib3==1.26.25.14", - "pyright==1.1.385", + "pyright==1.1.386", "pytest>=6.0", ] test = [ From bd7d595c6b37e68acaf988e3dded04395242474b Mon Sep 17 00:00:00 2001 From: James Addison <55152140+jayaddison@users.noreply.github.com> Date: Thu, 24 Oct 2024 18:29:56 +0000 Subject: [PATCH 4/9] html search: use a ``Map`` to collect file-term scores (#13060) Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- CHANGES.rst | 3 +++ sphinx/themes/basic/static/searchtools.js | 7 ++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 6cef031159d..b47f417e9a1 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -18,5 +18,8 @@ Features added Bugs fixed ---------- +* #13060: HTML Search: use ``Map`` to store per-file term scores. + Patch by James Addison + Testing ------- diff --git a/sphinx/themes/basic/static/searchtools.js b/sphinx/themes/basic/static/searchtools.js index 2c774d17aff..aaf078d2b91 100644 --- a/sphinx/themes/basic/static/searchtools.js +++ b/sphinx/themes/basic/static/searchtools.js @@ -547,8 +547,9 @@ const Search = { // set score for the word in each file recordFiles.forEach((file) => { - if (!scoreMap.has(file)) scoreMap.set(file, {}); - scoreMap.get(file)[word] = record.score; + if (!scoreMap.has(file)) scoreMap.set(file, new Map()); + const fileScores = scoreMap.get(file); + fileScores.set(word, record.score); }); }); @@ -587,7 +588,7 @@ const Search = { break; // select one (max) score for the file. - const score = Math.max(...wordList.map((w) => scoreMap.get(file)[w])); + const score = Math.max(...wordList.map((w) => scoreMap.get(file).get(w))); // add result to the result list results.push([ docNames[file], From 8682f64b6f696dfa2a210f347e4cc239a408e4eb Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Fri, 25 Oct 2024 17:49:30 +0100 Subject: [PATCH 5/9] Use keyword-arguments with ``IndexEntry`` --- sphinx/domains/python/__init__.py | 35 ++++- tests/test_domains/test_domain_py.py | 188 +++++++++++++++++++++------ 2 files changed, 176 insertions(+), 47 deletions(-) diff --git a/sphinx/domains/python/__init__.py b/sphinx/domains/python/__init__.py index cac8e017609..92243f6c78a 100644 --- a/sphinx/domains/python/__init__.py +++ b/sphinx/domains/python/__init__.py @@ -594,19 +594,42 @@ def generate(self, docnames: Iterable[str] | None = None, # first submodule - make parent a group head if entries: last = entries[-1] - entries[-1] = IndexEntry(last[0], 1, last[2], last[3], - last[4], last[5], last[6]) + entries[-1] = IndexEntry( + name=last.name, + subtype=1, + docname=last.docname, + anchor=last.anchor, + extra=last.extra, + qualifier=last.qualifier, + descr=last.descr, + ) elif not prev_modname.startswith(package): # submodule without parent in list, add dummy entry - entries.append(IndexEntry(stripped + package, 1, '', '', '', '', '')) + dummy_entry = IndexEntry( + name=stripped + package, + subtype=1, + docname='', + anchor='', + extra='', + qualifier='', + descr='', + ) + entries.append(dummy_entry) subtype = 2 else: num_toplevels += 1 subtype = 0 - qualifier = _('Deprecated') if deprecated else '' - entries.append(IndexEntry(stripped + modname, subtype, docname, - node_id, platforms, qualifier, synopsis)) + entry = IndexEntry( + name=stripped + modname, + subtype=subtype, + docname=module.docname, + anchor=module.node_id, + extra=module.platform, + qualifier=_('Deprecated') if module.deprecated else '', + descr=module.synopsis, + ) + entries.append(entry) prev_modname = modname # apply heuristics when to collapse modindex at page load: diff --git a/tests/test_domains/test_domain_py.py b/tests/test_domains/test_domain_py.py index 74531d1097f..9f19f1da34c 100644 --- a/tests/test_domains/test_domain_py.py +++ b/tests/test_domains/test_domain_py.py @@ -572,34 +572,67 @@ def test_module_index(app): index = PythonModuleIndex(app.env.domains.python_domain) assert index.generate() == ( [ - ('d', [IndexEntry('docutils', 0, 'index', 'module-docutils', '', '', '')]), + ( + 'd', + [ + IndexEntry( + name='docutils', + subtype=0, + docname='index', + anchor='module-docutils', + extra='', + qualifier='', + descr='', + ), + ], + ), ( 's', [ - IndexEntry('sphinx', 1, 'index', 'module-sphinx', '', '', ''), IndexEntry( - 'sphinx.builders', - 2, - 'index', - 'module-sphinx.builders', - '', - '', - '', + name='sphinx', + subtype=1, + docname='index', + anchor='module-sphinx', + extra='', + qualifier='', + descr='', + ), + IndexEntry( + name='sphinx.builders', + subtype=2, + docname='index', + anchor='module-sphinx.builders', + extra='', + qualifier='', + descr='', ), IndexEntry( - 'sphinx.builders.html', - 2, - 'index', - 'module-sphinx.builders.html', - '', - '', - '', + name='sphinx.builders.html', + subtype=2, + docname='index', + anchor='module-sphinx.builders.html', + extra='', + qualifier='', + descr='', ), IndexEntry( - 'sphinx.config', 2, 'index', 'module-sphinx.config', '', '', '' + name='sphinx.config', + subtype=2, + docname='index', + anchor='module-sphinx.config', + extra='', + qualifier='', + descr='', ), IndexEntry( - 'sphinx_intl', 0, 'index', 'module-sphinx_intl', '', '', '' + name='sphinx_intl', + subtype=0, + docname='index', + anchor='module-sphinx_intl', + extra='', + qualifier='', + descr='', ), ], ), @@ -618,9 +651,23 @@ def test_module_index_submodule(app): ( 's', [ - IndexEntry('sphinx', 1, '', '', '', '', ''), IndexEntry( - 'sphinx.config', 2, 'index', 'module-sphinx.config', '', '', '' + name='sphinx', + subtype=1, + docname='', + anchor='', + extra='', + qualifier='', + descr='', + ), + IndexEntry( + name='sphinx.config', + subtype=2, + docname='index', + anchor='module-sphinx.config', + extra='', + qualifier='', + descr='', ), ], ) @@ -636,8 +683,34 @@ def test_module_index_not_collapsed(app): index = PythonModuleIndex(app.env.domains.python_domain) assert index.generate() == ( [ - ('d', [IndexEntry('docutils', 0, 'index', 'module-docutils', '', '', '')]), - ('s', [IndexEntry('sphinx', 0, 'index', 'module-sphinx', '', '', '')]), + ( + 'd', + [ + IndexEntry( + name='docutils', + subtype=0, + docname='index', + anchor='module-docutils', + extra='', + qualifier='', + descr='', + ), + ], + ), + ( + 's', + [ + IndexEntry( + name='sphinx', + subtype=0, + docname='index', + anchor='module-sphinx', + extra='', + qualifier='', + descr='', + ), + ], + ), ], True, ) @@ -666,22 +739,22 @@ def test_modindex_common_prefix(app): 'b', [ IndexEntry( - 'sphinx.builders', - 1, - 'index', - 'module-sphinx.builders', - '', - '', - '', + name='sphinx.builders', + subtype=1, + docname='index', + anchor='module-sphinx.builders', + extra='', + qualifier='', + descr='', ), IndexEntry( - 'sphinx.builders.html', - 2, - 'index', - 'module-sphinx.builders.html', - '', - '', - '', + name='sphinx.builders.html', + subtype=2, + docname='index', + anchor='module-sphinx.builders.html', + extra='', + qualifier='', + descr='', ), ], ), @@ -689,17 +762,50 @@ def test_modindex_common_prefix(app): 'c', [ IndexEntry( - 'sphinx.config', 0, 'index', 'module-sphinx.config', '', '', '' - ) + name='sphinx.config', + subtype=0, + docname='index', + anchor='module-sphinx.config', + extra='', + qualifier='', + descr='', + ), + ], + ), + ( + 'd', + [ + IndexEntry( + name='docutils', + subtype=0, + docname='index', + anchor='module-docutils', + extra='', + qualifier='', + descr='', + ), ], ), - ('d', [IndexEntry('docutils', 0, 'index', 'module-docutils', '', '', '')]), ( 's', [ - IndexEntry('sphinx', 0, 'index', 'module-sphinx', '', '', ''), IndexEntry( - 'sphinx_intl', 0, 'index', 'module-sphinx_intl', '', '', '' + name='sphinx', + subtype=0, + docname='index', + anchor='module-sphinx', + extra='', + qualifier='', + descr='', + ), + IndexEntry( + name='sphinx_intl', + subtype=0, + docname='index', + anchor='module-sphinx_intl', + extra='', + qualifier='', + descr='', ), ], ), From f43951e3ffd5455970436810530b01e0dd22b972 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Fri, 25 Oct 2024 17:52:26 +0100 Subject: [PATCH 6/9] Use keyword-arguments with ``note_module()`` --- sphinx/domains/javascript.py | 2 +- sphinx/domains/python/__init__.py | 26 +++++++++++++++++--------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/sphinx/domains/javascript.py b/sphinx/domains/javascript.py index c5c0cdc3375..ec81375a6da 100644 --- a/sphinx/domains/javascript.py +++ b/sphinx/domains/javascript.py @@ -324,7 +324,7 @@ def run(self) -> list[Node]: domain = self.env.domains.javascript_domain node_id = make_id(self.env, self.state.document, 'module', mod_name) - domain.note_module(mod_name, node_id) + domain.note_module(modname=mod_name, node_id=node_id) # Make a duplicate entry in 'objects' to facilitate searching for # the module in JavaScriptDomain.find_obj() domain.note_object(mod_name, 'module', node_id, diff --git a/sphinx/domains/python/__init__.py b/sphinx/domains/python/__init__.py index 92243f6c78a..21c57d9bb87 100644 --- a/sphinx/domains/python/__init__.py +++ b/sphinx/domains/python/__init__.py @@ -471,11 +471,13 @@ def run(self) -> list[Node]: self.set_source_info(target) self.state.document.note_explicit_target(target) - domain.note_module(modname, - node_id, - self.options.get('synopsis', ''), - self.options.get('platform', ''), - 'deprecated' in self.options) + domain.note_module( + name=modname, + node_id=node_id, + synopsis=self.options.get('synopsis', ''), + platform=self.options.get('platform', ''), + deprecated='deprecated' in self.options, + ) domain.note_object(modname, 'module', node_id, location=target) # the platform and synopsis aren't printed; in fact, they are only @@ -727,14 +729,20 @@ def note_object(self, name: str, objtype: str, node_id: str, def modules(self) -> dict[str, ModuleEntry]: return self.data.setdefault('modules', {}) # modname -> ModuleEntry - def note_module(self, name: str, node_id: str, synopsis: str, - platform: str, deprecated: bool) -> None: + def note_module( + self, name: str, node_id: str, synopsis: str, platform: str, deprecated: bool + ) -> None: """Note a python module for cross reference. .. versionadded:: 2.1 """ - self.modules[name] = ModuleEntry(self.env.docname, node_id, - synopsis, platform, deprecated) + self.modules[name] = ModuleEntry( + docname=self.env.docname, + node_id=node_id, + synopsis=synopsis, + platform=platform, + deprecated=deprecated, + ) def clear_doc(self, docname: str) -> None: to_remove = [ From 342b597827fd52d5827bd6c0df2375bf88c9a98b Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Fri, 25 Oct 2024 17:55:13 +0100 Subject: [PATCH 7/9] Refactor PythonDomain Miscellaneous style changes. --- sphinx/domains/python/__init__.py | 51 ++++++++++++++++++------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/sphinx/domains/python/__init__.py b/sphinx/domains/python/__init__.py index 21c57d9bb87..eaa3b158d38 100644 --- a/sphinx/domains/python/__init__.py +++ b/sphinx/domains/python/__init__.py @@ -558,26 +558,32 @@ class PythonModuleIndex(Index): name = 'modindex' localname = _('Python Module Index') shortname = _('modules') + domain: PythonDomain def generate(self, docnames: Iterable[str] | None = None, ) -> tuple[list[tuple[str, list[IndexEntry]]], bool]: + doc_names = frozenset(docnames) if docnames is not None else None + content: dict[str, list[IndexEntry]] = {} # list of prefixes to ignore - ignores: list[str] = self.domain.env.config['modindex_common_prefix'] - ignores = sorted(ignores, key=len, reverse=True) + ignores: list[str] = sorted( + self.domain.env.config['modindex_common_prefix'], key=len, reverse=True + ) + # list of all modules, sorted by module name - modules = sorted(self.domain.data['modules'].items(), - key=lambda x: x[0].lower()) + modules = sorted(self.domain.modules.items(), key=lambda t: t[0].lower()) + # sort out collapsible modules prev_modname = '' - num_toplevels = 0 - for modname, (docname, node_id, synopsis, platforms, deprecated) in modules: - if docnames and docname not in docnames: + + num_top_levels = 0 + for modname, module in modules: + if doc_names and module.docname not in doc_names: continue for ignore in ignores: if modname.startswith(ignore): - modname = modname[len(ignore):] + modname = modname.removeprefix(ignore) stripped = ignore break else: @@ -589,7 +595,7 @@ def generate(self, docnames: Iterable[str] | None = None, entries = content.setdefault(modname[0].lower(), []) - package = modname.split('.')[0] + package = modname.split('.', maxsplit=1)[0] if package != modname: # it's a submodule if prev_modname == package: @@ -619,7 +625,7 @@ def generate(self, docnames: Iterable[str] | None = None, entries.append(dummy_entry) subtype = 2 else: - num_toplevels += 1 + num_top_levels += 1 subtype = 0 entry = IndexEntry( @@ -637,7 +643,7 @@ def generate(self, docnames: Iterable[str] | None = None, # apply heuristics when to collapse modindex at page load: # only collapse if number of toplevel modules is larger than # number of submodules - collapse = len(modules) - num_toplevels < num_toplevels + collapse = len(modules) - num_top_levels < num_top_levels # sort by first letter sorted_content = sorted(content.items()) @@ -885,9 +891,10 @@ def resolve_any_xref(self, env: BuildEnvironment, fromdocname: str, builder: Bui continue if obj[2] == 'module': - results.append(('py:mod', - self._make_module_refnode(builder, fromdocname, - name, contnode))) + results.append(( + 'py:mod', + self._make_module_refnode(builder, fromdocname, name, contnode) + )) else: # determine the content of the reference by conditions content = find_pending_xref_condition(node, 'resolved') @@ -905,16 +912,18 @@ def resolve_any_xref(self, env: BuildEnvironment, fromdocname: str, builder: Bui def _make_module_refnode(self, builder: Builder, fromdocname: str, name: str, contnode: Node) -> Element: # get additional info for modules - module = self.modules[name] - title = name + module: ModuleEntry = self.modules[name] + title_parts = [name] if module.synopsis: - title += ': ' + module.synopsis + title_parts.append(f': {module.synopsis}') if module.deprecated: - title += _(' (deprecated)') + title_parts.append(_(' (deprecated)')) if module.platform: - title += ' (' + module.platform + ')' - return make_refnode(builder, fromdocname, module.docname, module.node_id, - contnode, title) + title_parts.append(f' ({module.platform})') + title = ''.join(title_parts) + return make_refnode( + builder, fromdocname, module.docname, module.node_id, contnode, title + ) def get_objects(self) -> Iterator[tuple[str, str, str, str, str, int]]: for modname, mod in self.modules.items(): From 4f2de7ffc04d26e961692cbbfa692e72f046e4b1 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Fri, 25 Oct 2024 18:14:02 +0100 Subject: [PATCH 8/9] Rename ``index{cls,name}`` to ``index_{cls,name}`` --- sphinx/builders/html/__init__.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/sphinx/builders/html/__init__.py b/sphinx/builders/html/__init__.py index f85b5b17696..eacb333fbe9 100644 --- a/sphinx/builders/html/__init__.py +++ b/sphinx/builders/html/__init__.py @@ -505,10 +505,15 @@ def prepare_writing(self, docnames: Set[str]) -> None: rellinks: list[tuple[str, str, str, str]] = [] if self.use_index: rellinks.append(('genindex', _('General Index'), 'I', _('index'))) - for indexname, indexcls, _content, _collapse in self.domain_indices: + for index_name, index_cls, _content, _collapse in self.domain_indices: # if it has a short name - if indexcls.shortname: - rellinks.append((indexname, indexcls.localname, '', indexcls.shortname)) + if index_cls.shortname: + rellinks.append(( + index_name, + index_cls.localname, + '', + index_cls.shortname, + )) # add assets registered after ``Builder.init()``. for css_filename, attrs in self.app.registry.css_files: @@ -755,14 +760,14 @@ def write_genindex(self) -> None: self.handle_page('genindex', genindexcontext, 'genindex.html') def write_domain_indices(self) -> None: - for indexname, indexcls, content, collapse in self.domain_indices: - indexcontext = { - 'indextitle': indexcls.localname, + for index_name, index_cls, content, collapse in self.domain_indices: + index_context = { + 'indextitle': index_cls.localname, 'content': content, 'collapse_index': collapse, } - logger.info(indexname + ' ', nonl=True) - self.handle_page(indexname, indexcontext, 'domainindex.html') + logger.info('%s ', index_name, nonl=True) + self.handle_page(index_name, index_context, 'domainindex.html') def copy_image_files(self) -> None: if self.images: From 63a4175b532b2be294f30d0a03b596c0acbac513 Mon Sep 17 00:00:00 2001 From: James Addison <55152140+jayaddison@users.noreply.github.com> Date: Fri, 25 Oct 2024 17:58:05 +0000 Subject: [PATCH 9/9] linkcheck: Remove unused 'local' status code (#13068) --- sphinx/builders/linkcheck.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/sphinx/builders/linkcheck.py b/sphinx/builders/linkcheck.py index 28c977f2fc2..fcf994e8e03 100644 --- a/sphinx/builders/linkcheck.py +++ b/sphinx/builders/linkcheck.py @@ -45,7 +45,6 @@ class _Status(StrEnum): BROKEN = 'broken' IGNORED = 'ignored' - LOCAL = 'local' RATE_LIMITED = 'rate-limited' REDIRECTED = 'redirected' TIMEOUT = 'timeout' @@ -124,15 +123,6 @@ def process_result(self, result: CheckResult) -> None: else: msg = result.uri logger.info(darkgray('-ignored- ') + msg) - case _Status.LOCAL: - logger.info(darkgray('-local- ') + result.uri) - self.write_entry( - _Status.LOCAL, - result.docname, - filename, - result.lineno, - result.uri, - ) case _Status.WORKING: logger.info(darkgreen('ok ') + f'{result.uri}{result.message}') case _Status.TIMEOUT: