mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge pull request #6438 from tk0miya/refactor_type_annotation4
Migrate to py3 style type annotation: sphinx.util.requests (part3)
This commit is contained in:
commit
f8a501d868
@ -337,15 +337,13 @@ _coding_re = re.compile(r'coding[:=]\s*([-\w.]+)')
|
||||
def detect_encoding(readline: Callable[[], bytes]) -> str:
|
||||
"""Like tokenize.detect_encoding() from Py3k, but a bit simplified."""
|
||||
|
||||
def read_or_stop():
|
||||
# type: () -> bytes
|
||||
def read_or_stop() -> bytes:
|
||||
try:
|
||||
return readline()
|
||||
except StopIteration:
|
||||
return None
|
||||
|
||||
def get_normal_name(orig_enc):
|
||||
# type: (str) -> str
|
||||
def get_normal_name(orig_enc: str) -> str:
|
||||
"""Imitates get_normal_name in tokenizer.c."""
|
||||
# Only care about the first 12 characters.
|
||||
enc = orig_enc[:12].lower().replace('_', '-')
|
||||
@ -356,8 +354,7 @@ def detect_encoding(readline: Callable[[], bytes]) -> str:
|
||||
return 'iso-8859-1'
|
||||
return orig_enc
|
||||
|
||||
def find_cookie(line):
|
||||
# type: (bytes) -> str
|
||||
def find_cookie(line: bytes) -> str:
|
||||
try:
|
||||
line_string = line.decode('ascii')
|
||||
except UnicodeDecodeError:
|
||||
|
@ -8,15 +8,12 @@
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
from docutils import nodes
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
from docutils import nodes # NOQA
|
||||
from sphinx.builders.html import HTMLTranslator # NOQA
|
||||
from sphinx.builders.html import HTMLTranslator
|
||||
|
||||
|
||||
def get_node_equation_number(writer, node):
|
||||
# type: (HTMLTranslator, nodes.math_block) -> str
|
||||
def get_node_equation_number(writer: HTMLTranslator, node: nodes.math_block) -> str:
|
||||
if writer.builder.config.math_numfig and writer.builder.config.numfig:
|
||||
figtype = 'displaymath'
|
||||
if writer.builder.name == 'singlehtml':
|
||||
@ -31,10 +28,8 @@ def get_node_equation_number(writer, node):
|
||||
return node['number']
|
||||
|
||||
|
||||
def wrap_displaymath(text, label, numbering):
|
||||
# type: (str, str, bool) -> str
|
||||
def is_equation(part):
|
||||
# type: (str) -> str
|
||||
def wrap_displaymath(text: str, label: str, numbering: bool) -> str:
|
||||
def is_equation(part: str) -> str:
|
||||
return part.strip()
|
||||
|
||||
if label is None:
|
||||
|
@ -10,9 +10,14 @@
|
||||
|
||||
import re
|
||||
import warnings
|
||||
from typing import Any, cast
|
||||
from typing import Any, Callable, Iterable, List, Set, Tuple, Type
|
||||
from typing import cast
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.nodes import Element, Node
|
||||
from docutils.parsers.rst import Directive
|
||||
from docutils.parsers.rst.states import Inliner
|
||||
from docutils.statemachine import StringList
|
||||
|
||||
from sphinx import addnodes
|
||||
from sphinx.deprecation import RemovedInSphinx40Warning
|
||||
@ -21,11 +26,8 @@ from sphinx.util import logging
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
from typing import Callable, Iterable, List, Optional, Set, Tuple, Type # NOQA
|
||||
from docutils.parsers.rst.states import Inliner # NOQA
|
||||
from docutils.statemachine import StringList # NOQA
|
||||
from sphinx.builders import Builder # NOQA
|
||||
from sphinx.utils.tags import Tags # NOQA
|
||||
from sphinx.builders import Builder
|
||||
from sphinx.utils.tags import Tags
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -57,13 +59,11 @@ class NodeMatcher:
|
||||
# => [<reference ...>, <reference ...>, ...]
|
||||
"""
|
||||
|
||||
def __init__(self, *classes, **attrs):
|
||||
# type: (Type[nodes.Node], Any) -> None
|
||||
def __init__(self, *classes: Type[Node], **attrs) -> None:
|
||||
self.classes = classes
|
||||
self.attrs = attrs
|
||||
|
||||
def match(self, node):
|
||||
# type: (nodes.Node) -> bool
|
||||
def match(self, node: Node) -> bool:
|
||||
try:
|
||||
if self.classes and not isinstance(node, self.classes):
|
||||
return False
|
||||
@ -85,13 +85,11 @@ class NodeMatcher:
|
||||
# for non-Element nodes
|
||||
return False
|
||||
|
||||
def __call__(self, node):
|
||||
# type: (nodes.Node) -> bool
|
||||
def __call__(self, node: Node) -> bool:
|
||||
return self.match(node)
|
||||
|
||||
|
||||
def get_full_module_name(node):
|
||||
# type: (nodes.Node) -> str
|
||||
def get_full_module_name(node: Node) -> str:
|
||||
"""
|
||||
return full module dotted path like: 'docutils.nodes.paragraph'
|
||||
|
||||
@ -101,8 +99,7 @@ def get_full_module_name(node):
|
||||
return '{}.{}'.format(node.__module__, node.__class__.__name__)
|
||||
|
||||
|
||||
def repr_domxml(node, length=80):
|
||||
# type: (nodes.Node, Optional[int]) -> str
|
||||
def repr_domxml(node: Node, length: int = 80) -> str:
|
||||
"""
|
||||
return DOM XML representation of the specified node like:
|
||||
'<paragraph translatable="False"><inline classes="versionmodified">New in version...'
|
||||
@ -122,8 +119,7 @@ def repr_domxml(node, length=80):
|
||||
return text
|
||||
|
||||
|
||||
def apply_source_workaround(node):
|
||||
# type: (nodes.Element) -> None
|
||||
def apply_source_workaround(node: Element) -> None:
|
||||
# workaround: nodes.term have wrong rawsource if classifier is specified.
|
||||
# The behavior of docutils-0.11, 0.12 is:
|
||||
# * when ``term text : classifier1 : classifier2`` is specified,
|
||||
@ -186,8 +182,7 @@ IGNORED_NODES = (
|
||||
)
|
||||
|
||||
|
||||
def is_pending_meta(node):
|
||||
# type: (nodes.Node) -> bool
|
||||
def is_pending_meta(node: Node) -> bool:
|
||||
if (isinstance(node, nodes.pending) and
|
||||
isinstance(node.details.get('nodes', [None])[0], addnodes.meta)):
|
||||
return True
|
||||
@ -195,8 +190,7 @@ def is_pending_meta(node):
|
||||
return False
|
||||
|
||||
|
||||
def is_translatable(node):
|
||||
# type: (nodes.Node) -> bool
|
||||
def is_translatable(node: Node) -> bool:
|
||||
if isinstance(node, addnodes.translatable):
|
||||
return True
|
||||
|
||||
@ -251,8 +245,7 @@ META_TYPE_NODES = (
|
||||
)
|
||||
|
||||
|
||||
def extract_messages(doctree):
|
||||
# type: (nodes.Element) -> Iterable[Tuple[nodes.Element, str]]
|
||||
def extract_messages(doctree: Element) -> Iterable[Tuple[Element, str]]:
|
||||
"""Extract translatable messages from a document tree."""
|
||||
for node in doctree.traverse(is_translatable): # type: nodes.Element
|
||||
if isinstance(node, addnodes.translatable):
|
||||
@ -279,39 +272,34 @@ def extract_messages(doctree):
|
||||
yield node, msg
|
||||
|
||||
|
||||
def find_source_node(node):
|
||||
# type: (nodes.Element) -> str
|
||||
def find_source_node(node: Element) -> str:
|
||||
warnings.warn('find_source_node() is deprecated.',
|
||||
RemovedInSphinx40Warning)
|
||||
return get_node_source(node)
|
||||
|
||||
|
||||
def get_node_source(node):
|
||||
# type: (nodes.Element) -> str
|
||||
def get_node_source(node: Element) -> str:
|
||||
for pnode in traverse_parent(node):
|
||||
if pnode.source:
|
||||
return pnode.source
|
||||
return None
|
||||
|
||||
|
||||
def get_node_line(node):
|
||||
# type: (nodes.Element) -> int
|
||||
def get_node_line(node: Element) -> int:
|
||||
for pnode in traverse_parent(node):
|
||||
if pnode.line:
|
||||
return pnode.line
|
||||
return None
|
||||
|
||||
|
||||
def traverse_parent(node, cls=None):
|
||||
# type: (nodes.Element, Any) -> Iterable[nodes.Element]
|
||||
def traverse_parent(node: Element, cls: Any = None) -> Iterable[Element]:
|
||||
while node:
|
||||
if cls is None or isinstance(node, cls):
|
||||
yield node
|
||||
node = node.parent
|
||||
|
||||
|
||||
def get_prev_node(node):
|
||||
# type: (nodes.Node) -> nodes.Node
|
||||
def get_prev_node(node: Node) -> Node:
|
||||
pos = node.parent.index(node)
|
||||
if pos > 0:
|
||||
return node.parent[pos - 1]
|
||||
@ -319,8 +307,7 @@ def get_prev_node(node):
|
||||
return None
|
||||
|
||||
|
||||
def traverse_translatable_index(doctree):
|
||||
# type: (nodes.Element) -> Iterable[Tuple[nodes.Element, List[str]]]
|
||||
def traverse_translatable_index(doctree: Element) -> Iterable[Tuple[Element, List[str]]]:
|
||||
"""Traverse translatable index node from a document tree."""
|
||||
for node in doctree.traverse(NodeMatcher(addnodes.index, inline=False)): # type: addnodes.index # NOQA
|
||||
if 'raw_entries' in node:
|
||||
@ -330,8 +317,7 @@ def traverse_translatable_index(doctree):
|
||||
yield node, entries
|
||||
|
||||
|
||||
def nested_parse_with_titles(state, content, node):
|
||||
# type: (Any, StringList, nodes.Node) -> str
|
||||
def nested_parse_with_titles(state: Any, content: StringList, node: Node) -> str:
|
||||
"""Version of state.nested_parse() that allows titles and does not require
|
||||
titles to have the same decoration as the calling document.
|
||||
|
||||
@ -350,8 +336,7 @@ def nested_parse_with_titles(state, content, node):
|
||||
state.memo.section_level = surrounding_section_level
|
||||
|
||||
|
||||
def clean_astext(node):
|
||||
# type: (nodes.Element) -> str
|
||||
def clean_astext(node: Element) -> str:
|
||||
"""Like node.astext(), but ignore images."""
|
||||
node = node.deepcopy()
|
||||
for img in node.traverse(nodes.image):
|
||||
@ -361,8 +346,7 @@ def clean_astext(node):
|
||||
return node.astext()
|
||||
|
||||
|
||||
def split_explicit_title(text):
|
||||
# type: (str) -> Tuple[bool, str, str]
|
||||
def split_explicit_title(text: str) -> Tuple[bool, str, str]:
|
||||
"""Split role content into title and target, if given."""
|
||||
match = explicit_title_re.match(text)
|
||||
if match:
|
||||
@ -375,8 +359,7 @@ indextypes = [
|
||||
]
|
||||
|
||||
|
||||
def process_index_entry(entry, targetid):
|
||||
# type: (str, str) -> List[Tuple[str, str, str, str, str]]
|
||||
def process_index_entry(entry: str, targetid: str) -> List[Tuple[str, str, str, str, str]]:
|
||||
from sphinx.domains.python import pairindextypes
|
||||
|
||||
indexentries = [] # type: List[Tuple[str, str, str, str, str]]
|
||||
@ -414,8 +397,9 @@ def process_index_entry(entry, targetid):
|
||||
return indexentries
|
||||
|
||||
|
||||
def inline_all_toctrees(builder, docnameset, docname, tree, colorfunc, traversed):
|
||||
# type: (Builder, Set[str], str, nodes.document, Callable, List[str]) -> nodes.document
|
||||
def inline_all_toctrees(builder: "Builder", docnameset: Set[str], docname: str,
|
||||
tree: nodes.document, colorfunc: Callable, traversed: List[str]
|
||||
) -> nodes.document:
|
||||
"""Inline all toctrees in the *tree*.
|
||||
|
||||
Record all docnames in *docnameset*, and output docnames with *colorfunc*.
|
||||
@ -447,8 +431,8 @@ def inline_all_toctrees(builder, docnameset, docname, tree, colorfunc, traversed
|
||||
return tree
|
||||
|
||||
|
||||
def make_refnode(builder, fromdocname, todocname, targetid, child, title=None):
|
||||
# type: (Builder, str, str, str, nodes.Node, str) -> nodes.reference
|
||||
def make_refnode(builder: "Builder", fromdocname: str, todocname: str, targetid: str,
|
||||
child: Node, title: str = None) -> nodes.reference:
|
||||
"""Shortcut to create a reference node."""
|
||||
node = nodes.reference('', '', internal=True)
|
||||
if fromdocname == todocname and targetid:
|
||||
@ -465,19 +449,16 @@ def make_refnode(builder, fromdocname, todocname, targetid, child, title=None):
|
||||
return node
|
||||
|
||||
|
||||
def set_source_info(directive, node):
|
||||
# type: (Any, nodes.Node) -> None
|
||||
def set_source_info(directive: Directive, node: Node) -> None:
|
||||
node.source, node.line = \
|
||||
directive.state_machine.get_source_and_line(directive.lineno)
|
||||
|
||||
|
||||
def set_role_source_info(inliner, lineno, node):
|
||||
# type: (Inliner, int, nodes.Node) -> None
|
||||
def set_role_source_info(inliner: Inliner, lineno: int, node: Node) -> None:
|
||||
node.source, node.line = inliner.reporter.get_source_and_line(lineno) # type: ignore
|
||||
|
||||
|
||||
def copy_source_info(src, dst):
|
||||
# type: (nodes.Element, nodes.Element) -> None
|
||||
def copy_source_info(src: Element, dst: Element) -> None:
|
||||
dst.source = get_node_source(src)
|
||||
dst.line = get_node_line(src)
|
||||
|
||||
@ -493,8 +474,7 @@ NON_SMARTQUOTABLE_PARENT_NODES = (
|
||||
)
|
||||
|
||||
|
||||
def is_smartquotable(node):
|
||||
# type: (nodes.Node) -> bool
|
||||
def is_smartquotable(node: Node) -> bool:
|
||||
"""Check the node is smart-quotable or not."""
|
||||
if isinstance(node.parent, NON_SMARTQUOTABLE_PARENT_NODES):
|
||||
return False
|
||||
@ -506,8 +486,7 @@ def is_smartquotable(node):
|
||||
return True
|
||||
|
||||
|
||||
def process_only_nodes(document, tags):
|
||||
# type: (nodes.Node, Tags) -> None
|
||||
def process_only_nodes(document: Node, tags: "Tags") -> None:
|
||||
"""Filter ``only`` nodes which does not match *tags*."""
|
||||
for node in document.traverse(addnodes.only):
|
||||
try:
|
||||
@ -530,8 +509,7 @@ def process_only_nodes(document, tags):
|
||||
# monkey-patch Element.copy to copy the rawsource and line
|
||||
# for docutils-0.14 or older versions.
|
||||
|
||||
def _new_copy(self):
|
||||
# type: (nodes.Element) -> nodes.Element
|
||||
def _new_copy(self: Element) -> Element:
|
||||
newnode = self.__class__(self.rawsource, **self.attributes)
|
||||
if isinstance(self, nodes.Element):
|
||||
newnode.source = self.source
|
||||
|
@ -19,14 +19,11 @@ import time
|
||||
import warnings
|
||||
from io import StringIO
|
||||
from os import path
|
||||
from typing import Any, Generator, Iterator, List, Tuple, Type
|
||||
|
||||
from sphinx.deprecation import RemovedInSphinx30Warning, RemovedInSphinx40Warning
|
||||
from sphinx.testing.path import path as Path
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
from typing import Any, Iterator, List, Tuple # NOQA
|
||||
|
||||
# Errnos that we need.
|
||||
EEXIST = getattr(errno, 'EEXIST', 0) # RemovedInSphinx40Warning
|
||||
ENOENT = getattr(errno, 'ENOENT', 0) # RemovedInSphinx40Warning
|
||||
@ -41,19 +38,16 @@ EINVAL = getattr(errno, 'EINVAL', 0) # RemovedInSphinx40Warning
|
||||
SEP = "/"
|
||||
|
||||
|
||||
def os_path(canonicalpath):
|
||||
# type: (str) -> str
|
||||
def os_path(canonicalpath: str) -> str:
|
||||
return canonicalpath.replace(SEP, path.sep)
|
||||
|
||||
|
||||
def canon_path(nativepath):
|
||||
# type: (str) -> str
|
||||
def canon_path(nativepath: str) -> str:
|
||||
"""Return path in OS-independent form"""
|
||||
return nativepath.replace(path.sep, SEP)
|
||||
|
||||
|
||||
def relative_uri(base, to):
|
||||
# type: (str, str) -> str
|
||||
def relative_uri(base: str, to: str) -> str:
|
||||
"""Return a relative URL from ``base`` to ``to``."""
|
||||
if to.startswith(SEP):
|
||||
return to
|
||||
@ -76,22 +70,19 @@ def relative_uri(base, to):
|
||||
return ('..' + SEP) * (len(b2) - 1) + SEP.join(t2)
|
||||
|
||||
|
||||
def ensuredir(path):
|
||||
# type: (str) -> None
|
||||
def ensuredir(path: str) -> None:
|
||||
"""Ensure that a path exists."""
|
||||
os.makedirs(path, exist_ok=True)
|
||||
|
||||
|
||||
def walk(top, topdown=True, followlinks=False):
|
||||
# type: (str, bool, bool) -> Iterator[Tuple[str, List[str], List[str]]]
|
||||
def walk(top: str, topdown: bool = True, followlinks: bool = False) -> Iterator[Tuple[str, List[str], List[str]]]: # NOQA
|
||||
warnings.warn('sphinx.util.osutil.walk() is deprecated for removal. '
|
||||
'Please use os.walk() instead.',
|
||||
RemovedInSphinx40Warning)
|
||||
return os.walk(top, topdown=topdown, followlinks=followlinks)
|
||||
|
||||
|
||||
def mtimes_of_files(dirnames, suffix):
|
||||
# type: (List[str], str) -> Iterator[float]
|
||||
def mtimes_of_files(dirnames: List[str], suffix: str) -> Iterator[float]:
|
||||
for dirname in dirnames:
|
||||
for root, dirs, files in os.walk(dirname):
|
||||
for sfile in files:
|
||||
@ -102,8 +93,7 @@ def mtimes_of_files(dirnames, suffix):
|
||||
pass
|
||||
|
||||
|
||||
def movefile(source, dest):
|
||||
# type: (str, str) -> None
|
||||
def movefile(source: str, dest: str) -> None:
|
||||
"""Move a file, removing the destination if it exists."""
|
||||
if os.path.exists(dest):
|
||||
try:
|
||||
@ -113,16 +103,14 @@ def movefile(source, dest):
|
||||
os.rename(source, dest)
|
||||
|
||||
|
||||
def copytimes(source, dest):
|
||||
# type: (str, str) -> None
|
||||
def copytimes(source: str, dest: str) -> None:
|
||||
"""Copy a file's modification times."""
|
||||
st = os.stat(source)
|
||||
if hasattr(os, 'utime'):
|
||||
os.utime(dest, (st.st_atime, st.st_mtime))
|
||||
|
||||
|
||||
def copyfile(source, dest):
|
||||
# type: (str, str) -> None
|
||||
def copyfile(source: str, dest: str) -> None:
|
||||
"""Copy a file and its modification times, if possible.
|
||||
|
||||
Note: ``copyfile`` skips copying if the file has not been changed"""
|
||||
@ -139,18 +127,15 @@ no_fn_re = re.compile(r'[^a-zA-Z0-9_-]')
|
||||
project_suffix_re = re.compile(' Documentation$')
|
||||
|
||||
|
||||
def make_filename(string):
|
||||
# type: (str) -> str
|
||||
def make_filename(string: str) -> str:
|
||||
return no_fn_re.sub('', string) or 'sphinx'
|
||||
|
||||
|
||||
def make_filename_from_project(project):
|
||||
# type: (str) -> str
|
||||
def make_filename_from_project(project: str) -> str:
|
||||
return make_filename(project_suffix_re.sub('', project)).lower()
|
||||
|
||||
|
||||
def ustrftime(format, *args):
|
||||
# type: (str, Any) -> str
|
||||
def ustrftime(format: str, *args) -> str:
|
||||
"""[DEPRECATED] strftime for unicode strings."""
|
||||
warnings.warn('sphinx.util.osutil.ustrtime is deprecated for removal',
|
||||
RemovedInSphinx30Warning, stacklevel=2)
|
||||
@ -171,8 +156,7 @@ def ustrftime(format, *args):
|
||||
return r.encode().decode('unicode-escape')
|
||||
|
||||
|
||||
def relpath(path, start=os.curdir):
|
||||
# type: (str, str) -> str
|
||||
def relpath(path: str, start: str = os.curdir) -> str:
|
||||
"""Return a relative filepath to *path* either from the current directory or
|
||||
from an optional *start* directory.
|
||||
|
||||
@ -189,8 +173,7 @@ safe_relpath = relpath # for compatibility
|
||||
fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding()
|
||||
|
||||
|
||||
def abspath(pathdir):
|
||||
# type: (str) -> str
|
||||
def abspath(pathdir: str) -> str:
|
||||
if isinstance(pathdir, Path):
|
||||
return pathdir.abspath()
|
||||
else:
|
||||
@ -205,8 +188,7 @@ def abspath(pathdir):
|
||||
return pathdir
|
||||
|
||||
|
||||
def getcwd():
|
||||
# type: () -> str
|
||||
def getcwd() -> str:
|
||||
warnings.warn('sphinx.util.osutil.getcwd() is deprecated. '
|
||||
'Please use os.getcwd() instead.',
|
||||
RemovedInSphinx40Warning)
|
||||
@ -214,8 +196,7 @@ def getcwd():
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def cd(target_dir):
|
||||
# type: (str) -> Iterator[None]
|
||||
def cd(target_dir: str) -> Generator[None, None, None]:
|
||||
cwd = os.getcwd()
|
||||
try:
|
||||
os.chdir(target_dir)
|
||||
@ -236,19 +217,16 @@ class FileAvoidWrite:
|
||||
|
||||
Objects can be used as context managers.
|
||||
"""
|
||||
def __init__(self, path):
|
||||
# type: (str) -> None
|
||||
def __init__(self, path: str) -> None:
|
||||
self._path = path
|
||||
self._io = None # type: StringIO
|
||||
|
||||
def write(self, data):
|
||||
# type: (str) -> None
|
||||
def write(self, data: str) -> None:
|
||||
if not self._io:
|
||||
self._io = StringIO()
|
||||
self._io.write(data)
|
||||
|
||||
def close(self):
|
||||
# type: () -> None
|
||||
def close(self) -> None:
|
||||
"""Stop accepting writes and write file, if needed."""
|
||||
if not self._io:
|
||||
raise Exception('FileAvoidWrite does not support empty files.')
|
||||
@ -267,16 +245,14 @@ class FileAvoidWrite:
|
||||
with open(self._path, 'w') as f:
|
||||
f.write(buf)
|
||||
|
||||
def __enter__(self):
|
||||
# type: () -> FileAvoidWrite
|
||||
def __enter__(self) -> "FileAvoidWrite":
|
||||
return self
|
||||
|
||||
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.close()
|
||||
return True
|
||||
|
||||
def __getattr__(self, name):
|
||||
# type: (str) -> Any
|
||||
def __getattr__(self, name: str) -> Any:
|
||||
# Proxy to _io instance.
|
||||
if not self._io:
|
||||
raise Exception('Must write to FileAvoidWrite before other '
|
||||
@ -285,8 +261,7 @@ class FileAvoidWrite:
|
||||
return getattr(self._io, name)
|
||||
|
||||
|
||||
def rmtree(path):
|
||||
# type: (str) -> None
|
||||
def rmtree(path: str) -> None:
|
||||
if os.path.isdir(path):
|
||||
shutil.rmtree(path)
|
||||
else:
|
||||
|
@ -12,6 +12,7 @@ import os
|
||||
import time
|
||||
import traceback
|
||||
from math import sqrt
|
||||
from typing import Any, Callable, Dict, List, Sequence
|
||||
|
||||
try:
|
||||
import multiprocessing
|
||||
@ -21,10 +22,6 @@ except ImportError:
|
||||
from sphinx.errors import SphinxParallelError
|
||||
from sphinx.util import logging
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
from typing import Any, Callable, Dict, List, Sequence # NOQA
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@ -35,12 +32,10 @@ parallel_available = multiprocessing and (os.name == 'posix')
|
||||
class SerialTasks:
|
||||
"""Has the same interface as ParallelTasks, but executes tasks directly."""
|
||||
|
||||
def __init__(self, nproc=1):
|
||||
# type: (int) -> None
|
||||
def __init__(self, nproc: int = 1) -> None:
|
||||
pass
|
||||
|
||||
def add_task(self, task_func, arg=None, result_func=None):
|
||||
# type: (Callable, Any, Callable) -> None
|
||||
def add_task(self, task_func: Callable, arg: Any = None, result_func: Callable = None) -> None: # NOQA
|
||||
if arg is not None:
|
||||
res = task_func(arg)
|
||||
else:
|
||||
@ -48,16 +43,14 @@ class SerialTasks:
|
||||
if result_func:
|
||||
result_func(res)
|
||||
|
||||
def join(self):
|
||||
# type: () -> None
|
||||
def join(self) -> None:
|
||||
pass
|
||||
|
||||
|
||||
class ParallelTasks:
|
||||
"""Executes *nproc* tasks in parallel after forking."""
|
||||
|
||||
def __init__(self, nproc):
|
||||
# type: (int) -> None
|
||||
def __init__(self, nproc: int) -> None:
|
||||
self.nproc = nproc
|
||||
# (optional) function performed by each task on the result of main task
|
||||
self._result_funcs = {} # type: Dict[int, Callable]
|
||||
@ -74,8 +67,7 @@ class ParallelTasks:
|
||||
# task number of each subprocess
|
||||
self._taskid = 0
|
||||
|
||||
def _process(self, pipe, func, arg):
|
||||
# type: (Any, Callable, Any) -> None
|
||||
def _process(self, pipe: Any, func: Callable, arg: Any) -> None:
|
||||
try:
|
||||
collector = logging.LogCollector()
|
||||
with collector.collect():
|
||||
@ -91,8 +83,7 @@ class ParallelTasks:
|
||||
logging.convert_serializable(collector.logs)
|
||||
pipe.send((failed, collector.logs, ret))
|
||||
|
||||
def add_task(self, task_func, arg=None, result_func=None):
|
||||
# type: (Callable, Any, Callable) -> None
|
||||
def add_task(self, task_func: Callable, arg: Any = None, result_func: Callable = None) -> None: # NOQA
|
||||
tid = self._taskid
|
||||
self._taskid += 1
|
||||
self._result_funcs[tid] = result_func or (lambda arg, result: None)
|
||||
@ -104,13 +95,11 @@ class ParallelTasks:
|
||||
self._precvsWaiting[tid] = precv
|
||||
self._join_one()
|
||||
|
||||
def join(self):
|
||||
# type: () -> None
|
||||
def join(self) -> None:
|
||||
while self._pworking:
|
||||
self._join_one()
|
||||
|
||||
def _join_one(self):
|
||||
# type: () -> None
|
||||
def _join_one(self) -> None:
|
||||
for tid, pipe in self._precvs.items():
|
||||
if pipe.poll():
|
||||
exc, logs, result = pipe.recv()
|
||||
@ -132,8 +121,7 @@ class ParallelTasks:
|
||||
self._pworking += 1
|
||||
|
||||
|
||||
def make_chunks(arguments, nproc, maxbatch=10):
|
||||
# type: (Sequence[str], int, int) -> List[Any]
|
||||
def make_chunks(arguments: Sequence[str], nproc: int, maxbatch: int = 10) -> List[Any]:
|
||||
# determine how many documents to read in one go
|
||||
nargs = len(arguments)
|
||||
chunksize = nargs // nproc
|
||||
|
@ -10,11 +10,14 @@
|
||||
|
||||
import warnings
|
||||
from contextlib import contextmanager
|
||||
from typing import Generator, Union
|
||||
from urllib.parse import urlsplit
|
||||
|
||||
import pkg_resources
|
||||
import requests
|
||||
|
||||
from sphinx.config import Config
|
||||
|
||||
try:
|
||||
from requests.packages.urllib3.exceptions import SSLError
|
||||
except ImportError:
|
||||
@ -54,17 +57,12 @@ else:
|
||||
pkg_resources.VersionConflict):
|
||||
pass # ignored
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
from typing import Any, Generator, Union # NOQA
|
||||
from sphinx.config import Config # NOQA
|
||||
|
||||
useragent_header = [('User-Agent',
|
||||
'Mozilla/5.0 (X11; Linux x86_64; rv:25.0) Gecko/20100101 Firefox/25.0')]
|
||||
|
||||
|
||||
def is_ssl_error(exc):
|
||||
# type: (Exception) -> bool
|
||||
def is_ssl_error(exc: Exception) -> bool:
|
||||
"""Check an exception is SSLError."""
|
||||
if isinstance(exc, SSLError):
|
||||
return True
|
||||
@ -77,8 +75,7 @@ def is_ssl_error(exc):
|
||||
|
||||
|
||||
@contextmanager
|
||||
def ignore_insecure_warning(**kwargs):
|
||||
# type: (Any) -> Generator
|
||||
def ignore_insecure_warning(**kwargs) -> Generator[None, None, None]:
|
||||
with warnings.catch_warnings():
|
||||
if not kwargs.get('verify') and InsecureRequestWarning:
|
||||
# ignore InsecureRequestWarning if verify=False
|
||||
@ -86,8 +83,7 @@ def ignore_insecure_warning(**kwargs):
|
||||
yield
|
||||
|
||||
|
||||
def _get_tls_cacert(url, config):
|
||||
# type: (str, Config) -> Union[str, bool]
|
||||
def _get_tls_cacert(url: str, config: Config) -> Union[str, bool]:
|
||||
"""Get additional CA cert for a specific URL.
|
||||
|
||||
This also returns ``False`` if verification is disabled.
|
||||
@ -109,8 +105,7 @@ def _get_tls_cacert(url, config):
|
||||
return certs.get(hostname, True)
|
||||
|
||||
|
||||
def get(url, **kwargs):
|
||||
# type: (str, Any) -> requests.Response
|
||||
def get(url: str, **kwargs) -> requests.Response:
|
||||
"""Sends a GET request like requests.get().
|
||||
|
||||
This sets up User-Agent header and TLS verification automatically."""
|
||||
@ -123,8 +118,7 @@ def get(url, **kwargs):
|
||||
return requests.get(url, **kwargs)
|
||||
|
||||
|
||||
def head(url, **kwargs):
|
||||
# type: (str, Any) -> requests.Response
|
||||
def head(url: str, **kwargs) -> requests.Response:
|
||||
"""Sends a HEAD request like requests.head().
|
||||
|
||||
This sets up User-Agent header and TLS verification automatically."""
|
||||
|
@ -11,23 +11,20 @@
|
||||
import re
|
||||
from collections import defaultdict
|
||||
from contextlib import contextmanager
|
||||
from typing import Dict, Generator
|
||||
from unicodedata import east_asian_width
|
||||
|
||||
from docutils.parsers.rst import roles
|
||||
from docutils.parsers.rst.languages import en as english
|
||||
from docutils.statemachine import StringList
|
||||
from docutils.utils import Reporter
|
||||
from jinja2 import Environment
|
||||
from jinja2 import environmentfilter
|
||||
|
||||
from sphinx.locale import __
|
||||
from sphinx.util import docutils
|
||||
from sphinx.util import logging
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
from typing import Callable, Dict, Generator # NOQA
|
||||
from docutils.statemachine import StringList # NOQA
|
||||
from jinja2 import Environment # NOQA
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
docinfo_re = re.compile(':\\w+:.*?')
|
||||
@ -40,18 +37,15 @@ WIDECHARS = defaultdict(lambda: "WF") # type: Dict[str, str]
|
||||
WIDECHARS["ja"] = "WFA" # In Japanese, Ambiguous characters also have double width
|
||||
|
||||
|
||||
def escape(text):
|
||||
# type: (str) -> str
|
||||
def escape(text: str) -> str:
|
||||
text = symbols_re.sub(r'\\\1', text)
|
||||
text = re.sub(r'^\.', r'\.', text) # escape a dot at top
|
||||
return text
|
||||
|
||||
|
||||
def textwidth(text, widechars='WF'):
|
||||
# type: (str, str) -> int
|
||||
def textwidth(text: str, widechars: str = 'WF') -> int:
|
||||
"""Get width of text."""
|
||||
def charwidth(char, widechars):
|
||||
# type: (str, str) -> int
|
||||
def charwidth(char: str, widechars: str) -> int:
|
||||
if east_asian_width(char) in widechars:
|
||||
return 2
|
||||
else:
|
||||
@ -61,8 +55,7 @@ def textwidth(text, widechars='WF'):
|
||||
|
||||
|
||||
@environmentfilter
|
||||
def heading(env, text, level=1):
|
||||
# type: (Environment, str, int) -> str
|
||||
def heading(env: Environment, text: str, level: int = 1) -> str:
|
||||
"""Create a heading for *level*."""
|
||||
assert level <= 3
|
||||
width = textwidth(text, WIDECHARS[env.language]) # type: ignore
|
||||
@ -71,8 +64,7 @@ def heading(env, text, level=1):
|
||||
|
||||
|
||||
@contextmanager
|
||||
def default_role(docname, name):
|
||||
# type: (str, str) -> Generator
|
||||
def default_role(docname: str, name: str) -> Generator[None, None, None]:
|
||||
if name:
|
||||
dummy_reporter = Reporter('', 4, 4)
|
||||
role_fn, _ = roles.role(name, english, 0, dummy_reporter)
|
||||
@ -86,8 +78,7 @@ def default_role(docname, name):
|
||||
docutils.unregister_role('')
|
||||
|
||||
|
||||
def prepend_prolog(content, prolog):
|
||||
# type: (StringList, str) -> None
|
||||
def prepend_prolog(content: StringList, prolog: str) -> None:
|
||||
"""Prepend a string to content body as prolog."""
|
||||
if prolog:
|
||||
pos = 0
|
||||
@ -109,8 +100,7 @@ def prepend_prolog(content, prolog):
|
||||
content.insert(pos + lineno + 1, '', '<generated>', 0)
|
||||
|
||||
|
||||
def append_epilog(content, epilog):
|
||||
# type: (StringList, str) -> None
|
||||
def append_epilog(content: StringList, epilog: str) -> None:
|
||||
"""Append a string to content body as epilog."""
|
||||
if epilog:
|
||||
content.append('', '<generated>', 0)
|
||||
|
@ -26,14 +26,12 @@
|
||||
"""
|
||||
|
||||
import re
|
||||
from typing import Generator, Iterable, Tuple
|
||||
|
||||
from docutils.utils import smartquotes
|
||||
|
||||
from sphinx.util.docutils import __version_info__ as docutils_version
|
||||
|
||||
if False: # For type annotation
|
||||
from typing import Generator, Iterable, Tuple # NOQA
|
||||
|
||||
|
||||
langquotes = {'af': '“”‘’',
|
||||
'af-x-altquot': '„”‚’',
|
||||
@ -125,8 +123,7 @@ langquotes = {'af': '“”‘’',
|
||||
}
|
||||
|
||||
|
||||
def educateQuotes(text, language='en'):
|
||||
# type: (str, str) -> str
|
||||
def educateQuotes(text: str, language: str = 'en') -> str:
|
||||
"""
|
||||
Parameter: - text string (unicode or bytes).
|
||||
- language (`BCP 47` language tag.)
|
||||
@ -240,8 +237,10 @@ def educateQuotes(text, language='en'):
|
||||
return text
|
||||
|
||||
|
||||
def educate_tokens(text_tokens, attr=smartquotes.default_smartypants_attr, language='en'):
|
||||
# type: (Iterable[Tuple[str, str]], str, str) -> Generator[str, None, None]
|
||||
def educate_tokens(text_tokens: Iterable[Tuple[str, str]],
|
||||
attr: str = smartquotes.default_smartypants_attr,
|
||||
language: str = 'en'
|
||||
) -> Generator[str, None, None]:
|
||||
"""Return iterator that "educates" the items of `text_tokens`.
|
||||
|
||||
This is modified to intercept the ``attr='2'`` as it was used by the
|
||||
|
@ -18,18 +18,15 @@ except ImportError:
|
||||
|
||||
|
||||
class BaseStemmer:
|
||||
def stem(self, word):
|
||||
# type: (str) -> str
|
||||
def stem(self, word: str) -> str:
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class PyStemmer(BaseStemmer):
|
||||
def __init__(self):
|
||||
# type: () -> None
|
||||
def __init__(self) -> None:
|
||||
self.stemmer = _PyStemmer('porter')
|
||||
|
||||
def stem(self, word):
|
||||
# type: (str) -> str
|
||||
def stem(self, word: str) -> str:
|
||||
return self.stemmer.stemWord(word)
|
||||
|
||||
|
||||
@ -37,13 +34,11 @@ class StandardStemmer(PorterStemmer, BaseStemmer): # type: ignore
|
||||
"""All those porter stemmer implementations look hideous;
|
||||
make at least the stem method nicer.
|
||||
"""
|
||||
def stem(self, word): # type: ignore
|
||||
# type: (str) -> str
|
||||
def stem(self, word: str) -> str: # type: ignore
|
||||
return super().stem(word, 0, len(word) - 1)
|
||||
|
||||
|
||||
def get_stemmer():
|
||||
# type: () -> BaseStemmer
|
||||
def get_stemmer() -> BaseStemmer:
|
||||
if PYSTEMMER:
|
||||
return PyStemmer()
|
||||
else:
|
||||
|
@ -30,8 +30,7 @@
|
||||
|
||||
class PorterStemmer:
|
||||
|
||||
def __init__(self):
|
||||
# type: () -> None
|
||||
def __init__(self) -> None:
|
||||
"""The main part of the stemming algorithm starts here.
|
||||
b is a buffer holding a word to be stemmed. The letters are in b[k0],
|
||||
b[k0+1] ... ending at b[k]. In fact k0 = 0 in this demo program. k is
|
||||
@ -47,8 +46,7 @@ class PorterStemmer:
|
||||
self.k0 = 0
|
||||
self.j = 0 # j is a general offset into the string
|
||||
|
||||
def cons(self, i):
|
||||
# type: (int) -> int
|
||||
def cons(self, i: int) -> int:
|
||||
"""cons(i) is TRUE <=> b[i] is a consonant."""
|
||||
if self.b[i] == 'a' or self.b[i] == 'e' or self.b[i] == 'i' \
|
||||
or self.b[i] == 'o' or self.b[i] == 'u':
|
||||
@ -60,8 +58,7 @@ class PorterStemmer:
|
||||
return (not self.cons(i - 1))
|
||||
return 1
|
||||
|
||||
def m(self):
|
||||
# type: () -> int
|
||||
def m(self) -> int:
|
||||
"""m() measures the number of consonant sequences between k0 and j.
|
||||
if c is a consonant sequence and v a vowel sequence, and <..>
|
||||
indicates arbitrary presence,
|
||||
@ -98,16 +95,14 @@ class PorterStemmer:
|
||||
i = i + 1
|
||||
i = i + 1
|
||||
|
||||
def vowelinstem(self):
|
||||
# type: () -> int
|
||||
def vowelinstem(self) -> int:
|
||||
"""vowelinstem() is TRUE <=> k0,...j contains a vowel"""
|
||||
for i in range(self.k0, self.j + 1):
|
||||
if not self.cons(i):
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def doublec(self, j):
|
||||
# type: (int) -> int
|
||||
def doublec(self, j: int) -> int:
|
||||
"""doublec(j) is TRUE <=> j,(j-1) contain a double consonant."""
|
||||
if j < (self.k0 + 1):
|
||||
return 0
|
||||
@ -115,8 +110,7 @@ class PorterStemmer:
|
||||
return 0
|
||||
return self.cons(j)
|
||||
|
||||
def cvc(self, i):
|
||||
# type: (int) -> int
|
||||
def cvc(self, i: int) -> int:
|
||||
"""cvc(i) is TRUE <=> i-2,i-1,i has the form
|
||||
consonant - vowel - consonant
|
||||
and also if the second c is not w,x or y. this is used when trying to
|
||||
@ -133,8 +127,7 @@ class PorterStemmer:
|
||||
return 0
|
||||
return 1
|
||||
|
||||
def ends(self, s):
|
||||
# type: (str) -> int
|
||||
def ends(self, s: str) -> int:
|
||||
"""ends(s) is TRUE <=> k0,...k ends with the string s."""
|
||||
length = len(s)
|
||||
if s[length - 1] != self.b[self.k]: # tiny speed-up
|
||||
@ -146,22 +139,19 @@ class PorterStemmer:
|
||||
self.j = self.k - length
|
||||
return 1
|
||||
|
||||
def setto(self, s):
|
||||
# type: (str) -> None
|
||||
def setto(self, s: str) -> None:
|
||||
"""setto(s) sets (j+1),...k to the characters in the string s,
|
||||
readjusting k."""
|
||||
length = len(s)
|
||||
self.b = self.b[:self.j + 1] + s + self.b[self.j + length + 1:]
|
||||
self.k = self.j + length
|
||||
|
||||
def r(self, s):
|
||||
# type: (str) -> None
|
||||
def r(self, s: str) -> None:
|
||||
"""r(s) is used further down."""
|
||||
if self.m() > 0:
|
||||
self.setto(s)
|
||||
|
||||
def step1ab(self):
|
||||
# type: () -> None
|
||||
def step1ab(self) -> None:
|
||||
"""step1ab() gets rid of plurals and -ed or -ing. e.g.
|
||||
|
||||
caresses -> caress
|
||||
@ -208,15 +198,13 @@ class PorterStemmer:
|
||||
elif (self.m() == 1 and self.cvc(self.k)):
|
||||
self.setto("e")
|
||||
|
||||
def step1c(self):
|
||||
# type: () -> None
|
||||
def step1c(self) -> None:
|
||||
"""step1c() turns terminal y to i when there is another vowel in
|
||||
the stem."""
|
||||
if (self.ends("y") and self.vowelinstem()):
|
||||
self.b = self.b[:self.k] + 'i' + self.b[self.k + 1:]
|
||||
|
||||
def step2(self):
|
||||
# type: () -> None
|
||||
def step2(self) -> None:
|
||||
"""step2() maps double suffices to single ones.
|
||||
so -ization ( = -ize plus -ation) maps to -ize etc. note that the
|
||||
string before the suffix must give m() > 0.
|
||||
@ -275,8 +263,7 @@ class PorterStemmer:
|
||||
self.r("log")
|
||||
# To match the published algorithm, delete this phrase
|
||||
|
||||
def step3(self):
|
||||
# type: () -> None
|
||||
def step3(self) -> None:
|
||||
"""step3() dels with -ic-, -full, -ness etc. similar strategy
|
||||
to step2."""
|
||||
if self.b[self.k] == 'e':
|
||||
@ -298,8 +285,7 @@ class PorterStemmer:
|
||||
if self.ends("ness"):
|
||||
self.r("")
|
||||
|
||||
def step4(self):
|
||||
# type: () -> None
|
||||
def step4(self) -> None:
|
||||
"""step4() takes off -ant, -ence etc., in context <c>vcvc<v>."""
|
||||
if self.b[self.k - 1] == 'a':
|
||||
if self.ends("al"):
|
||||
@ -382,8 +368,7 @@ class PorterStemmer:
|
||||
if self.m() > 1:
|
||||
self.k = self.j
|
||||
|
||||
def step5(self):
|
||||
# type: () -> None
|
||||
def step5(self) -> None:
|
||||
"""step5() removes a final -e if m() > 1, and changes -ll to -l if
|
||||
m() > 1.
|
||||
"""
|
||||
@ -395,8 +380,7 @@ class PorterStemmer:
|
||||
if self.b[self.k] == 'l' and self.doublec(self.k) and self.m() > 1:
|
||||
self.k = self.k - 1
|
||||
|
||||
def stem(self, p, i, j):
|
||||
# type: (str, int, int) -> str
|
||||
def stem(self, p: str, i: int, j: int) -> str:
|
||||
"""In stem(p,i,j), p is a char pointer, and the string to be stemmed
|
||||
is from p[i] to p[j] inclusive. Typically i is zero and j is the
|
||||
offset to the last character of a string, (p[j+1] == '\0'). The
|
||||
|
@ -9,7 +9,9 @@
|
||||
"""
|
||||
|
||||
import os
|
||||
from typing import Dict
|
||||
|
||||
from jinja2.loaders import BaseLoader
|
||||
from jinja2.sandbox import SandboxedEnvironment
|
||||
|
||||
from sphinx import package_dir
|
||||
@ -17,58 +19,45 @@ from sphinx.jinja2glue import SphinxFileSystemLoader
|
||||
from sphinx.locale import get_translator
|
||||
from sphinx.util import rst, texescape
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
from typing import Dict # NOQA
|
||||
from jinja2.loaders import BaseLoader # NOQA
|
||||
|
||||
|
||||
class BaseRenderer:
|
||||
def __init__(self, loader=None):
|
||||
# type: (BaseLoader) -> None
|
||||
def __init__(self, loader: BaseLoader = None) -> None:
|
||||
self.env = SandboxedEnvironment(loader=loader, extensions=['jinja2.ext.i18n'])
|
||||
self.env.filters['repr'] = repr
|
||||
self.env.install_gettext_translations(get_translator()) # type: ignore
|
||||
|
||||
def render(self, template_name, context):
|
||||
# type: (str, Dict) -> str
|
||||
def render(self, template_name: str, context: Dict) -> str:
|
||||
return self.env.get_template(template_name).render(context)
|
||||
|
||||
def render_string(self, source, context):
|
||||
# type: (str, Dict) -> str
|
||||
def render_string(self, source: str, context: Dict) -> str:
|
||||
return self.env.from_string(source).render(context)
|
||||
|
||||
|
||||
class FileRenderer(BaseRenderer):
|
||||
def __init__(self, search_path):
|
||||
# type: (str) -> None
|
||||
def __init__(self, search_path: str) -> None:
|
||||
loader = SphinxFileSystemLoader(search_path)
|
||||
super().__init__(loader)
|
||||
|
||||
@classmethod
|
||||
def render_from_file(cls, filename, context):
|
||||
# type: (str, Dict) -> str
|
||||
def render_from_file(cls, filename: str, context: Dict) -> str:
|
||||
dirname = os.path.dirname(filename)
|
||||
basename = os.path.basename(filename)
|
||||
return cls(dirname).render(basename, context)
|
||||
|
||||
|
||||
class SphinxRenderer(FileRenderer):
|
||||
def __init__(self, template_path=None):
|
||||
# type: (str) -> None
|
||||
def __init__(self, template_path: str = None) -> None:
|
||||
if template_path is None:
|
||||
template_path = os.path.join(package_dir, 'templates')
|
||||
super().__init__(template_path)
|
||||
|
||||
@classmethod
|
||||
def render_from_file(cls, filename, context):
|
||||
# type: (str, Dict) -> str
|
||||
def render_from_file(cls, filename: str, context: Dict) -> str:
|
||||
return FileRenderer.render_from_file(filename, context)
|
||||
|
||||
|
||||
class LaTeXRenderer(SphinxRenderer):
|
||||
def __init__(self, template_path=None):
|
||||
# type: (str) -> None
|
||||
def __init__(self, template_path: str = None) -> None:
|
||||
if template_path is None:
|
||||
template_path = os.path.join(package_dir, 'templates', 'latex')
|
||||
super().__init__(template_path)
|
||||
@ -87,8 +76,7 @@ class LaTeXRenderer(SphinxRenderer):
|
||||
|
||||
|
||||
class ReSTRenderer(SphinxRenderer):
|
||||
def __init__(self, template_path=None, language=None):
|
||||
# type: (str, str) -> None
|
||||
def __init__(self, template_path: str = None, language: str = None) -> None:
|
||||
super().__init__(template_path)
|
||||
|
||||
# add language to environment
|
||||
|
Loading…
Reference in New Issue
Block a user