Migrate to py3 style type annotation: sphinx.util.docutils

This commit is contained in:
Takeshi KOMIYA 2019-06-02 17:48:07 +09:00
parent dd4741c352
commit 086eac3914

View File

@ -16,33 +16,33 @@ from contextlib import contextmanager
from copy import copy
from distutils.version import LooseVersion
from os import path
from typing import IO, cast
from types import ModuleType
from typing import Any, Callable, Dict, Generator, IO, List, Set, Tuple, Type
from typing import cast
import docutils
from docutils import nodes
from docutils.io import FileOutput
from docutils.nodes import Element, Node, system_message
from docutils.parsers.rst import Directive, directives, roles, convert_directive_function
from docutils.statemachine import StateMachine
from docutils.parsers.rst.states import Inliner
from docutils.statemachine import StateMachine, State, StringList
from docutils.utils import Reporter, unescape
from sphinx.deprecation import RemovedInSphinx30Warning
from sphinx.errors import ExtensionError, SphinxError
from sphinx.locale import __
from sphinx.util import logging
from sphinx.util.typing import RoleFunction
logger = logging.getLogger(__name__)
report_re = re.compile('^(.+?:(?:\\d+)?): \\((DEBUG|INFO|WARNING|ERROR|SEVERE)/(\\d+)?\\) ')
if False:
# For type annotation
from types import ModuleType # NOQA
from typing import Any, Callable, Dict, Generator, List, Set, Tuple, Type # NOQA
from docutils.parsers.rst.states import Inliner # NOQA
from docutils.statemachine import State, StringList # NOQA
from sphinx.builders import Builder # NOQA
from sphinx.config import Config # NOQA
from sphinx.environment import BuildEnvironment # NOQA
from sphinx.util.typing import RoleFunction # NOQA
from sphinx.builders import Builder
from sphinx.config import Config
from sphinx.environment import BuildEnvironment
__version_info__ = tuple(LooseVersion(docutils.__version__).version)
@ -50,8 +50,7 @@ additional_nodes = set() # type: Set[Type[nodes.Element]]
@contextmanager
def docutils_namespace():
# type: () -> Generator[None, None, None]
def docutils_namespace() -> Generator[None, None, None]:
"""Create namespace for reST parsers."""
try:
_directives = copy(directives._directives) # type: ignore
@ -67,14 +66,12 @@ def docutils_namespace():
additional_nodes.discard(node)
def is_directive_registered(name):
# type: (str) -> bool
def is_directive_registered(name: str) -> bool:
"""Check the *name* directive is already registered."""
return name in directives._directives # type: ignore
def register_directive(name, directive):
# type: (str, Type[Directive]) -> None
def register_directive(name: str, directive: Type[Directive]) -> None:
"""Register a directive to docutils.
This modifies global state of docutils. So it is better to use this
@ -83,14 +80,12 @@ def register_directive(name, directive):
directives.register_directive(name, directive)
def is_role_registered(name):
# type: (str) -> bool
def is_role_registered(name: str) -> bool:
"""Check the *name* role is already registered."""
return name in roles._roles # type: ignore
def register_role(name, role):
# type: (str, RoleFunction) -> None
def register_role(name: str, role: RoleFunction) -> None:
"""Register a role to docutils.
This modifies global state of docutils. So it is better to use this
@ -99,20 +94,17 @@ def register_role(name, role):
roles.register_local_role(name, role)
def unregister_role(name):
# type: (str) -> None
def unregister_role(name: str) -> None:
"""Unregister a role from docutils."""
roles._roles.pop(name, None) # type: ignore
def is_node_registered(node):
# type: (Type[nodes.Element]) -> bool
def is_node_registered(node: Type[Element]) -> bool:
"""Check the *node* is already registered."""
return hasattr(nodes.GenericNodeVisitor, 'visit_' + node.__name__)
def register_node(node):
# type: (Type[nodes.Element]) -> None
def register_node(node: Type[Element]) -> None:
"""Register a node to docutils.
This modifies global state of some visitors. So it is better to use this
@ -123,8 +115,7 @@ def register_node(node):
additional_nodes.add(node)
def unregister_node(node):
# type: (Type[nodes.Element]) -> None
def unregister_node(node: Type[Element]) -> None:
"""Unregister a node from docutils.
This is inverse of ``nodes._add_nodes_class_names()``.
@ -137,8 +128,7 @@ def unregister_node(node):
@contextmanager
def patched_get_language():
# type: () -> Generator[None, None, None]
def patched_get_language() -> Generator[None, None, None]:
"""Patch docutils.languages.get_language() temporarily.
This ignores the second argument ``reporter`` to suppress warnings.
@ -146,8 +136,7 @@ def patched_get_language():
"""
from docutils.languages import get_language
def patched_get_language(language_code, reporter=None):
# type: (str, Reporter) -> Any
def patched_get_language(language_code: str, reporter: Reporter = None) -> Any:
return get_language(language_code)
try:
@ -159,8 +148,7 @@ def patched_get_language():
@contextmanager
def using_user_docutils_conf(confdir):
# type: (str) -> Generator[None, None, None]
def using_user_docutils_conf(confdir: str) -> Generator[None, None, None]:
"""Let docutils know the location of ``docutils.conf`` for Sphinx."""
try:
docutilsconfig = os.environ.get('DOCUTILSCONFIG', None)
@ -176,8 +164,7 @@ def using_user_docutils_conf(confdir):
@contextmanager
def patch_docutils(confdir=None):
# type: (str) -> Generator[None, None, None]
def patch_docutils(confdir: str = None) -> Generator[None, None, None]:
"""Patch to docutils temporarily."""
with patched_get_language(), using_user_docutils_conf(confdir):
yield
@ -191,35 +178,30 @@ class sphinx_domains:
"""Monkey-patch directive and role dispatch, so that domain-specific
markup takes precedence.
"""
def __init__(self, env):
# type: (BuildEnvironment) -> None
def __init__(self, env: "BuildEnvironment") -> None:
self.env = env
self.directive_func = None # type: Callable
self.roles_func = None # type: Callable
def __enter__(self):
# type: () -> None
def __enter__(self) -> None:
self.enable()
def __exit__(self, type, value, traceback):
# type: (str, str, str) -> None
def __exit__(self, exc_type: Type[Exception], exc_value: Exception, traceback: Any) -> bool: # NOQA
self.disable()
return True
def enable(self):
# type: () -> None
def enable(self) -> None:
self.directive_func = directives.directive
self.role_func = roles.role
directives.directive = self.lookup_directive # type: ignore
roles.role = self.lookup_role # type: ignore
def disable(self):
# type: () -> None
def disable(self) -> None:
directives.directive = self.directive_func
roles.role = self.role_func
def lookup_domain_element(self, type, name):
# type: (str, str) -> Any
def lookup_domain_element(self, type: str, name: str) -> Any:
"""Lookup a markup element (directive or role), given its name which can
be a full name (with domain).
"""
@ -247,15 +229,13 @@ class sphinx_domains:
raise ElementLookupError
def lookup_directive(self, name, lang_module, document):
# type: (str, ModuleType, nodes.document) -> Tuple[Type[Directive], List[nodes.system_message]] # NOQA
def lookup_directive(self, name: str, lang_module: ModuleType, document: nodes.document) -> Tuple[Type[Directive], List[system_message]]: # NOQA
try:
return self.lookup_domain_element('directive', name)
except ElementLookupError:
return self.directive_func(name, lang_module, document)
def lookup_role(self, name, lang_module, lineno, reporter):
# type: (str, ModuleType, int, Reporter) -> Tuple[RoleFunction, List[nodes.system_message]] # NOQA
def lookup_role(self, name: str, lang_module: ModuleType, lineno: int, reporter: Reporter) -> Tuple[RoleFunction, List[system_message]]: # NOQA
try:
return self.lookup_domain_element('role', name)
except ElementLookupError:
@ -263,8 +243,7 @@ class sphinx_domains:
class WarningStream:
def write(self, text):
# type: (str) -> None
def write(self, text: str) -> None:
matched = report_re.search(text)
if not matched:
logger.warning(text.rstrip("\r\n"))
@ -276,16 +255,14 @@ class WarningStream:
class LoggingReporter(Reporter):
@classmethod
def from_reporter(cls, reporter):
# type: (Reporter) -> LoggingReporter
def from_reporter(cls, reporter: Reporter) -> "LoggingReporter":
"""Create an instance of LoggingReporter from other reporter object."""
return cls(reporter.source, reporter.report_level, reporter.halt_level,
reporter.debug_flag, reporter.error_handler)
def __init__(self, source, report_level=Reporter.WARNING_LEVEL,
halt_level=Reporter.SEVERE_LEVEL, debug=False,
error_handler='backslashreplace'):
# type: (str, int, int, bool, str) -> None
def __init__(self, source: str, report_level: int = Reporter.WARNING_LEVEL,
halt_level: int = Reporter.SEVERE_LEVEL, debug: bool = False,
error_handler: str = 'backslashreplace') -> None:
stream = cast(IO, WarningStream())
super().__init__(source, report_level, halt_level,
stream, debug, error_handler=error_handler)
@ -294,18 +271,15 @@ class LoggingReporter(Reporter):
class NullReporter(Reporter):
"""A dummy reporter; write nothing."""
def __init__(self):
# type: () -> None
def __init__(self) -> None:
super().__init__('', 999, 4)
def is_html5_writer_available():
# type: () -> bool
def is_html5_writer_available() -> bool:
return __version_info__ > (0, 13, 0)
def directive_helper(obj, has_content=None, argument_spec=None, **option_spec):
# type: (Any, bool, Tuple[int, int, bool], Any) -> Any
def directive_helper(obj: Any, has_content: bool = None, argument_spec: Tuple[int, int, bool] = None, **option_spec) -> Any: # NOQA
warnings.warn('function based directive support is now deprecated. '
'Use class based directive instead.',
RemovedInSphinx30Warning)
@ -323,8 +297,7 @@ def directive_helper(obj, has_content=None, argument_spec=None, **option_spec):
@contextmanager
def switch_source_input(state, content):
# type: (State, StringList) -> Generator[None, None, None]
def switch_source_input(state: State, content: StringList) -> Generator[None, None, None]:
"""Switch current source input of state temporarily."""
try:
# remember the original ``get_source_and_line()`` method
@ -344,13 +317,11 @@ def switch_source_input(state, content):
class SphinxFileOutput(FileOutput):
"""Better FileOutput class for Sphinx."""
def __init__(self, **kwargs):
# type: (Any) -> None
def __init__(self, **kwargs) -> None:
self.overwrite_if_changed = kwargs.pop('overwrite_if_changed', False)
super().__init__(**kwargs)
def write(self, data):
# type: (str) -> str
def write(self, data: str) -> str:
if (self.destination_path and self.autoclose and 'b' not in self.mode and
self.overwrite_if_changed and os.path.exists(self.destination_path)):
with open(self.destination_path, encoding=self.encoding) as f:
@ -371,19 +342,16 @@ class SphinxDirective(Directive):
"""
@property
def env(self):
# type: () -> BuildEnvironment
def env(self) -> "BuildEnvironment":
"""Reference to the :class:`.BuildEnvironment` object."""
return self.state.document.settings.env
@property
def config(self):
# type: () -> Config
def config(self) -> "Config":
"""Reference to the :class:`.Config` object."""
return self.env.config
def set_source_info(self, node):
# type: (nodes.Node) -> None
def set_source_info(self, node: Node) -> None:
"""Set source and line number to the node."""
node.source, node.line = self.state_machine.get_source_and_line(self.lineno)
@ -406,8 +374,9 @@ class SphinxRole:
content = None #: A list of strings, the directive content for customization
#: (from the "role" directive).
def __call__(self, name, 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 __call__(self, name: str, rawtext: str, text: str, lineno: int,
inliner: Inliner, options: Dict = {}, content: List[str] = []
) -> Tuple[List[Node], List[system_message]]:
self.rawtext = rawtext
self.text = unescape(text)
self.lineno = lineno
@ -427,24 +396,20 @@ class SphinxRole:
return self.run()
def run(self):
# type: () -> Tuple[List[nodes.Node], List[nodes.system_message]]
def run(self) -> Tuple[List[Node], List[system_message]]:
raise NotImplementedError
@property
def env(self):
# type: () -> BuildEnvironment
def env(self) -> "BuildEnvironment":
"""Reference to the :class:`.BuildEnvironment` object."""
return self.inliner.document.settings.env
@property
def config(self):
# type: () -> Config
def config(self) -> "Config":
"""Reference to the :class:`.Config` object."""
return self.env.config
def set_source_info(self, node, lineno=None):
# type: (nodes.Node, int) -> None
def set_source_info(self, node: Node, lineno: int = None) -> None:
if lineno is None:
lineno = self.lineno
@ -466,8 +431,9 @@ class ReferenceRole(SphinxRole):
# \x00 means the "<" was backslash-escaped
explicit_title_re = re.compile(r'^(.+?)\s*(?<!\x00)<(.*?)>$', re.DOTALL)
def __call__(self, name, 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 __call__(self, name: str, rawtext: str, text: str, lineno: int,
inliner: Inliner, options: Dict = {}, content: List[str] = []
) -> Tuple[List[Node], List[system_message]]:
matched = self.explicit_title_re.match(text)
if matched:
self.has_explicit_title = True
@ -490,8 +456,7 @@ class SphinxTranslator(nodes.NodeVisitor):
This class is strongly coupled with Sphinx.
"""
def __init__(self, document, builder):
# type: (nodes.document, Builder) -> None
def __init__(self, document: nodes.document, builder: "Builder") -> None:
super().__init__(document)
self.builder = builder
self.config = builder.config
@ -503,8 +468,7 @@ class SphinxTranslator(nodes.NodeVisitor):
__document_cache__ = None # type: nodes.document
def new_document(source_path, settings=None):
# type: (str, Any) -> nodes.document
def new_document(source_path: str, settings: Any = None) -> nodes.document:
"""Return a new empty document object. This is an alternative of docutils'.
This is a simple wrapper for ``docutils.utils.new_document()``. It