From 1124052f9204fb4a8b867687bc9efc5fb2f3f8f5 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 25 Dec 2019 01:30:15 +0900 Subject: [PATCH 1/4] Migrate to py3 style type annotation: sphinx.highlighting --- sphinx/highlighting.py | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/sphinx/highlighting.py b/sphinx/highlighting.py index 4a92d82b7..694a41d61 100644 --- a/sphinx/highlighting.py +++ b/sphinx/highlighting.py @@ -12,14 +12,17 @@ import html import warnings 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 @@ -29,12 +32,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__) @@ -67,9 +64,8 @@ class PygmentsBridge: html_formatter = HtmlFormatter latex_formatter = LatexFormatter - def __init__(self, dest='html', stylename='sphinx', trim_doctest_flags=None, - latex_engine=None): - # type: (str, str, bool, str) -> None + def __init__(self, dest: str = 'html', stylename: str = 'sphinx', + trim_doctest_flags: bool = None, latex_engine: str = None) -> None: self.dest = dest self.latex_engine = latex_engine @@ -86,8 +82,7 @@ class PygmentsBridge: warnings.warn('trim_doctest_flags option for PygmentsBridge is now deprecated.', RemovedInSphinx30Warning, stacklevel=2) - 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': @@ -98,13 +93,11 @@ 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 unhighlighted(self, source): - # type: (str) -> str + def unhighlighted(self, source: str) -> str: warnings.warn('PygmentsBridge.unhighlighted() is now deprecated.', RemovedInSphinx30Warning, stacklevel=2) if self.dest == 'html': @@ -117,8 +110,8 @@ class PygmentsBridge: return '\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n' + \ source + '\\end{Verbatim}\n' - 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 = {} @@ -161,8 +154,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() @@ -196,8 +189,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') From 1734844e7c0e08475da28c41f13e9d581cb6e3e3 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 25 Dec 2019 02:26:32 +0900 Subject: [PATCH 2/4] Migrate to py3 style type annotation: sphinx.jinja2glue --- sphinx/jinja2glue.py | 60 ++++++++++++++++---------------------------- 1 file changed, 21 insertions(+), 39 deletions(-) diff --git a/sphinx/jinja2glue.py b/sphinx/jinja2glue.py index 1a356c021..060b9f1bd 100644 --- a/sphinx/jinja2glue.py +++ b/sphinx/jinja2glue.py @@ -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('!'): From 562fc581d1f6297d09e4cb9f0c9e1ee61e29d29d Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 25 Dec 2019 02:26:30 +0900 Subject: [PATCH 3/4] Migrate to py3 style type annotation: sphinx.roles --- sphinx/roles.py | 117 ++++++++++++++++++++++-------------------------- 1 file changed, 54 insertions(+), 63 deletions(-) diff --git a/sphinx/roles.py b/sphinx/roles.py index 381b9b6f0..5757183ce 100644 --- a/sphinx/roles.py +++ b/sphinx/roles.py @@ -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'(? 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'(? 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(): From fb09f3463b38c1e5c80aa7eace515b52a25b2a78 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 25 Dec 2019 02:26:26 +0900 Subject: [PATCH 4/4] Migrate to py3 style type annotation: sphinx.theming --- sphinx/theming.py | 57 +++++++++++++++++------------------------------ 1 file changed, 21 insertions(+), 36 deletions(-) diff --git a/sphinx/theming.py b/sphinx/theming.py index 669e71dde..bbbac5488 100644 --- a/sphinx/theming.py +++ b/sphinx/theming.py @@ -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)