Merge branch '2.0'

This commit is contained in:
Takeshi KOMIYA 2019-07-06 13:38:15 +09:00
commit 089046979f
12 changed files with 181 additions and 264 deletions

View File

@ -14,12 +14,20 @@ import warnings
from collections import defaultdict from collections import defaultdict
from copy import copy from copy import copy
from os import path from os import path
from typing import Any, Callable, Dict, Generator, Iterator, List, Set, Tuple, Union
from docutils import nodes
from docutils.nodes import Node
from sphinx import addnodes from sphinx import addnodes
from sphinx.config import Config
from sphinx.deprecation import RemovedInSphinx40Warning from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.domains import Domain
from sphinx.environment.adapters.toctree import TocTree from sphinx.environment.adapters.toctree import TocTree
from sphinx.errors import SphinxError, BuildEnvironmentError, DocumentError, ExtensionError from sphinx.errors import SphinxError, BuildEnvironmentError, DocumentError, ExtensionError
from sphinx.events import EventManager
from sphinx.locale import __ from sphinx.locale import __
from sphinx.project import Project
from sphinx.transforms import SphinxTransformer from sphinx.transforms import SphinxTransformer
from sphinx.util import DownloadFiles, FilenameUniqDict from sphinx.util import DownloadFiles, FilenameUniqDict
from sphinx.util import logging from sphinx.util import logging
@ -29,14 +37,9 @@ from sphinx.util.nodes import is_translatable
if False: if False:
# For type annotation # For type annotation
from typing import Any, Callable, Dict, IO, Iterator, List, Optional, Set, Tuple, Union # NOQA from sphinx.application import Sphinx
from docutils import nodes # NOQA from sphinx.builders import Builder
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
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -89,8 +92,7 @@ class BuildEnvironment:
# --------- ENVIRONMENT INITIALIZATION ------------------------------------- # --------- ENVIRONMENT INITIALIZATION -------------------------------------
def __init__(self, app=None): def __init__(self, app: "Sphinx" = None):
# type: (Sphinx) -> None
self.app = None # type: Sphinx self.app = None # type: Sphinx
self.doctreedir = None # type: str self.doctreedir = None # type: str
self.srcdir = None # type: str self.srcdir = None # type: str
@ -188,19 +190,16 @@ class BuildEnvironment:
if app: if app:
self.setup(app) self.setup(app)
def __getstate__(self): def __getstate__(self) -> Dict:
# type: () -> Dict
"""Obtains serializable data for pickling.""" """Obtains serializable data for pickling."""
__dict__ = self.__dict__.copy() __dict__ = self.__dict__.copy()
__dict__.update(app=None, domains={}, events=None) # clear unpickable attributes __dict__.update(app=None, domains={}, events=None) # clear unpickable attributes
return __dict__ return __dict__
def __setstate__(self, state): def __setstate__(self, state: Dict) -> None:
# type: (Dict) -> None
self.__dict__.update(state) self.__dict__.update(state)
def setup(self, app): def setup(self, app: "Sphinx") -> None:
# type: (Sphinx) -> None
"""Set up BuildEnvironment object.""" """Set up BuildEnvironment object."""
if self.version and self.version != app.registry.get_envversion(app): if self.version and self.version != app.registry.get_envversion(app):
raise BuildEnvironmentError(__('build environment version not current')) raise BuildEnvironmentError(__('build environment version not current'))
@ -228,8 +227,7 @@ class BuildEnvironment:
# initialie settings # initialie settings
self._update_settings(app.config) self._update_settings(app.config)
def _update_config(self, config): def _update_config(self, config: Config) -> None:
# type: (Config) -> None
"""Update configurations by new one.""" """Update configurations by new one."""
self.config_status = CONFIG_OK self.config_status = CONFIG_OK
if self.config is None: if self.config is None:
@ -246,8 +244,7 @@ class BuildEnvironment:
self.config = config self.config = config
def _update_settings(self, config): def _update_settings(self, config: Config) -> None:
# type: (Config) -> None
"""Update settings by new config.""" """Update settings by new config."""
self.settings['input_encoding'] = config.source_encoding self.settings['input_encoding'] = config.source_encoding
self.settings['trim_footnote_reference_space'] = config.trim_footnote_reference_space self.settings['trim_footnote_reference_space'] = config.trim_footnote_reference_space
@ -256,8 +253,7 @@ class BuildEnvironment:
# Allow to disable by 3rd party extension (workaround) # Allow to disable by 3rd party extension (workaround)
self.settings.setdefault('smart_quotes', True) self.settings.setdefault('smart_quotes', True)
def set_versioning_method(self, method, compare): def set_versioning_method(self, method: Union[str, Callable], compare: bool) -> None:
# type: (Union[str, Callable], bool) -> None
"""This sets the doctree versioning method for this environment. """This sets the doctree versioning method for this environment.
Versioning methods are a builder property; only builders with the same Versioning methods are a builder property; only builders with the same
@ -280,8 +276,7 @@ class BuildEnvironment:
self.versioning_condition = condition self.versioning_condition = condition
self.versioning_compare = compare self.versioning_compare = compare
def clear_doc(self, docname): def clear_doc(self, docname: str) -> None:
# type: (str) -> None
"""Remove all traces of a source file in the inventory.""" """Remove all traces of a source file in the inventory."""
if docname in self.all_docs: if docname in self.all_docs:
self.all_docs.pop(docname, None) self.all_docs.pop(docname, None)
@ -291,8 +286,8 @@ class BuildEnvironment:
for domain in self.domains.values(): for domain in self.domains.values():
domain.clear_doc(docname) domain.clear_doc(docname)
def merge_info_from(self, docnames, other, app): def merge_info_from(self, docnames: List[str], other: "BuildEnvironment",
# type: (List[str], BuildEnvironment, Sphinx) -> None app: "Sphinx") -> None:
"""Merge global information gathered about *docnames* while reading them """Merge global information gathered about *docnames* while reading them
from the *other* environment. from the *other* environment.
@ -309,16 +304,14 @@ class BuildEnvironment:
domain.merge_domaindata(docnames, other.domaindata[domainname]) domain.merge_domaindata(docnames, other.domaindata[domainname])
self.events.emit('env-merge-info', self, docnames, other) self.events.emit('env-merge-info', self, docnames, other)
def path2doc(self, filename): def path2doc(self, filename: str) -> str:
# type: (str) -> Optional[str]
"""Return the docname for the filename if the file is document. """Return the docname for the filename if the file is document.
*filename* should be absolute or relative to the source directory. *filename* should be absolute or relative to the source directory.
""" """
return self.project.path2doc(filename) return self.project.path2doc(filename)
def doc2path(self, docname, base=True, suffix=None): def doc2path(self, docname: str, base: Union[bool, str] = True, suffix: str = None) -> str:
# type: (str, Union[bool, str], str) -> str
"""Return the filename for the document name. """Return the filename for the document name.
If *base* is True, return absolute path under self.srcdir. If *base* is True, return absolute path under self.srcdir.
@ -341,8 +334,7 @@ class BuildEnvironment:
pathname = path.join(base, pathname) # type: ignore pathname = path.join(base, pathname) # type: ignore
return pathname return pathname
def relfn2path(self, filename, docname=None): def relfn2path(self, filename: str, docname: str = None) -> Tuple[str, str]:
# type: (str, str) -> Tuple[str, str]
"""Return paths to a file referenced from a document, relative to """Return paths to a file referenced from a document, relative to
documentation root and absolute. documentation root and absolute.
@ -361,13 +353,11 @@ class BuildEnvironment:
return rel_fn, path.abspath(path.join(self.srcdir, rel_fn)) return rel_fn, path.abspath(path.join(self.srcdir, rel_fn))
@property @property
def found_docs(self): def found_docs(self) -> Set[str]:
# type: () -> Set[str]
"""contains all existing docnames.""" """contains all existing docnames."""
return self.project.docnames return self.project.docnames
def find_files(self, config, builder): def find_files(self, config: Config, builder: "Builder") -> None:
# type: (Config, Builder) -> None
"""Find all source files in the source dir and put them in """Find all source files in the source dir and put them in
self.found_docs. self.found_docs.
""" """
@ -395,8 +385,7 @@ class BuildEnvironment:
except OSError as exc: except OSError as exc:
raise DocumentError(__('Failed to scan documents in %s: %r') % (self.srcdir, exc)) raise DocumentError(__('Failed to scan documents in %s: %r') % (self.srcdir, exc))
def get_outdated_files(self, config_changed): def get_outdated_files(self, config_changed: bool) -> Tuple[Set[str], Set[str], Set[str]]:
# type: (bool) -> Tuple[Set[str], Set[str], Set[str]]
"""Return (added, changed, removed) sets.""" """Return (added, changed, removed) sets."""
# clear all files no longer present # clear all files no longer present
removed = set(self.all_docs) - self.found_docs removed = set(self.all_docs) - self.found_docs
@ -446,8 +435,7 @@ class BuildEnvironment:
return added, changed, removed return added, changed, removed
def check_dependents(self, app, already): def check_dependents(self, app: "Sphinx", already: Set[str]) -> Generator[str, None, None]:
# type: (Sphinx, Set[str]) -> Iterator[str]
to_rewrite = [] # type: List[str] to_rewrite = [] # type: List[str]
for docnames in self.events.emit('env-get-updated', self): for docnames in self.events.emit('env-get-updated', self):
to_rewrite.extend(docnames) to_rewrite.extend(docnames)
@ -457,8 +445,7 @@ class BuildEnvironment:
# --------- SINGLE FILE READING -------------------------------------------- # --------- SINGLE FILE READING --------------------------------------------
def prepare_settings(self, docname): def prepare_settings(self, docname: str) -> None:
# type: (str) -> None
"""Prepare to set up environment for reading.""" """Prepare to set up environment for reading."""
self.temp_data['docname'] = docname self.temp_data['docname'] = docname
# defaults to the global default, but can be re-set in a document # defaults to the global default, but can be re-set in a document
@ -469,13 +456,11 @@ class BuildEnvironment:
# utilities to use while reading a document # utilities to use while reading a document
@property @property
def docname(self): def docname(self) -> str:
# type: () -> str
"""Returns the docname of the document currently being parsed.""" """Returns the docname of the document currently being parsed."""
return self.temp_data['docname'] return self.temp_data['docname']
def new_serialno(self, category=''): def new_serialno(self, category: str = '') -> int:
# type: (str) -> int
"""Return a serial number, e.g. for index entry targets. """Return a serial number, e.g. for index entry targets.
The number is guaranteed to be unique in the current document. The number is guaranteed to be unique in the current document.
@ -485,8 +470,7 @@ class BuildEnvironment:
self.temp_data[key] = cur + 1 self.temp_data[key] = cur + 1
return cur return cur
def note_dependency(self, filename): def note_dependency(self, filename: str) -> None:
# type: (str) -> None
"""Add *filename* as a dependency of the current document. """Add *filename* as a dependency of the current document.
This means that the document will be rebuilt if this file changes. This means that the document will be rebuilt if this file changes.
@ -495,8 +479,7 @@ class BuildEnvironment:
""" """
self.dependencies[self.docname].add(filename) self.dependencies[self.docname].add(filename)
def note_included(self, filename): def note_included(self, filename: str) -> None:
# type: (str) -> None
"""Add *filename* as a included from other document. """Add *filename* as a included from other document.
This means the document is not orphaned. This means the document is not orphaned.
@ -505,15 +488,13 @@ class BuildEnvironment:
""" """
self.included[self.docname].add(self.path2doc(filename)) self.included[self.docname].add(self.path2doc(filename))
def note_reread(self): def note_reread(self) -> None:
# type: () -> None
"""Add the current document to the list of documents that will """Add the current document to the list of documents that will
automatically be re-read at the next build. automatically be re-read at the next build.
""" """
self.reread_always.add(self.docname) self.reread_always.add(self.docname)
def get_domain(self, domainname): def get_domain(self, domainname: str) -> Domain:
# type: (str) -> Domain
"""Return the domain instance with the specified name. """Return the domain instance with the specified name.
Raises an ExtensionError if the domain is not registered. Raises an ExtensionError if the domain is not registered.
@ -525,8 +506,7 @@ class BuildEnvironment:
# --------- RESOLVING REFERENCES AND TOCTREES ------------------------------ # --------- RESOLVING REFERENCES AND TOCTREES ------------------------------
def get_doctree(self, docname): def get_doctree(self, docname: str) -> nodes.document:
# type: (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."""
filename = path.join(self.doctreedir, docname + '.doctree') filename = path.join(self.doctreedir, docname + '.doctree')
with open(filename, 'rb') as f: with open(filename, 'rb') as f:
@ -535,9 +515,9 @@ class BuildEnvironment:
doctree.reporter = LoggingReporter(self.doc2path(docname)) doctree.reporter = LoggingReporter(self.doc2path(docname))
return doctree return doctree
def get_and_resolve_doctree(self, docname, builder, doctree=None, def get_and_resolve_doctree(self, docname: str, builder: "Builder",
prune_toctrees=True, includehidden=False): doctree: nodes.document = None, prune_toctrees: bool = True,
# type: (str, Builder, nodes.document, bool, bool) -> nodes.document includehidden: bool = False) -> nodes.document:
"""Read the doctree from the pickle, resolve cross-references and """Read the doctree from the pickle, resolve cross-references and
toctrees and return it. toctrees and return it.
""" """
@ -559,9 +539,9 @@ class BuildEnvironment:
return doctree return doctree
def resolve_toctree(self, docname, builder, toctree, prune=True, maxdepth=0, def resolve_toctree(self, docname: str, builder: "Builder", toctree: addnodes.toctree,
titles_only=False, collapse=False, includehidden=False): prune: bool = True, maxdepth: int = 0, titles_only: bool = False,
# type: (str, Builder, addnodes.toctree, bool, int, bool, bool, bool) -> nodes.Node collapse: bool = False, includehidden: bool = False) -> Node:
"""Resolve a *toctree* node into individual bullet lists with titles """Resolve a *toctree* node into individual bullet lists with titles
as items, returning None (if no containing titles are found) or as items, returning None (if no containing titles are found) or
a new node. a new node.
@ -577,12 +557,11 @@ class BuildEnvironment:
maxdepth, titles_only, collapse, maxdepth, titles_only, collapse,
includehidden) includehidden)
def resolve_references(self, doctree, fromdocname, builder): def resolve_references(self, doctree: nodes.document, fromdocname: str,
# type: (nodes.document, str, Builder) -> None builder: "Builder") -> None:
self.apply_post_transforms(doctree, fromdocname) self.apply_post_transforms(doctree, fromdocname)
def apply_post_transforms(self, doctree, docname): def apply_post_transforms(self, doctree: nodes.document, docname: str) -> None:
# type: (nodes.document, str) -> None
"""Apply all post-transforms.""" """Apply all post-transforms."""
try: try:
# set env.docname during applying post-transforms # set env.docname during applying post-transforms
@ -599,12 +578,10 @@ class BuildEnvironment:
# allow custom references to be resolved # allow custom references to be resolved
self.events.emit('doctree-resolved', doctree, docname) self.events.emit('doctree-resolved', doctree, docname)
def collect_relations(self): def collect_relations(self) -> Dict[str, List[str]]:
# type: () -> Dict[str, List[str]]
traversed = set() traversed = set()
def traverse_toctree(parent, docname): def traverse_toctree(parent: str, docname: str) -> Iterator[Tuple[str, str]]:
# type: (str, str) -> Iterator[Tuple[str, str]]
if parent == docname: if parent == docname:
logger.warning(__('self referenced toctree found. Ignored.'), location=docname) logger.warning(__('self referenced toctree found. Ignored.'), location=docname)
return return
@ -633,8 +610,7 @@ class BuildEnvironment:
return relations return relations
def check_consistency(self): def check_consistency(self) -> None:
# type: () -> None
"""Do consistency checks.""" """Do consistency checks."""
included = set().union(*self.included.values()) # type: ignore included = set().union(*self.included.values()) # type: ignore
for docname in sorted(self.all_docs): for docname in sorted(self.all_docs):

View File

@ -8,18 +8,14 @@
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
if False: from sphinx.environment import BuildEnvironment
# For type annotation
from sphinx.environment import BuildEnvironment # NOQA
class ImageAdapter: class ImageAdapter:
def __init__(self, env): def __init__(self, env: BuildEnvironment) -> None:
# type: (BuildEnvironment) -> None
self.env = env self.env = env
def get_original_image_uri(self, name): def get_original_image_uri(self, name: str) -> str:
# type: (str) -> str
"""Get the original image URI.""" """Get the original image URI."""
while name in self.env.original_image_uri: while name in self.env.original_image_uri:
name = self.env.original_image_uri[name] name = self.env.original_image_uri[name]

View File

@ -11,33 +11,30 @@ import bisect
import re import re
import unicodedata import unicodedata
from itertools import groupby from itertools import groupby
from typing import Any, Dict, Pattern, List, Tuple
from sphinx.builders import Builder
from sphinx.environment import BuildEnvironment
from sphinx.errors import NoUri from sphinx.errors import NoUri
from sphinx.locale import _, __ from sphinx.locale import _, __
from sphinx.util import split_into, logging from sphinx.util import split_into, logging
if False:
# For type annotation
from typing import Any, Dict, Pattern, List, Tuple # NOQA
from sphinx.builders import Builder # NOQA
from sphinx.environment import BuildEnvironment # NOQA
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class IndexEntries: class IndexEntries:
def __init__(self, env): def __init__(self, env: BuildEnvironment) -> None:
# type: (BuildEnvironment) -> None
self.env = env self.env = env
def create_index(self, builder, group_entries=True, def create_index(self, builder: Builder, group_entries: bool = True,
_fixre=re.compile(r'(.*) ([(][^()]*[)])')): _fixre: Pattern = re.compile(r'(.*) ([(][^()]*[)])')
# type: (Builder, bool, Pattern) -> List[Tuple[str, List[Tuple[str, Any]]]] ) -> List[Tuple[str, List[Tuple[str, Any]]]]:
"""Create the real index from the collected index entries.""" """Create the real index from the collected index entries."""
new = {} # type: Dict[str, List] new = {} # type: Dict[str, List]
def add_entry(word, subword, main, link=True, dic=new, key=None): def add_entry(word: str, subword: str, main: str, link: bool = True,
# type: (str, str, str, bool, Dict, str) -> None dic: Dict = new, key: str = None) -> None:
# Force the word to be unicode if it's a ASCII bytestring. # Force the word to be unicode if it's a ASCII bytestring.
# This will solve problems with unicode normalization later. # This will solve problems with unicode normalization later.
# For instance the RFC role will add bytestrings at the moment # For instance the RFC role will add bytestrings at the moment
@ -91,8 +88,7 @@ class IndexEntries:
# sort the index entries; put all symbols at the front, even those # sort the index entries; put all symbols at the front, even those
# following the letters in ASCII, this is where the chr(127) comes from # following the letters in ASCII, this is where the chr(127) comes from
def keyfunc(entry): def keyfunc(entry: Tuple[str, List]) -> Tuple[str, str]:
# type: (Tuple[str, List]) -> Tuple[str, str]
key, (void, void, category_key) = entry key, (void, void, category_key) = entry
if category_key: if category_key:
# using specified category key to sort # using specified category key to sort
@ -138,8 +134,7 @@ class IndexEntries:
i += 1 i += 1
# group the entries by letter # group the entries by letter
def keyfunc2(item): def keyfunc2(item: Tuple[str, List]) -> str:
# type: (Tuple[str, List]) -> str
# hack: mutating the subitems dicts to a list in the keyfunc # hack: mutating the subitems dicts to a list in the keyfunc
k, v = item k, v = item
v[1] = sorted((si, se) for (si, (se, void, void)) in v[1].items()) v[1] = sorted((si, se) for (si, (se, void, void)) in v[1].items())

View File

@ -8,9 +8,11 @@
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
from typing import Iterable, cast from typing import cast
from typing import Iterable, List
from docutils import nodes from docutils import nodes
from docutils.nodes import Element, Node
from sphinx import addnodes from sphinx import addnodes
from sphinx.locale import __ from sphinx.locale import __
@ -20,20 +22,18 @@ from sphinx.util.nodes import clean_astext, process_only_nodes
if False: if False:
# For type annotation # For type annotation
from typing import Any, Dict, List # NOQA from sphinx.builders import Builder
from sphinx.builders import Builder # NOQA from sphinx.environment import BuildEnvironment
from sphinx.environment import BuildEnvironment # NOQA
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class TocTree: class TocTree:
def __init__(self, env): def __init__(self, env: "BuildEnvironment") -> None:
# type: (BuildEnvironment) -> None
self.env = env self.env = env
def note(self, docname, toctreenode): def note(self, docname: str, toctreenode: addnodes.toctree) -> None:
# type: (str, addnodes.toctree) -> None
"""Note a TOC tree directive in a document and gather information about """Note a TOC tree directive in a document and gather information about
file relations from it. file relations from it.
""" """
@ -48,9 +48,9 @@ class TocTree:
self.env.files_to_rebuild.setdefault(includefile, set()).add(docname) self.env.files_to_rebuild.setdefault(includefile, set()).add(docname)
self.env.toctree_includes.setdefault(docname, []).extend(includefiles) self.env.toctree_includes.setdefault(docname, []).extend(includefiles)
def resolve(self, docname, builder, toctree, prune=True, maxdepth=0, def resolve(self, docname: str, builder: "Builder", toctree: addnodes.toctree,
titles_only=False, collapse=False, includehidden=False): prune: bool = True, maxdepth: int = 0, titles_only: bool = False,
# type: (str, Builder, addnodes.toctree, bool, int, bool, bool, bool) -> nodes.Element collapse: bool = False, includehidden: bool = False) -> Element:
"""Resolve a *toctree* node into individual bullet lists with titles """Resolve a *toctree* node into individual bullet lists with titles
as items, returning None (if no containing titles are found) or as items, returning None (if no containing titles are found) or
a new node. a new node.
@ -86,8 +86,7 @@ class TocTree:
toctree_ancestors = self.get_toctree_ancestors(docname) toctree_ancestors = self.get_toctree_ancestors(docname)
excluded = Matcher(self.env.config.exclude_patterns) excluded = Matcher(self.env.config.exclude_patterns)
def _toctree_add_classes(node, depth): def _toctree_add_classes(node: Element, depth: int) -> None:
# type: (nodes.Element, int) -> None
"""Add 'toctree-l%d' and 'current' classes to the toctree.""" """Add 'toctree-l%d' and 'current' classes to the toctree."""
for subnode in node.children: for subnode in node.children:
if isinstance(subnode, (addnodes.compact_paragraph, if isinstance(subnode, (addnodes.compact_paragraph,
@ -105,7 +104,7 @@ class TocTree:
if not subnode['anchorname']: if not subnode['anchorname']:
# give the whole branch a 'current' class # give the whole branch a 'current' class
# (useful for styling it differently) # (useful for styling it differently)
branchnode = subnode # type: nodes.Element branchnode = subnode # type: Element
while branchnode: while branchnode:
branchnode['classes'].append('current') branchnode['classes'].append('current')
branchnode = branchnode.parent branchnode = branchnode.parent
@ -117,11 +116,12 @@ class TocTree:
subnode['iscurrent'] = True subnode['iscurrent'] = True
subnode = subnode.parent subnode = subnode.parent
def _entries_from_toctree(toctreenode, parents, separate=False, subtree=False): def _entries_from_toctree(toctreenode: addnodes.toctree, parents: List[str],
# type: (addnodes.toctree, List[str], bool, bool) -> List[nodes.Element] separate: bool = False, subtree: bool = False
) -> List[Element]:
"""Return TOC entries for a toctree node.""" """Return TOC entries for a toctree node."""
refs = [(e[0], e[1]) for e in toctreenode['entries']] refs = [(e[0], e[1]) for e in toctreenode['entries']]
entries = [] # type: List[nodes.Element] entries = [] # type: List[Element]
for (title, ref) in refs: for (title, ref) in refs:
try: try:
refdoc = None refdoc = None
@ -265,8 +265,7 @@ class TocTree:
docname, refnode['refuri']) + refnode['anchorname'] docname, refnode['refuri']) + refnode['anchorname']
return newnode return newnode
def get_toctree_ancestors(self, docname): def get_toctree_ancestors(self, docname: str) -> List[str]:
# type: (str) -> List[str]
parent = {} parent = {}
for p, children in self.env.toctree_includes.items(): for p, children in self.env.toctree_includes.items():
for child in children: for child in children:
@ -278,8 +277,8 @@ class TocTree:
d = parent[d] d = parent[d]
return ancestors return ancestors
def _toctree_prune(self, node, depth, maxdepth, collapse=False): def _toctree_prune(self, node: Element, depth: int, maxdepth: int, collapse: bool = False
# type: (nodes.Element, int, int, bool) -> None ) -> None:
"""Utility: Cut a TOC at a specified depth.""" """Utility: Cut a TOC at a specified depth."""
for subnode in node.children[:]: for subnode in node.children[:]:
if isinstance(subnode, (addnodes.compact_paragraph, if isinstance(subnode, (addnodes.compact_paragraph,
@ -300,8 +299,7 @@ class TocTree:
# recurse on visible children # recurse on visible children
self._toctree_prune(subnode, depth + 1, maxdepth, collapse) self._toctree_prune(subnode, depth + 1, maxdepth, collapse)
def get_toc_for(self, docname, builder): def get_toc_for(self, docname: str, builder: "Builder") -> Node:
# type: (str, Builder) -> nodes.Node
"""Return a TOC nodetree -- for use on the same page only!""" """Return a TOC nodetree -- for use on the same page only!"""
tocdepth = self.env.metadata[docname].get('tocdepth', 0) tocdepth = self.env.metadata[docname].get('tocdepth', 0)
try: try:
@ -316,11 +314,11 @@ class TocTree:
node['refuri'] = node['anchorname'] or '#' node['refuri'] = node['anchorname'] or '#'
return toc return toc
def get_toctree_for(self, docname, builder, collapse, **kwds): def get_toctree_for(self, docname: str, builder: "Builder", collapse: bool, **kwds
# type: (str, Builder, bool, Any) -> nodes.Element ) -> Element:
"""Return the global TOC nodetree.""" """Return the global TOC nodetree."""
doctree = self.env.get_doctree(self.env.config.master_doc) doctree = self.env.get_doctree(self.env.config.master_doc)
toctrees = [] # type: List[nodes.Element] toctrees = [] # type: List[Element]
if 'includehidden' not in kwds: if 'includehidden' not in kwds:
kwds['includehidden'] = True kwds['includehidden'] = True
if 'maxdepth' not in kwds: if 'maxdepth' not in kwds:

View File

@ -8,12 +8,12 @@
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
if False: from typing import Dict, List, Set
# For type annotation
from typing import Dict, List, Set # NOQA from docutils import nodes
from docutils import nodes # NOQA
from sphinx.sphinx import Sphinx # NOQA from sphinx.application import Sphinx
from sphinx.environment import BuildEnvironment # NOQA from sphinx.environment import BuildEnvironment
class EnvironmentCollector: class EnvironmentCollector:
@ -27,8 +27,7 @@ class EnvironmentCollector:
listener_ids = None # type: Dict[str, int] listener_ids = None # type: Dict[str, int]
def enable(self, app): def enable(self, app: Sphinx) -> None:
# type: (Sphinx) -> None
assert self.listener_ids is None assert self.listener_ids is None
self.listener_ids = { self.listener_ids = {
'doctree-read': app.connect('doctree-read', self.process_doc), 'doctree-read': app.connect('doctree-read', self.process_doc),
@ -38,43 +37,39 @@ class EnvironmentCollector:
'env-get-outdated': app.connect('env-get-outdated', self.get_outdated_docs), 'env-get-outdated': app.connect('env-get-outdated', self.get_outdated_docs),
} }
def disable(self, app): def disable(self, app: Sphinx) -> None:
# type: (Sphinx) -> None
assert self.listener_ids is not None assert self.listener_ids is not None
for listener_id in self.listener_ids.values(): for listener_id in self.listener_ids.values():
app.disconnect(listener_id) app.disconnect(listener_id)
self.listener_ids = None self.listener_ids = None
def clear_doc(self, app, env, docname): def clear_doc(self, app: Sphinx, env: BuildEnvironment, docname: str) -> None:
# type: (Sphinx, BuildEnvironment, str) -> None
"""Remove specified data of a document. """Remove specified data of a document.
This method is called on the removal of the document.""" This method is called on the removal of the document."""
raise NotImplementedError raise NotImplementedError
def merge_other(self, app, env, docnames, other): def merge_other(self, app: Sphinx, env: BuildEnvironment,
# type: (Sphinx, BuildEnvironment, Set[str], BuildEnvironment) -> None docnames: Set[str], other: BuildEnvironment) -> None:
"""Merge in specified data regarding docnames from a different `BuildEnvironment` """Merge in specified data regarding docnames from a different `BuildEnvironment`
object which coming from a subprocess in parallel builds.""" object which coming from a subprocess in parallel builds."""
raise NotImplementedError raise NotImplementedError
def process_doc(self, app, doctree): def process_doc(self, app: Sphinx, doctree: nodes.document) -> None:
# type: (Sphinx, nodes.document) -> None
"""Process a document and gather specific data from it. """Process a document and gather specific data from it.
This method is called after the document is read.""" This method is called after the document is read."""
raise NotImplementedError raise NotImplementedError
def get_updated_docs(self, app, env): def get_updated_docs(self, app: Sphinx, env: BuildEnvironment) -> List[str]:
# type: (Sphinx, BuildEnvironment) -> List[str]
"""Return a list of docnames to re-read. """Return a list of docnames to re-read.
This methods is called after reading the whole of documents (experimental). This methods is called after reading the whole of documents (experimental).
""" """
return [] return []
def get_outdated_docs(self, app, env, added, changed, removed): def get_outdated_docs(self, app: Sphinx, env: BuildEnvironment,
# type: (Sphinx, BuildEnvironment, str, Set[str], Set[str], Set[str]) -> List[str] added: Set[str], changed: Set[str], removed: Set[str]) -> List[str]:
"""Return a list of docnames to re-read. """Return a list of docnames to re-read.
This methods is called before reading the documents. This methods is called before reading the documents.

View File

@ -11,22 +11,21 @@
import os import os
from glob import glob from glob import glob
from os import path from os import path
from typing import Any, Dict, List, Set
from docutils import nodes from docutils import nodes
from docutils.nodes import Node
from docutils.utils import relative_path from docutils.utils import relative_path
from sphinx import addnodes from sphinx import addnodes
from sphinx.application import Sphinx
from sphinx.environment import BuildEnvironment
from sphinx.environment.collectors import EnvironmentCollector from sphinx.environment.collectors import EnvironmentCollector
from sphinx.locale import __ from sphinx.locale import __
from sphinx.util import logging from sphinx.util import logging
from sphinx.util.i18n import get_image_filename_for_language, search_image_for_language from sphinx.util.i18n import get_image_filename_for_language, search_image_for_language
from sphinx.util.images import guess_mimetype from sphinx.util.images import guess_mimetype
if False:
# For type annotation
from typing import Dict, List, Set # NOQA
from sphinx.sphinx import Sphinx # NOQA
from sphinx.environment import BuildEnvironment # NOQA
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -34,16 +33,14 @@ logger = logging.getLogger(__name__)
class ImageCollector(EnvironmentCollector): class ImageCollector(EnvironmentCollector):
"""Image files collector for sphinx.environment.""" """Image files collector for sphinx.environment."""
def clear_doc(self, app, env, docname): def clear_doc(self, app: Sphinx, env: BuildEnvironment, docname: str) -> None:
# type: (Sphinx, BuildEnvironment, str) -> None
env.images.purge_doc(docname) env.images.purge_doc(docname)
def merge_other(self, app, env, docnames, other): def merge_other(self, app: Sphinx, env: BuildEnvironment,
# type: (Sphinx, BuildEnvironment, Set[str], BuildEnvironment) -> None docnames: Set[str], other: BuildEnvironment) -> None:
env.images.merge_other(docnames, other.images) env.images.merge_other(docnames, other.images)
def process_doc(self, app, doctree): def process_doc(self, app: Sphinx, doctree: nodes.document) -> None:
# type: (Sphinx, nodes.document) -> None
"""Process and rewrite image URIs.""" """Process and rewrite image URIs."""
docname = app.env.docname docname = app.env.docname
@ -92,8 +89,8 @@ class ImageCollector(EnvironmentCollector):
continue continue
app.env.images.add_file(docname, imgpath) app.env.images.add_file(docname, imgpath)
def collect_candidates(self, env, imgpath, candidates, node): def collect_candidates(self, env: BuildEnvironment, imgpath: str,
# type: (BuildEnvironment, str, Dict[str, str], nodes.Node) -> None candidates: Dict[str, str], node: Node) -> None:
globbed = {} # type: Dict[str, List[str]] globbed = {} # type: Dict[str, List[str]]
for filename in glob(imgpath): for filename in glob(imgpath):
new_imgpath = relative_path(path.join(env.srcdir, 'dummy'), new_imgpath = relative_path(path.join(env.srcdir, 'dummy'),
@ -115,16 +112,14 @@ class ImageCollector(EnvironmentCollector):
class DownloadFileCollector(EnvironmentCollector): class DownloadFileCollector(EnvironmentCollector):
"""Download files collector for sphinx.environment.""" """Download files collector for sphinx.environment."""
def clear_doc(self, app, env, docname): def clear_doc(self, app: Sphinx, env: BuildEnvironment, docname: str) -> None:
# type: (Sphinx, BuildEnvironment, str) -> None
env.dlfiles.purge_doc(docname) env.dlfiles.purge_doc(docname)
def merge_other(self, app, env, docnames, other): def merge_other(self, app: Sphinx, env: BuildEnvironment,
# type: (Sphinx, BuildEnvironment, Set[str], BuildEnvironment) -> None docnames: Set[str], other: BuildEnvironment) -> None:
env.dlfiles.merge_other(docnames, other.dlfiles) env.dlfiles.merge_other(docnames, other.dlfiles)
def process_doc(self, app, doctree): def process_doc(self, app: Sphinx, doctree: nodes.document) -> None:
# type: (Sphinx, nodes.document) -> None
"""Process downloadable file paths. """ """Process downloadable file paths. """
for node in doctree.traverse(addnodes.download_reference): for node in doctree.traverse(addnodes.download_reference):
targetname = node['reftarget'] targetname = node['reftarget']
@ -140,8 +135,7 @@ class DownloadFileCollector(EnvironmentCollector):
node['filename'] = app.env.dlfiles.add_file(app.env.docname, rel_filename) node['filename'] = app.env.dlfiles.add_file(app.env.docname, rel_filename)
def setup(app): def setup(app: Sphinx) -> Dict[str, Any]:
# type: (Sphinx) -> Dict
app.add_env_collector(ImageCollector) app.add_env_collector(ImageCollector)
app.add_env_collector(DownloadFileCollector) app.add_env_collector(DownloadFileCollector)

View File

@ -10,35 +10,30 @@
import os import os
from os import path from os import path
from typing import Any, Dict, Set
from docutils import nodes
from docutils.utils import relative_path from docutils.utils import relative_path
from sphinx.application import Sphinx
from sphinx.environment import BuildEnvironment
from sphinx.environment.collectors import EnvironmentCollector from sphinx.environment.collectors import EnvironmentCollector
from sphinx.util.osutil import fs_encoding from sphinx.util.osutil import fs_encoding
if False:
# For type annotation
from typing import Dict, Set # NOQA
from docutils import nodes # NOQA
from sphinx.sphinx import Sphinx # NOQA
from sphinx.environment import BuildEnvironment # NOQA
class DependenciesCollector(EnvironmentCollector): class DependenciesCollector(EnvironmentCollector):
"""dependencies collector for sphinx.environment.""" """dependencies collector for sphinx.environment."""
def clear_doc(self, app, env, docname): def clear_doc(self, app: Sphinx, env: BuildEnvironment, docname: str) -> None:
# type: (Sphinx, BuildEnvironment, str) -> None
env.dependencies.pop(docname, None) env.dependencies.pop(docname, None)
def merge_other(self, app, env, docnames, other): def merge_other(self, app: Sphinx, env: BuildEnvironment,
# type: (Sphinx, BuildEnvironment, Set[str], BuildEnvironment) -> None docnames: Set[str], other: BuildEnvironment) -> None:
for docname in docnames: for docname in docnames:
if docname in other.dependencies: if docname in other.dependencies:
env.dependencies[docname] = other.dependencies[docname] env.dependencies[docname] = other.dependencies[docname]
def process_doc(self, app, doctree): def process_doc(self, app: Sphinx, doctree: nodes.document) -> None:
# type: (Sphinx, nodes.document) -> None
"""Process docutils-generated dependency info.""" """Process docutils-generated dependency info."""
cwd = os.getcwd() cwd = os.getcwd()
frompath = path.join(path.normpath(app.srcdir), 'dummy') frompath = path.join(path.normpath(app.srcdir), 'dummy')
@ -55,8 +50,7 @@ class DependenciesCollector(EnvironmentCollector):
app.env.dependencies[app.env.docname].add(relpath) app.env.dependencies[app.env.docname].add(relpath)
def setup(app): def setup(app: Sphinx) -> Dict[str, Any]:
# type: (Sphinx) -> Dict
app.add_env_collector(DependenciesCollector) app.add_env_collector(DependenciesCollector)
return { return {

View File

@ -8,34 +8,31 @@
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
from typing import Any, Dict, Set
from docutils import nodes
from sphinx import addnodes from sphinx import addnodes
from sphinx.application import Sphinx
from sphinx.environment import BuildEnvironment
from sphinx.environment.collectors import EnvironmentCollector from sphinx.environment.collectors import EnvironmentCollector
from sphinx.util import split_index_msg, logging from sphinx.util import split_index_msg, logging
if False:
# For type annotation
from typing import Dict, Set # NOQA
from docutils import nodes # NOQA
from sphinx.applicatin import Sphinx # NOQA
from sphinx.environment import BuildEnvironment # NOQA
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class IndexEntriesCollector(EnvironmentCollector): class IndexEntriesCollector(EnvironmentCollector):
name = 'indices' name = 'indices'
def clear_doc(self, app, env, docname): def clear_doc(self, app: Sphinx, env: BuildEnvironment, docname: str) -> None:
# type: (Sphinx, BuildEnvironment, str) -> None
env.indexentries.pop(docname, None) env.indexentries.pop(docname, None)
def merge_other(self, app, env, docnames, other): def merge_other(self, app: Sphinx, env: BuildEnvironment,
# type: (Sphinx, BuildEnvironment, Set[str], BuildEnvironment) -> None docnames: Set[str], other: BuildEnvironment) -> None:
for docname in docnames: for docname in docnames:
env.indexentries[docname] = other.indexentries[docname] env.indexentries[docname] = other.indexentries[docname]
def process_doc(self, app, doctree): def process_doc(self, app: Sphinx, doctree: nodes.document) -> None:
# type: (Sphinx, nodes.document) -> None
docname = app.env.docname docname = app.env.docname
entries = app.env.indexentries[docname] = [] entries = app.env.indexentries[docname] = []
for node in doctree.traverse(addnodes.index): for node in doctree.traverse(addnodes.index):
@ -50,8 +47,7 @@ class IndexEntriesCollector(EnvironmentCollector):
entries.append(entry) entries.append(entry)
def setup(app): def setup(app: Sphinx) -> Dict[str, Any]:
# type: (Sphinx) -> Dict
app.add_env_collector(IndexEntriesCollector) app.add_env_collector(IndexEntriesCollector)
return { return {

View File

@ -8,33 +8,28 @@
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
from typing import List, cast from typing import Any, Dict, List, Set
from typing import cast
from docutils import nodes from docutils import nodes
from sphinx.application import Sphinx
from sphinx.environment import BuildEnvironment
from sphinx.environment.collectors import EnvironmentCollector from sphinx.environment.collectors import EnvironmentCollector
if False:
# For type annotation
from typing import Dict, Set # NOQA
from sphinx.sphinx import Sphinx # NOQA
from sphinx.environment import BuildEnvironment # NOQA
class MetadataCollector(EnvironmentCollector): class MetadataCollector(EnvironmentCollector):
"""metadata collector for sphinx.environment.""" """metadata collector for sphinx.environment."""
def clear_doc(self, app, env, docname): def clear_doc(self, app: Sphinx, env: BuildEnvironment, docname: str) -> None:
# type: (Sphinx, BuildEnvironment, str) -> None
env.metadata.pop(docname, None) env.metadata.pop(docname, None)
def merge_other(self, app, env, docnames, other): def merge_other(self, app: Sphinx, env: BuildEnvironment,
# type: (Sphinx, BuildEnvironment, Set[str], BuildEnvironment) -> None docnames: Set[str], other: BuildEnvironment) -> None:
for docname in docnames: for docname in docnames:
env.metadata[docname] = other.metadata[docname] env.metadata[docname] = other.metadata[docname]
def process_doc(self, app, doctree): def process_doc(self, app: Sphinx, doctree: nodes.document) -> None:
# type: (Sphinx, nodes.document) -> None
"""Process the docinfo part of the doctree as metadata. """Process the docinfo part of the doctree as metadata.
Keep processing minimal -- just return what docutils says. Keep processing minimal -- just return what docutils says.
@ -67,8 +62,7 @@ class MetadataCollector(EnvironmentCollector):
doctree.pop(0) doctree.pop(0)
def setup(app): def setup(app: Sphinx) -> Dict[str, Any]:
# type: (Sphinx) -> Dict
app.add_env_collector(MetadataCollector) app.add_env_collector(MetadataCollector)
return { return {

View File

@ -8,34 +8,30 @@
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
from typing import Any, Dict, Set
from docutils import nodes from docutils import nodes
from sphinx.application import Sphinx
from sphinx.environment import BuildEnvironment
from sphinx.environment.collectors import EnvironmentCollector from sphinx.environment.collectors import EnvironmentCollector
from sphinx.transforms import SphinxContentsFilter from sphinx.transforms import SphinxContentsFilter
if False:
# For type annotation
from typing import Dict, Set # NOQA
from sphinx.sphinx import Sphinx # NOQA
from sphinx.environment import BuildEnvironment # NOQA
class TitleCollector(EnvironmentCollector): class TitleCollector(EnvironmentCollector):
"""title collector for sphinx.environment.""" """title collector for sphinx.environment."""
def clear_doc(self, app, env, docname): def clear_doc(self, app: Sphinx, env: BuildEnvironment, docname: str) -> None:
# type: (Sphinx, BuildEnvironment, str) -> None
env.titles.pop(docname, None) env.titles.pop(docname, None)
env.longtitles.pop(docname, None) env.longtitles.pop(docname, None)
def merge_other(self, app, env, docnames, other): def merge_other(self, app: Sphinx, env: BuildEnvironment,
# type: (Sphinx, BuildEnvironment, Set[str], BuildEnvironment) -> None docnames: Set[str], other: BuildEnvironment) -> None:
for docname in docnames: for docname in docnames:
env.titles[docname] = other.titles[docname] env.titles[docname] = other.titles[docname]
env.longtitles[docname] = other.longtitles[docname] env.longtitles[docname] = other.longtitles[docname]
def process_doc(self, app, doctree): def process_doc(self, app: Sphinx, doctree: nodes.document) -> None:
# type: (Sphinx, nodes.document) -> None
"""Add a title node to the document (just copy the first section title), """Add a title node to the document (just copy the first section title),
and store that title in the environment. and store that title in the environment.
""" """
@ -59,8 +55,7 @@ class TitleCollector(EnvironmentCollector):
app.env.longtitles[app.env.docname] = longtitlenode app.env.longtitles[app.env.docname] = longtitlenode
def setup(app): def setup(app: Sphinx) -> Dict[str, Any]:
# type: (Sphinx) -> Dict
app.add_env_collector(TitleCollector) app.add_env_collector(TitleCollector)
return { return {

View File

@ -8,22 +8,21 @@
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
from typing import Any, Dict, List, Set, Tuple, Type, TypeVar
from typing import cast from typing import cast
from docutils import nodes from docutils import nodes
from docutils.nodes import Element, Node
from sphinx import addnodes from sphinx import addnodes
from sphinx.application import Sphinx
from sphinx.environment import BuildEnvironment
from sphinx.environment.adapters.toctree import TocTree from sphinx.environment.adapters.toctree import TocTree
from sphinx.environment.collectors import EnvironmentCollector from sphinx.environment.collectors import EnvironmentCollector
from sphinx.locale import __ from sphinx.locale import __
from sphinx.transforms import SphinxContentsFilter from sphinx.transforms import SphinxContentsFilter
from sphinx.util import url_re, logging from sphinx.util import url_re, logging
if False:
# For type annotation
from typing import Dict, List, Set, Tuple, Type, TypeVar # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.environment import BuildEnvironment # NOQA
N = TypeVar('N') N = TypeVar('N')
@ -31,8 +30,7 @@ logger = logging.getLogger(__name__)
class TocTreeCollector(EnvironmentCollector): class TocTreeCollector(EnvironmentCollector):
def clear_doc(self, app, env, docname): def clear_doc(self, app: Sphinx, env: BuildEnvironment, docname: str) -> None:
# type: (Sphinx, BuildEnvironment, str) -> None
env.tocs.pop(docname, None) env.tocs.pop(docname, None)
env.toc_secnumbers.pop(docname, None) env.toc_secnumbers.pop(docname, None)
env.toc_fignumbers.pop(docname, None) env.toc_fignumbers.pop(docname, None)
@ -46,8 +44,8 @@ class TocTreeCollector(EnvironmentCollector):
if not fnset: if not fnset:
del env.files_to_rebuild[subfn] del env.files_to_rebuild[subfn]
def merge_other(self, app, env, docnames, other): def merge_other(self, app: Sphinx, env: BuildEnvironment, docnames: Set[str],
# type: (Sphinx, BuildEnvironment, Set[str], BuildEnvironment) -> None other: BuildEnvironment) -> None:
for docname in docnames: for docname in docnames:
env.tocs[docname] = other.tocs[docname] env.tocs[docname] = other.tocs[docname]
env.toc_num_entries[docname] = other.toc_num_entries[docname] env.toc_num_entries[docname] = other.toc_num_entries[docname]
@ -61,14 +59,12 @@ class TocTreeCollector(EnvironmentCollector):
for subfn, fnset in other.files_to_rebuild.items(): for subfn, fnset in other.files_to_rebuild.items():
env.files_to_rebuild.setdefault(subfn, set()).update(fnset & set(docnames)) env.files_to_rebuild.setdefault(subfn, set()).update(fnset & set(docnames))
def process_doc(self, app, doctree): def process_doc(self, app: Sphinx, doctree: nodes.document) -> None:
# type: (Sphinx, nodes.document) -> None
"""Build a TOC from the doctree and store it in the inventory.""" """Build a TOC from the doctree and store it in the inventory."""
docname = app.env.docname docname = app.env.docname
numentries = [0] # nonlocal again... numentries = [0] # nonlocal again...
def traverse_in_section(node, cls): def traverse_in_section(node: Element, cls: Type[N]) -> List[N]:
# type: (nodes.Element, Type[N]) -> List[N]
"""Like traverse(), but stay within the same section.""" """Like traverse(), but stay within the same section."""
result = [] # type: List[N] result = [] # type: List[N]
if isinstance(node, cls): if isinstance(node, cls):
@ -80,9 +76,8 @@ class TocTreeCollector(EnvironmentCollector):
result.extend(traverse_in_section(child, cls)) result.extend(traverse_in_section(child, cls))
return result return result
def build_toc(node, depth=1): def build_toc(node: Element, depth: int = 1) -> nodes.bullet_list:
# type: (nodes.Element, int) -> nodes.bullet_list entries = [] # type: List[Element]
entries = [] # type: List[nodes.Element]
for sectionnode in node: for sectionnode in node:
# find all toctree nodes in this section and add them # find all toctree nodes in this section and add them
# to the toc (just copying the toctree node which is then # to the toc (just copying the toctree node which is then
@ -107,7 +102,7 @@ class TocTreeCollector(EnvironmentCollector):
'', '', internal=True, refuri=docname, '', '', internal=True, refuri=docname,
anchorname=anchorname, *nodetext) anchorname=anchorname, *nodetext)
para = addnodes.compact_paragraph('', '', reference) para = addnodes.compact_paragraph('', '', reference)
item = nodes.list_item('', para) # type: nodes.Element item = nodes.list_item('', para) # type: Element
sub_item = build_toc(sectionnode, depth + 1) sub_item = build_toc(sectionnode, depth + 1)
if sub_item: if sub_item:
item += sub_item item += sub_item
@ -135,12 +130,10 @@ class TocTreeCollector(EnvironmentCollector):
app.env.tocs[docname] = nodes.bullet_list('') app.env.tocs[docname] = nodes.bullet_list('')
app.env.toc_num_entries[docname] = numentries[0] app.env.toc_num_entries[docname] = numentries[0]
def get_updated_docs(self, app, env): def get_updated_docs(self, app: Sphinx, env: BuildEnvironment) -> List[str]:
# type: (Sphinx, BuildEnvironment) -> List[str]
return self.assign_section_numbers(env) + self.assign_figure_numbers(env) return self.assign_section_numbers(env) + self.assign_figure_numbers(env)
def assign_section_numbers(self, env): def assign_section_numbers(self, env: BuildEnvironment) -> List[str]:
# type: (BuildEnvironment) -> List[str]
"""Assign a section number to each heading under a numbered toctree.""" """Assign a section number to each heading under a numbered toctree."""
# a list of all docnames whose section numbers changed # a list of all docnames whose section numbers changed
rewrite_needed = [] rewrite_needed = []
@ -149,8 +142,7 @@ class TocTreeCollector(EnvironmentCollector):
old_secnumbers = env.toc_secnumbers old_secnumbers = env.toc_secnumbers
env.toc_secnumbers = {} env.toc_secnumbers = {}
def _walk_toc(node, secnums, depth, titlenode=None): def _walk_toc(node: Element, secnums: Dict, depth: int, titlenode: nodes.title = None) -> None: # NOQA
# type: (nodes.Element, Dict, int, nodes.title) -> None
# titlenode is the title of the document, it will get assigned a # titlenode is the title of the document, it will get assigned a
# secnumber too, so that it shows up in next/prev/parent rellinks # secnumber too, so that it shows up in next/prev/parent rellinks
for subnode in node.children: for subnode in node.children:
@ -184,8 +176,7 @@ class TocTreeCollector(EnvironmentCollector):
elif isinstance(subnode, addnodes.toctree): elif isinstance(subnode, addnodes.toctree):
_walk_toctree(subnode, depth) _walk_toctree(subnode, depth)
def _walk_toctree(toctreenode, depth): def _walk_toctree(toctreenode: addnodes.toctree, depth: int) -> None:
# type: (addnodes.toctree, int) -> None
if depth == 0: if depth == 0:
return return
for (title, ref) in toctreenode['entries']: for (title, ref) in toctreenode['entries']:
@ -216,8 +207,7 @@ class TocTreeCollector(EnvironmentCollector):
return rewrite_needed return rewrite_needed
def assign_figure_numbers(self, env): def assign_figure_numbers(self, env: BuildEnvironment) -> List[str]:
# type: (BuildEnvironment) -> List[str]
"""Assign a figure number to each figure under a numbered toctree.""" """Assign a figure number to each figure under a numbered toctree."""
rewrite_needed = [] rewrite_needed = []
@ -227,8 +217,7 @@ class TocTreeCollector(EnvironmentCollector):
env.toc_fignumbers = {} env.toc_fignumbers = {}
fignum_counter = {} # type: Dict[str, Dict[Tuple[int, ...], int]] fignum_counter = {} # type: Dict[str, Dict[Tuple[int, ...], int]]
def get_figtype(node): def get_figtype(node: Node) -> str:
# type: (nodes.Node) -> str
for domain in env.domains.values(): for domain in env.domains.values():
figtype = domain.get_enumerable_node_type(node) figtype = domain.get_enumerable_node_type(node)
if figtype: if figtype:
@ -236,8 +225,7 @@ class TocTreeCollector(EnvironmentCollector):
return None return None
def get_section_number(docname, section): def get_section_number(docname: str, section: nodes.section) -> Tuple[int, ...]:
# type: (str, nodes.section) -> Tuple[int, ...]
anchorname = '#' + section['ids'][0] anchorname = '#' + section['ids'][0]
secnumbers = env.toc_secnumbers.get(docname, {}) secnumbers = env.toc_secnumbers.get(docname, {})
if anchorname in secnumbers: if anchorname in secnumbers:
@ -247,24 +235,22 @@ class TocTreeCollector(EnvironmentCollector):
return secnum or tuple() return secnum or tuple()
def get_next_fignumber(figtype, secnum): def get_next_fignumber(figtype: str, secnum: Tuple[int, ...]) -> Tuple[int, ...]:
# type: (str, Tuple[int, ...]) -> Tuple[int, ...]
counter = fignum_counter.setdefault(figtype, {}) counter = fignum_counter.setdefault(figtype, {})
secnum = secnum[:env.config.numfig_secnum_depth] secnum = secnum[:env.config.numfig_secnum_depth]
counter[secnum] = counter.get(secnum, 0) + 1 counter[secnum] = counter.get(secnum, 0) + 1
return secnum + (counter[secnum],) return secnum + (counter[secnum],)
def register_fignumber(docname, secnum, figtype, fignode): def register_fignumber(docname: str, secnum: Tuple[int, ...],
# type: (str, Tuple[int, ...], str, nodes.Element) -> None figtype: str, fignode: Element) -> None:
env.toc_fignumbers.setdefault(docname, {}) env.toc_fignumbers.setdefault(docname, {})
fignumbers = env.toc_fignumbers[docname].setdefault(figtype, {}) fignumbers = env.toc_fignumbers[docname].setdefault(figtype, {})
figure_id = fignode['ids'][0] figure_id = fignode['ids'][0]
fignumbers[figure_id] = get_next_fignumber(figtype, secnum) fignumbers[figure_id] = get_next_fignumber(figtype, secnum)
def _walk_doctree(docname, doctree, secnum): def _walk_doctree(docname: str, doctree: Element, secnum: Tuple[int, ...]) -> None:
# type: (str, nodes.Element, Tuple[int, ...]) -> None
for subnode in doctree.children: for subnode in doctree.children:
if isinstance(subnode, nodes.section): if isinstance(subnode, nodes.section):
next_secnum = get_section_number(docname, subnode) next_secnum = get_section_number(docname, subnode)
@ -286,8 +272,7 @@ class TocTreeCollector(EnvironmentCollector):
_walk_doctree(docname, subnode, secnum) _walk_doctree(docname, subnode, secnum)
def _walk_doc(docname, secnum): def _walk_doc(docname: str, secnum: Tuple[int, ...]) -> None:
# type: (str, Tuple[int, ...]) -> None
if docname not in assigned: if docname not in assigned:
assigned.add(docname) assigned.add(docname)
doctree = env.get_doctree(docname) doctree = env.get_doctree(docname)
@ -302,8 +287,7 @@ class TocTreeCollector(EnvironmentCollector):
return rewrite_needed return rewrite_needed
def setup(app): def setup(app: Sphinx) -> Dict[str, Any]:
# type: (Sphinx) -> Dict
app.add_env_collector(TocTreeCollector) app.add_env_collector(TocTreeCollector)
return { return {

View File

@ -166,7 +166,7 @@ class DownloadFiles(dict):
Hence don't hack this directly. Hence don't hack this directly.
""" """
def add_file(self, docname: str, filename: str) -> None: def add_file(self, docname: str, filename: str) -> str:
if filename not in self: if filename not in self:
digest = md5(filename.encode()).hexdigest() digest = md5(filename.encode()).hexdigest()
dest = '%s/%s' % (digest, os.path.basename(filename)) dest = '%s/%s' % (digest, os.path.basename(filename))