diff options
Diffstat (limited to 'sphinx/util/docutils.py')
-rw-r--r-- | sphinx/util/docutils.py | 164 |
1 files changed, 64 insertions, 100 deletions
diff --git a/sphinx/util/docutils.py b/sphinx/util/docutils.py index eebfec464..b9795e7e4 100644 --- a/sphinx/util/docutils.py +++ b/sphinx/util/docutils.py @@ -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, Optional, 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 + directives.directive = self.lookup_directive + roles.role = self.lookup_role - 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,24 +229,21 @@ 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, directive_name: str, language_module: ModuleType, document: nodes.document) -> Tuple[Optional[Type[Directive]], List[system_message]]: # NOQA try: - return self.lookup_domain_element('directive', name) + return self.lookup_domain_element('directive', directive_name) except ElementLookupError: - return self.directive_func(name, lang_module, document) + return self.directive_func(directive_name, language_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, role_name: str, language_module: ModuleType, lineno: int, reporter: Reporter) -> Tuple[RoleFunction, List[system_message]]: # NOQA try: - return self.lookup_domain_element('role', name) + return self.lookup_domain_element('role', role_name) except ElementLookupError: - return self.role_func(name, lang_module, lineno, reporter) + return self.role_func(role_name, language_module, lineno, reporter) 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 |