from __future__ import annotations
import os
import pickle
import types
from os import path
from typing import TYPE_CHECKING
from sphinx.application import ENV_PICKLE_FILENAME, Sphinx
from sphinx.builders.html import BuildInfo, StandaloneHTMLBuilder
from sphinx.locale import get_translation
from sphinx.util.osutil import SEP, copyfile, ensuredir, os_path
from sphinxcontrib.serializinghtml import jsonimpl
if TYPE_CHECKING:
from collections.abc import Sequence
from typing import Any, Protocol
class SerialisingImplementation(Protocol):
def dump(self, obj: Any, file: Any, *args: Any, **kwargs: Any) -> None: ...
def dumps(self, obj: Any, *args: Any, **kwargs: Any) -> str | bytes: ...
def load(self, file: Any, *args: Any, **kwargs: Any) -> Any: ...
def loads(self, data: Any, *args: Any, **kwargs: Any) -> Any: ...
__version__ = '2.0.0'
__version_info__ = (2, 0, 0)
package_dir = path.abspath(path.dirname(__file__))
__ = get_translation(__name__, 'console')
#: the filename for the "last build" file (for serializing builders)
LAST_BUILD_FILENAME = 'last_build'
[documentos]
class SerializingHTMLBuilder(StandaloneHTMLBuilder):
"""
An abstract builder that serializes the generated HTML.
"""
#: the serializing implementation to use. Set this to a module that
#: implements a `dump`, `load`, `dumps` and `loads` functions
#: (pickle, json etc.)
implementation: SerialisingImplementation
implementation_dumps_unicode = False
#: additional arguments for dump()
additional_dump_args: Sequence[Any] = ()
#: the filename for the global context file
globalcontext_filename: str = ''
supported_image_types = ['image/svg+xml', 'image/png',
'image/gif', 'image/jpeg']
def init(self) -> None:
self.build_info = BuildInfo(self.config, self.tags)
self.imagedir = '_images'
self.current_docname = ''
self.theme = None # type: ignore[assignment] # no theme necessary
self.templates = None # no template bridge necessary
self.init_templates()
self.init_highlighter()
self.init_css_files()
self.init_js_files()
self.use_index = self.get_builder_config('use_index', 'html')
def get_target_uri(self, docname: str, typ: str | None = None) -> str:
if docname == 'index':
return ''
if docname.endswith(SEP + 'index'):
return docname[:-5] # up to sep
return docname + SEP
def dump_context(self, context: dict[str, Any], filename: str | os.PathLike[str]) -> None:
context = context.copy()
if 'css_files' in context:
context['css_files'] = [css.filename for css in context['css_files']]
if 'script_files' in context:
context['script_files'] = [js.filename for js in context['script_files']]
if self.implementation_dumps_unicode:
with open(filename, 'w', encoding='utf-8') as ft:
self.implementation.dump(context, ft, *self.additional_dump_args)
else:
with open(filename, 'wb') as fb:
self.implementation.dump(context, fb, *self.additional_dump_args)
def handle_page(self, pagename: str, ctx: dict[str, Any], templatename: str = 'page.html',
outfilename: str | None = None, event_arg: Any = None) -> None:
ctx['current_page_name'] = pagename
ctx.setdefault('pathto', lambda p: p)
self.add_sidebars(pagename, ctx)
if not outfilename:
outfilename = path.join(self.outdir,
os_path(pagename) + self.out_suffix)
# we're not taking the return value here, since no template is
# actually rendered
self.app.emit('html-page-context', pagename, templatename, ctx, event_arg)
# make context object serializable
for key in list(ctx):
if isinstance(ctx[key], types.FunctionType):
del ctx[key]
ensuredir(path.dirname(outfilename))
self.dump_context(ctx, outfilename)
# if there is a source file, copy the source file for the
# "show source" link
if ctx.get('sourcename'):
source_name = path.join(self.outdir, '_sources',
os_path(ctx['sourcename']))
ensuredir(path.dirname(source_name))
copyfile(self.env.doc2path(pagename), source_name)
def handle_finish(self) -> None:
# dump the global context
outfilename = path.join(self.outdir, self.globalcontext_filename)
self.dump_context(self.globalcontext, outfilename)
# super here to dump the search index
super().handle_finish()
# copy the environment file from the doctree dir to the output dir
# as needed by the web app
copyfile(path.join(self.doctreedir, ENV_PICKLE_FILENAME),
path.join(self.outdir, ENV_PICKLE_FILENAME))
# touch 'last build' file, used by the web application to determine
# when to reload its environment and clear the cache
open(path.join(self.outdir, LAST_BUILD_FILENAME), 'w').close()
[documentos]
class PickleHTMLBuilder(SerializingHTMLBuilder):
"""
A Builder that dumps the generated HTML into pickle files.
"""
name = 'pickle'
epilog = __('You can now process the pickle files in %(outdir)s.')
implementation = pickle
implementation_dumps_unicode = False
additional_dump_args: tuple[Any] = (pickle.HIGHEST_PROTOCOL,)
indexer_format = pickle
indexer_dumps_unicode = False
out_suffix = '.fpickle'
globalcontext_filename = 'globalcontext.pickle'
searchindex_filename = 'searchindex.pickle'
[documentos]
class JSONHTMLBuilder(SerializingHTMLBuilder):
"""
A builder that dumps the generated HTML into JSON files.
"""
name = 'json'
epilog = __('You can now process the JSON files in %(outdir)s.')
implementation = jsonimpl
implementation_dumps_unicode = True
indexer_format = jsonimpl
indexer_dumps_unicode = True
out_suffix = '.fjson'
globalcontext_filename = 'globalcontext.json'
searchindex_filename = 'searchindex.json'
def setup(app: Sphinx) -> dict[str, Any]:
app.require_sphinx('5.0')
app.setup_extension('sphinx.builders.html')
app.add_builder(JSONHTMLBuilder)
app.add_builder(PickleHTMLBuilder)
app.add_message_catalog(__name__, path.join(package_dir, 'locales'))
return {
'version': __version__,
'parallel_read_safe': True,
'parallel_write_safe': True,
}