mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Cache doctrees more efficiently
This commit is contained in:
@@ -6,7 +6,7 @@ import functools
|
|||||||
import os
|
import os
|
||||||
import pickle
|
import pickle
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from copy import copy, deepcopy
|
from copy import copy
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from os import path
|
from os import path
|
||||||
from typing import TYPE_CHECKING, Any, Callable, Generator, Iterator
|
from typing import TYPE_CHECKING, Any, Callable, Generator, Iterator
|
||||||
@@ -178,6 +178,9 @@ class BuildEnvironment:
|
|||||||
# docnames to re-read unconditionally on next build
|
# docnames to re-read unconditionally on next build
|
||||||
self.reread_always: set[str] = set()
|
self.reread_always: set[str] = set()
|
||||||
|
|
||||||
|
# docname -> pickled doctree
|
||||||
|
self._pickled_doctree_cache: dict[str, bytes] = {}
|
||||||
|
|
||||||
# File metadata
|
# File metadata
|
||||||
# docname -> dict of metadata items
|
# docname -> dict of metadata items
|
||||||
self.metadata: dict[str, dict[str, Any]] = defaultdict(dict)
|
self.metadata: dict[str, dict[str, Any]] = defaultdict(dict)
|
||||||
@@ -577,20 +580,22 @@ class BuildEnvironment:
|
|||||||
|
|
||||||
def get_doctree(self, docname: str) -> nodes.document:
|
def get_doctree(self, docname: str) -> nodes.document:
|
||||||
"""Read the doctree for a file from the pickle and return it."""
|
"""Read the doctree for a file from the pickle and return it."""
|
||||||
doctreedir = self.doctreedir
|
try:
|
||||||
|
serialised = self._pickled_doctree_cache[docname]
|
||||||
@functools.lru_cache(maxsize=None)
|
except KeyError:
|
||||||
def _load_doctree_from_disk(docname: str) -> nodes.document:
|
filename = path.join(self.doctreedir, docname + '.doctree')
|
||||||
"""Read the doctree for a file from the pickle and return it."""
|
|
||||||
filename = path.join(doctreedir, docname + '.doctree')
|
|
||||||
with open(filename, 'rb') as f:
|
with open(filename, 'rb') as f:
|
||||||
return pickle.load(f)
|
serialised = self._pickled_doctree_cache[docname] = f.read()
|
||||||
|
|
||||||
doctree = deepcopy(_load_doctree_from_disk(docname))
|
doctree = pickle.loads(serialised)
|
||||||
doctree.settings.env = self
|
doctree.settings.env = self
|
||||||
doctree.reporter = LoggingReporter(self.doc2path(docname))
|
doctree.reporter = LoggingReporter(self.doc2path(docname))
|
||||||
return doctree
|
return doctree
|
||||||
|
|
||||||
|
@functools.cached_property
|
||||||
|
def master_doctree(self) -> nodes.document:
|
||||||
|
return self.get_doctree(self.config.root_doc)
|
||||||
|
|
||||||
def get_and_resolve_doctree(
|
def get_and_resolve_doctree(
|
||||||
self,
|
self,
|
||||||
docname: str,
|
docname: str,
|
||||||
|
|||||||
@@ -319,7 +319,7 @@ class TocTree:
|
|||||||
def get_toctree_for(self, docname: str, builder: Builder, collapse: bool,
|
def get_toctree_for(self, docname: str, builder: Builder, collapse: bool,
|
||||||
**kwargs: Any) -> Element | None:
|
**kwargs: Any) -> Element | None:
|
||||||
"""Return the global TOC nodetree."""
|
"""Return the global TOC nodetree."""
|
||||||
doctree = self.env.get_doctree(self.env.config.root_doc)
|
doctree = self.env.master_doctree
|
||||||
toctrees: list[Element] = []
|
toctrees: list[Element] = []
|
||||||
if 'includehidden' not in kwargs:
|
if 'includehidden' not in kwargs:
|
||||||
kwargs['includehidden'] = True
|
kwargs['includehidden'] = True
|
||||||
|
|||||||
@@ -156,6 +156,10 @@ class SphinxTestApp(application.Sphinx):
|
|||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f'<{self.__class__.__name__} buildername={self.builder.name!r}>'
|
return f'<{self.__class__.__name__} buildername={self.builder.name!r}>'
|
||||||
|
|
||||||
|
def build(self, force_all: bool = False, filenames: list[str] | None = None) -> None:
|
||||||
|
self.env._pickled_doctree_cache.clear()
|
||||||
|
super().build(force_all, filenames)
|
||||||
|
|
||||||
|
|
||||||
class SphinxTestAppWrapperForSkipBuilding:
|
class SphinxTestAppWrapperForSkipBuilding:
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -135,6 +135,7 @@ def test_gettext_index_entries(app):
|
|||||||
'gettext_additional_targets': []})
|
'gettext_additional_targets': []})
|
||||||
def test_gettext_disable_index_entries(app):
|
def test_gettext_disable_index_entries(app):
|
||||||
# regression test for #976
|
# regression test for #976
|
||||||
|
app.env._pickled_doctree_cache.clear() # clear cache
|
||||||
app.builder.build(['index_entries'])
|
app.builder.build(['index_entries'])
|
||||||
|
|
||||||
_msgid_getter = re.compile(r'msgid "(.*)"').search
|
_msgid_getter = re.compile(r'msgid "(.*)"').search
|
||||||
@@ -165,7 +166,7 @@ def test_gettext_disable_index_entries(app):
|
|||||||
|
|
||||||
@pytest.mark.sphinx('gettext', testroot='intl', srcdir='gettext')
|
@pytest.mark.sphinx('gettext', testroot='intl', srcdir='gettext')
|
||||||
def test_gettext_template(app):
|
def test_gettext_template(app):
|
||||||
app.builder.build_all()
|
app.build()
|
||||||
assert (app.outdir / 'sphinx.pot').isfile()
|
assert (app.outdir / 'sphinx.pot').isfile()
|
||||||
|
|
||||||
result = (app.outdir / 'sphinx.pot').read_text(encoding='utf8')
|
result = (app.outdir / 'sphinx.pot').read_text(encoding='utf8')
|
||||||
|
|||||||
@@ -665,7 +665,7 @@ def test_numfig_without_numbered_toctree_warn(app, warning):
|
|||||||
index = (app.srcdir / 'index.rst').read_text(encoding='utf8')
|
index = (app.srcdir / 'index.rst').read_text(encoding='utf8')
|
||||||
index = re.sub(':numbered:.*', '', index)
|
index = re.sub(':numbered:.*', '', index)
|
||||||
(app.srcdir / 'index.rst').write_text(index, encoding='utf8')
|
(app.srcdir / 'index.rst').write_text(index, encoding='utf8')
|
||||||
app.builder.build_all()
|
app.build()
|
||||||
|
|
||||||
warnings = warning.getvalue()
|
warnings = warning.getvalue()
|
||||||
assert 'index.rst:47: WARNING: numfig is disabled. :numref: is ignored.' not in warnings
|
assert 'index.rst:47: WARNING: numfig is disabled. :numref: is ignored.' not in warnings
|
||||||
|
|||||||
@@ -429,7 +429,7 @@ def test_numref_with_prefix2(app, status, warning):
|
|||||||
'latex', testroot='numfig',
|
'latex', testroot='numfig',
|
||||||
confoverrides={'numfig': True, 'language': 'ja'})
|
confoverrides={'numfig': True, 'language': 'ja'})
|
||||||
def test_numref_with_language_ja(app, status, warning):
|
def test_numref_with_language_ja(app, status, warning):
|
||||||
app.builder.build_all()
|
app.build()
|
||||||
result = (app.outdir / 'python.tex').read_text(encoding='utf8')
|
result = (app.outdir / 'python.tex').read_text(encoding='utf8')
|
||||||
print(result)
|
print(result)
|
||||||
print(status.getvalue())
|
print(status.getvalue())
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ def test_mathjax_align(app, status, warning):
|
|||||||
confoverrides={'math_number_all': True,
|
confoverrides={'math_number_all': True,
|
||||||
'extensions': ['sphinx.ext.mathjax']})
|
'extensions': ['sphinx.ext.mathjax']})
|
||||||
def test_math_number_all_mathjax(app, status, warning):
|
def test_math_number_all_mathjax(app, status, warning):
|
||||||
app.builder.build_all()
|
app.build()
|
||||||
|
|
||||||
content = (app.outdir / 'index.html').read_text(encoding='utf8')
|
content = (app.outdir / 'index.html').read_text(encoding='utf8')
|
||||||
html = (r'<div class="math notranslate nohighlight" id="equation-index-0">\s*'
|
html = (r'<div class="math notranslate nohighlight" id="equation-index-0">\s*'
|
||||||
@@ -119,7 +119,7 @@ def test_math_number_all_mathjax(app, status, warning):
|
|||||||
@pytest.mark.sphinx('latex', testroot='ext-math',
|
@pytest.mark.sphinx('latex', testroot='ext-math',
|
||||||
confoverrides={'extensions': ['sphinx.ext.mathjax']})
|
confoverrides={'extensions': ['sphinx.ext.mathjax']})
|
||||||
def test_math_number_all_latex(app, status, warning):
|
def test_math_number_all_latex(app, status, warning):
|
||||||
app.builder.build_all()
|
app.build()
|
||||||
|
|
||||||
content = (app.outdir / 'python.tex').read_text(encoding='utf8')
|
content = (app.outdir / 'python.tex').read_text(encoding='utf8')
|
||||||
macro = (r'\\begin{equation\*}\s*'
|
macro = (r'\\begin{equation\*}\s*'
|
||||||
|
|||||||
Reference in New Issue
Block a user