Merge branch '2.0'

This commit is contained in:
Takeshi KOMIYA 2019-12-25 11:41:54 +09:00
commit d717f5ae31
41 changed files with 560 additions and 869 deletions

10
CHANGES
View File

@ -68,7 +68,7 @@ Bugs fixed
Testing
--------
Release 2.3.1 (in development)
Release 2.3.2 (in development)
==============================
Dependencies
@ -89,6 +89,14 @@ Bugs fixed
Testing
--------
Release 2.3.1 (released Dec 22, 2019)
=====================================
Bugs fixed
----------
* #6936: sphinx-autogen: raises AttributeError
Release 2.3.0 (released Dec 15, 2019)
=====================================

View File

@ -9,6 +9,7 @@
"""
import warnings
from typing import Any, Dict, List, Sequence
from docutils import nodes
@ -16,8 +17,7 @@ from sphinx.deprecation import RemovedInSphinx40Warning
if False:
# For type annotation
from typing import Any, Dict, List, Sequence # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.application import Sphinx
class translatable(nodes.Node):
@ -34,18 +34,15 @@ class translatable(nodes.Node):
Because they are used at final step; extraction.
"""
def preserve_original_messages(self):
# type: () -> None
def preserve_original_messages(self) -> None:
"""Preserve original translatable messages."""
raise NotImplementedError
def apply_translated_message(self, original_message, translated_message):
# type: (str, str) -> None
def apply_translated_message(self, original_message: str, translated_message: str) -> None:
"""Apply translated message."""
raise NotImplementedError
def extract_original_messages(self):
# type: () -> Sequence[str]
def extract_original_messages(self) -> Sequence[str]:
"""Extract translation messages.
:returns: list of extracted messages or messages generator
@ -61,8 +58,7 @@ class not_smartquotable:
class toctree(nodes.General, nodes.Element, translatable):
"""Node for inserting a "TOC tree"."""
def preserve_original_messages(self):
# type: () -> None
def preserve_original_messages(self) -> None:
# toctree entries
rawentries = self.setdefault('rawentries', [])
for title, docname in self['entries']:
@ -73,8 +69,7 @@ class toctree(nodes.General, nodes.Element, translatable):
if self.get('caption'):
self['rawcaption'] = self['caption']
def apply_translated_message(self, original_message, translated_message):
# type: (str, str) -> None
def apply_translated_message(self, original_message: str, translated_message: str) -> None:
# toctree entries
for i, (title, docname) in enumerate(self['entries']):
if title == original_message:
@ -84,8 +79,7 @@ class toctree(nodes.General, nodes.Element, translatable):
if self.get('rawcaption') == original_message:
self['caption'] = translated_message
def extract_original_messages(self):
# type: () -> List[str]
def extract_original_messages(self) -> List[str]:
messages = [] # type: List[str]
# toctree entries
@ -143,8 +137,7 @@ class desc_type(nodes.Part, nodes.Inline, nodes.FixedTextElement):
class desc_returns(desc_type):
"""Node for a "returns" annotation (a la -> in Python)."""
def astext(self):
# type: () -> str
def astext(self) -> str:
return ' -> ' + super().astext()
@ -165,8 +158,7 @@ class desc_optional(nodes.Part, nodes.Inline, nodes.FixedTextElement):
"""Node for marking optional parts of the parameter list."""
child_text_separator = ', '
def astext(self):
# type: () -> str
def astext(self) -> str:
return '[' + super().astext() + ']'
@ -313,8 +305,7 @@ class abbreviation(nodes.abbreviation):
.. deprecated:: 2.0
"""
def __init__(self, rawsource='', text='', *children, **attributes):
# type: (str, str, *nodes.Node, **Any) -> None
def __init__(self, rawsource: str = '', text: str = '', *children, **attributes) -> None:
warnings.warn("abbrevition node for Sphinx was replaced by docutils'.",
RemovedInSphinx40Warning, stacklevel=2)
@ -325,8 +316,7 @@ class manpage(nodes.Inline, nodes.FixedTextElement):
"""Node for references to manpages."""
def setup(app):
# type: (Sphinx) -> Dict[str, Any]
def setup(app: "Sphinx") -> Dict[str, Any]:
app.add_node(toctree)
app.add_node(desc)
app.add_node(desc_signature)

View File

@ -18,8 +18,12 @@ import warnings
from collections import deque
from io import StringIO
from os import path
from typing import Any, Callable, Dict, IO, List, Tuple, Union
from docutils import nodes
from docutils.nodes import Element, TextElement
from docutils.parsers.rst import Directive, roles
from docutils.transforms import Transform
from pygments.lexer import Lexer
import sphinx
@ -27,12 +31,16 @@ from sphinx import package_dir, locale
from sphinx.config import Config
from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.environment import BuildEnvironment
from sphinx.environment.collectors import EnvironmentCollector
from sphinx.errors import ApplicationError, ConfigError, VersionRequirementError
from sphinx.events import EventManager
from sphinx.extension import Extension
from sphinx.highlighting import lexer_classes, lexers
from sphinx.locale import __
from sphinx.project import Project
from sphinx.registry import SphinxComponentRegistry
from sphinx.roles import XRefRole
from sphinx.theming import Theme
from sphinx.util import docutils
from sphinx.util import logging
from sphinx.util import progress_message
@ -42,21 +50,14 @@ from sphinx.util.i18n import CatalogRepository
from sphinx.util.logging import prefixed_warnings
from sphinx.util.osutil import abspath, ensuredir, relpath
from sphinx.util.tags import Tags
from sphinx.util.typing import RoleFunction, TitleGetter
if False:
# For type annotation
from typing import Any, Callable, Dict, IO, Iterable, Iterator, List, Tuple, Union # NOQA
from docutils.nodes import Node # NOQA
from typing import Type # for python3.5.1
from docutils import nodes # NOQA
from docutils.parsers import Parser # NOQA
from docutils.transforms import Transform # NOQA
from sphinx.builders import Builder # NOQA
from sphinx.domains import Domain, Index # NOQA
from sphinx.environment.collectors import EnvironmentCollector # NOQA
from sphinx.extension import Extension # NOQA
from sphinx.roles import XRefRole # NOQA
from sphinx.theming import Theme # NOQA
from sphinx.util.typing import RoleFunction, TitleGetter # NOQA
from sphinx.builders import Builder
builtin_extensions = (
'sphinx.addnodes',
@ -132,11 +133,11 @@ class Sphinx:
:ivar outdir: Directory for storing build documents.
"""
def __init__(self, srcdir, confdir, outdir, doctreedir, buildername,
confoverrides=None, status=sys.stdout, warning=sys.stderr,
freshenv=False, warningiserror=False, tags=None, verbosity=0,
parallel=0, keep_going=False):
# type: (str, str, str, str, str, Dict, IO, IO, bool, bool, List[str], int, int, bool) -> None # NOQA
def __init__(self, srcdir: str, confdir: str, outdir: str, doctreedir: str,
buildername: str, confoverrides: Dict = None,
status: IO = sys.stdout, warning: IO = sys.stderr,
freshenv: bool = False, warningiserror: bool = False, tags: List[str] = None,
verbosity: int = 0, parallel: int = 0, keep_going: bool = False) -> None:
self.phase = BuildPhase.INITIALIZATION
self.verbosity = verbosity
self.extensions = {} # type: Dict[str, Extension]
@ -270,8 +271,7 @@ class Sphinx:
# set up the builder
self._init_builder()
def _init_i18n(self):
# type: () -> None
def _init_i18n(self) -> None:
"""Load translated strings from the configured localedirs if enabled in
the configuration.
"""
@ -296,8 +296,7 @@ class Sphinx:
else:
logger.info(__('not available for built-in messages'))
def _init_env(self, freshenv):
# type: (bool) -> None
def _init_env(self, freshenv: bool) -> None:
filename = path.join(self.doctreedir, ENV_PICKLE_FILENAME)
if freshenv or not os.path.exists(filename):
self.env = BuildEnvironment()
@ -313,28 +312,24 @@ class Sphinx:
logger.info(__('failed: %s'), err)
self._init_env(freshenv=True)
def preload_builder(self, name):
# type: (str) -> None
def preload_builder(self, name: str) -> None:
self.registry.preload_builder(self, name)
def create_builder(self, name):
# type: (str) -> Builder
def create_builder(self, name: str) -> "Builder":
if name is None:
logger.info(__('No builder selected, using default: html'))
name = 'html'
return self.registry.create_builder(self, name)
def _init_builder(self):
# type: () -> None
def _init_builder(self) -> None:
self.builder.set_environment(self.env)
self.builder.init()
self.events.emit('builder-inited')
# ---- main "build" method -------------------------------------------------
def build(self, force_all=False, filenames=None):
# type: (bool, List[str]) -> None
def build(self, force_all: bool = False, filenames: List[str] = None) -> None:
self.phase = BuildPhase.READING
try:
if force_all:
@ -385,8 +380,7 @@ class Sphinx:
# ---- general extensibility interface -------------------------------------
def setup_extension(self, extname):
# type: (str) -> None
def setup_extension(self, extname: str) -> None:
"""Import and setup a Sphinx extension module.
Load the extension given by the module *name*. Use this if your
@ -396,8 +390,7 @@ class Sphinx:
logger.debug('[app] setting up extension: %r', extname)
self.registry.load_extension(self, extname)
def require_sphinx(self, version):
# type: (str) -> None
def require_sphinx(self, version: str) -> None:
"""Check the Sphinx version if requested.
Compare *version* (which must be a ``major.minor`` version string, e.g.
@ -410,8 +403,7 @@ class Sphinx:
raise VersionRequirementError(version)
# event interface
def connect(self, event, callback):
# type: (str, Callable) -> int
def connect(self, event: str, callback: Callable) -> int:
"""Register *callback* to be called when *event* is emitted.
For details on available core events and the arguments of callback
@ -424,14 +416,12 @@ class Sphinx:
logger.debug('[app] connecting event %r: %r [id=%s]', event, callback, listener_id)
return listener_id
def disconnect(self, listener_id):
# type: (int) -> None
def disconnect(self, listener_id: int) -> None:
"""Unregister callback by *listener_id*."""
logger.debug('[app] disconnecting event: [id=%s]', listener_id)
self.events.disconnect(listener_id)
def emit(self, event, *args):
# type: (str, Any) -> List
def emit(self, event: str, *args) -> List:
"""Emit *event* and pass *arguments* to the callback functions.
Return the return values of all callbacks as a list. Do not emit core
@ -439,8 +429,7 @@ class Sphinx:
"""
return self.events.emit(event, *args)
def emit_firstresult(self, event, *args):
# type: (str, Any) -> Any
def emit_firstresult(self, event: str, *args) -> Any:
"""Emit *event* and pass *arguments* to the callback functions.
Return the result of the first callback that doesn't return ``None``.
@ -451,8 +440,7 @@ class Sphinx:
# registering addon parts
def add_builder(self, builder, override=False):
# type: (Type[Builder], bool) -> None
def add_builder(self, builder: "Type[Builder]", override: bool = False) -> None:
"""Register a new builder.
*builder* must be a class that inherits from
@ -464,8 +452,8 @@ class Sphinx:
self.registry.add_builder(builder, override=override)
# TODO(stephenfin): Describe 'types' parameter
def add_config_value(self, name, default, rebuild, types=()):
# type: (str, Any, Union[bool, str], Any) -> None
def add_config_value(self, name: str, default: Any, rebuild: Union[bool, str],
types: Any = ()) -> None:
"""Register a configuration value.
This is necessary for Sphinx to recognize new values and set default
@ -497,8 +485,7 @@ class Sphinx:
rebuild = 'env' if rebuild else ''
self.config.add(name, default, rebuild, types)
def add_event(self, name):
# type: (str) -> None
def add_event(self, name: str) -> None:
"""Register an event called *name*.
This is needed to be able to emit it.
@ -506,8 +493,8 @@ class Sphinx:
logger.debug('[app] adding event: %r', name)
self.events.add(name)
def set_translator(self, name, translator_class, override=False):
# type: (str, Type[nodes.NodeVisitor], bool) -> None
def set_translator(self, name: str, translator_class: "Type[nodes.NodeVisitor]",
override: bool = False) -> None:
"""Register or override a Docutils translator class.
This is used to register a custom output translator or to replace a
@ -520,8 +507,7 @@ class Sphinx:
"""
self.registry.add_translator(name, translator_class, override=override)
def add_node(self, node, override=False, **kwds):
# type: (Type[nodes.Element], bool, Any) -> None
def add_node(self, node: "Type[Element]", override: bool = False, **kwds) -> None:
"""Register a Docutils node class.
This is necessary for Docutils internals. It may also be used in the
@ -559,8 +545,9 @@ class Sphinx:
docutils.register_node(node)
self.registry.add_translation_handlers(node, **kwds)
def add_enumerable_node(self, node, figtype, title_getter=None, override=False, **kwds):
# type: (Type[nodes.Element], str, TitleGetter, bool, Any) -> None
def add_enumerable_node(self, node: "Type[Element]", figtype: str,
title_getter: TitleGetter = None, override: bool = False,
**kwds) -> None:
"""Register a Docutils node class as a numfig target.
Sphinx numbers the node automatically. And then the users can refer it
@ -587,8 +574,7 @@ class Sphinx:
self.registry.add_enumerable_node(node, figtype, title_getter, override=override)
self.add_node(node, override=override, **kwds)
def add_directive(self, name, cls, override=False):
# type: (str, Type[Directive], bool) -> None
def add_directive(self, name: str, cls: "Type[Directive]", override: bool = False):
"""Register a Docutils directive.
*name* must be the prospective directive name. *cls* is a directive
@ -632,8 +618,7 @@ class Sphinx:
docutils.register_directive(name, cls)
def add_role(self, name, role, override=False):
# type: (str, Any, bool) -> None
def add_role(self, name: str, role: Any, override: bool = False) -> None:
"""Register a Docutils role.
*name* must be the role name that occurs in the source, *role* the role
@ -650,8 +635,7 @@ class Sphinx:
name, type='app', subtype='add_role')
docutils.register_role(name, role)
def add_generic_role(self, name, nodeclass, override=False):
# type: (str, Any, bool) -> None
def add_generic_role(self, name: str, nodeclass: Any, override: bool = False) -> None:
"""Register a generic Docutils role.
Register a Docutils role that does nothing but wrap its contents in the
@ -670,8 +654,7 @@ class Sphinx:
role = roles.GenericRole(name, nodeclass)
docutils.register_role(name, role)
def add_domain(self, domain, override=False):
# type: (Type[Domain], bool) -> None
def add_domain(self, domain: "Type[Domain]", override: bool = False) -> None:
"""Register a domain.
Make the given *domain* (which must be a class; more precisely, a
@ -683,8 +666,8 @@ class Sphinx:
"""
self.registry.add_domain(domain, override=override)
def add_directive_to_domain(self, domain, name, cls, override=False):
# type: (str, str, Type[Directive], bool) -> None
def add_directive_to_domain(self, domain: str, name: str,
cls: "Type[Directive]", override: bool = False) -> None:
"""Register a Docutils directive in a domain.
Like :meth:`add_directive`, but the directive is added to the domain
@ -696,8 +679,8 @@ class Sphinx:
"""
self.registry.add_directive_to_domain(domain, name, cls, override=override)
def add_role_to_domain(self, domain, name, role, override=False):
# type: (str, str, Union[RoleFunction, XRefRole], bool) -> None
def add_role_to_domain(self, domain: str, name: str, role: Union[RoleFunction, XRefRole],
override: bool = False) -> None:
"""Register a Docutils role in a domain.
Like :meth:`add_role`, but the role is added to the domain named
@ -709,8 +692,8 @@ class Sphinx:
"""
self.registry.add_role_to_domain(domain, name, role, override=override)
def add_index_to_domain(self, domain, index, override=False):
# type: (str, Type[Index], bool) -> None
def add_index_to_domain(self, domain: str, index: "Type[Index]", override: bool = False
) -> None:
"""Register a custom index for a domain.
Add a custom *index* class to the domain named *domain*. *index* must
@ -722,10 +705,10 @@ class Sphinx:
"""
self.registry.add_index_to_domain(domain, index)
def add_object_type(self, directivename, rolename, indextemplate='',
parse_node=None, ref_nodeclass=None, objname='',
doc_field_types=[], override=False):
# type: (str, str, str, Callable, Type[nodes.TextElement], str, List, bool) -> None
def add_object_type(self, directivename: str, rolename: str, indextemplate: str = '',
parse_node: Callable = None, ref_nodeclass: "Type[TextElement]" = None,
objname: str = '', doc_field_types: List = [], override: bool = False
) -> None:
"""Register a new object type.
This method is a very convenient way to add a new :term:`object` type
@ -786,9 +769,9 @@ class Sphinx:
ref_nodeclass, objname, doc_field_types,
override=override)
def add_crossref_type(self, directivename, rolename, indextemplate='',
ref_nodeclass=None, objname='', override=False):
# type: (str, str, str, Type[nodes.TextElement], str, bool) -> None
def add_crossref_type(self, directivename: str, rolename: str, indextemplate: str = '',
ref_nodeclass: "Type[TextElement]" = None, objname: str = '',
override: bool = False) -> None:
"""Register a new crossref object type.
This method is very similar to :meth:`add_object_type` except that the
@ -822,8 +805,7 @@ class Sphinx:
indextemplate, ref_nodeclass, objname,
override=override)
def add_transform(self, transform):
# type: (Type[Transform]) -> None
def add_transform(self, transform: "Type[Transform]") -> None:
"""Register a Docutils transform to be applied after parsing.
Add the standard docutils :class:`Transform` subclass *transform* to
@ -856,8 +838,7 @@ class Sphinx:
""" # NOQA
self.registry.add_transform(transform)
def add_post_transform(self, transform):
# type: (Type[Transform]) -> None
def add_post_transform(self, transform: "Type[Transform]") -> None:
"""Register a Docutils transform to be applied before writing.
Add the standard docutils :class:`Transform` subclass *transform* to
@ -866,16 +847,14 @@ class Sphinx:
"""
self.registry.add_post_transform(transform)
def add_javascript(self, filename, **kwargs):
# type: (str, **str) -> None
def add_javascript(self, filename: str, **kwargs: str) -> None:
"""An alias of :meth:`add_js_file`."""
warnings.warn('The app.add_javascript() is deprecated. '
'Please use app.add_js_file() instead.',
RemovedInSphinx40Warning, stacklevel=2)
self.add_js_file(filename, **kwargs)
def add_js_file(self, filename, **kwargs):
# type: (str, **str) -> None
def add_js_file(self, filename: str, **kwargs: str) -> None:
"""Register a JavaScript file to include in the HTML output.
Add *filename* to the list of JavaScript files that the default HTML
@ -901,8 +880,7 @@ class Sphinx:
if hasattr(self.builder, 'add_js_file'):
self.builder.add_js_file(filename, **kwargs) # type: ignore
def add_css_file(self, filename, **kwargs):
# type: (str, **str) -> None
def add_css_file(self, filename: str, **kwargs: str) -> None:
"""Register a stylesheet to include in the HTML output.
Add *filename* to the list of CSS files that the default HTML template
@ -941,8 +919,8 @@ class Sphinx:
if hasattr(self.builder, 'add_css_file'):
self.builder.add_css_file(filename, **kwargs) # type: ignore
def add_stylesheet(self, filename, alternate=False, title=None):
# type: (str, bool, str) -> None
def add_stylesheet(self, filename: str, alternate: bool = False, title: str = None
) -> None:
"""An alias of :meth:`add_css_file`."""
warnings.warn('The app.add_stylesheet() is deprecated. '
'Please use app.add_css_file() instead.',
@ -959,8 +937,7 @@ class Sphinx:
self.add_css_file(filename, **attributes)
def add_latex_package(self, packagename, options=None):
# type: (str, str) -> None
def add_latex_package(self, packagename: str, options: str = None) -> None:
r"""Register a package to include in the LaTeX source code.
Add *packagename* to the list of packages that LaTeX source code will
@ -978,8 +955,7 @@ class Sphinx:
"""
self.registry.add_latex_package(packagename, options)
def add_lexer(self, alias, lexer):
# type: (str, Union[Lexer, Type[Lexer]]) -> None
def add_lexer(self, alias: str, lexer: Union[Lexer, "Type[Lexer]"]) -> None:
"""Register a new lexer for source code.
Use *lexer* to highlight code blocks with the given language *alias*.
@ -998,8 +974,7 @@ class Sphinx:
else:
lexer_classes[alias] = lexer
def add_autodocumenter(self, cls, override=False):
# type: (Any, bool) -> None
def add_autodocumenter(self, cls: Any, override: bool = False) -> None:
"""Register a new documenter class for the autodoc extension.
Add *cls* as a new documenter class for the :mod:`sphinx.ext.autodoc`
@ -1019,8 +994,8 @@ class Sphinx:
self.registry.add_documenter(cls.objtype, cls)
self.add_directive('auto' + cls.objtype, AutodocDirective, override=override)
def add_autodoc_attrgetter(self, typ, getter):
# type: (Type, Callable[[Any, str, Any], Any]) -> None
def add_autodoc_attrgetter(self, typ: "Type", getter: Callable[[Any, str, Any], Any]
) -> None:
"""Register a new ``getattr``-like function for the autodoc extension.
Add *getter*, which must be a function with an interface compatible to
@ -1034,8 +1009,7 @@ class Sphinx:
logger.debug('[app] adding autodoc attrgetter: %r', (typ, getter))
self.registry.add_autodoc_attrgetter(typ, getter)
def add_search_language(self, cls):
# type: (Any) -> None
def add_search_language(self, cls: Any) -> None:
"""Register a new language for the HTML search index.
Add *cls*, which must be a subclass of
@ -1051,8 +1025,7 @@ class Sphinx:
assert issubclass(cls, SearchLanguage)
languages[cls.lang] = cls
def add_source_suffix(self, suffix, filetype, override=False):
# type: (str, str, bool) -> None
def add_source_suffix(self, suffix: str, filetype: str, override: bool = False) -> None:
"""Register a suffix of source files.
Same as :confval:`source_suffix`. The users can override this
@ -1062,8 +1035,7 @@ class Sphinx:
"""
self.registry.add_source_suffix(suffix, filetype, override=override)
def add_source_parser(self, *args, **kwargs):
# type: (Any, Any) -> None
def add_source_parser(self, *args, **kwargs) -> None:
"""Register a parser class.
.. versionadded:: 1.4
@ -1075,8 +1047,7 @@ class Sphinx:
"""
self.registry.add_source_parser(*args, **kwargs)
def add_env_collector(self, collector):
# type: (Type[EnvironmentCollector]) -> None
def add_env_collector(self, collector: "Type[EnvironmentCollector]") -> None:
"""Register an environment collector class.
Refer to :ref:`collector-api`.
@ -1086,8 +1057,7 @@ class Sphinx:
logger.debug('[app] adding environment collector: %r', collector)
collector().enable(self)
def add_html_theme(self, name, theme_path):
# type: (str, str) -> None
def add_html_theme(self, name: str, theme_path: str) -> None:
"""Register a HTML Theme.
The *name* is a name of theme, and *path* is a full path to the theme
@ -1098,8 +1068,9 @@ class Sphinx:
logger.debug('[app] adding HTML theme: %r, %r', name, theme_path)
self.html_themes[name] = theme_path
def add_html_math_renderer(self, name, inline_renderers=None, block_renderers=None):
# type: (str, Tuple[Callable, Callable], Tuple[Callable, Callable]) -> None
def add_html_math_renderer(self, name: str,
inline_renderers: Tuple[Callable, Callable] = None,
block_renderers: Tuple[Callable, Callable] = None) -> None:
"""Register a math renderer for HTML.
The *name* is a name of math renderer. Both *inline_renderers* and
@ -1113,8 +1084,7 @@ class Sphinx:
"""
self.registry.add_html_math_renderer(name, inline_renderers, block_renderers)
def add_message_catalog(self, catalog, locale_dir):
# type: (str, str) -> None
def add_message_catalog(self, catalog: str, locale_dir: str) -> None:
"""Register a message catalog.
The *catalog* is a name of catalog, and *locale_dir* is a base path
@ -1127,8 +1097,7 @@ class Sphinx:
locale.init_console(locale_dir, catalog)
# ---- other methods -------------------------------------------------
def is_parallel_allowed(self, typ):
# type: (str) -> bool
def is_parallel_allowed(self, typ: str) -> bool:
"""Check parallel processing is allowed or not.
``typ`` is a type of processing; ``'read'`` or ``'write'``.
@ -1170,8 +1139,7 @@ class TemplateBridge:
that renders templates given a template name and a context.
"""
def init(self, builder, theme=None, dirs=None):
# type: (Builder, Theme, List[str]) -> None
def init(self, builder: "Builder", theme: Theme = None, dirs: List[str] = None) -> None:
"""Called by the builder to initialize the template system.
*builder* is the builder object; you'll probably want to look at the
@ -1182,23 +1150,20 @@ class TemplateBridge:
"""
raise NotImplementedError('must be implemented in subclasses')
def newest_template_mtime(self):
# type: () -> float
def newest_template_mtime(self) -> float:
"""Called by the builder to determine if output files are outdated
because of template changes. Return the mtime of the newest template
file that was changed. The default implementation returns ``0``.
"""
return 0
def render(self, template, context):
# type: (str, Dict) -> None
def render(self, template: str, context: Dict) -> None:
"""Called by the builder to render a template given as a filename with
a specified context (a Python dictionary).
"""
raise NotImplementedError('must be implemented in subclasses')
def render_string(self, template, context):
# type: (str, Dict) -> str
def render_string(self, template: str, context: Dict) -> str:
"""Called by the builder to render a template given as a string with a
specified context (a Python dictionary).
"""

View File

@ -14,7 +14,9 @@ import types
import warnings
from collections import OrderedDict
from os import path, getenv
from typing import Any, NamedTuple, Union
from typing import (
Any, Callable, Dict, Generator, Iterator, List, NamedTuple, Set, Tuple, Union
)
from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.errors import ConfigError, ExtensionError
@ -23,14 +25,13 @@ from sphinx.util import logging
from sphinx.util.i18n import format_date
from sphinx.util.osutil import cd
from sphinx.util.pycompat import execfile_
from sphinx.util.tags import Tags
from sphinx.util.typing import NoneType
if False:
# For type annotation
from typing import Callable, Dict, Generator, Iterator, List, Set, Tuple # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.environment import BuildEnvironment # NOQA
from sphinx.util.tags import Tags # NOQA
from sphinx.application import Sphinx
from sphinx.environment import BuildEnvironment
logger = logging.getLogger(__name__)
@ -43,8 +44,7 @@ ConfigValue = NamedTuple('ConfigValue', [('name', str),
('rebuild', Union[bool, str])])
def is_serializable(obj):
# type: (Any) -> bool
def is_serializable(obj: Any) -> bool:
"""Check if object is serializable or not."""
if isinstance(obj, UNSERIALIZABLE_TYPES):
return False
@ -64,12 +64,10 @@ class ENUM:
Example:
app.add_config_value('latex_show_urls', 'no', None, ENUM('no', 'footnote', 'inline'))
"""
def __init__(self, *candidates):
# type: (str) -> None
def __init__(self, *candidates: str) -> None:
self.candidates = candidates
def match(self, value):
# type: (Union[str, List, Tuple]) -> bool
def match(self, value: Union[str, List, Tuple]) -> bool:
if isinstance(value, (list, tuple)):
return all(item in self.candidates for item in value)
else:
@ -156,8 +154,7 @@ class Config:
'env', []),
} # type: Dict[str, Tuple]
def __init__(self, config={}, overrides={}):
# type: (Dict[str, Any], Dict[str, Any]) -> None
def __init__(self, config: Dict[str, Any] = {}, overrides: Dict[str, Any] = {}) -> None:
self.overrides = dict(overrides)
self.values = Config.config_values.copy()
self._raw_config = config
@ -171,15 +168,13 @@ class Config:
self.extensions = config.get('extensions', []) # type: List[str]
@classmethod
def read(cls, confdir, overrides=None, tags=None):
# type: (str, Dict, Tags) -> Config
def read(cls, confdir: str, overrides: Dict = None, tags: Tags = None) -> "Config":
"""Create a Config object from configuration file."""
filename = path.join(confdir, CONFIG_FILENAME)
namespace = eval_config_file(filename, tags)
return cls(namespace, overrides or {})
def convert_overrides(self, name, value):
# type: (str, Any) -> Any
def convert_overrides(self, name: str, value: Any) -> Any:
if not isinstance(value, str):
return value
else:
@ -212,8 +207,7 @@ class Config:
else:
return value
def pre_init_values(self):
# type: () -> None
def pre_init_values(self) -> None:
"""
Initialize some limited config variables before initialize i18n and loading extensions
"""
@ -227,8 +221,7 @@ class Config:
except ValueError as exc:
logger.warning("%s", exc)
def init_values(self):
# type: () -> None
def init_values(self) -> None:
config = self._raw_config
for valname, value in self.overrides.items():
try:
@ -250,8 +243,7 @@ class Config:
if name in self.values:
self.__dict__[name] = config[name]
def __getattr__(self, name):
# type: (str) -> Any
def __getattr__(self, name: str) -> Any:
if name.startswith('_'):
raise AttributeError(name)
if name not in self.values:
@ -261,42 +253,34 @@ class Config:
return default(self)
return default
def __getitem__(self, name):
# type: (str) -> str
def __getitem__(self, name: str) -> str:
return getattr(self, name)
def __setitem__(self, name, value):
# type: (str, Any) -> None
def __setitem__(self, name: str, value: Any) -> None:
setattr(self, name, value)
def __delitem__(self, name):
# type: (str) -> None
def __delitem__(self, name: str) -> None:
delattr(self, name)
def __contains__(self, name):
# type: (str) -> bool
def __contains__(self, name: str) -> bool:
return name in self.values
def __iter__(self):
# type: () -> Generator[ConfigValue, None, None]
def __iter__(self) -> Generator[ConfigValue, None, None]:
for name, value in self.values.items():
yield ConfigValue(name, getattr(self, name), value[1])
def add(self, name, default, rebuild, types):
# type: (str, Any, Union[bool, str], Any) -> None
def add(self, name: str, default: Any, rebuild: Union[bool, str], types: Any) -> None:
if name in self.values:
raise ExtensionError(__('Config value %r already present') % name)
else:
self.values[name] = (default, rebuild, types)
def filter(self, rebuild):
# type: (Union[str, List[str]]) -> Iterator[ConfigValue]
def filter(self, rebuild: Union[str, List[str]]) -> Iterator[ConfigValue]:
if isinstance(rebuild, str):
rebuild = [rebuild]
return (value for value in self if value.rebuild in rebuild)
def __getstate__(self):
# type: () -> Dict
def __getstate__(self) -> Dict:
"""Obtains serializable data for pickling."""
# remove potentially pickling-problematic values from config
__dict__ = {}
@ -319,13 +303,11 @@ class Config:
return __dict__
def __setstate__(self, state):
# type: (Dict) -> None
def __setstate__(self, state: Dict) -> None:
self.__dict__.update(state)
def eval_config_file(filename, tags):
# type: (str, Tags) -> Dict[str, Any]
def eval_config_file(filename: str, tags: Tags) -> Dict[str, Any]:
"""Evaluate a config file."""
namespace = {} # type: Dict[str, Any]
namespace['__file__'] = filename
@ -349,8 +331,7 @@ def eval_config_file(filename, tags):
return namespace
def convert_source_suffix(app, config):
# type: (Sphinx, Config) -> None
def convert_source_suffix(app: "Sphinx", config: Config) -> None:
"""This converts old styled source_suffix to new styled one.
* old style: str or list
@ -375,8 +356,7 @@ def convert_source_suffix(app, config):
"But `%r' is given." % source_suffix))
def init_numfig_format(app, config):
# type: (Sphinx, Config) -> None
def init_numfig_format(app: "Sphinx", config: Config) -> None:
"""Initialize :confval:`numfig_format`."""
numfig_format = {'section': _('Section %s'),
'figure': _('Fig. %s'),
@ -388,8 +368,7 @@ def init_numfig_format(app, config):
config.numfig_format = numfig_format # type: ignore
def correct_copyright_year(app, config):
# type: (Sphinx, Config) -> None
def correct_copyright_year(app: "Sphinx", config: Config) -> None:
"""correct values of copyright year that are not coherent with
the SOURCE_DATE_EPOCH environment variable (if set)
@ -402,8 +381,7 @@ def correct_copyright_year(app, config):
config[k] = copyright_year_re.sub(replace, config[k])
def check_confval_types(app, config):
# type: (Sphinx, Config) -> None
def check_confval_types(app: "Sphinx", config: Config) -> None:
"""check all values for deviation from the default value's type, since
that can result in TypeErrors all over the place NB.
"""
@ -458,8 +436,7 @@ def check_confval_types(app, config):
default=type(default)))
def check_unicode(config):
# type: (Config) -> None
def check_unicode(config: Config) -> None:
"""check all string values for non-ASCII characters in bytestrings,
since that can result in UnicodeErrors all over the place
"""
@ -475,16 +452,15 @@ def check_unicode(config):
'Please use Unicode strings, e.g. %r.'), name, 'Content')
def check_primary_domain(app, config):
# type: (Sphinx, Config) -> None
def check_primary_domain(app: "Sphinx", config: Config) -> None:
primary_domain = config.primary_domain
if primary_domain and not app.registry.has_domain(primary_domain):
logger.warning(__('primary_domain %r not found, ignored.'), primary_domain)
config.primary_domain = None # type: ignore
def check_master_doc(app, env, added, changed, removed):
# type: (Sphinx, BuildEnvironment, Set[str], Set[str], Set[str]) -> Set[str]
def check_master_doc(app: "Sphinx", env: "BuildEnvironment", added: Set[str],
changed: Set[str], removed: Set[str]) -> Set[str]:
"""Adjust master_doc to 'contents' to support an old project which does not have
no master_doc setting.
"""
@ -498,8 +474,7 @@ def check_master_doc(app, env, added, changed, removed):
return changed
def setup(app):
# type: (Sphinx) -> Dict[str, Any]
def setup(app: "Sphinx") -> Dict[str, Any]:
app.connect('config-inited', convert_source_suffix)
app.connect('config-inited', init_numfig_format)
app.connect('config-inited', correct_copyright_year)

View File

@ -11,11 +11,8 @@
import sys
import warnings
from importlib import import_module
if False:
# For type annotation
from typing import Any, Dict # NOQA
from typing import Type # for python3.5.1
from typing import Any, Dict
from typing import Type # for python3.5.1
class RemovedInSphinx40Warning(DeprecationWarning):
@ -29,22 +26,20 @@ class RemovedInSphinx50Warning(PendingDeprecationWarning):
RemovedInNextVersionWarning = RemovedInSphinx40Warning
def deprecated_alias(modname, objects, warning):
# type: (str, Dict, Type[Warning]) -> None
def deprecated_alias(modname: str, objects: Dict, warning: Type[Warning]) -> None:
module = import_module(modname)
sys.modules[modname] = _ModuleWrapper(module, modname, objects, warning) # type: ignore
class _ModuleWrapper:
def __init__(self, module, modname, objects, warning):
# type: (Any, str, Dict, Type[Warning]) -> None
def __init__(self, module: Any, modname: str, objects: Dict, warning: Type[Warning]
) -> None:
self._module = module
self._modname = modname
self._objects = objects
self._warning = warning
def __getattr__(self, name):
# type: (str) -> Any
def __getattr__(self, name: str) -> Any:
if name in self._objects:
warnings.warn("%s.%s is deprecated. Check CHANGES for Sphinx "
"API modifications." % (self._modname, name),
@ -57,33 +52,27 @@ class _ModuleWrapper:
class DeprecatedDict(dict):
"""A deprecated dict which warns on each access."""
def __init__(self, data, message, warning):
# type: (Dict, str, Type[Warning]) -> None
def __init__(self, data: Dict, message: str, warning: Type[Warning]) -> None:
self.message = message
self.warning = warning
super().__init__(data)
def __setitem__(self, key, value):
# type: (str, Any) -> None
def __setitem__(self, key: str, value: Any) -> None:
warnings.warn(self.message, self.warning, stacklevel=2)
super().__setitem__(key, value)
def setdefault(self, key, default=None):
# type: (str, Any) -> None
def setdefault(self, key: str, default: Any = None) -> Any:
warnings.warn(self.message, self.warning, stacklevel=2)
return super().setdefault(key, default)
def __getitem__(self, key):
# type: (str) -> None
def __getitem__(self, key: str) -> None:
warnings.warn(self.message, self.warning, stacklevel=2)
return super().__getitem__(key)
def get(self, key, default=None):
# type: (str, Any) -> None
def get(self, key: str, default: Any = None) -> Any:
warnings.warn(self.message, self.warning, stacklevel=2)
return super().get(key, default)
def update(self, other=None): # type: ignore
# type: (Dict) -> None
def update(self, other: Dict = None) -> None: # type: ignore
warnings.warn(self.message, self.warning, stacklevel=2)
super().update(other)

View File

@ -640,7 +640,7 @@ class BuildEnvironment:
@property
def indexentries(self) -> Dict[str, List[Tuple[str, str, str, str, str]]]:
warnings.warn('env.indexentries() is deprecated. Please use IndexDomain instead.',
RemovedInSphinx40Warning)
RemovedInSphinx40Warning, stacklevel=2)
from sphinx.domains.index import IndexDomain
domain = cast(IndexDomain, self.get_domain('index'))
return domain.entries

View File

@ -12,8 +12,10 @@ import re
import unicodedata
from itertools import groupby
from typing import Any, Dict, Pattern, List, Tuple
from typing import cast
from sphinx.builders import Builder
from sphinx.domains.index import IndexDomain
from sphinx.environment import BuildEnvironment
from sphinx.errors import NoUri
from sphinx.locale import _, __
@ -53,7 +55,8 @@ class IndexEntries:
# maintain links in sorted/deterministic order
bisect.insort(entry[0], (main, uri))
for fn, entries in self.env.indexentries.items():
domain = cast(IndexDomain, self.env.get_domain('index'))
for fn, entries in domain.entries.items():
# new entry types must be listed in directives/other.py!
for type, value, tid, main, index_key in entries:
try:

View File

@ -12,9 +12,12 @@ from typing import Dict, List, Set
from docutils import nodes
from sphinx.application import Sphinx
from sphinx.environment import BuildEnvironment
if False:
# For type annotation
from sphinx.application import Sphinx
class EnvironmentCollector:
"""An EnvironmentCollector is a specific data collector from each document.
@ -27,7 +30,7 @@ class EnvironmentCollector:
listener_ids = None # type: Dict[str, int]
def enable(self, app: 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),
@ -37,38 +40,38 @@ class EnvironmentCollector:
'env-get-outdated': app.connect('env-get-outdated', self.get_outdated_docs),
}
def disable(self, app: 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: Sphinx, env: BuildEnvironment, docname: 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: Sphinx, env: BuildEnvironment,
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: Sphinx, doctree: 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: Sphinx, env: 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: Sphinx, env: BuildEnvironment,
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.

View File

@ -9,9 +9,7 @@
:license: BSD, see LICENSE for details.
"""
if False:
# For type annotation
from typing import Any # NOQA
from typing import Any
class SphinxError(Exception):
@ -51,21 +49,18 @@ class ExtensionError(SphinxError):
"""Extension error."""
category = 'Extension error'
def __init__(self, message, orig_exc=None):
# type: (str, Exception) -> None
def __init__(self, message: str, orig_exc: Exception = None) -> None:
super().__init__(message)
self.message = message
self.orig_exc = orig_exc
def __repr__(self):
# type: () -> str
def __repr__(self) -> str:
if self.orig_exc:
return '%s(%r, %r)' % (self.__class__.__name__,
self.message, self.orig_exc)
return '%s(%r)' % (self.__class__.__name__, self.message)
def __str__(self):
# type: () -> str
def __str__(self) -> str:
parent_str = super().__str__()
if self.orig_exc:
return '%s (exception: %s)' % (parent_str, self.orig_exc)
@ -102,21 +97,18 @@ class SphinxParallelError(SphinxError):
category = 'Sphinx parallel build error'
def __init__(self, message, traceback):
# type: (str, Any) -> None
def __init__(self, message: str, traceback: Any) -> None:
self.message = message
self.traceback = traceback
def __str__(self):
# type: () -> str
def __str__(self) -> str:
return self.message
class PycodeError(Exception):
"""Pycode Python source code analyser error."""
def __str__(self):
# type: () -> str
def __str__(self) -> str:
res = self.args[0]
if len(self.args) > 1:
res += ' (exception was: %r)' % self.args[1]

View File

@ -12,6 +12,7 @@
import warnings
from collections import OrderedDict, defaultdict
from typing import Any, Callable, Dict, List
from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.errors import ExtensionError
@ -20,8 +21,8 @@ from sphinx.util import logging
if False:
# For type annotation
from typing import Any, Callable, Dict, List # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.application import Sphinx
logger = logging.getLogger(__name__)
@ -50,8 +51,7 @@ core_events = {
class EventManager:
"""Event manager for Sphinx."""
def __init__(self, app=None):
# type: (Sphinx) -> None
def __init__(self, app: "Sphinx" = None) -> None:
if app is None:
warnings.warn('app argument is required for EventManager.',
RemovedInSphinx40Warning)
@ -60,15 +60,13 @@ class EventManager:
self.listeners = defaultdict(OrderedDict) # type: Dict[str, Dict[int, Callable]]
self.next_listener_id = 0
def add(self, name):
# type: (str) -> None
def add(self, name: str) -> None:
"""Register a custom Sphinx event."""
if name in self.events:
raise ExtensionError(__('Event %r already present') % name)
self.events[name] = ''
def connect(self, name, callback):
# type: (str, Callable) -> int
def connect(self, name: str, callback: Callable) -> int:
"""Connect a handler to specific event."""
if name not in self.events:
raise ExtensionError(__('Unknown event name: %s') % name)
@ -78,14 +76,12 @@ class EventManager:
self.listeners[name][listener_id] = callback
return listener_id
def disconnect(self, listener_id):
# type: (int) -> None
def disconnect(self, listener_id: int) -> None:
"""Disconnect a handler."""
for event in self.listeners.values():
event.pop(listener_id, None)
def emit(self, name, *args):
# type: (str, Any) -> List
def emit(self, name: str, *args) -> List:
"""Emit a Sphinx event."""
try:
logger.debug('[app] emitting event: %r%s', name, repr(args)[:100])
@ -103,8 +99,7 @@ class EventManager:
results.append(callback(self.app, *args))
return results
def emit_firstresult(self, name, *args):
# type: (str, Any) -> Any
def emit_firstresult(self, name: str, *args) -> Any:
"""Emit a Sphinx event and returns first result.
This returns the result of the first handler that doesn't return ``None``.

View File

@ -59,6 +59,11 @@ class DummyApplication:
self.registry = SphinxComponentRegistry()
self.messagelog = [] # type: List[str]
self.verbosity = 0
self._warncount = 0
self.warningiserror = False
def emit_firstresult(self, *args) -> None:
pass
def setup_documenters(app: Any) -> None:

View File

@ -8,22 +8,22 @@
:license: BSD, see LICENSE for details.
"""
from typing import Any, Dict
from sphinx.config import Config
from sphinx.errors import VersionRequirementError
from sphinx.locale import __
from sphinx.util import logging
if False:
# For type annotation
from typing import Any, Dict # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.config import Config # NOQA
from sphinx.application import Sphinx
logger = logging.getLogger(__name__)
class Extension:
def __init__(self, name, module, **kwargs):
# type: (str, Any, Any) -> None
def __init__(self, name: str, module: Any, **kwargs) -> None:
self.name = name
self.module = module
self.metadata = kwargs
@ -40,8 +40,7 @@ class Extension:
self.parallel_write_safe = kwargs.pop('parallel_write_safe', True)
def verify_needs_extensions(app, config):
# type: (Sphinx, Config) -> None
def verify_needs_extensions(app: "Sphinx", config: Config) -> None:
"""Verify the required Sphinx extensions are loaded."""
if config.needs_extensions is None:
return
@ -60,8 +59,7 @@ def verify_needs_extensions(app, config):
(extname, reqversion, extension.version))
def setup(app):
# type: (Sphinx) -> Dict[str, Any]
def setup(app: "Sphinx") -> Dict[str, Any]:
app.connect('config-inited', verify_needs_extensions)
return {

View File

@ -10,14 +10,17 @@
from functools import partial
from importlib import import_module
from typing import Any, Dict
from pygments import highlight
from pygments.filters import ErrorToken
from pygments.formatter import Formatter
from pygments.formatters import HtmlFormatter, LatexFormatter
from pygments.lexer import Lexer
from pygments.lexers import get_lexer_by_name, guess_lexer
from pygments.lexers import PythonLexer, Python3Lexer, PythonConsoleLexer, \
CLexer, TextLexer, RstLexer
from pygments.style import Style
from pygments.styles import get_style_by_name
from pygments.util import ClassNotFound
@ -25,12 +28,6 @@ from sphinx.locale import __
from sphinx.pygments_styles import SphinxStyle, NoneStyle
from sphinx.util import logging, texescape
if False:
# For type annotation
from typing import Any, Dict # NOQA
from pygments.formatter import Formatter # NOQA
from pygments.style import Style # NOQA
logger = logging.getLogger(__name__)
@ -63,8 +60,8 @@ class PygmentsBridge:
html_formatter = HtmlFormatter
latex_formatter = LatexFormatter
def __init__(self, dest='html', stylename='sphinx', latex_engine=None):
# type: (str, str, str) -> None
def __init__(self, dest: str = 'html', stylename: str = 'sphinx',
latex_engine: str = None) -> None:
self.dest = dest
self.latex_engine = latex_engine
@ -76,8 +73,7 @@ class PygmentsBridge:
self.formatter = self.latex_formatter
self.formatter_args['commandprefix'] = 'PYG'
def get_style(self, stylename):
# type: (str) -> Style
def get_style(self, stylename: str) -> Style:
if stylename is None or stylename == 'sphinx':
return SphinxStyle
elif stylename == 'none':
@ -88,13 +84,12 @@ class PygmentsBridge:
else:
return get_style_by_name(stylename)
def get_formatter(self, **kwargs):
# type: (Any) -> Formatter
def get_formatter(self, **kwargs) -> Formatter:
kwargs.update(self.formatter_args)
return self.formatter(**kwargs)
def get_lexer(self, source, lang, opts=None, force=False, location=None):
# type: (str, str, Dict, bool, Any) -> Lexer
def get_lexer(self, source: str, lang: str, opts: Dict = None,
force: bool = False, location: Any = None) -> Lexer:
if not opts:
opts = {}
@ -137,8 +132,8 @@ class PygmentsBridge:
return lexer
def highlight_block(self, source, lang, opts=None, force=False, location=None, **kwargs):
# type: (str, str, Dict, bool, Any, Any) -> str
def highlight_block(self, source: str, lang: str, opts: Dict = None,
force: bool = False, location: Any = None, **kwargs) -> str:
if not isinstance(source, str):
source = source.decode()
@ -167,8 +162,7 @@ class PygmentsBridge:
# MEMO: this is done to escape Unicode chars with non-Unicode engines
return texescape.hlescape(hlsource, self.latex_engine)
def get_stylesheet(self):
# type: () -> str
def get_stylesheet(self) -> str:
formatter = self.get_formatter()
if self.dest == 'html':
return formatter.get_style_defs('.highlight')

View File

@ -9,16 +9,23 @@
"""
import codecs
import warnings
from typing import Any
from typing import Any, List, Tuple
from typing import Type # for python3.5.1
from docutils import nodes
from docutils.core import Publisher
from docutils.io import FileInput, NullOutput
from docutils.frontend import Values
from docutils.io import FileInput, Input, NullOutput
from docutils.parsers import Parser
from docutils.parsers.rst import Parser as RSTParser
from docutils.readers import standalone
from docutils.statemachine import StringList, string2lines
from docutils.transforms import Transform
from docutils.transforms.references import DanglingReferences
from docutils.writers import UnfilteredWriter
from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
from sphinx.environment import BuildEnvironment
from sphinx.errors import FiletypeNotFoundError
from sphinx.transforms import (
AutoIndexUpgrader, DoctreeReadEvent, FigureAligner, SphinxTransformer
@ -34,15 +41,7 @@ from sphinx.versioning import UIDTransform
if False:
# For type annotation
from typing import Dict, List, Tuple # NOQA
from typing import Type # for python3.5.1
from docutils import nodes # NOQA
from docutils.frontend import Values # NOQA
from docutils.io import Input # NOQA
from docutils.parsers import Parser # NOQA
from docutils.transforms import Transform # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.environment import BuildEnvironment # NOQA
from sphinx.application import Sphinx
logger = logging.getLogger(__name__)
@ -57,8 +56,7 @@ class SphinxBaseReader(standalone.Reader):
transforms = [] # type: List[Type[Transform]]
def __init__(self, *args, **kwargs):
# type: (Any, Any) -> None
def __init__(self, *args, **kwargs) -> None:
from sphinx.application import Sphinx
if len(args) > 0 and isinstance(args[0], Sphinx):
self._app = args[0]
@ -68,26 +66,22 @@ class SphinxBaseReader(standalone.Reader):
super().__init__(*args, **kwargs)
@property
def app(self):
# type: () -> Sphinx
def app(self) -> "Sphinx":
warnings.warn('SphinxBaseReader.app is deprecated.',
RemovedInSphinx40Warning, stacklevel=2)
return self._app
@property
def env(self):
# type: () -> BuildEnvironment
def env(self) -> BuildEnvironment:
warnings.warn('SphinxBaseReader.env is deprecated.',
RemovedInSphinx40Warning, stacklevel=2)
return self._env
def setup(self, app):
# type: (Sphinx) -> None
def setup(self, app: "Sphinx") -> None:
self._app = app # hold application object only for compatibility
self._env = app.env
def get_transforms(self):
# type: () -> List[Type[Transform]]
def get_transforms(self) -> List[Type[Transform]]:
transforms = super().get_transforms() + self.transforms
# remove transforms which is not needed for Sphinx
@ -98,8 +92,7 @@ class SphinxBaseReader(standalone.Reader):
return transforms
def new_document(self):
# type: () -> nodes.document
def new_document(self) -> nodes.document:
"""Creates a new document object which having a special reporter object good
for logging.
"""
@ -121,13 +114,11 @@ class SphinxStandaloneReader(SphinxBaseReader):
A basic document reader for Sphinx.
"""
def setup(self, app):
# type: (Sphinx) -> None
def setup(self, app: "Sphinx") -> None:
self.transforms = self.transforms + app.registry.get_transforms()
super().setup(app)
def read(self, source, parser, settings):
# type: (Input, Parser, Values) -> nodes.document
def read(self, source: Input, parser: Parser, settings: Values) -> nodes.document:
self.source = source
if not self.parser:
self.parser = parser
@ -136,8 +127,7 @@ class SphinxStandaloneReader(SphinxBaseReader):
self.parse()
return self.document
def read_source(self, env):
# type: (BuildEnvironment) -> str
def read_source(self, env: BuildEnvironment) -> str:
"""Read content from source and do post-process."""
content = self.source.read()
@ -156,8 +146,7 @@ class SphinxI18nReader(SphinxBaseReader):
Because the translated texts are partial and they don't have correct line numbers.
"""
def setup(self, app):
# type: (Sphinx) -> None
def setup(self, app: "Sphinx") -> None:
super().setup(app)
self.transforms = self.transforms + app.registry.get_transforms()
@ -174,27 +163,24 @@ class SphinxDummyWriter(UnfilteredWriter):
supported = ('html',) # needed to keep "meta" nodes
def translate(self):
# type: () -> None
def translate(self) -> None:
pass
def SphinxDummySourceClass(source, *args, **kwargs):
# type: (Any, Any, Any) -> Any
def SphinxDummySourceClass(source: Any, *args, **kwargs) -> Any:
"""Bypass source object as is to cheat Publisher."""
return source
class SphinxFileInput(FileInput):
"""A basic FileInput for Sphinx."""
def __init__(self, *args, **kwargs):
# type: (Any, Any) -> None
def __init__(self, *args, **kwargs) -> None:
kwargs['error_handler'] = 'sphinx'
super().__init__(*args, **kwargs)
def read_doc(app, env, filename):
# type: (Sphinx, BuildEnvironment, str) -> nodes.document
def read_doc(app: "Sphinx", env: BuildEnvironment, filename: str) -> nodes.document:
"""Parse a document and convert to doctree."""
# set up error_handler for the target document
error_handler = UnicodeDecodeErrorHandler(env.docname)

View File

@ -10,42 +10,37 @@
from os import path
from pprint import pformat
from typing import Any, Callable, Iterator, Tuple # NOQA
from typing import Any, Callable, Dict, Iterator, List, Tuple, Union
from jinja2 import FileSystemLoader, BaseLoader, TemplateNotFound, \
contextfunction
from jinja2 import FileSystemLoader, BaseLoader, TemplateNotFound, contextfunction
from jinja2.environment import Environment
from jinja2.sandbox import SandboxedEnvironment
from jinja2.utils import open_if_exists
from sphinx.application import TemplateBridge
from sphinx.theming import Theme
from sphinx.util import logging
from sphinx.util.osutil import mtimes_of_files
if False:
# For type annotation
from typing import Dict, List, Union # NOQA
from jinja2.environment import Environment # NOQA
from sphinx.builders import Builder # NOQA
from sphinx.theming import Theme # NOQA
from sphinx.builders import Builder
def _tobool(val):
# type: (str) -> bool
def _tobool(val: str) -> bool:
if isinstance(val, str):
return val.lower() in ('true', '1', 'yes', 'on')
return bool(val)
def _toint(val):
# type: (str) -> int
def _toint(val: str) -> int:
try:
return int(val)
except ValueError:
return 0
def _todim(val):
# type: (Union[int, str]) -> str
def _todim(val: Union[int, str]) -> str:
"""
Make val a css dimension. In particular the following transformations
are performed:
@ -63,8 +58,7 @@ def _todim(val):
return val # type: ignore
def _slice_index(values, slices):
# type: (List, int) -> Iterator[List]
def _slice_index(values: List, slices: int) -> Iterator[List]:
seq = list(values)
length = 0
for value in values:
@ -85,8 +79,7 @@ def _slice_index(values, slices):
yield seq[start:offset]
def accesskey(context, key):
# type: (Any, str) -> str
def accesskey(context: Any, key: str) -> str:
"""Helper to output each access key only once."""
if '_accesskeys' not in context:
context.vars['_accesskeys'] = {}
@ -97,24 +90,20 @@ def accesskey(context, key):
class idgen:
def __init__(self):
# type: () -> None
def __init__(self) -> None:
self.id = 0
def current(self):
# type: () -> int
def current(self) -> int:
return self.id
def __next__(self):
# type: () -> int
def __next__(self) -> int:
self.id += 1
return self.id
next = __next__ # Python 2/Jinja compatibility
@contextfunction
def warning(context, message, *args, **kwargs):
# type: (Dict, str, Any, Any) -> str
def warning(context: Dict, message: str, *args, **kwargs) -> str:
if 'pagename' in context:
filename = context.get('pagename') + context.get('file_suffix', '')
message = 'in rendering %s: %s' % (filename, message)
@ -129,8 +118,7 @@ class SphinxFileSystemLoader(FileSystemLoader):
template names.
"""
def get_source(self, environment, template):
# type: (Environment, str) -> Tuple[str, str, Callable]
def get_source(self, environment: Environment, template: str) -> Tuple[str, str, Callable]:
for searchpath in self.searchpath:
filename = path.join(searchpath, template)
f = open_if_exists(filename)
@ -141,8 +129,7 @@ class SphinxFileSystemLoader(FileSystemLoader):
mtime = path.getmtime(filename)
def uptodate():
# type: () -> bool
def uptodate() -> bool:
try:
return path.getmtime(filename) == mtime
except OSError:
@ -158,8 +145,7 @@ class BuiltinTemplateLoader(TemplateBridge, BaseLoader):
# TemplateBridge interface
def init(self, builder, theme=None, dirs=None):
# type: (Builder, Theme, List[str]) -> None
def init(self, builder: "Builder", theme: Theme = None, dirs: List[str] = None) -> None:
# create a chain of paths to search
if theme:
# the theme's own dir and its bases' dirs
@ -202,22 +188,18 @@ class BuiltinTemplateLoader(TemplateBridge, BaseLoader):
if use_i18n:
self.environment.install_gettext_translations(builder.app.translator) # type: ignore # NOQA
def render(self, template, context): # type: ignore
# type: (str, Dict) -> str
def render(self, template: str, context: Dict) -> str: # type: ignore
return self.environment.get_template(template).render(context)
def render_string(self, source, context):
# type: (str, Dict) -> str
def render_string(self, source: str, context: Dict) -> str:
return self.environment.from_string(source).render(context)
def newest_template_mtime(self):
# type: () -> float
def newest_template_mtime(self) -> float:
return max(mtimes_of_files(self.pathchain, '.html'))
# Loader interface
def get_source(self, environment, template):
# type: (Environment, str) -> Tuple[str, str, Callable]
def get_source(self, environment: Environment, template: str) -> Tuple[str, str, Callable]:
loaders = self.loaders
# exclamation mark starts search from theme
if template.startswith('!'):

View File

@ -12,10 +12,9 @@ import gettext
import locale
from collections import UserString, defaultdict
from gettext import NullTranslations
from typing import Any, Callable, Dict, Iterable, List, Tuple, Union
if False:
# For type annotation
from typing import Any, Callable, Dict, Iterable, List, Tuple, Union # NOQA
from sphinx.deprecation import RemovedInSphinx30Warning
class _TranslationProxy(UserString):
@ -32,32 +31,27 @@ class _TranslationProxy(UserString):
"""
__slots__ = ('_func', '_args')
def __new__(cls, func, *args): # type: ignore
# type: (Callable, str) -> object
def __new__(cls, func: Callable, *args: str) -> object: # type: ignore
if not args:
# not called with "function" and "arguments", but a plain string
return str(func)
return object.__new__(cls)
def __getnewargs__(self):
# type: () -> Tuple[str]
def __getnewargs__(self) -> Tuple[str]:
return (self._func,) + self._args # type: ignore
def __init__(self, func, *args):
# type: (Callable, str) -> None
def __init__(self, func: Callable, *args: str) -> None:
self._func = func
self._args = args
@property
def data(self): # type: ignore
# type: () -> str
def data(self) -> str: # type: ignore
return self._func(*self._args)
# replace function from UserString; it instantiates a self.__class__
# for the encoding result
def encode(self, encoding=None, errors=None): # type: ignore
# type: (str, str) -> bytes
def encode(self, encoding: str = None, errors: str = None) -> bytes: # type: ignore
if encoding:
if errors:
return self.data.encode(encoding, errors)
@ -66,58 +60,45 @@ class _TranslationProxy(UserString):
else:
return self.data.encode()
def __dir__(self):
# type: () -> List[str]
def __dir__(self) -> List[str]:
return dir(str)
def __str__(self):
# type: () -> str
def __str__(self) -> str:
return str(self.data)
def __add__(self, other): # type: ignore
# type: (str) -> str
def __add__(self, other: str) -> str: # type: ignore
return self.data + other
def __radd__(self, other):
# type: (str) -> str
def __radd__(self, other: str) -> str:
return other + self.data
def __mod__(self, other): # type: ignore
# type: (str) -> str
def __mod__(self, other: str) -> str: # type: ignore
return self.data % other
def __rmod__(self, other):
# type: (str) -> str
def __rmod__(self, other: str) -> str:
return other % self.data
def __mul__(self, other): # type: ignore
# type: (Any) -> str
def __mul__(self, other: Any) -> str: # type: ignore
return self.data * other
def __rmul__(self, other):
# type: (Any) -> str
def __rmul__(self, other: Any) -> str:
return other * self.data
def __getattr__(self, name):
# type: (str) -> Any
def __getattr__(self, name: str) -> Any:
if name == '__members__':
return self.__dir__()
return getattr(self.data, name)
def __getstate__(self):
# type: () -> Tuple[Callable, Tuple[str, ...]]
def __getstate__(self) -> Tuple[Callable, Tuple[str, ...]]:
return self._func, self._args
def __setstate__(self, tup):
# type: (Tuple[Callable, Tuple[str]]) -> None
def __setstate__(self, tup: Tuple[Callable, Tuple[str]]) -> None:
self._func, self._args = tup
def __copy__(self):
# type: () -> _TranslationProxy
def __copy__(self) -> "_TranslationProxy":
return self
def __repr__(self):
# type: () -> str
def __repr__(self) -> str:
try:
return 'i' + repr(str(self.data))
except Exception:
@ -127,8 +108,8 @@ class _TranslationProxy(UserString):
translators = defaultdict(NullTranslations) # type: Dict[Tuple[str, str], NullTranslations]
def init(locale_dirs, language, catalog='sphinx', namespace='general'):
# type: (List[str], str, str, str) -> Tuple[NullTranslations, bool]
def init(locale_dirs: List[str], language: str,
catalog: str = 'sphinx', namespace: str = 'general') -> Tuple[NullTranslations, bool]:
"""Look for message catalogs in `locale_dirs` and *ensure* that there is at
least a NullTranslations catalog set in `translators`. If called multiple
times or if several ``.mo`` files are found, their contents are merged
@ -167,8 +148,7 @@ def init(locale_dirs, language, catalog='sphinx', namespace='general'):
return translator, has_translation
def setlocale(category, value=None):
# type: (int, Union[str, Iterable[str]]) -> None
def setlocale(category: int, value: Union[str, Iterable[str]] = None) -> None:
"""Update locale settings.
This does not throw any exception even if update fails.
@ -188,8 +168,7 @@ def setlocale(category, value=None):
pass
def init_console(locale_dir, catalog):
# type: (str, str) -> Tuple[NullTranslations, bool]
def init_console(locale_dir: str, catalog: str) -> Tuple[NullTranslations, bool]:
"""Initialize locale for console.
.. versionadded:: 1.8
@ -204,18 +183,15 @@ def init_console(locale_dir, catalog):
return init([locale_dir], language, catalog, 'console')
def get_translator(catalog='sphinx', namespace='general'):
# type: (str, str) -> NullTranslations
def get_translator(catalog: str = 'sphinx', namespace: str = 'general') -> NullTranslations:
return translators[(namespace, catalog)]
def is_translator_registered(catalog='sphinx', namespace='general'):
# type: (str, str) -> bool
def is_translator_registered(catalog: str = 'sphinx', namespace: str = 'general') -> bool:
return (namespace, catalog) in translators
def _lazy_translate(catalog, namespace, message):
# type: (str, str, str) -> str
def _lazy_translate(catalog: str, namespace: str, message: str) -> str:
"""Used instead of _ when creating TranslationProxy, because _ is
not bound yet at that time.
"""
@ -248,8 +224,7 @@ def get_translation(catalog, namespace='general'):
.. versionadded:: 1.8
"""
def gettext(message, *args):
# type: (str, *Any) -> str
def gettext(message: str, *args) -> str:
if not is_translator_registered(catalog, namespace):
# not initialized yet
return _TranslationProxy(_lazy_translate, catalog, namespace, message) # type: ignore # NOQA

View File

@ -8,8 +8,11 @@
:license: BSD, see LICENSE for details.
"""
from typing import Any, Dict, List, Union
import docutils.parsers
import docutils.parsers.rst
from docutils import nodes
from docutils.parsers.rst import states
from docutils.statemachine import StringList
from docutils.transforms.universal import SmartQuotes
@ -18,11 +21,9 @@ from sphinx.util.rst import append_epilog, prepend_prolog
if False:
# For type annotation
from typing import Any, Dict, List, Union # NOQA
from typing import Type # for python3.5.1
from docutils import nodes # NOQA
from docutils.transforms import Transform # NOQA
from sphinx.application import Sphinx # NOQA
from typing import Type # NOQA # for python3.5.1
from sphinx.application import Sphinx
class Parser(docutils.parsers.Parser):
@ -48,8 +49,7 @@ class Parser(docutils.parsers.Parser):
``warn()`` and ``info()`` is deprecated. Use :mod:`sphinx.util.logging` instead.
"""
def set_application(self, app):
# type: (Sphinx) -> None
def set_application(self, app: "Sphinx") -> None:
"""set_application will be called from Sphinx to set app and other instance variables
:param sphinx.application.Sphinx app: Sphinx application object
@ -62,8 +62,7 @@ class Parser(docutils.parsers.Parser):
class RSTParser(docutils.parsers.rst.Parser, Parser):
"""A reST parser for Sphinx."""
def get_transforms(self):
# type: () -> List[Type[Transform]]
def get_transforms(self) -> List["Type[Transform]"]:
"""Sphinx's reST parser replaces a transform class for smart-quotes by own's
refs: sphinx.io.SphinxStandaloneReader
@ -72,8 +71,7 @@ class RSTParser(docutils.parsers.rst.Parser, Parser):
transforms.remove(SmartQuotes)
return transforms
def parse(self, inputstring, document):
# type: (Union[str, StringList], nodes.document) -> None
def parse(self, inputstring: Union[str, StringList], document: nodes.document) -> None:
"""Parse text and generate a document tree."""
self.setup_parse(inputstring, document) # type: ignore
self.statemachine = states.RSTStateMachine(
@ -95,15 +93,13 @@ class RSTParser(docutils.parsers.rst.Parser, Parser):
self.statemachine.run(inputlines, document, inliner=self.inliner)
self.finish_parse()
def decorate(self, content):
# type: (StringList) -> None
def decorate(self, content: StringList) -> None:
"""Preprocess reST content before parsing."""
prepend_prolog(content, self.config.rst_prolog)
append_epilog(content, self.config.rst_epilog)
def setup(app):
# type: (Sphinx) -> Dict[str, Any]
def setup(app: "Sphinx") -> Dict[str, Any]:
app.add_source_parser(RSTParser)
return {

View File

@ -11,36 +11,37 @@
import traceback
from importlib import import_module
from types import MethodType
from typing import Any, Callable, Dict, Iterator, List, Tuple, Union
from docutils import nodes
from docutils.io import Input
from docutils.nodes import Element, Node, TextElement
from docutils.parsers import Parser
from docutils.parsers.rst import Directive
from docutils.transforms import Transform
from pkg_resources import iter_entry_points
from sphinx.domains import ObjType
from sphinx.builders import Builder
from sphinx.config import Config
from sphinx.deprecation import RemovedInSphinx30Warning
from sphinx.domains import Domain, Index, ObjType
from sphinx.domains.std import GenericObject, Target
from sphinx.environment import BuildEnvironment
from sphinx.errors import ExtensionError, SphinxError, VersionRequirementError
from sphinx.extension import Extension
from sphinx.io import SphinxFileInput
from sphinx.locale import __
from sphinx.parsers import Parser as SphinxParser
from sphinx.roles import XRefRole
from sphinx.util import logging
from sphinx.util.logging import prefixed_warnings
from sphinx.util.typing import RoleFunction, TitleGetter
if False:
# For type annotation
from typing import Any, Callable, Dict, Iterator, List, Tuple, Union # NOQA
from typing import Type # for python3.5.1
from docutils import nodes # NOQA
from docutils.io import Input # NOQA
from docutils.parsers import Parser # NOQA
from docutils.transforms import Transform # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.builders import Builder # NOQA
from sphinx.config import Config # NOQA
from sphinx.domains import Domain, Index # NOQA
from sphinx.environment import BuildEnvironment # NOQA
from sphinx.ext.autodoc import Documenter # NOQA
from sphinx.io import SphinxFileInput # NOQA
from sphinx.util.typing import RoleFunction, TitleGetter # NOQA
from sphinx.application import Sphinx
from sphinx.ext.autodoc import Documenter
logger = logging.getLogger(__name__)
@ -52,8 +53,7 @@ EXTENSION_BLACKLIST = {
class SphinxComponentRegistry:
def __init__(self):
# type: () -> None
def __init__(self) -> None:
#: special attrgetter for autodoc; class object -> attrgetter
self.autodoc_attrgettrs = {} # type: Dict[Type, Callable[[Any, str, Any], Any]]
@ -87,7 +87,7 @@ class SphinxComponentRegistry:
#: additional enumerable nodes
#: a dict of node class -> tuple of figtype and title_getter function
self.enumerable_nodes = {} # type: Dict[Type[nodes.Node], Tuple[str, TitleGetter]]
self.enumerable_nodes = {} # type: Dict[Type[Node], Tuple[str, TitleGetter]]
#: HTML inline and block math renderers
#: a dict of name -> tuple of visit function and depart function
@ -122,8 +122,7 @@ class SphinxComponentRegistry:
#: additional transforms; list of transforms
self.transforms = [] # type: List[Type[Transform]]
def add_builder(self, builder, override=False):
# type: (Type[Builder], bool) -> None
def add_builder(self, builder: "Type[Builder]", override: bool = False) -> None:
logger.debug('[app] adding builder: %r', builder)
if not hasattr(builder, 'name'):
raise ExtensionError(__('Builder class %s has no "name" attribute') % builder)
@ -132,8 +131,7 @@ class SphinxComponentRegistry:
(builder.name, self.builders[builder.name].__module__))
self.builders[builder.name] = builder
def preload_builder(self, app, name):
# type: (Sphinx, str) -> None
def preload_builder(self, app: "Sphinx", name: str) -> None:
if name is None:
return
@ -147,26 +145,22 @@ class SphinxComponentRegistry:
self.load_extension(app, entry_point.module_name)
def create_builder(self, app, name):
# type: (Sphinx, str) -> Builder
def create_builder(self, app: "Sphinx", name: str) -> Builder:
if name not in self.builders:
raise SphinxError(__('Builder name %s not registered') % name)
return self.builders[name](app)
def add_domain(self, domain, override=False):
# type: (Type[Domain], bool) -> None
def add_domain(self, domain: "Type[Domain]", override: bool = False) -> None:
logger.debug('[app] adding domain: %r', domain)
if domain.name in self.domains and not override:
raise ExtensionError(__('domain %s already registered') % domain.name)
self.domains[domain.name] = domain
def has_domain(self, domain):
# type: (str) -> bool
def has_domain(self, domain: str) -> bool:
return domain in self.domains
def create_domains(self, env):
# type: (BuildEnvironment) -> Iterator[Domain]
def create_domains(self, env: BuildEnvironment) -> Iterator[Domain]:
for DomainClass in self.domains.values():
domain = DomainClass(env)
@ -179,8 +173,8 @@ class SphinxComponentRegistry:
yield domain
def add_directive_to_domain(self, domain, name, cls, override=False):
# type: (str, str, Type[Directive], bool) -> None
def add_directive_to_domain(self, domain: str, name: str,
cls: "Type[Directive]", override: bool = False) -> None:
logger.debug('[app] adding directive to domain: %r', (domain, name, cls))
if domain not in self.domains:
raise ExtensionError(__('domain %s not yet registered') % domain)
@ -191,8 +185,9 @@ class SphinxComponentRegistry:
(name, domain))
directives[name] = cls
def add_role_to_domain(self, domain, name, role, override=False):
# type: (str, str, Union[RoleFunction, XRefRole], bool) -> None
def add_role_to_domain(self, domain: str, name: str,
role: Union[RoleFunction, XRefRole], override: bool = False
) -> None:
logger.debug('[app] adding role to domain: %r', (domain, name, role))
if domain not in self.domains:
raise ExtensionError(__('domain %s not yet registered') % domain)
@ -202,8 +197,8 @@ class SphinxComponentRegistry:
(name, domain))
roles[name] = role
def add_index_to_domain(self, domain, index, override=False):
# type: (str, Type[Index], bool) -> None
def add_index_to_domain(self, domain: str, index: "Type[Index]",
override: bool = False) -> None:
logger.debug('[app] adding index to domain: %r', (domain, index))
if domain not in self.domains:
raise ExtensionError(__('domain %s not yet registered') % domain)
@ -213,10 +208,10 @@ class SphinxComponentRegistry:
(index.name, domain))
indices.append(index)
def add_object_type(self, directivename, rolename, indextemplate='',
parse_node=None, ref_nodeclass=None, objname='',
doc_field_types=[], override=False):
# type: (str, str, str, Callable, Type[nodes.TextElement], str, List, bool) -> None
def add_object_type(self, directivename: str, rolename: str, indextemplate: str = '',
parse_node: Callable = None, ref_nodeclass: "Type[TextElement]" = None,
objname: str = '', doc_field_types: List = [], override: bool = False
) -> None:
logger.debug('[app] adding object type: %r',
(directivename, rolename, indextemplate, parse_node,
ref_nodeclass, objname, doc_field_types))
@ -237,9 +232,9 @@ class SphinxComponentRegistry:
directivename)
object_types[directivename] = ObjType(objname or directivename, rolename)
def add_crossref_type(self, directivename, rolename, indextemplate='',
ref_nodeclass=None, objname='', override=False):
# type: (str, str, str, Type[nodes.TextElement], str, bool) -> None
def add_crossref_type(self, directivename: str, rolename: str, indextemplate: str = '',
ref_nodeclass: "Type[TextElement]" = None, objname: str = '',
override: bool = False) -> None:
logger.debug('[app] adding crossref type: %r',
(directivename, rolename, indextemplate, ref_nodeclass, objname))
@ -257,17 +252,16 @@ class SphinxComponentRegistry:
directivename)
object_types[directivename] = ObjType(objname or directivename, rolename)
def add_source_suffix(self, suffix, filetype, override=False):
# type: (str, str, bool) -> None
def add_source_suffix(self, suffix: str, filetype: str, override: bool = False) -> None:
logger.debug('[app] adding source_suffix: %r, %r', suffix, filetype)
if suffix in self.source_suffix and not override:
raise ExtensionError(__('source_suffix %r is already registered') % suffix)
else:
self.source_suffix[suffix] = filetype
def add_source_parser(self, parser, **kwargs):
# type: (Type[Parser], bool) -> None
def add_source_parser(self, parser: "Type[Parser]", **kwargs) -> None:
logger.debug('[app] adding search source_parser: %r', parser)
# create a map from filetype to parser
for filetype in parser.supported:
if filetype in self.source_parsers and not kwargs.get('override'):
@ -276,27 +270,23 @@ class SphinxComponentRegistry:
else:
self.source_parsers[filetype] = parser
def get_source_parser(self, filetype):
# type: (str) -> Type[Parser]
def get_source_parser(self, filetype: str) -> "Type[Parser]":
try:
return self.source_parsers[filetype]
except KeyError:
raise SphinxError(__('Source parser for %s not registered') % filetype)
def get_source_parsers(self):
# type: () -> Dict[str, Type[Parser]]
def get_source_parsers(self) -> Dict[str, "Type[Parser]"]:
return self.source_parsers
def create_source_parser(self, app, filename):
# type: (Sphinx, str) -> Parser
def create_source_parser(self, app: "Sphinx", filename: str) -> Parser:
parser_class = self.get_source_parser(filename)
parser = parser_class()
if isinstance(parser, SphinxParser):
parser.set_application(app)
return parser
def get_source_input(self, filetype):
# type: (str) -> Type[Input]
def get_source_input(self, filetype: str) -> "Type[Input]":
try:
return self.source_inputs[filetype]
except KeyError:
@ -306,15 +296,14 @@ class SphinxComponentRegistry:
except KeyError:
return None
def add_translator(self, name, translator, override=False):
# type: (str, Type[nodes.NodeVisitor], bool) -> None
def add_translator(self, name: str, translator: "Type[nodes.NodeVisitor]",
override: bool = False) -> None:
logger.debug('[app] Change of translator for the %s builder.' % name)
if name in self.translators and not override:
raise ExtensionError(__('Translator for %r already exists') % name)
self.translators[name] = translator
def add_translation_handlers(self, node, **kwargs):
# type: (Type[nodes.Element], Any) -> None
def add_translation_handlers(self, node: "Type[Element]", **kwargs) -> None:
logger.debug('[app] adding translation_handlers: %r, %r', node, kwargs)
for builder_name, handlers in kwargs.items():
translation_handlers = self.translation_handlers.setdefault(builder_name, {})
@ -325,13 +314,11 @@ class SphinxComponentRegistry:
raise ExtensionError(__('kwargs for add_node() must be a (visit, depart) '
'function tuple: %r=%r') % builder_name, handlers)
def get_translator_class(self, builder):
# type: (Builder) -> Type[nodes.NodeVisitor]
def get_translator_class(self, builder: Builder) -> "Type[nodes.NodeVisitor]":
return self.translators.get(builder.name,
builder.default_translator_class)
def create_translator(self, builder, *args):
# type: (Builder, Any) -> nodes.NodeVisitor
def create_translator(self, builder: Builder, *args) -> nodes.NodeVisitor:
translator_class = self.get_translator_class(builder)
assert translator_class, "translator not found for %s" % builder.name
translator = translator_class(*args)
@ -349,54 +336,48 @@ class SphinxComponentRegistry:
return translator
def add_transform(self, transform):
# type: (Type[Transform]) -> None
def add_transform(self, transform: "Type[Transform]") -> None:
logger.debug('[app] adding transform: %r', transform)
self.transforms.append(transform)
def get_transforms(self):
# type: () -> List[Type[Transform]]
def get_transforms(self) -> List["Type[Transform]"]:
return self.transforms
def add_post_transform(self, transform):
# type: (Type[Transform]) -> None
def add_post_transform(self, transform: "Type[Transform]") -> None:
logger.debug('[app] adding post transform: %r', transform)
self.post_transforms.append(transform)
def get_post_transforms(self):
# type: () -> List[Type[Transform]]
def get_post_transforms(self) -> List["Type[Transform]"]:
return self.post_transforms
def add_documenter(self, objtype, documenter):
# type: (str, Type[Documenter]) -> None
def add_documenter(self, objtype: str, documenter: "Type[Documenter]") -> None:
self.documenters[objtype] = documenter
def add_autodoc_attrgetter(self, typ, attrgetter):
# type: (Type, Callable[[Any, str, Any], Any]) -> None
def add_autodoc_attrgetter(self, typ: "Type",
attrgetter: Callable[[Any, str, Any], Any]) -> None:
self.autodoc_attrgettrs[typ] = attrgetter
def add_css_files(self, filename, **attributes):
self.css_files.append((filename, attributes))
def add_js_file(self, filename, **attributes):
# type: (str, **str) -> None
def add_js_file(self, filename: str, **attributes: str) -> None:
logger.debug('[app] adding js_file: %r, %r', filename, attributes)
self.js_files.append((filename, attributes))
def add_latex_package(self, name, options):
# type: (str, str) -> None
def add_latex_package(self, name: str, options: str) -> None:
logger.debug('[app] adding latex package: %r', name)
self.latex_packages.append((name, options))
def add_enumerable_node(self, node, figtype, title_getter=None, override=False):
# type: (Type[nodes.Node], str, TitleGetter, bool) -> None
def add_enumerable_node(self, node: "Type[Node]", figtype: str,
title_getter: TitleGetter = None, override: bool = False) -> None:
logger.debug('[app] adding enumerable node: (%r, %r, %r)', node, figtype, title_getter)
if node in self.enumerable_nodes and not override:
raise ExtensionError(__('enumerable_node %r already registered') % node)
self.enumerable_nodes[node] = (figtype, title_getter)
def add_html_math_renderer(self, name, inline_renderers, block_renderers):
# type: (str, Tuple[Callable, Callable], Tuple[Callable, Callable]) -> None
def add_html_math_renderer(self, name: str,
inline_renderers: Tuple[Callable, Callable],
block_renderers: Tuple[Callable, Callable]) -> None:
logger.debug('[app] adding html_math_renderer: %s, %r, %r',
name, inline_renderers, block_renderers)
if name in self.html_inline_math_renderers:
@ -405,8 +386,7 @@ class SphinxComponentRegistry:
self.html_inline_math_renderers[name] = inline_renderers
self.html_block_math_renderers[name] = block_renderers
def load_extension(self, app, extname):
# type: (Sphinx, str) -> None
def load_extension(self, app: "Sphinx", extname: str) -> None:
"""Load a Sphinx extension."""
if extname in app.extensions: # alread loaded
return
@ -451,8 +431,7 @@ class SphinxComponentRegistry:
app.extensions[extname] = Extension(extname, mod, **metadata)
def get_envversion(self, app):
# type: (Sphinx) -> Dict[str, str]
def get_envversion(self, app: "Sphinx") -> Dict[str, str]:
from sphinx.environment import ENV_VERSION
envversion = {ext.name: ext.metadata['env_version'] for ext in app.extensions.values()
if ext.metadata.get('env_version')}
@ -460,8 +439,7 @@ class SphinxComponentRegistry:
return envversion
def merge_source_suffix(app, config):
# type: (Sphinx, Config) -> None
def merge_source_suffix(app: "Sphinx", config: Config) -> None:
"""Merge source_suffix which specified by user and added by extensions."""
for suffix, filetype in app.registry.source_suffix.items():
if suffix not in app.config.source_suffix:
@ -475,8 +453,7 @@ def merge_source_suffix(app, config):
app.registry.source_suffix = app.config.source_suffix
def setup(app):
# type: (Sphinx) -> Dict[str, Any]
def setup(app: "Sphinx") -> Dict[str, Any]:
app.connect('config-inited', merge_source_suffix)
return {

View File

@ -10,25 +10,27 @@
import re
import warnings
from typing import Any, Dict, List, Tuple
from typing import Type # for python3.5.1
from docutils import nodes, utils
from docutils.nodes import Element, Node, TextElement, system_message
from docutils.parsers.rst.states import Inliner
from sphinx import addnodes
from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.locale import _
from sphinx.util import ws_re
from sphinx.util.docutils import ReferenceRole, SphinxRole
from sphinx.util.nodes import split_explicit_title, process_index_entry, \
set_role_source_info
from sphinx.util.nodes import (
split_explicit_title, process_index_entry, set_role_source_info
)
from sphinx.util.typing import RoleFunction
if False:
# For type annotation
from typing import Any, Dict, List, Tuple # NOQA
from typing import Type # for python3.5.1
from docutils.parsers.rst.states import Inliner # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.environment import BuildEnvironment # NOQA
from sphinx.util.typing import RoleFunction # NOQA
from sphinx.application import Sphinx
from sphinx.environment import BuildEnvironment
generic_docroles = {
@ -71,12 +73,12 @@ class XRefRole(ReferenceRole):
* Subclassing and overwriting `process_link()` and/or `result_nodes()`.
"""
nodeclass = addnodes.pending_xref # type: Type[nodes.Element]
innernodeclass = nodes.literal # type: Type[nodes.TextElement]
nodeclass = addnodes.pending_xref # type: Type[Element]
innernodeclass = nodes.literal # type: Type[TextElement]
def __init__(self, fix_parens=False, lowercase=False,
nodeclass=None, innernodeclass=None, warn_dangling=False):
# type: (bool, bool, Type[nodes.Element], Type[nodes.TextElement], bool) -> None
def __init__(self, fix_parens: bool = False, lowercase: bool = False,
nodeclass: Type[Element] = None, innernodeclass: Type[TextElement] = None,
warn_dangling: bool = False) -> None:
self.fix_parens = fix_parens
self.lowercase = lowercase
self.warn_dangling = warn_dangling
@ -87,8 +89,8 @@ class XRefRole(ReferenceRole):
super().__init__()
def _fix_parens(self, env, has_explicit_title, title, target):
# type: (BuildEnvironment, bool, str, str) -> Tuple[str, str]
def _fix_parens(self, env: "BuildEnvironment", has_explicit_title: bool, title: str,
target: str) -> Tuple[str, str]:
warnings.warn('XRefRole._fix_parens() is deprecated.',
RemovedInSphinx40Warning, stacklevel=2)
if not has_explicit_title:
@ -103,8 +105,7 @@ class XRefRole(ReferenceRole):
target = target[:-2]
return title, target
def update_title_and_target(self, title, target):
# type: (str, str) -> Tuple[str, str]
def update_title_and_target(self, title: str, target: str) -> Tuple[str, str]:
if not self.has_explicit_title:
if title.endswith('()'):
# remove parentheses
@ -117,8 +118,7 @@ class XRefRole(ReferenceRole):
target = target[:-2]
return title, target
def run(self):
# type: () -> Tuple[List[nodes.Node], List[nodes.system_message]]
def run(self) -> Tuple[List[Node], List[system_message]]:
if ':' not in self.name:
self.refdomain, self.reftype = '', self.name
self.classes = ['xref', self.reftype]
@ -132,8 +132,7 @@ class XRefRole(ReferenceRole):
else:
return self.create_xref_node()
def create_non_xref_node(self):
# type: () -> Tuple[List[nodes.Node], List[nodes.system_message]]
def create_non_xref_node(self) -> Tuple[List[Node], List[system_message]]:
text = utils.unescape(self.text[1:])
if self.fix_parens:
self.has_explicit_title = False # treat as implicit
@ -142,8 +141,7 @@ class XRefRole(ReferenceRole):
node = self.innernodeclass(self.rawtext, text, classes=self.classes)
return self.result_nodes(self.inliner.document, self.env, node, is_ref=False)
def create_xref_node(self):
# type: () -> Tuple[List[nodes.Node], List[nodes.system_message]]
def create_xref_node(self) -> Tuple[List[Node], List[system_message]]:
target = self.target
title = self.title
if self.lowercase:
@ -170,8 +168,8 @@ class XRefRole(ReferenceRole):
# methods that can be overwritten
def process_link(self, env, refnode, has_explicit_title, title, target):
# type: (BuildEnvironment, nodes.Element, bool, str, str) -> Tuple[str, str]
def process_link(self, env: "BuildEnvironment", refnode: Element, has_explicit_title: bool,
title: str, target: str) -> Tuple[str, str]:
"""Called after parsing title and target text, and creating the
reference node (given in *refnode*). This method can alter the
reference node and must return a new (or the same) ``(title, target)``
@ -179,8 +177,8 @@ class XRefRole(ReferenceRole):
"""
return title, ws_re.sub(' ', target)
def result_nodes(self, document, env, node, is_ref):
# type: (nodes.document, BuildEnvironment, nodes.Element, bool) -> Tuple[List[nodes.Node], List[nodes.system_message]] # NOQA
def result_nodes(self, document: nodes.document, env: "BuildEnvironment", node: Element,
is_ref: bool) -> Tuple[List[Node], List[system_message]]:
"""Called before returning the finished nodes. *node* is the reference
node if one was created (*is_ref* is then true), else the content node.
This method can add other nodes and must return a ``(nodes, messages)``
@ -190,16 +188,17 @@ class XRefRole(ReferenceRole):
class AnyXRefRole(XRefRole):
def process_link(self, env, refnode, has_explicit_title, title, target):
# type: (BuildEnvironment, nodes.Element, bool, str, str) -> Tuple[str, str]
def process_link(self, env: "BuildEnvironment", refnode: Element, has_explicit_title: bool,
title: str, target: str) -> Tuple[str, str]:
result = super().process_link(env, refnode, has_explicit_title, title, target)
# add all possible context info (i.e. std:program, py:module etc.)
refnode.attributes.update(env.ref_context)
return result
def indexmarkup_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
# type: (str, str, str, int, Inliner, Dict, List[str]) -> Tuple[List[nodes.Node], List[nodes.system_message]] # NOQA
def indexmarkup_role(typ: str, rawtext: str, text: str, lineno: int, inliner: Inliner,
options: Dict = {}, content: List[str] = []
) -> Tuple[List[Node], List[system_message]]:
"""Role for PEP/RFC references that generate an index entry."""
warnings.warn('indexmarkup_role() is deprecated. Please use PEP or RFC class instead.',
RemovedInSphinx40Warning, stacklevel=2)
@ -267,8 +266,7 @@ def indexmarkup_role(typ, rawtext, text, lineno, inliner, options={}, content=[]
class PEP(ReferenceRole):
def run(self):
# type: () -> Tuple[List[nodes.Node], List[nodes.system_message]]
def run(self) -> Tuple[List[Node], List[system_message]]:
target_id = 'index-%s' % self.env.new_serialno('index')
entries = [('single', _('Python Enhancement Proposals; PEP %s') % self.target,
target_id, '', None)]
@ -293,8 +291,7 @@ class PEP(ReferenceRole):
return [index, target, reference], []
def build_uri(self):
# type: () -> str
def build_uri(self) -> str:
base_url = self.inliner.document.settings.pep_base_url
ret = self.target.split('#', 1)
if len(ret) == 2:
@ -304,8 +301,7 @@ class PEP(ReferenceRole):
class RFC(ReferenceRole):
def run(self):
# type: () -> Tuple[List[nodes.Node], List[nodes.system_message]] # NOQA
def run(self) -> Tuple[List[Node], List[system_message]]:
target_id = 'index-%s' % self.env.new_serialno('index')
entries = [('single', 'RFC; RFC %s' % self.target, target_id, '', None)]
@ -329,8 +325,7 @@ class RFC(ReferenceRole):
return [index, target, reference], []
def build_uri(self):
# type: () -> str
def build_uri(self) -> str:
base_url = self.inliner.document.settings.rfc_base_url
ret = self.target.split('#', 1)
if len(ret) == 2:
@ -342,8 +337,9 @@ class RFC(ReferenceRole):
_amp_re = re.compile(r'(?<!&)&(?![&\s])')
def menusel_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
# type: (str, str, str, int, Inliner, Dict, List[str]) -> Tuple[List[nodes.Node], List[nodes.system_message]] # NOQA
def menusel_role(typ: str, rawtext: str, text: str, lineno: int, inliner: Inliner,
options: Dict = {}, content: List[str] = []
) -> Tuple[List[Node], List[system_message]]:
warnings.warn('menusel_role() is deprecated. '
'Please use MenuSelection or GUILabel class instead.',
RemovedInSphinx40Warning, stacklevel=2)
@ -382,8 +378,7 @@ def menusel_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
class GUILabel(SphinxRole):
amp_re = re.compile(r'(?<!&)&(?![&\s])')
def run(self):
# type: () -> Tuple[List[nodes.Node], List[nodes.system_message]]
def run(self) -> Tuple[List[Node], List[system_message]]:
node = nodes.inline(rawtext=self.rawtext, classes=[self.name])
spans = self.amp_re.split(self.text)
node += nodes.Text(spans.pop(0))
@ -399,8 +394,7 @@ class GUILabel(SphinxRole):
class MenuSelection(GUILabel):
def run(self):
# type: () -> Tuple[List[nodes.Node], List[nodes.system_message]]
def run(self) -> Tuple[List[Node], List[system_message]]:
self.text = self.text.replace('-->', '\N{TRIANGULAR BULLET}')
return super().run()
@ -409,9 +403,9 @@ _litvar_re = re.compile('{([^}]+)}')
parens_re = re.compile(r'(\\*{|\\*})')
def emph_literal_role(typ, rawtext, text, lineno, inliner,
options={}, content=[]):
# type: (str, str, str, int, Inliner, Dict, List[str]) -> Tuple[List[nodes.Node], List[nodes.system_message]] # NOQA
def emph_literal_role(typ: str, rawtext: str, text: str, lineno: int, inliner: Inliner,
options: Dict = {}, content: List[str] = []
) -> Tuple[List[Node], List[system_message]]:
warnings.warn('emph_literal_role() is deprecated. '
'Please use EmphasizedLiteral class instead.',
RemovedInSphinx40Warning, stacklevel=2)
@ -465,17 +459,15 @@ def emph_literal_role(typ, rawtext, text, lineno, inliner,
class EmphasizedLiteral(SphinxRole):
parens_re = re.compile(r'(\\\\|\\{|\\}|{|})')
def run(self):
# type: () -> Tuple[List[nodes.Node], List[nodes.system_message]]
def run(self) -> Tuple[List[Node], List[system_message]]:
children = self.parse(self.text)
node = nodes.literal(self.rawtext, '', *children,
role=self.name.lower(), classes=[self.name])
return [node], []
def parse(self, text):
# type: (str) -> List[nodes.Node]
result = [] # type: List[nodes.Node]
def parse(self, text: str) -> List[Node]:
result = [] # type: List[Node]
stack = ['']
for part in self.parens_re.split(text):
@ -517,8 +509,9 @@ class EmphasizedLiteral(SphinxRole):
_abbr_re = re.compile(r'\((.*)\)$', re.S)
def abbr_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
# type: (str, str, str, int, Inliner, Dict, List[str]) -> Tuple[List[nodes.Node], List[nodes.system_message]] # NOQA
def abbr_role(typ: str, rawtext: str, text: str, lineno: int, inliner: Inliner,
options: Dict = {}, content: List[str] = []
) -> Tuple[List[Node], List[system_message]]:
warnings.warn('abbr_role() is deprecated. Please use Abbrevation class instead.',
RemovedInSphinx40Warning, stacklevel=2)
text = utils.unescape(text)
@ -535,8 +528,7 @@ def abbr_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
class Abbreviation(SphinxRole):
abbr_re = re.compile(r'\((.*)\)$', re.S)
def run(self):
# type: () -> Tuple[List[nodes.Node], List[nodes.system_message]]
def run(self) -> Tuple[List[Node], List[system_message]]:
matched = self.abbr_re.search(self.text)
if matched:
text = self.text[:matched.start()].strip()
@ -547,8 +539,9 @@ class Abbreviation(SphinxRole):
return [nodes.abbreviation(self.rawtext, text, **self.options)], []
def index_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
# type: (str, str, str, int, Inliner, Dict, List[str]) -> Tuple[List[nodes.Node], List[nodes.system_message]] # NOQA
def index_role(typ: str, rawtext: str, text: str, lineno: int, inliner: Inliner,
options: Dict = {}, content: List[str] = []
) -> Tuple[List[Node], List[system_message]]:
warnings.warn('index_role() is deprecated. Please use Index class instead.',
RemovedInSphinx40Warning, stacklevel=2)
# create new reference target
@ -579,8 +572,7 @@ def index_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
class Index(ReferenceRole):
def run(self):
# type: () -> Tuple[List[nodes.Node], List[nodes.system_message]]
def run(self) -> Tuple[List[Node], List[system_message]]:
target_id = 'index-%s' % self.env.new_serialno('index')
if self.has_explicit_title:
# if an explicit target is given, process it as a full entry
@ -619,8 +611,7 @@ specific_docroles = {
} # type: Dict[str, RoleFunction]
def setup(app):
# type: (Sphinx) -> Dict[str, Any]
def setup(app: "Sphinx") -> Dict[str, Any]:
from docutils.parsers.rst import roles
for rolename, nodeclass in generic_docroles.items():

View File

@ -13,21 +13,21 @@ import re
import warnings
from importlib import import_module
from os import path
from typing import Any, Dict, IO, Iterable, List, Tuple, Set
from docutils import nodes
from docutils.nodes import Node
from sphinx import addnodes
from sphinx import package_dir
from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.environment import BuildEnvironment
from sphinx.search.jssplitter import splitter_code
from sphinx.util import jsdump, rpartition
if False:
# For type annotation
from typing import Any, Dict, IO, Iterable, List, Tuple, Set # NOQA
from typing import Type # for python3.5.1
from docutils import nodes # NOQA
from sphinx.environment import BuildEnvironment # NOQA
class SearchLanguage:
@ -69,19 +69,16 @@ var Stemmer = function() {
_word_re = re.compile(r'(?u)\w+')
def __init__(self, options):
# type: (Dict) -> None
def __init__(self, options: Dict) -> None:
self.options = options
self.init(options)
def init(self, options):
# type: (Dict) -> None
def init(self, options: Dict) -> None:
"""
Initialize the class with the options the user has given.
"""
def split(self, input):
# type: (str) -> List[str]
def split(self, input: str) -> List[str]:
"""
This method splits a sentence into words. Default splitter splits input
at white spaces, which should be enough for most languages except CJK
@ -89,8 +86,7 @@ var Stemmer = function() {
"""
return self._word_re.findall(input)
def stem(self, word):
# type: (str) -> str
def stem(self, word: str) -> str:
"""
This method implements stemming algorithm of the Python version.
@ -103,8 +99,7 @@ var Stemmer = function() {
"""
return word
def word_filter(self, word):
# type: (str) -> bool
def word_filter(self, word: str) -> bool:
"""
Return true if the target word should be registered in the search index.
This method is called after stemming.
@ -121,8 +116,7 @@ var Stemmer = function() {
from sphinx.search.en import SearchEnglish
def parse_stop_word(source):
# type: (str) -> Set[str]
def parse_stop_word(source: str) -> Set[str]:
"""
parse snowball style word list like this:
@ -166,24 +160,20 @@ class _JavaScriptIndex:
PREFIX = 'Search.setIndex('
SUFFIX = ')'
def dumps(self, data):
# type: (Any) -> str
def dumps(self, data: Any) -> str:
return self.PREFIX + jsdump.dumps(data) + self.SUFFIX
def loads(self, s):
# type: (str) -> Any
def loads(self, s: str) -> Any:
data = s[len(self.PREFIX):-len(self.SUFFIX)]
if not data or not s.startswith(self.PREFIX) or not \
s.endswith(self.SUFFIX):
raise ValueError('invalid data')
return jsdump.loads(data)
def dump(self, data, f):
# type: (Any, IO) -> None
def dump(self, data: Any, f: IO) -> None:
f.write(self.dumps(data))
def load(self, f):
# type: (IO) -> Any
def load(self, f: IO) -> Any:
return self.loads(f.read())
@ -195,15 +185,13 @@ class WordCollector(nodes.NodeVisitor):
A special visitor that collects words for the `IndexBuilder`.
"""
def __init__(self, document, lang):
# type: (nodes.document, SearchLanguage) -> None
def __init__(self, document: nodes.document, lang: SearchLanguage) -> None:
super().__init__(document)
self.found_words = [] # type: List[str]
self.found_title_words = [] # type: List[str]
self.lang = lang
def is_meta_keywords(self, node, nodetype=None):
# type: (addnodes.meta, Any) -> bool
def is_meta_keywords(self, node: addnodes.meta, nodetype: Any = None) -> bool:
if nodetype is not None:
warnings.warn('"nodetype" argument for WordCollector.is_meta_keywords() '
'is deprecated.', RemovedInSphinx40Warning)
@ -217,8 +205,7 @@ class WordCollector(nodes.NodeVisitor):
return False
def dispatch_visit(self, node):
# type: (nodes.Node) -> None
def dispatch_visit(self, node: Node) -> None:
if isinstance(node, nodes.comment):
raise nodes.SkipNode
elif isinstance(node, nodes.raw):
@ -251,8 +238,7 @@ class IndexBuilder:
'pickle': pickle
}
def __init__(self, env, lang, options, scoring):
# type: (BuildEnvironment, str, Dict, str) -> None
def __init__(self, env: BuildEnvironment, lang: str, options: Dict, scoring: str) -> None:
self.env = env
self._titles = {} # type: Dict[str, str]
# docname -> title
@ -292,8 +278,7 @@ class IndexBuilder:
self.js_scorer_code = ''
self.js_splitter_code = splitter_code
def load(self, stream, format):
# type: (IO, Any) -> None
def load(self, stream: IO, format: Any) -> None:
"""Reconstruct from frozen data."""
if isinstance(format, str):
format = self.formats[format]
@ -306,8 +291,7 @@ class IndexBuilder:
self._filenames = dict(zip(index2fn, frozen['filenames']))
self._titles = dict(zip(index2fn, frozen['titles']))
def load_terms(mapping):
# type: (Dict[str, Any]) -> Dict[str, Set[str]]
def load_terms(mapping: Dict[str, Any]) -> Dict[str, Set[str]]:
rv = {}
for k, v in mapping.items():
if isinstance(v, int):
@ -320,15 +304,14 @@ class IndexBuilder:
self._title_mapping = load_terms(frozen['titleterms'])
# no need to load keywords/objtypes
def dump(self, stream, format):
# type: (IO, Any) -> None
def dump(self, stream: IO, format: Any) -> None:
"""Dump the frozen index to a stream."""
if isinstance(format, str):
format = self.formats[format]
format.dump(self.freeze(), stream)
def get_objects(self, fn2index):
# type: (Dict[str, int]) -> Dict[str, Dict[str, Tuple[int, int, int, str]]]
def get_objects(self, fn2index: Dict[str, int]
) -> Dict[str, Dict[str, Tuple[int, int, int, str]]]:
rv = {} # type: Dict[str, Dict[str, Tuple[int, int, int, str]]]
otypes = self._objtypes
onames = self._objnames
@ -364,8 +347,7 @@ class IndexBuilder:
pdict[name] = (fn2index[docname], typeindex, prio, shortanchor)
return rv
def get_terms(self, fn2index):
# type: (Dict) -> Tuple[Dict[str, List[str]], Dict[str, List[str]]]
def get_terms(self, fn2index: Dict) -> Tuple[Dict[str, List[str]], Dict[str, List[str]]]:
rvs = {}, {} # type: Tuple[Dict[str, List[str]], Dict[str, List[str]]]
for rv, mapping in zip(rvs, (self._mapping, self._title_mapping)):
for k, v in mapping.items():
@ -377,8 +359,7 @@ class IndexBuilder:
rv[k] = sorted([fn2index[fn] for fn in v if fn in fn2index])
return rvs
def freeze(self):
# type: () -> Dict[str, Any]
def freeze(self) -> Dict[str, Any]:
"""Create a usable data structure for serializing."""
docnames, titles = zip(*sorted(self._titles.items()))
filenames = [self._filenames.get(docname) for docname in docnames]
@ -392,12 +373,10 @@ class IndexBuilder:
objects=objects, objtypes=objtypes, objnames=objnames,
titleterms=title_terms, envversion=self.env.version)
def label(self):
# type: () -> str
def label(self) -> str:
return "%s (code: %s)" % (self.lang.language_name, self.lang.lang)
def prune(self, docnames):
# type: (Iterable[str]) -> None
def prune(self, docnames: Iterable[str]) -> None:
"""Remove data for all docnames not in the list."""
new_titles = {}
new_filenames = {}
@ -412,8 +391,7 @@ class IndexBuilder:
for wordnames in self._title_mapping.values():
wordnames.intersection_update(docnames)
def feed(self, docname, filename, title, doctree):
# type: (str, str, str, nodes.document) -> None
def feed(self, docname: str, filename: str, title: str, doctree: nodes.document) -> None:
"""Feed a doctree to the index."""
self._titles[docname] = title
self._filenames[docname] = filename
@ -422,8 +400,7 @@ class IndexBuilder:
doctree.walk(visitor)
# memoize self.lang.stem
def stem(word):
# type: (str) -> str
def stem(word: str) -> str:
try:
return self._stem_cache[word]
except KeyError:
@ -447,8 +424,7 @@ class IndexBuilder:
if _filter(stemmed_word) and not already_indexed:
self._mapping.setdefault(stemmed_word, set()).add(docname)
def context_for_searchtool(self):
# type: () -> Dict[str, Any]
def context_for_searchtool(self) -> Dict[str, Any]:
return {
'search_language_stemming_code': self.lang.js_stemmer_code,
'search_language_stop_words': jsdump.dumps(sorted(self.lang.stopwords)),
@ -456,8 +432,7 @@ class IndexBuilder:
'search_word_splitter_code': self.js_splitter_code,
}
def get_js_stemmer_rawcode(self):
# type: () -> str
def get_js_stemmer_rawcode(self) -> str:
if self.lang.js_stemmer_rawcode:
return path.join(package_dir, 'search', 'non-minified-js',
self.lang.js_stemmer_rawcode)

View File

@ -8,13 +8,11 @@
:license: BSD, see LICENSE for details.
"""
from sphinx.search import SearchLanguage, parse_stop_word
from typing import Dict
import snowballstemmer
if False:
# For type annotation
from typing import Any # NOQA
from sphinx.search import SearchLanguage, parse_stop_word
danish_stopwords = parse_stop_word('''
@ -128,10 +126,8 @@ class SearchDanish(SearchLanguage):
js_stemmer_code = js_stemmer
stopwords = danish_stopwords
def init(self, options):
# type: (Any) -> None
def init(self, options: Dict) -> None:
self.stemmer = snowballstemmer.stemmer('danish')
def stem(self, word):
# type: (str) -> str
def stem(self, word: str) -> str:
return self.stemmer.stemWord(word.lower())

View File

@ -8,13 +8,11 @@
:license: BSD, see LICENSE for details.
"""
from sphinx.search import SearchLanguage, parse_stop_word
from typing import Dict
import snowballstemmer
if False:
# For type annotation
from typing import Any # NOQA
from sphinx.search import SearchLanguage, parse_stop_word
german_stopwords = parse_stop_word('''
@ -311,10 +309,8 @@ class SearchGerman(SearchLanguage):
js_stemmer_code = js_stemmer
stopwords = german_stopwords
def init(self, options):
# type: (Any) -> None
def init(self, options: Dict) -> None:
self.stemmer = snowballstemmer.stemmer('german')
def stem(self, word):
# type: (str) -> str
def stem(self, word: str) -> str:
return self.stemmer.stemWord(word.lower())

View File

@ -8,13 +8,11 @@
:license: BSD, see LICENSE for details.
"""
from typing import Dict
from sphinx.search import SearchLanguage
from sphinx.util.stemmer import get_stemmer
if False:
# For type annotation
from typing import Dict # NOQA
english_stopwords = set("""
a and are as at
be but by
@ -220,10 +218,8 @@ class SearchEnglish(SearchLanguage):
js_stemmer_code = js_porter_stemmer
stopwords = english_stopwords
def init(self, options):
# type: (Dict) -> None
def init(self, options: Dict) -> None:
self.stemmer = get_stemmer()
def stem(self, word):
# type: (str) -> str
def stem(self, word: str) -> str:
return self.stemmer.stem(word.lower())

View File

@ -8,13 +8,11 @@
:license: BSD, see LICENSE for details.
"""
from sphinx.search import SearchLanguage, parse_stop_word
from typing import Dict
import snowballstemmer
if False:
# For type annotation
from typing import Any # NOQA
from sphinx.search import SearchLanguage, parse_stop_word
spanish_stopwords = parse_stop_word('''
@ -371,10 +369,8 @@ class SearchSpanish(SearchLanguage):
js_stemmer_code = js_stemmer
stopwords = spanish_stopwords
def init(self, options):
# type: (Any) -> None
def init(self, options: Dict) -> None:
self.stemmer = snowballstemmer.stemmer('spanish')
def stem(self, word):
# type: (str) -> str
def stem(self, word: str) -> str:
return self.stemmer.stemWord(word.lower())

View File

@ -8,13 +8,11 @@
:license: BSD, see LICENSE for details.
"""
from sphinx.search import SearchLanguage, parse_stop_word
from typing import Dict
import snowballstemmer
if False:
# For type annotation
from typing import Any # NOQA
from sphinx.search import SearchLanguage, parse_stop_word
finnish_stopwords = parse_stop_word('''
@ -121,10 +119,8 @@ class SearchFinnish(SearchLanguage):
js_stemmer_code = js_stemmer
stopwords = finnish_stopwords
def init(self, options):
# type: (Any) -> None
def init(self, options: Dict) -> None:
self.stemmer = snowballstemmer.stemmer('finnish')
def stem(self, word):
# type: (str) -> str
def stem(self, word: str) -> str:
return self.stemmer.stemWord(word.lower())

View File

@ -8,13 +8,11 @@
:license: BSD, see LICENSE for details.
"""
from sphinx.search import SearchLanguage, parse_stop_word
from typing import Dict
import snowballstemmer
if False:
# For type annotation
from typing import Any # NOQA
from sphinx.search import SearchLanguage, parse_stop_word
french_stopwords = parse_stop_word('''
@ -207,10 +205,8 @@ class SearchFrench(SearchLanguage):
js_stemmer_code = js_stemmer
stopwords = french_stopwords
def init(self, options):
# type: (Any) -> None
def init(self, options: Dict) -> None:
self.stemmer = snowballstemmer.stemmer('french')
def stem(self, word):
# type: (str) -> str
def stem(self, word: str) -> str:
return self.stemmer.stemWord(word.lower())

View File

@ -8,13 +8,11 @@
:license: BSD, see LICENSE for details.
"""
from sphinx.search import SearchLanguage, parse_stop_word
from typing import Dict
import snowballstemmer
if False:
# For type annotation
from typing import Any # NOQA
from sphinx.search import SearchLanguage, parse_stop_word
hungarian_stopwords = parse_stop_word('''
@ -235,10 +233,8 @@ class SearchHungarian(SearchLanguage):
js_stemmer_code = js_stemmer
stopwords = hungarian_stopwords
def init(self, options):
# type: (Any) -> None
def init(self, options: Dict) -> None:
self.stemmer = snowballstemmer.stemmer('hungarian')
def stem(self, word):
# type: (str) -> str
def stem(self, word: str) -> str:
return self.stemmer.stemWord(word.lower())

View File

@ -8,13 +8,11 @@
:license: BSD, see LICENSE for details.
"""
from sphinx.search import SearchLanguage, parse_stop_word
from typing import Dict
import snowballstemmer
if False:
# For type annotation
from typing import Any # NOQA
from sphinx.search import SearchLanguage, parse_stop_word
italian_stopwords = parse_stop_word('''
@ -324,10 +322,8 @@ class SearchItalian(SearchLanguage):
js_stemmer_code = js_stemmer
stopwords = italian_stopwords
def init(self, options):
# type: (Any) -> None
def init(self, options: Dict) -> None:
self.stemmer = snowballstemmer.stemmer('italian')
def stem(self, word):
# type: (str) -> str
def stem(self, word: str) -> str:
return self.stemmer.stemWord(word.lower())

View File

@ -19,6 +19,7 @@
import os
import re
import sys
from typing import Any, Dict, List
try:
import MeCab
@ -36,21 +37,13 @@ from sphinx.errors import SphinxError, ExtensionError
from sphinx.search import SearchLanguage
from sphinx.util import import_object
if False:
# For type annotation
from typing import Any, Dict, List # NOQA
class BaseSplitter:
def __init__(self, options):
# type: (Dict) -> None
def __init__(self, options: Dict) -> None:
self.options = options
def split(self, input):
# type: (str) -> List[str]
def split(self, input: str) -> List[str]:
"""
:param str input:
:return:
:rtype: list[str]
@ -59,8 +52,7 @@ class BaseSplitter:
class MecabSplitter(BaseSplitter):
def __init__(self, options):
# type: (Dict) -> None
def __init__(self, options: Dict) -> None:
super().__init__(options)
self.ctypes_libmecab = None # type: Any
self.ctypes_mecab = None # type: Any
@ -70,8 +62,7 @@ class MecabSplitter(BaseSplitter):
self.init_native(options)
self.dict_encode = options.get('dic_enc', 'utf-8')
def split(self, input):
# type: (str) -> List[str]
def split(self, input: str) -> List[str]:
if native_module:
result = self.native.parse(input)
else:
@ -79,16 +70,14 @@ class MecabSplitter(BaseSplitter):
self.ctypes_mecab, input.encode(self.dict_encode))
return result.split(' ')
def init_native(self, options):
# type: (Dict) -> None
def init_native(self, options: Dict) -> None:
param = '-Owakati'
dict = options.get('dict')
if dict:
param += ' -d %s' % dict
self.native = MeCab.Tagger(param)
def init_ctypes(self, options):
# type: (Dict) -> None
def init_ctypes(self, options: Dict) -> None:
import ctypes.util
lib = options.get('lib')
@ -124,8 +113,7 @@ class MecabSplitter(BaseSplitter):
if self.ctypes_mecab is None:
raise SphinxError('mecab initialization failed')
def __del__(self):
# type: () -> None
def __del__(self) -> None:
if self.ctypes_libmecab:
self.ctypes_libmecab.mecab_destroy(self.ctypes_mecab)
@ -133,21 +121,18 @@ MeCabBinder = MecabSplitter # keep backward compatibility until Sphinx-1.6
class JanomeSplitter(BaseSplitter):
def __init__(self, options):
# type: (Dict) -> None
def __init__(self, options: Dict) -> None:
super().__init__(options)
self.user_dict = options.get('user_dic')
self.user_dict_enc = options.get('user_dic_enc', 'utf8')
self.init_tokenizer()
def init_tokenizer(self):
# type: () -> None
def init_tokenizer(self) -> None:
if not janome_module:
raise RuntimeError('Janome is not available')
self.tokenizer = janome.tokenizer.Tokenizer(udic=self.user_dict, udic_enc=self.user_dict_enc)
def split(self, input):
# type: (str) -> List[str]
def split(self, input: str) -> List[str]:
result = ' '.join(token.surface for token in self.tokenizer.tokenize(input))
return result.split(' ')
@ -423,23 +408,20 @@ class DefaultSplitter(BaseSplitter):
'': 1082, '': -270, '': 306, '': -673, '': -496}
# ctype_
def ctype_(self, char):
# type: (str) -> str
def ctype_(self, char: str) -> str:
for pattern, value in self.patterns_.items():
if pattern.match(char):
return value
return 'O'
# ts_
def ts_(self, dict, key):
# type: (Dict[str, int], str) -> int
def ts_(self, dict: Dict[str, int], key: str) -> int:
if key in dict:
return dict[key]
return 0
# segment
def split(self, input):
# type: (str) -> List[str]
def split(self, input: str) -> List[str]:
if not input:
return []
@ -542,8 +524,7 @@ class SearchJapanese(SearchLanguage):
lang = 'ja'
language_name = 'Japanese'
def init(self, options):
# type: (Dict) -> None
def init(self, options: Dict) -> None:
dotted_path = options.get('type', 'sphinx.search.ja.DefaultSplitter')
try:
self.splitter = import_object(dotted_path)(options)
@ -551,14 +532,11 @@ class SearchJapanese(SearchLanguage):
raise ExtensionError("Splitter module %r can't be imported" %
dotted_path)
def split(self, input):
# type: (str) -> List[str]
def split(self, input: str) -> List[str]:
return self.splitter.split(input)
def word_filter(self, stemmed_word):
# type: (str) -> bool
def word_filter(self, stemmed_word: str) -> bool:
return len(stemmed_word) > 1
def stem(self, word):
# type: (str) -> str
def stem(self, word: str) -> str:
return word

View File

@ -8,13 +8,11 @@
:license: BSD, see LICENSE for details.
"""
from sphinx.search import SearchLanguage, parse_stop_word
from typing import Dict
import snowballstemmer
if False:
# For type annotation
from typing import Any # NOQA
from sphinx.search import SearchLanguage, parse_stop_word
dutch_stopwords = parse_stop_word('''
@ -135,10 +133,8 @@ class SearchDutch(SearchLanguage):
js_stemmer_code = js_stemmer
stopwords = dutch_stopwords
def init(self, options):
# type: (Any) -> None
def init(self, options: Dict) -> None:
self.stemmer = snowballstemmer.stemmer('dutch')
def stem(self, word):
# type: (str) -> str
def stem(self, word: str) -> str:
return self.stemmer.stemWord(word.lower())

View File

@ -8,13 +8,11 @@
:license: BSD, see LICENSE for details.
"""
from sphinx.search import SearchLanguage, parse_stop_word
from typing import Dict
import snowballstemmer
if False:
# For type annotation
from typing import Any # NOQA
from sphinx.search import SearchLanguage, parse_stop_word
norwegian_stopwords = parse_stop_word('''
@ -210,10 +208,8 @@ class SearchNorwegian(SearchLanguage):
js_stemmer_code = js_stemmer
stopwords = norwegian_stopwords
def init(self, options):
# type: (Any) -> None
def init(self, options: Dict) -> None:
self.stemmer = snowballstemmer.stemmer('norwegian')
def stem(self, word):
# type: (str) -> str
def stem(self, word: str) -> str:
return self.stemmer.stemWord(word.lower())

View File

@ -8,13 +8,11 @@
:license: BSD, see LICENSE for details.
"""
from sphinx.search import SearchLanguage, parse_stop_word
from typing import Dict
import snowballstemmer
if False:
# For type annotation
from typing import Any # NOQA
from sphinx.search import SearchLanguage, parse_stop_word
portuguese_stopwords = parse_stop_word('''
@ -270,10 +268,8 @@ class SearchPortuguese(SearchLanguage):
js_stemmer_code = js_stemmer
stopwords = portuguese_stopwords
def init(self, options):
# type: (Any) -> None
def init(self, options: Dict) -> None:
self.stemmer = snowballstemmer.stemmer('portuguese')
def stem(self, word):
# type: (str) -> str
def stem(self, word: str) -> str:
return self.stemmer.stemWord(word.lower())

File diff suppressed because one or more lines are too long

View File

@ -8,13 +8,11 @@
:license: BSD, see LICENSE for details.
"""
from sphinx.search import SearchLanguage, parse_stop_word
from typing import Dict
import snowballstemmer
if False:
# For type annotation
from typing import Any # NOQA
from sphinx.search import SearchLanguage, parse_stop_word
russian_stopwords = parse_stop_word('''
@ -259,10 +257,8 @@ class SearchRussian(SearchLanguage):
js_stemmer_code = js_stemmer
stopwords = russian_stopwords
def init(self, options):
# type: (Any) -> None
def init(self, options: Dict) -> None:
self.stemmer = snowballstemmer.stemmer('russian')
def stem(self, word):
# type: (str) -> str
def stem(self, word: str) -> str:
return self.stemmer.stemWord(word.lower())

View File

@ -8,13 +8,12 @@
:license: BSD, see LICENSE for details.
"""
from sphinx.search import SearchLanguage, parse_stop_word
from typing import Dict
import snowballstemmer
if False:
# For type annotation
from typing import Any
from sphinx.search import SearchLanguage, parse_stop_word
swedish_stopwords = parse_stop_word('''
| source: http://snowball.tartarus.org/algorithms/swedish/stop.txt
@ -147,10 +146,8 @@ class SearchSwedish(SearchLanguage):
js_stemmer_code = js_stemmer
stopwords = swedish_stopwords
def init(self, options):
# type: (Any) -> None
def init(self, options: Dict) -> None:
self.stemmer = snowballstemmer.stemmer('swedish')
def stem(self, word):
# type: (str) -> str
def stem(self, word: str) -> str:
return self.stemmer.stemWord(word.lower())

File diff suppressed because one or more lines are too long

View File

@ -10,6 +10,7 @@
import os
import re
from typing import Dict, List
from sphinx.search import SearchLanguage
from sphinx.util.stemmer import get_stemmer
@ -20,10 +21,6 @@ try:
except ImportError:
JIEBA = False
if False:
# For type annotation
from typing import Dict, List # NOQA
english_stopwords = set("""
a and are as at
be but by
@ -235,8 +232,7 @@ class SearchChinese(SearchLanguage):
latin1_letters = re.compile(r'[a-zA-Z0-9_]+')
latin_terms = [] # type: List[str]
def init(self, options):
# type: (Dict) -> None
def init(self, options: Dict) -> None:
if JIEBA:
dict_path = options.get('dict')
if dict_path and os.path.isfile(dict_path):
@ -244,8 +240,7 @@ class SearchChinese(SearchLanguage):
self.stemmer = get_stemmer()
def split(self, input):
# type: (str) -> List[str]
def split(self, input: str) -> List[str]:
chinese = [] # type: List[str]
if JIEBA:
chinese = list(jieba.cut_for_search(input))
@ -255,13 +250,10 @@ class SearchChinese(SearchLanguage):
self.latin_terms.extend(latin1)
return chinese + latin1
def word_filter(self, stemmed_word):
# type: (str) -> bool
def word_filter(self, stemmed_word: str) -> bool:
return len(stemmed_word) > 1
def stem(self, word):
# type: (str) -> str
def stem(self, word: str) -> str:
# Don't stem Latin words that are long enough to be relevant for search
# if not stemmed, but would be too short after being stemmed
# avoids some issues with acronyms

View File

@ -13,6 +13,7 @@ import os
import shutil
import tempfile
from os import path
from typing import Any, Dict, List
from zipfile import ZipFile
import pkg_resources
@ -23,19 +24,18 @@ from sphinx.locale import __
from sphinx.util import logging
from sphinx.util.osutil import ensuredir
logger = logging.getLogger(__name__)
if False:
# For type annotation
from typing import Any, Dict, List # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.application import Sphinx
logger = logging.getLogger(__name__)
NODEFAULT = object()
THEMECONF = 'theme.conf'
def extract_zip(filename, targetdir):
# type: (str, str) -> None
def extract_zip(filename: str, targetdir: str) -> None:
"""Extract zip file to target directory."""
ensuredir(targetdir)
@ -54,8 +54,7 @@ class Theme:
This class supports both theme directory and theme archive (zipped theme)."""
def __init__(self, name, theme_path, factory):
# type: (str, str, HTMLThemeFactory) -> None
def __init__(self, name: str, theme_path: str, factory: "HTMLThemeFactory") -> None:
self.name = name
self.base = None
self.rootdir = None
@ -87,8 +86,7 @@ class Theme:
raise ThemeError(__('no theme named %r found, inherited by %r') %
(inherit, name))
def get_theme_dirs(self):
# type: () -> List[str]
def get_theme_dirs(self) -> List[str]:
"""Return a list of theme directories, beginning with this theme's,
then the base theme's, then that one's base theme's, etc.
"""
@ -97,8 +95,7 @@ class Theme:
else:
return [self.themedir] + self.base.get_theme_dirs()
def get_config(self, section, name, default=NODEFAULT):
# type: (str, str, Any) -> Any
def get_config(self, section: str, name: str, default: Any = NODEFAULT) -> Any:
"""Return the value for a theme configuration setting, searching the
base theme chain.
"""
@ -114,8 +111,7 @@ class Theme:
else:
return default
def get_options(self, overrides={}):
# type: (Dict[str, Any]) -> Dict[str, Any]
def get_options(self, overrides: Dict[str, Any] = {}) -> Dict[str, Any]:
"""Return a dictionary of theme options and their values."""
if self.base:
options = self.base.get_options()
@ -135,8 +131,7 @@ class Theme:
return options
def cleanup(self):
# type: () -> None
def cleanup(self) -> None:
"""Remove temporary directories."""
if self.rootdir:
try:
@ -147,8 +142,7 @@ class Theme:
self.base.cleanup()
def is_archived_theme(filename):
# type: (str) -> bool
def is_archived_theme(filename: str) -> bool:
"""Check the specified file is an archived theme file or not."""
try:
with ZipFile(filename) as f:
@ -160,23 +154,20 @@ def is_archived_theme(filename):
class HTMLThemeFactory:
"""A factory class for HTML Themes."""
def __init__(self, app):
# type: (Sphinx) -> None
def __init__(self, app: "Sphinx") -> None:
self.app = app
self.themes = app.html_themes
self.load_builtin_themes()
if getattr(app.config, 'html_theme_path', None):
self.load_additional_themes(app.config.html_theme_path)
def load_builtin_themes(self):
# type: () -> None
def load_builtin_themes(self) -> None:
"""Load built-in themes."""
themes = self.find_themes(path.join(package_dir, 'themes'))
for name, theme in themes.items():
self.themes[name] = theme
def load_additional_themes(self, theme_paths):
# type: (str) -> None
def load_additional_themes(self, theme_paths: str) -> None:
"""Load additional themes placed at specified directories."""
for theme_path in theme_paths:
abs_theme_path = path.abspath(path.join(self.app.confdir, theme_path))
@ -184,8 +175,7 @@ class HTMLThemeFactory:
for name, theme in themes.items():
self.themes[name] = theme
def load_extra_theme(self, name):
# type: (str) -> None
def load_extra_theme(self, name: str) -> None:
"""Try to load a theme having specifed name."""
if name == 'alabaster':
self.load_alabaster_theme()
@ -194,14 +184,12 @@ class HTMLThemeFactory:
else:
self.load_external_theme(name)
def load_alabaster_theme(self):
# type: () -> None
def load_alabaster_theme(self) -> None:
"""Load alabaster theme."""
import alabaster
self.themes['alabaster'] = path.join(alabaster.get_path(), 'alabaster')
def load_sphinx_rtd_theme(self):
# type: () -> None
def load_sphinx_rtd_theme(self) -> None:
"""Load sphinx_rtd_theme theme (if exists)."""
try:
import sphinx_rtd_theme
@ -210,8 +198,7 @@ class HTMLThemeFactory:
except ImportError:
pass
def load_external_theme(self, name):
# type: (str) -> None
def load_external_theme(self, name: str) -> None:
"""Try to load a theme using entry_points.
Sphinx refers to ``sphinx_themes`` entry_points.
@ -225,8 +212,7 @@ class HTMLThemeFactory:
except StopIteration:
pass
def find_themes(self, theme_path):
# type: (str) -> Dict[str, str]
def find_themes(self, theme_path: str) -> Dict[str, str]:
"""Search themes from specified directory."""
themes = {} # type: Dict[str, str]
if not path.isdir(theme_path):
@ -247,8 +233,7 @@ class HTMLThemeFactory:
return themes
def create(self, name):
# type: (str) -> Theme
def create(self, name: str) -> Theme:
"""Create an instance of theme."""
if name not in self.themes:
self.load_extra_theme(name)

View File

@ -273,8 +273,7 @@ class DoctestTransform(SphinxTransform):
"""Set "doctest" style to each doctest_block node"""
default_priority = 500
def apply(self, **kwargs):
# type: (Any) -> None
def apply(self, **kwargs) -> None:
for node in self.document.traverse(nodes.doctest_block):
node['classes'].append('doctest')

View File

@ -119,8 +119,7 @@ def get_matching_docs(dirname: str, suffixes: List[str],
break
def get_filetype(source_suffix, filename):
# type: (Dict[str, str], str) -> str
def get_filetype(source_suffix: Dict[str, str], filename: str) -> str:
for suffix, filetype in source_suffix.items():
if filename.endswith(suffix):
# If default filetype (None), considered as restructuredtext.

View File

@ -12,15 +12,17 @@ import pickle
from itertools import product, zip_longest
from operator import itemgetter
from os import path
from typing import Any, Dict, Iterator
from uuid import uuid4
from docutils import nodes
from docutils.nodes import Node
from sphinx.transforms import SphinxTransform
if False:
# For type annotation
from typing import Any, Dict, Iterator # NOQA
from docutils import nodes # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.application import Sphinx
try:
import Levenshtein
@ -32,8 +34,7 @@ except ImportError:
VERSIONING_RATIO = 65
def add_uids(doctree, condition):
# type: (nodes.Node, Any) -> Iterator[nodes.Node]
def add_uids(doctree: Node, condition: Any) -> Iterator[Node]:
"""Add a unique id to every node in the `doctree` which matches the
condition and yield the nodes.
@ -48,8 +49,7 @@ def add_uids(doctree, condition):
yield node
def merge_doctrees(old, new, condition):
# type: (nodes.Node, nodes.Node, Any) -> Iterator[nodes.Node]
def merge_doctrees(old: Node, new: Node, condition: Any) -> Iterator[Node]:
"""Merge the `old` doctree with the `new` one while looking at nodes
matching the `condition`.
@ -116,8 +116,7 @@ def merge_doctrees(old, new, condition):
yield new_node
def get_ratio(old, new):
# type: (str, str) -> float
def get_ratio(old: str, new: str) -> float:
"""Return a "similiarity ratio" (in percent) representing the similarity
between the two strings where 0 is equal and anything above less than equal.
"""
@ -130,8 +129,7 @@ def get_ratio(old, new):
return levenshtein_distance(old, new) / (len(old) / 100.0)
def levenshtein_distance(a, b):
# type: (str, str) -> int
def levenshtein_distance(a: str, b: str) -> int:
"""Return the Levenshtein edit distance between two strings *a* and *b*."""
if a == b:
return 0
@ -155,8 +153,7 @@ class UIDTransform(SphinxTransform):
"""Add UIDs to doctree for versioning."""
default_priority = 880
def apply(self, **kwargs):
# type: (Any) -> None
def apply(self, **kwargs) -> None:
env = self.env
old_doctree = None
if not env.versioning_condition:
@ -178,8 +175,7 @@ class UIDTransform(SphinxTransform):
list(merge_doctrees(old_doctree, self.document, env.versioning_condition))
def setup(app):
# type: (Sphinx) -> Dict[str, Any]
def setup(app: "Sphinx") -> Dict[str, Any]:
app.add_transform(UIDTransform)
return {