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 copy import copy
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.config import Config
from sphinx.deprecation import RemovedInSphinx40Warning
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
@ -29,14 +37,9 @@ 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__)
@ -89,8 +92,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
@ -188,19 +190,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'))
@ -228,8 +227,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:
@ -246,8 +244,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
@ -256,8 +253,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
@ -280,8 +276,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)
@ -291,8 +286,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.
@ -309,16 +304,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.
@ -341,8 +334,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.
@ -361,13 +353,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.
"""
@ -395,8 +385,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
@ -446,8 +435,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)
@ -457,8 +445,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
@ -469,13 +456,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.
@ -485,8 +470,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.
@ -495,8 +479,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.
@ -505,15 +488,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.
@ -525,8 +506,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:
@ -535,9 +515,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.
"""
@ -559,9 +539,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.
@ -577,12 +557,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
@ -599,12 +578,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
@ -633,8 +610,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):

View File

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

View File

@ -11,33 +11,30 @@ import bisect
import re
import unicodedata
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.locale import _, __
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__)
class IndexEntries:
def __init__(self, env):
# type: (BuildEnvironment) -> None
def __init__(self, env: BuildEnvironment) -> None:
self.env = env
def create_index(self, builder, group_entries=True,
_fixre=re.compile(r'(.*) ([(][^()]*[)])')):
# type: (Builder, bool, Pattern) -> List[Tuple[str, List[Tuple[str, Any]]]]
def create_index(self, builder: Builder, group_entries: bool = True,
_fixre: Pattern = re.compile(r'(.*) ([(][^()]*[)])')
) -> List[Tuple[str, List[Tuple[str, Any]]]]:
"""Create the real index from the collected index entries."""
new = {} # type: Dict[str, List]
def add_entry(word, subword, main, link=True, dic=new, key=None):
# type: (str, str, str, bool, Dict, str) -> None
def add_entry(word: str, subword: str, main: str, link: bool = True,
dic: Dict = new, key: str = None) -> None:
# Force the word to be unicode if it's a ASCII bytestring.
# This will solve problems with unicode normalization later.
# 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
# following the letters in ASCII, this is where the chr(127) comes from
def keyfunc(entry):
# type: (Tuple[str, List]) -> Tuple[str, str]
def keyfunc(entry: Tuple[str, List]) -> Tuple[str, str]:
key, (void, void, category_key) = entry
if category_key:
# using specified category key to sort
@ -138,8 +134,7 @@ class IndexEntries:
i += 1
# group the entries by letter
def keyfunc2(item):
# type: (Tuple[str, List]) -> str
def keyfunc2(item: Tuple[str, List]) -> str:
# hack: mutating the subitems dicts to a list in the keyfunc
k, v = item
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.
"""
from typing import Iterable, cast
from typing import cast
from typing import Iterable, List
from docutils import nodes
from docutils.nodes import Element, Node
from sphinx import addnodes
from sphinx.locale import __
@ -20,20 +22,18 @@ from sphinx.util.nodes import clean_astext, process_only_nodes
if False:
# For type annotation
from typing import Any, Dict, List # NOQA
from sphinx.builders import Builder # NOQA
from sphinx.environment import BuildEnvironment # NOQA
from sphinx.builders import Builder
from sphinx.environment import BuildEnvironment
logger = logging.getLogger(__name__)
class TocTree:
def __init__(self, env):
# type: (BuildEnvironment) -> None
def __init__(self, env: "BuildEnvironment") -> None:
self.env = env
def note(self, docname, toctreenode):
# type: (str, addnodes.toctree) -> None
def note(self, docname: str, toctreenode: addnodes.toctree) -> None:
"""Note a TOC tree directive in a document and gather information about
file relations from it.
"""
@ -48,9 +48,9 @@ class TocTree:
self.env.files_to_rebuild.setdefault(includefile, set()).add(docname)
self.env.toctree_includes.setdefault(docname, []).extend(includefiles)
def resolve(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.Element
def resolve(self, docname: str, builder: "Builder", toctree: addnodes.toctree,
prune: bool = True, maxdepth: int = 0, titles_only: bool = False,
collapse: bool = False, includehidden: bool = False) -> Element:
"""Resolve a *toctree* node into individual bullet lists with titles
as items, returning None (if no containing titles are found) or
a new node.
@ -86,8 +86,7 @@ class TocTree:
toctree_ancestors = self.get_toctree_ancestors(docname)
excluded = Matcher(self.env.config.exclude_patterns)
def _toctree_add_classes(node, depth):
# type: (nodes.Element, int) -> None
def _toctree_add_classes(node: Element, depth: int) -> None:
"""Add 'toctree-l%d' and 'current' classes to the toctree."""
for subnode in node.children:
if isinstance(subnode, (addnodes.compact_paragraph,
@ -105,7 +104,7 @@ class TocTree:
if not subnode['anchorname']:
# give the whole branch a 'current' class
# (useful for styling it differently)
branchnode = subnode # type: nodes.Element
branchnode = subnode # type: Element
while branchnode:
branchnode['classes'].append('current')
branchnode = branchnode.parent
@ -117,11 +116,12 @@ class TocTree:
subnode['iscurrent'] = True
subnode = subnode.parent
def _entries_from_toctree(toctreenode, parents, separate=False, subtree=False):
# type: (addnodes.toctree, List[str], bool, bool) -> List[nodes.Element]
def _entries_from_toctree(toctreenode: addnodes.toctree, parents: List[str],
separate: bool = False, subtree: bool = False
) -> List[Element]:
"""Return TOC entries for a toctree node."""
refs = [(e[0], e[1]) for e in toctreenode['entries']]
entries = [] # type: List[nodes.Element]
entries = [] # type: List[Element]
for (title, ref) in refs:
try:
refdoc = None
@ -265,8 +265,7 @@ class TocTree:
docname, refnode['refuri']) + refnode['anchorname']
return newnode
def get_toctree_ancestors(self, docname):
# type: (str) -> List[str]
def get_toctree_ancestors(self, docname: str) -> List[str]:
parent = {}
for p, children in self.env.toctree_includes.items():
for child in children:
@ -278,8 +277,8 @@ class TocTree:
d = parent[d]
return ancestors
def _toctree_prune(self, node, depth, maxdepth, collapse=False):
# type: (nodes.Element, int, int, bool) -> None
def _toctree_prune(self, node: Element, depth: int, maxdepth: int, collapse: bool = False
) -> None:
"""Utility: Cut a TOC at a specified depth."""
for subnode in node.children[:]:
if isinstance(subnode, (addnodes.compact_paragraph,
@ -300,8 +299,7 @@ class TocTree:
# recurse on visible children
self._toctree_prune(subnode, depth + 1, maxdepth, collapse)
def get_toc_for(self, docname, builder):
# type: (str, Builder) -> nodes.Node
def get_toc_for(self, docname: str, builder: "Builder") -> Node:
"""Return a TOC nodetree -- for use on the same page only!"""
tocdepth = self.env.metadata[docname].get('tocdepth', 0)
try:
@ -316,11 +314,11 @@ class TocTree:
node['refuri'] = node['anchorname'] or '#'
return toc
def get_toctree_for(self, docname, builder, collapse, **kwds):
# type: (str, Builder, bool, Any) -> nodes.Element
def get_toctree_for(self, docname: str, builder: "Builder", collapse: bool, **kwds
) -> Element:
"""Return the global TOC nodetree."""
doctree = self.env.get_doctree(self.env.config.master_doc)
toctrees = [] # type: List[nodes.Element]
toctrees = [] # type: List[Element]
if 'includehidden' not in kwds:
kwds['includehidden'] = True
if 'maxdepth' not in kwds:

View File

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

View File

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

View File

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

View File

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

View File

@ -8,33 +8,28 @@
: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 sphinx.application import Sphinx
from sphinx.environment import BuildEnvironment
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):
"""metadata collector for sphinx.environment."""
def clear_doc(self, app, env, docname):
# type: (Sphinx, BuildEnvironment, str) -> None
def clear_doc(self, app: Sphinx, env: BuildEnvironment, docname: str) -> None:
env.metadata.pop(docname, None)
def merge_other(self, app, env, docnames, other):
# type: (Sphinx, BuildEnvironment, Set[str], BuildEnvironment) -> None
def merge_other(self, app: Sphinx, env: BuildEnvironment,
docnames: Set[str], other: BuildEnvironment) -> None:
for docname in docnames:
env.metadata[docname] = other.metadata[docname]
def process_doc(self, app, doctree):
# type: (Sphinx, nodes.document) -> None
def process_doc(self, app: Sphinx, doctree: nodes.document) -> None:
"""Process the docinfo part of the doctree as metadata.
Keep processing minimal -- just return what docutils says.
@ -67,8 +62,7 @@ class MetadataCollector(EnvironmentCollector):
doctree.pop(0)
def setup(app):
# type: (Sphinx) -> Dict
def setup(app: Sphinx) -> Dict[str, Any]:
app.add_env_collector(MetadataCollector)
return {

View File

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

View File

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

View File

@ -166,7 +166,7 @@ class DownloadFiles(dict):
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:
digest = md5(filename.encode()).hexdigest()
dest = '%s/%s' % (digest, os.path.basename(filename))