diff --git a/sphinx/environment/__init__.py b/sphinx/environment/__init__.py index 0fa966658..59d120e82 100644 --- a/sphinx/environment/__init__.py +++ b/sphinx/environment/__init__.py @@ -15,14 +15,22 @@ from collections import defaultdict from copy import copy from io import BytesIO from os import path +from typing import Any, Callable, Dict, Generator, IO, Iterator, List, Set, Tuple, Union + +from docutils import nodes +from docutils.nodes import Node from sphinx import addnodes +from sphinx.config import Config from sphinx.deprecation import ( RemovedInSphinx30Warning, RemovedInSphinx40Warning, deprecated_alias ) +from sphinx.domains import Domain from sphinx.environment.adapters.toctree import TocTree from sphinx.errors import SphinxError, BuildEnvironmentError, DocumentError, ExtensionError +from sphinx.events import EventManager from sphinx.locale import __ +from sphinx.project import Project from sphinx.transforms import SphinxTransformer from sphinx.util import DownloadFiles, FilenameUniqDict from sphinx.util import logging @@ -32,14 +40,8 @@ from sphinx.util.nodes import is_translatable if False: # For type annotation - from typing import Any, Callable, Dict, IO, Iterator, List, Optional, Set, Tuple, Union # NOQA - from docutils import nodes # NOQA - from sphinx.application import Sphinx # NOQA - from sphinx.builders import Builder # NOQA - from sphinx.config import Config # NOQA - from sphinx.event import EventManager # NOQA - from sphinx.domains import Domain # NOQA - from sphinx.project import Project # NOQA + from sphinx.application import Sphinx + from sphinx.builders import Builder logger = logging.getLogger(__name__) @@ -92,8 +94,7 @@ class BuildEnvironment: # --------- ENVIRONMENT INITIALIZATION ------------------------------------- - def __init__(self, app=None): - # type: (Sphinx) -> None + def __init__(self, app: "Sphinx" = None): self.app = None # type: Sphinx self.doctreedir = None # type: str self.srcdir = None # type: str @@ -191,19 +192,16 @@ class BuildEnvironment: if app: self.setup(app) - def __getstate__(self): - # type: () -> Dict + def __getstate__(self) -> Dict: """Obtains serializable data for pickling.""" __dict__ = self.__dict__.copy() __dict__.update(app=None, domains={}, events=None) # clear unpickable attributes return __dict__ - def __setstate__(self, state): - # type: (Dict) -> None + def __setstate__(self, state: Dict) -> None: self.__dict__.update(state) - def setup(self, app): - # type: (Sphinx) -> None + def setup(self, app: "Sphinx") -> None: """Set up BuildEnvironment object.""" if self.version and self.version != app.registry.get_envversion(app): raise BuildEnvironmentError(__('build environment version not current')) @@ -231,8 +229,7 @@ class BuildEnvironment: # initialie settings self._update_settings(app.config) - def _update_config(self, config): - # type: (Config) -> None + def _update_config(self, config: Config) -> None: """Update configurations by new one.""" self.config_status = CONFIG_OK if self.config is None: @@ -249,8 +246,7 @@ class BuildEnvironment: self.config = config - def _update_settings(self, config): - # type: (Config) -> None + def _update_settings(self, config: Config) -> None: """Update settings by new config.""" self.settings['input_encoding'] = config.source_encoding self.settings['trim_footnote_reference_space'] = config.trim_footnote_reference_space @@ -259,8 +255,7 @@ class BuildEnvironment: # Allow to disable by 3rd party extension (workaround) self.settings.setdefault('smart_quotes', True) - def set_versioning_method(self, method, compare): - # type: (Union[str, Callable], bool) -> None + def set_versioning_method(self, method: Union[str, Callable], compare: bool) -> None: """This sets the doctree versioning method for this environment. Versioning methods are a builder property; only builders with the same @@ -283,8 +278,7 @@ class BuildEnvironment: self.versioning_condition = condition self.versioning_compare = compare - def clear_doc(self, docname): - # type: (str) -> None + def clear_doc(self, docname: str) -> None: """Remove all traces of a source file in the inventory.""" if docname in self.all_docs: self.all_docs.pop(docname, None) @@ -294,8 +288,8 @@ class BuildEnvironment: for domain in self.domains.values(): domain.clear_doc(docname) - def merge_info_from(self, docnames, other, app): - # type: (List[str], BuildEnvironment, Sphinx) -> None + def merge_info_from(self, docnames: List[str], other: "BuildEnvironment", + app: "Sphinx") -> None: """Merge global information gathered about *docnames* while reading them from the *other* environment. @@ -312,16 +306,14 @@ class BuildEnvironment: domain.merge_domaindata(docnames, other.domaindata[domainname]) self.events.emit('env-merge-info', self, docnames, other) - def path2doc(self, filename): - # type: (str) -> Optional[str] + def path2doc(self, filename: str) -> str: """Return the docname for the filename if the file is document. *filename* should be absolute or relative to the source directory. """ return self.project.path2doc(filename) - def doc2path(self, docname, base=True, suffix=None): - # type: (str, Union[bool, str], str) -> str + def doc2path(self, docname: str, base: Union[bool, str] = True, suffix: str = None) -> str: """Return the filename for the document name. If *base* is True, return absolute path under self.srcdir. @@ -344,8 +336,7 @@ class BuildEnvironment: pathname = path.join(base, pathname) # type: ignore return pathname - def relfn2path(self, filename, docname=None): - # type: (str, str) -> Tuple[str, str] + def relfn2path(self, filename: str, docname: str = None) -> Tuple[str, str]: """Return paths to a file referenced from a document, relative to documentation root and absolute. @@ -364,13 +355,11 @@ class BuildEnvironment: return rel_fn, path.abspath(path.join(self.srcdir, rel_fn)) @property - def found_docs(self): - # type: () -> Set[str] + def found_docs(self) -> Set[str]: """contains all existing docnames.""" return self.project.docnames - def find_files(self, config, builder): - # type: (Config, Builder) -> None + def find_files(self, config: Config, builder: "Builder") -> None: """Find all source files in the source dir and put them in self.found_docs. """ @@ -398,8 +387,7 @@ class BuildEnvironment: except OSError as exc: raise DocumentError(__('Failed to scan documents in %s: %r') % (self.srcdir, exc)) - def get_outdated_files(self, config_changed): - # type: (bool) -> Tuple[Set[str], Set[str], Set[str]] + def get_outdated_files(self, config_changed: bool) -> Tuple[Set[str], Set[str], Set[str]]: """Return (added, changed, removed) sets.""" # clear all files no longer present removed = set(self.all_docs) - self.found_docs @@ -449,8 +437,7 @@ class BuildEnvironment: return added, changed, removed - def check_dependents(self, app, already): - # type: (Sphinx, Set[str]) -> Iterator[str] + def check_dependents(self, app: "Sphinx", already: Set[str]) -> Generator[str, None, None]: to_rewrite = [] # type: List[str] for docnames in self.events.emit('env-get-updated', self): to_rewrite.extend(docnames) @@ -460,8 +447,7 @@ class BuildEnvironment: # --------- SINGLE FILE READING -------------------------------------------- - def prepare_settings(self, docname): - # type: (str) -> None + def prepare_settings(self, docname: str) -> None: """Prepare to set up environment for reading.""" self.temp_data['docname'] = docname # defaults to the global default, but can be re-set in a document @@ -472,13 +458,11 @@ class BuildEnvironment: # utilities to use while reading a document @property - def docname(self): - # type: () -> str + def docname(self) -> str: """Returns the docname of the document currently being parsed.""" return self.temp_data['docname'] - def new_serialno(self, category=''): - # type: (str) -> int + def new_serialno(self, category: str = '') -> int: """Return a serial number, e.g. for index entry targets. The number is guaranteed to be unique in the current document. @@ -488,8 +472,7 @@ class BuildEnvironment: self.temp_data[key] = cur + 1 return cur - def note_dependency(self, filename): - # type: (str) -> None + def note_dependency(self, filename: str) -> None: """Add *filename* as a dependency of the current document. This means that the document will be rebuilt if this file changes. @@ -498,8 +481,7 @@ class BuildEnvironment: """ self.dependencies[self.docname].add(filename) - def note_included(self, filename): - # type: (str) -> None + def note_included(self, filename: str) -> None: """Add *filename* as a included from other document. This means the document is not orphaned. @@ -508,15 +490,13 @@ class BuildEnvironment: """ self.included[self.docname].add(self.path2doc(filename)) - def note_reread(self): - # type: () -> None + def note_reread(self) -> None: """Add the current document to the list of documents that will automatically be re-read at the next build. """ self.reread_always.add(self.docname) - def get_domain(self, domainname): - # type: (str) -> Domain + def get_domain(self, domainname: str) -> Domain: """Return the domain instance with the specified name. Raises an ExtensionError if the domain is not registered. @@ -528,8 +508,7 @@ class BuildEnvironment: # --------- RESOLVING REFERENCES AND TOCTREES ------------------------------ - def get_doctree(self, docname): - # type: (str) -> nodes.document + def get_doctree(self, docname: str) -> nodes.document: """Read the doctree for a file from the pickle and return it.""" filename = path.join(self.doctreedir, docname + '.doctree') with open(filename, 'rb') as f: @@ -538,9 +517,9 @@ class BuildEnvironment: doctree.reporter = LoggingReporter(self.doc2path(docname)) return doctree - def get_and_resolve_doctree(self, docname, builder, doctree=None, - prune_toctrees=True, includehidden=False): - # type: (str, Builder, nodes.document, bool, bool) -> nodes.document + def get_and_resolve_doctree(self, docname: str, builder: "Builder", + doctree: nodes.document = None, prune_toctrees: bool = True, + includehidden: bool = False) -> nodes.document: """Read the doctree from the pickle, resolve cross-references and toctrees and return it. """ @@ -562,9 +541,9 @@ class BuildEnvironment: return doctree - def resolve_toctree(self, docname, builder, toctree, prune=True, maxdepth=0, - titles_only=False, collapse=False, includehidden=False): - # type: (str, Builder, addnodes.toctree, bool, int, bool, bool, bool) -> nodes.Node + def resolve_toctree(self, docname: str, builder: "Builder", toctree: addnodes.toctree, + prune: bool = True, maxdepth: int = 0, titles_only: bool = False, + collapse: bool = False, includehidden: bool = False) -> Node: """Resolve a *toctree* node into individual bullet lists with titles as items, returning None (if no containing titles are found) or a new node. @@ -580,12 +559,11 @@ class BuildEnvironment: maxdepth, titles_only, collapse, includehidden) - def resolve_references(self, doctree, fromdocname, builder): - # type: (nodes.document, str, Builder) -> None + def resolve_references(self, doctree: nodes.document, fromdocname: str, + builder: "Builder") -> None: self.apply_post_transforms(doctree, fromdocname) - def apply_post_transforms(self, doctree, docname): - # type: (nodes.document, str) -> None + def apply_post_transforms(self, doctree: nodes.document, docname: str) -> None: """Apply all post-transforms.""" try: # set env.docname during applying post-transforms @@ -602,12 +580,10 @@ class BuildEnvironment: # allow custom references to be resolved self.events.emit('doctree-resolved', doctree, docname) - def collect_relations(self): - # type: () -> Dict[str, List[str]] + def collect_relations(self) -> Dict[str, List[str]]: traversed = set() - def traverse_toctree(parent, docname): - # type: (str, str) -> Iterator[Tuple[str, str]] + def traverse_toctree(parent: str, docname: str) -> Iterator[Tuple[str, str]]: if parent == docname: logger.warning(__('self referenced toctree found. Ignored.'), location=docname) return @@ -636,8 +612,7 @@ class BuildEnvironment: return relations - def check_consistency(self): - # type: () -> None + def check_consistency(self) -> None: """Do consistency checks.""" included = set().union(*self.included.values()) # type: ignore for docname in sorted(self.all_docs): @@ -660,48 +635,41 @@ class BuildEnvironment: # --------- METHODS FOR COMPATIBILITY -------------------------------------- - def update(self, config, srcdir, doctreedir): - # type: (Config, str, str) -> List[str] + def update(self, config: Config, srcdir: str, doctreedir: str) -> List[str]: warnings.warn('env.update() is deprecated. Please use builder.read() instead.', RemovedInSphinx30Warning, stacklevel=2) return self.app.builder.read() - def _read_serial(self, docnames, app): - # type: (List[str], Sphinx) -> None + def _read_serial(self, docnames: List[str], app: "Sphinx") -> None: warnings.warn('env._read_serial() is deprecated. Please use builder.read() instead.', RemovedInSphinx30Warning, stacklevel=2) return self.app.builder._read_serial(docnames) - def _read_parallel(self, docnames, app, nproc): - # type: (List[str], Sphinx, int) -> None + def _read_parallel(self, docnames: List[str], app: "Sphinx", nproc: int) -> None: warnings.warn('env._read_parallel() is deprecated. Please use builder.read() instead.', RemovedInSphinx30Warning, stacklevel=2) return self.app.builder._read_parallel(docnames, nproc) - def read_doc(self, docname, app=None): - # type: (str, Sphinx) -> None + def read_doc(self, docname: str, app: "Sphinx" = None) -> None: warnings.warn('env.read_doc() is deprecated. Please use builder.read_doc() instead.', RemovedInSphinx30Warning, stacklevel=2) self.app.builder.read_doc(docname) - def write_doctree(self, docname, doctree): - # type: (str, nodes.document) -> None + def write_doctree(self, docname: str, doctree: nodes.document) -> None: warnings.warn('env.write_doctree() is deprecated. ' 'Please use builder.write_doctree() instead.', RemovedInSphinx30Warning, stacklevel=2) self.app.builder.write_doctree(docname, doctree) @property - def _nitpick_ignore(self): - # type: () -> List[str] + def _nitpick_ignore(self) -> List[str]: warnings.warn('env._nitpick_ignore is deprecated. ' 'Please use config.nitpick_ignore instead.', RemovedInSphinx30Warning, stacklevel=2) return self.config.nitpick_ignore @staticmethod - def load(f, app=None): - # type: (IO, Sphinx) -> BuildEnvironment + def load(f: IO, app: "Sphinx" = None) -> "BuildEnvironment": warnings.warn('BuildEnvironment.load() is deprecated. ' 'Please use pickle.load() instead.', RemovedInSphinx30Warning, stacklevel=2) @@ -717,8 +685,7 @@ class BuildEnvironment: return env @classmethod - def loads(cls, string, app=None): - # type: (bytes, Sphinx) -> BuildEnvironment + def loads(cls, string: bytes, app: "Sphinx" = None) -> "BuildEnvironment": warnings.warn('BuildEnvironment.loads() is deprecated. ' 'Please use pickle.loads() instead.', RemovedInSphinx30Warning, stacklevel=2) @@ -726,8 +693,7 @@ class BuildEnvironment: return cls.load(io, app) @classmethod - def frompickle(cls, filename, app): - # type: (str, Sphinx) -> BuildEnvironment + def frompickle(cls, filename: str, app: "Sphinx") -> "BuildEnvironment": warnings.warn('BuildEnvironment.frompickle() is deprecated. ' 'Please use pickle.load() instead.', RemovedInSphinx30Warning, stacklevel=2) @@ -735,16 +701,14 @@ class BuildEnvironment: return cls.load(f, app) @staticmethod - def dump(env, f): - # type: (BuildEnvironment, IO) -> None + def dump(env: "BuildEnvironment", f: IO) -> None: warnings.warn('BuildEnvironment.dump() is deprecated. ' 'Please use pickle.dump() instead.', RemovedInSphinx30Warning, stacklevel=2) pickle.dump(env, f, pickle.HIGHEST_PROTOCOL) @classmethod - def dumps(cls, env): - # type: (BuildEnvironment) -> bytes + def dumps(cls, env: "BuildEnvironment") -> bytes: warnings.warn('BuildEnvironment.dumps() is deprecated. ' 'Please use pickle.dumps() instead.', RemovedInSphinx30Warning, stacklevel=2) @@ -752,8 +716,7 @@ class BuildEnvironment: cls.dump(env, io) return io.getvalue() - def topickle(self, filename): - # type: (str) -> None + def topickle(self, filename: str) -> None: warnings.warn('env.topickle() is deprecated. ' 'Please use pickle.dump() instead.', RemovedInSphinx30Warning, stacklevel=2) @@ -761,15 +724,14 @@ class BuildEnvironment: self.dump(self, f) @property - def versionchanges(self): - # type: () -> Dict[str, List[Tuple[str, str, int, str, str, str]]] + def versionchanges(self) -> Dict[str, List[Tuple[str, str, int, str, str, str]]]: warnings.warn('env.versionchanges() is deprecated. ' 'Please use ChangeSetDomain instead.', RemovedInSphinx30Warning, stacklevel=2) return self.domaindata['changeset']['changes'] - def note_versionchange(self, type, version, node, lineno): - # type: (str, str, addnodes.versionmodified, int) -> None + def note_versionchange(self, type: str, version: str, + node: addnodes.versionmodified, lineno: int) -> None: warnings.warn('env.note_versionchange() is deprecated. ' 'Please use ChangeSetDomain.note_changeset() instead.', RemovedInSphinx30Warning, stacklevel=2)