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