summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sphinx/builders/__init__.py131
1 files changed, 46 insertions, 85 deletions
diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py
index 8eaa0e215..546b9b0a8 100644
--- a/sphinx/builders/__init__.py
+++ b/sphinx/builders/__init__.py
@@ -11,22 +11,27 @@
import pickle
import time
from os import path
+from typing import Any, Dict, Iterable, List, Sequence, Set, Tuple, Type, Union
from docutils import nodes
+from docutils.nodes import Node
-from sphinx.environment import CONFIG_OK, CONFIG_CHANGED_REASON
+from sphinx.config import Config
+from sphinx.environment import BuildEnvironment, CONFIG_OK, CONFIG_CHANGED_REASON
from sphinx.environment.adapters.asset import ImageAdapter
from sphinx.errors import SphinxError
+from sphinx.events import EventManager
from sphinx.io import read_doc
from sphinx.locale import __
from sphinx.util import import_object, logging, rst, progress_message, status_iterator
from sphinx.util.build_phase import BuildPhase
from sphinx.util.console import bold # type: ignore
from sphinx.util.docutils import sphinx_domains
-from sphinx.util.i18n import CatalogRepository, docname_to_domain
+from sphinx.util.i18n import CatalogInfo, CatalogRepository, docname_to_domain
from sphinx.util.osutil import SEP, ensuredir, relative_uri, relpath
from sphinx.util.parallel import ParallelTasks, SerialTasks, make_chunks, \
parallel_available
+from sphinx.util.tags import Tags
# side effect: registers roles and directives
from sphinx import roles # noqa
@@ -39,13 +44,7 @@ except ImportError:
if False:
# For type annotation
- from typing import Any, Dict, Iterable, List, Sequence, Set, Tuple, Type, Union # NOQA
- from sphinx.application import Sphinx # NOQA
- from sphinx.config import Config # NOQA
- from sphinx.environment import BuildEnvironment # NOQA
- from sphinx.events import EventManager # NOQA
- from sphinx.util.i18n import CatalogInfo # NOQA
- from sphinx.util.tags import Tags # NOQA
+ from sphinx.application import Sphinx
logger = logging.getLogger(__name__)
@@ -84,8 +83,7 @@ class Builder:
#: The builder supports data URIs or not.
supported_data_uri_images = False
- def __init__(self, app):
- # type: (Sphinx) -> None
+ def __init__(self, app: "Sphinx") -> None:
self.srcdir = app.srcdir
self.confdir = app.confdir
self.outdir = app.outdir
@@ -113,20 +111,17 @@ class Builder:
self.parallel_ok = False
self.finish_tasks = None # type: Any
- def set_environment(self, env):
- # type: (BuildEnvironment) -> None
+ def set_environment(self, env: BuildEnvironment) -> None:
"""Store BuildEnvironment object."""
self.env = env
self.env.set_versioning_method(self.versioning_method,
self.versioning_compare)
- def get_translator_class(self, *args):
- # type: (Any) -> Type[nodes.NodeVisitor]
+ def get_translator_class(self, *args) -> Type[nodes.NodeVisitor]:
"""Return a class of translator."""
return self.app.registry.get_translator_class(self)
- def create_translator(self, *args):
- # type: (Any) -> nodes.NodeVisitor
+ def create_translator(self, *args) -> nodes.NodeVisitor:
"""Return an instance of translator.
This method returns an instance of ``default_translator_class`` by default.
@@ -135,15 +130,13 @@ class Builder:
return self.app.registry.create_translator(self, *args)
# helper methods
- def init(self):
- # type: () -> None
+ def init(self) -> None:
"""Load necessary templates and perform initialization. The default
implementation does nothing.
"""
pass
- def create_template_bridge(self):
- # type: () -> None
+ def create_template_bridge(self) -> None:
"""Return the template bridge configured."""
if self.config.template_bridge:
self.templates = import_object(self.config.template_bridge,
@@ -152,8 +145,7 @@ class Builder:
from sphinx.jinja2glue import BuiltinTemplateLoader
self.templates = BuiltinTemplateLoader()
- def get_target_uri(self, docname, typ=None):
- # type: (str, str) -> str
+ def get_target_uri(self, docname: str, typ: str = None) -> str:
"""Return the target URI for a document name.
*typ* can be used to qualify the link characteristic for individual
@@ -161,8 +153,7 @@ class Builder:
"""
raise NotImplementedError
- def get_relative_uri(self, from_, to, typ=None):
- # type: (str, str, str) -> str
+ def get_relative_uri(self, from_: str, to: str, typ: str = None) -> str:
"""Return a relative URI between two source filenames.
May raise environment.NoUri if there's no way to return a sensible URI.
@@ -170,8 +161,7 @@ class Builder:
return relative_uri(self.get_target_uri(from_),
self.get_target_uri(to, typ))
- def get_outdated_docs(self):
- # type: () -> Union[str, Iterable[str]]
+ def get_outdated_docs(self) -> Union[str, Iterable[str]]:
"""Return an iterable of output files that are outdated, or a string
describing what an update build will build.
@@ -181,13 +171,11 @@ class Builder:
"""
raise NotImplementedError
- def get_asset_paths(self):
- # type: () -> List[str]
+ def get_asset_paths(self) -> List[str]:
"""Return list of paths for assets (ex. templates, CSS, etc.)."""
return []
- def post_process_images(self, doctree):
- # type: (nodes.Node) -> None
+ def post_process_images(self, doctree: Node) -> None:
"""Pick the best candidate for all image URIs."""
images = ImageAdapter(self.env)
for node in doctree.traverse(nodes.image):
@@ -220,13 +208,11 @@ class Builder:
# compile po methods
- def compile_catalogs(self, catalogs, message):
- # type: (Set[CatalogInfo], str) -> None
+ def compile_catalogs(self, catalogs: Set[CatalogInfo], message: str) -> None:
if not self.config.gettext_auto_build:
return
- def cat2relpath(cat):
- # type: (CatalogInfo) -> str
+ def cat2relpath(cat: CatalogInfo) -> str:
return relpath(cat.mo_path, self.env.srcdir).replace(path.sep, SEP)
logger.info(bold(__('building [mo]: ')) + message)
@@ -235,17 +221,14 @@ class Builder:
stringify_func=cat2relpath):
catalog.write_mo(self.config.language)
- def compile_all_catalogs(self):
- # type: () -> None
+ def compile_all_catalogs(self) -> None:
repo = CatalogRepository(self.srcdir, self.config.locale_dirs,
self.config.language, self.config.source_encoding)
message = __('all of %d po files') % len(list(repo.catalogs))
self.compile_catalogs(set(repo.catalogs), message)
- def compile_specific_catalogs(self, specified_files):
- # type: (List[str]) -> None
- def to_domain(fpath):
- # type: (str) -> str
+ def compile_specific_catalogs(self, specified_files: List[str]) -> None:
+ def to_domain(fpath: str) -> str:
docname = self.env.path2doc(path.abspath(fpath))
if docname:
return docname_to_domain(docname, self.config.gettext_compact)
@@ -262,8 +245,7 @@ class Builder:
message = __('targets for %d po files that are specified') % len(catalogs)
self.compile_catalogs(catalogs, message)
- def compile_update_catalogs(self):
- # type: () -> None
+ def compile_update_catalogs(self) -> None:
repo = CatalogRepository(self.srcdir, self.config.locale_dirs,
self.config.language, self.config.source_encoding)
catalogs = {c for c in repo.catalogs if c.is_outdated()}
@@ -272,13 +254,11 @@ class Builder:
# build methods
- def build_all(self):
- # type: () -> None
+ def build_all(self) -> None:
"""Build all source files."""
self.build(None, summary=__('all source files'), method='all')
- def build_specific(self, filenames):
- # type: (List[str]) -> None
+ def build_specific(self, filenames: List[str]) -> None:
"""Only rebuild as much as needed for changes in the *filenames*."""
# bring the filenames to the canonical format, that is,
# relative to the source directory and without source_suffix.
@@ -306,8 +286,7 @@ class Builder:
self.build(to_write, method='specific',
summary=__('%d source files given on command line') % len(to_write))
- def build_update(self):
- # type: () -> None
+ def build_update(self) -> None:
"""Only rebuild what was changed or added since last build."""
to_build = self.get_outdated_docs()
if isinstance(to_build, str):
@@ -318,8 +297,7 @@ class Builder:
summary=__('targets for %d source files that are out of date') %
len(to_build))
- def build(self, docnames, summary=None, method='update'):
- # type: (Iterable[str], str, str) -> None
+ def build(self, docnames: Iterable[str], summary: str = None, method: str = 'update') -> None: # NOQA
"""Main build method.
First updates the environment, and then calls :meth:`write`.
@@ -387,8 +365,7 @@ class Builder:
# wait for all tasks
self.finish_tasks.join()
- def read(self):
- # type: () -> List[str]
+ def read(self) -> List[str]:
"""(Re-)read all files new or changed since last update.
Store all environment docnames in the canonical format (ie using SEP as
@@ -450,8 +427,7 @@ class Builder:
return sorted(docnames)
- def _read_serial(self, docnames):
- # type: (List[str]) -> None
+ def _read_serial(self, docnames: List[str]) -> None:
for docname in status_iterator(docnames, __('reading sources... '), "purple",
len(docnames), self.app.verbosity):
# remove all inventory entries for that file
@@ -459,23 +435,20 @@ class Builder:
self.env.clear_doc(docname)
self.read_doc(docname)
- def _read_parallel(self, docnames, nproc):
- # type: (List[str], int) -> None
+ def _read_parallel(self, docnames: List[str], nproc: int) -> None:
# clear all outdated docs at once
for docname in docnames:
self.events.emit('env-purge-doc', self.env, docname)
self.env.clear_doc(docname)
- def read_process(docs):
- # type: (List[str]) -> bytes
+ def read_process(docs: List[str]) -> bytes:
self.env.app = self.app
for docname in docs:
self.read_doc(docname)
# allow pickling self to send it back
return pickle.dumps(self.env, pickle.HIGHEST_PROTOCOL)
- def merge(docs, otherenv):
- # type: (List[str], bytes) -> None
+ def merge(docs: List[str], otherenv: bytes) -> None:
env = pickle.loads(otherenv)
self.env.merge_info_from(docs, env, self.app)
@@ -490,8 +463,7 @@ class Builder:
logger.info(bold(__('waiting for workers...')))
tasks.join()
- def read_doc(self, docname):
- # type: (str) -> None
+ def read_doc(self, docname: str) -> None:
"""Parse a file and add/update inventory entries for the doctree."""
self.env.prepare_settings(docname)
@@ -516,8 +488,7 @@ class Builder:
self.write_doctree(docname, doctree)
- def write_doctree(self, docname, doctree):
- # type: (str, nodes.document) -> None
+ def write_doctree(self, docname: str, doctree: nodes.document) -> None:
"""Write the doctree to a file."""
# make it picklable
doctree.reporter = None
@@ -531,8 +502,7 @@ class Builder:
with open(doctree_filename, 'wb') as f:
pickle.dump(doctree, f, pickle.HIGHEST_PROTOCOL)
- def write(self, build_docnames, updated_docnames, method='update'):
- # type: (Iterable[str], Sequence[str], str) -> None
+ def write(self, build_docnames: Iterable[str], updated_docnames: Sequence[str], method: str = 'update') -> None: # NOQA
if build_docnames is None or build_docnames == ['__all__']:
# build_all
build_docnames = self.env.found_docs
@@ -561,8 +531,7 @@ class Builder:
else:
self._write_serial(sorted(docnames))
- def _write_serial(self, docnames):
- # type: (Sequence[str]) -> None
+ def _write_serial(self, docnames: Sequence[str]) -> None:
with logging.pending_warnings():
for docname in status_iterator(docnames, __('writing output... '), "darkgreen",
len(docnames), self.app.verbosity):
@@ -572,10 +541,8 @@ class Builder:
self.write_doc_serialized(docname, doctree)
self.write_doc(docname, doctree)
- def _write_parallel(self, docnames, nproc):
- # type: (Sequence[str], int) -> None
- def write_process(docs):
- # type: (List[Tuple[str, nodes.document]]) -> None
+ def _write_parallel(self, docnames: Sequence[str], nproc: int) -> None:
+ def write_process(docs: List[Tuple[str, nodes.document]]) -> None:
self.app.phase = BuildPhase.WRITING
for docname, doctree in docs:
self.write_doc(docname, doctree)
@@ -605,41 +572,35 @@ class Builder:
logger.info(bold(__('waiting for workers...')))
tasks.join()
- def prepare_writing(self, docnames):
- # type: (Set[str]) -> None
+ def prepare_writing(self, docnames: Set[str]) -> None:
"""A place where you can add logic before :meth:`write_doc` is run"""
raise NotImplementedError
- def write_doc(self, docname, doctree):
- # type: (str, nodes.document) -> None
+ def write_doc(self, docname: str, doctree: nodes.document) -> None:
"""Where you actually write something to the filesystem."""
raise NotImplementedError
- def write_doc_serialized(self, docname, doctree):
- # type: (str, nodes.document) -> None
+ def write_doc_serialized(self, docname: str, doctree: nodes.document) -> None:
"""Handle parts of write_doc that must be called in the main process
if parallel build is active.
"""
pass
- def finish(self):
- # type: () -> None
+ def finish(self) -> None:
"""Finish the building process.
The default implementation does nothing.
"""
pass
- def cleanup(self):
- # type: () -> None
+ def cleanup(self) -> None:
"""Cleanup any resources.
The default implementation does nothing.
"""
pass
- def get_builder_config(self, option, default):
- # type: (str, str) -> Any
+ def get_builder_config(self, option: str, default: str) -> Any:
"""Return a builder specific option.
This method allows customization of common builder settings by