summaryrefslogtreecommitdiff
path: root/sphinx/builders/html.py
diff options
context:
space:
mode:
Diffstat (limited to 'sphinx/builders/html.py')
-rw-r--r--sphinx/builders/html.py521
1 files changed, 230 insertions, 291 deletions
diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py
index 1212229fb..e395144aa 100644
--- a/sphinx/builders/html.py
+++ b/sphinx/builders/html.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
"""
sphinx.builders.html
~~~~~~~~~~~~~~~~~~~~
@@ -9,7 +8,8 @@
:license: BSD, see LICENSE for details.
"""
-import codecs
+import html
+import pickle
import posixpath
import re
import sys
@@ -25,14 +25,11 @@ from docutils.frontend import OptionParser
from docutils.io import DocTreeInput, StringOutput
from docutils.readers.doctree import Reader as DoctreeReader
from docutils.utils import relative_path
-from six import iteritems, text_type, string_types
-from six.moves import cPickle as pickle
from sphinx import package_dir, __display_version__
from sphinx.application import ENV_PICKLE_FILENAME
from sphinx.builders import Builder
-from sphinx.config import string_classes
-from sphinx.deprecation import RemovedInSphinx20Warning, RemovedInSphinx30Warning
+from sphinx.deprecation import RemovedInSphinx30Warning, RemovedInSphinx40Warning
from sphinx.environment.adapters.asset import ImageAdapter
from sphinx.environment.adapters.indexentries import IndexEntries
from sphinx.environment.adapters.toctree import TocTree
@@ -51,15 +48,14 @@ from sphinx.util.matching import patmatch, Matcher, DOTFILES
from sphinx.util.nodes import inline_all_toctrees
from sphinx.util.osutil import SEP, os_path, relative_uri, ensuredir, \
movefile, copyfile
-from sphinx.util.pycompat import htmlescape
from sphinx.writers.html import HTMLWriter, HTMLTranslator
if False:
# For type annotation
- from typing import Any, Dict, IO, Iterable, Iterator, List, Type, Tuple, Union # NOQA
+ from typing import Any, Dict, IO, Iterable, Iterator, List, Set, Type, Tuple, Union # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.config import Config # NOQA
- from sphinx.domains import Domain, Index # NOQA
+ from sphinx.domains import Domain, Index, IndexEntry # NOQA
from sphinx.util.tags import Tags # NOQA
# Experimental HTML5 Writer
@@ -79,7 +75,7 @@ return_codes_re = re.compile('[\r\n]+')
def get_stable_hash(obj):
- # type: (Any) -> unicode
+ # type: (Any) -> str
"""
Return a stable hash for a Python data structure. We can't just use
the md5 of str(obj) since for example dictionary items are enumerated
@@ -89,69 +85,22 @@ def get_stable_hash(obj):
return get_stable_hash(list(obj.items()))
elif isinstance(obj, (list, tuple)):
obj = sorted(get_stable_hash(o) for o in obj)
- return md5(text_type(obj).encode('utf8')).hexdigest()
+ return md5(str(obj).encode()).hexdigest()
-class CSSContainer(list):
- """The container for stylesheets.
-
- To support the extensions which access the container directly, this wraps
- the entry with Stylesheet class.
- """
- def append(self, obj):
- # type: (Union[unicode, Stylesheet]) -> None
- if isinstance(obj, Stylesheet):
- super(CSSContainer, self).append(obj)
- else:
- super(CSSContainer, self).append(Stylesheet(obj))
-
- def insert(self, index, obj):
- # type: (int, Union[unicode, Stylesheet]) -> None
- warnings.warn('builder.css_files is deprecated. '
- 'Please use app.add_stylesheet() instead.',
- RemovedInSphinx20Warning, stacklevel=2)
- if isinstance(obj, Stylesheet):
- super(CSSContainer, self).insert(index, obj)
- else:
- super(CSSContainer, self).insert(index, Stylesheet(obj))
-
- def extend(self, other): # type: ignore
- # type: (List[Union[unicode, Stylesheet]]) -> None
- warnings.warn('builder.css_files is deprecated. '
- 'Please use app.add_stylesheet() instead.',
- RemovedInSphinx20Warning, stacklevel=2)
- for item in other:
- self.append(item)
-
- def __iadd__(self, other): # type: ignore
- # type: (List[Union[unicode, Stylesheet]]) -> CSSContainer
- warnings.warn('builder.css_files is deprecated. '
- 'Please use app.add_stylesheet() instead.',
- RemovedInSphinx20Warning, stacklevel=2)
- for item in other:
- self.append(item)
- return self
-
- def __add__(self, other):
- # type: (List[Union[unicode, Stylesheet]]) -> CSSContainer
- ret = CSSContainer(self)
- ret += other
- return ret
-
-
-class Stylesheet(text_type):
+class Stylesheet(str):
"""A metadata of stylesheet.
To keep compatibility with old themes, an instance of stylesheet behaves as
its filename (str).
"""
- attributes = None # type: Dict[unicode, unicode]
- filename = None # type: unicode
+ attributes = None # type: Dict[str, str]
+ filename = None # type: str
def __new__(cls, filename, *args, **attributes):
- # type: (unicode, unicode, unicode) -> None
- self = text_type.__new__(cls, filename) # type: ignore
+ # type: (str, str, str) -> None
+ self = str.__new__(cls, filename) # type: ignore
self.filename = filename
self.attributes = attributes
self.attributes.setdefault('rel', 'stylesheet')
@@ -166,14 +115,14 @@ class Stylesheet(text_type):
class JSContainer(list):
"""The container for JavaScript scripts."""
def insert(self, index, obj):
- # type: (int, unicode) -> None
+ # type: (int, str) -> None
warnings.warn('builder.script_files is deprecated. '
'Please use app.add_js_file() instead.',
RemovedInSphinx30Warning, stacklevel=2)
- super(JSContainer, self).insert(index, obj)
+ super().insert(index, obj)
def extend(self, other): # type: ignore
- # type: (List[unicode]) -> None
+ # type: (List[str]) -> None
warnings.warn('builder.script_files is deprecated. '
'Please use app.add_js_file() instead.',
RemovedInSphinx30Warning, stacklevel=2)
@@ -181,7 +130,7 @@ class JSContainer(list):
self.append(item)
def __iadd__(self, other): # type: ignore
- # type: (List[unicode]) -> JSContainer
+ # type: (List[str]) -> JSContainer
warnings.warn('builder.script_files is deprecated. '
'Please use app.add_js_file() instead.',
RemovedInSphinx30Warning, stacklevel=2)
@@ -190,25 +139,25 @@ class JSContainer(list):
return self
def __add__(self, other):
- # type: (List[unicode]) -> JSContainer
+ # type: (List[str]) -> JSContainer
ret = JSContainer(self)
ret += other
return ret
-class JavaScript(text_type):
+class JavaScript(str):
"""A metadata of javascript file.
To keep compatibility with old themes, an instance of javascript behaves as
its filename (str).
"""
- attributes = None # type: Dict[unicode, unicode]
- filename = None # type: unicode
+ attributes = None # type: Dict[str, str]
+ filename = None # type: str
def __new__(cls, filename, **attributes):
- # type: (unicode, **unicode) -> None
- self = text_type.__new__(cls, filename) # type: ignore
+ # type: (str, **str) -> None
+ self = str.__new__(cls, filename) # type: ignore
self.filename = filename
self.attributes = attributes
self.attributes.setdefault('type', 'text/javascript')
@@ -216,7 +165,7 @@ class JavaScript(text_type):
return self
-class BuildInfo(object):
+class BuildInfo:
"""buildinfo file manipulator.
HTMLBuilder and its family are storing their own envdata to ``.buildinfo``.
@@ -240,9 +189,9 @@ class BuildInfo(object):
raise ValueError(__('build info file is broken: %r') % exc)
def __init__(self, config=None, tags=None, config_categories=[]):
- # type: (Config, Tags, List[unicode]) -> None
- self.config_hash = u''
- self.tags_hash = u''
+ # type: (Config, Tags, List[str]) -> None
+ self.config_hash = ''
+ self.tags_hash = ''
if config:
values = dict((c.name, c.value) for c in config.filter(config_categories))
@@ -256,10 +205,6 @@ class BuildInfo(object):
return (self.config_hash == other.config_hash and
self.tags_hash == other.tags_hash)
- def __ne__(self, other): # type: ignore
- # type: (BuildInfo) -> bool
- return not (self == other) # for py27
-
def dump(self, f):
# type: (IO) -> None
f.write('# Sphinx build info version 1\n'
@@ -300,18 +245,18 @@ class StandaloneHTMLBuilder(Builder):
# use html5 translator by default
default_html5_translator = False
- imgpath = None # type: unicode
- domain_indices = [] # type: List[Tuple[unicode, Type[Index], List[Tuple[unicode, List[List[Union[unicode, int]]]]], bool]] # NOQA
+ imgpath = None # type: str
+ domain_indices = [] # type: List[Tuple[str, Type[Index], List[Tuple[str, List[IndexEntry]]], bool]] # NOQA
# cached publisher object for snippets
_publisher = None
def __init__(self, app):
# type: (Sphinx) -> None
- super(StandaloneHTMLBuilder, self).__init__(app)
+ super().__init__(app)
# CSS files
- self.css_files = CSSContainer() # type: List[Dict[unicode, unicode]]
+ self.css_files = [] # type: List[Dict[str, str]]
# JS files
self.script_files = JSContainer() # type: List[JavaScript]
@@ -322,35 +267,38 @@ class StandaloneHTMLBuilder(Builder):
# basename of images directory
self.imagedir = '_images'
# section numbers for headings in the currently visited document
- self.secnumbers = {} # type: Dict[unicode, Tuple[int, ...]]
+ self.secnumbers = {} # type: Dict[str, Tuple[int, ...]]
# currently written docname
- self.current_docname = None # type: unicode
+ self.current_docname = None # type: str
self.init_templates()
self.init_highlighter()
self.init_css_files()
self.init_js_files()
- if self.config.html_file_suffix is not None:
- self.out_suffix = self.config.html_file_suffix
- if self.config.html_link_suffix is not None:
- self.link_suffix = self.config.html_link_suffix
+ html_file_suffix = self.get_builder_config('file_suffix', 'html')
+ if html_file_suffix is not None:
+ self.out_suffix = html_file_suffix
+
+ html_link_suffix = self.get_builder_config('link_suffix', 'html')
+ if html_link_suffix is not None:
+ self.link_suffix = html_link_suffix
else:
self.link_suffix = self.out_suffix
self.use_index = self.get_builder_config('use_index', 'html')
if self.config.html_experimental_html5_writer and not html5_ready:
- self.app.warn(('html_experimental_html5_writer is set, but current version '
- 'is old. Docutils\' version should be 0.13 or newer, but %s.') %
- docutils.__version__)
+ logger.warning(__('html_experimental_html5_writer is set, but current version '
+ 'is old. Docutils\' version should be 0.13 or newer, but %s.'),
+ docutils.__version__)
def create_build_info(self):
# type: () -> BuildInfo
return BuildInfo(self.config, self.tags, ['html'])
def _get_translations_js(self):
- # type: () -> unicode
+ # type: () -> str
candidates = [path.join(dir, self.config.language,
'LC_MESSAGES', 'sphinx.js')
for dir in self.config.locale_dirs] + \
@@ -365,7 +313,7 @@ class StandaloneHTMLBuilder(Builder):
return None
def get_theme_config(self):
- # type: () -> Tuple[unicode, Dict]
+ # type: () -> Tuple[str, Dict]
return self.config.html_theme, self.config.html_theme_options
def init_templates(self):
@@ -397,7 +345,7 @@ class StandaloneHTMLBuilder(Builder):
self.add_css_file(filename, **attrs)
def add_css_file(self, filename, **kwargs):
- # type: (unicode, **unicode) -> None
+ # type: (str, **str) -> None
if '://' not in filename:
filename = posixpath.join('_static', filename)
@@ -420,15 +368,15 @@ class StandaloneHTMLBuilder(Builder):
self.add_js_file('translations.js')
def add_js_file(self, filename, **kwargs):
- # type: (unicode, **unicode) -> None
+ # type: (str, **str) -> None
if filename and '://' not in filename:
filename = posixpath.join('_static', filename)
self.script_files.append(JavaScript(filename, **kwargs))
@property
- def default_translator_class(self):
- # type: () -> nodes.NodeVisitor
+ def default_translator_class(self): # type: ignore
+ # type: () -> Type[nodes.NodeVisitor]
use_html5_writer = self.config.html_experimental_html5_writer
if use_html5_writer is None:
use_html5_writer = self.default_html5_translator
@@ -440,7 +388,7 @@ class StandaloneHTMLBuilder(Builder):
@property
def math_renderer_name(self):
- # type: () -> unicode
+ # type: () -> str
name = self.get_builder_config('math_renderer', 'html')
if name is not None:
# use given name
@@ -460,18 +408,17 @@ class StandaloneHTMLBuilder(Builder):
return None
def get_outdated_docs(self):
- # type: () -> Iterator[unicode]
+ # type: () -> Iterator[str]
try:
with open(path.join(self.outdir, '.buildinfo')) as fp:
buildinfo = BuildInfo.load(fp)
if self.build_info != buildinfo:
- for docname in self.env.found_docs:
- yield docname
+ yield from self.env.found_docs
return
except ValueError as exc:
logger.warning(__('Failed to read build info file: %r'), exc)
- except IOError:
+ except OSError:
# ignore errors on reading
pass
@@ -493,20 +440,20 @@ class StandaloneHTMLBuilder(Builder):
template_mtime)
if srcmtime > targetmtime:
yield docname
- except EnvironmentError:
+ except OSError:
# source doesn't exist anymore
pass
def get_asset_paths(self):
- # type: () -> List[unicode]
+ # type: () -> List[str]
return self.config.html_extra_path + self.config.html_static_path
def render_partial(self, node):
- # type: (nodes.Nodes) -> Dict[unicode, unicode]
+ # type: (nodes.Node) -> Dict[str, str]
"""Utility: Render a lone doctree node."""
if node is None:
return {'fragment': ''}
- doc = new_document(b'<partial node>')
+ doc = new_document('<partial node>')
doc.append(node)
if self._publisher is None:
@@ -528,7 +475,7 @@ class StandaloneHTMLBuilder(Builder):
return pub.writer.parts
def prepare_writing(self, docnames):
- # type: (Iterable[unicode]) -> nodes.Node
+ # type: (Set[str]) -> None
# create the search indexer
self.indexer = None
if self.search:
@@ -545,7 +492,7 @@ class StandaloneHTMLBuilder(Builder):
self.docsettings = OptionParser(
defaults=self.env.settings,
components=(self.docwriter,),
- read_config_files=True).get_default_values()
+ read_config_files=True).get_default_values() # type: Any
self.docsettings.compact_lists = bool(self.config.html_compact_lists)
# determine the additional indices to include
@@ -557,7 +504,7 @@ class StandaloneHTMLBuilder(Builder):
domain = None # type: Domain
domain = self.env.domains[domain_name]
for indexcls in domain.indices:
- indexname = '%s-%s' % (domain.name, indexcls.name) # type: unicode
+ indexname = '%s-%s' % (domain.name, indexcls.name)
if isinstance(indices_config, list):
if indexname not in indices_config:
continue
@@ -581,12 +528,12 @@ class StandaloneHTMLBuilder(Builder):
favicon = self.config.html_favicon and \
path.basename(self.config.html_favicon) or ''
- if not isinstance(self.config.html_use_opensearch, string_types):
+ if not isinstance(self.config.html_use_opensearch, str):
logger.warning(__('html_use_opensearch config value must now be a string'))
self.relations = self.env.collect_relations()
- rellinks = [] # type: List[Tuple[unicode, unicode, unicode, unicode]]
+ rellinks = [] # type: List[Tuple[str, str, str, str]]
if self.use_index:
rellinks.append(('genindex', _('General Index'), 'I', _('index')))
for indexname, indexcls, content, collapse in self.domain_indices:
@@ -602,43 +549,43 @@ class StandaloneHTMLBuilder(Builder):
else:
stylename = 'default.css'
- self.globalcontext = dict(
- embedded = self.embedded,
- project = self.config.project,
- release = return_codes_re.sub('', self.config.release),
- version = self.config.version,
- last_updated = self.last_updated,
- copyright = self.config.copyright,
- master_doc = self.config.master_doc,
- use_opensearch = self.config.html_use_opensearch,
- docstitle = self.config.html_title,
- shorttitle = self.config.html_short_title,
- show_copyright = self.config.html_show_copyright,
- show_sphinx = self.config.html_show_sphinx,
- has_source = self.config.html_copy_source,
- show_source = self.config.html_show_sourcelink,
- sourcelink_suffix = self.config.html_sourcelink_suffix,
- file_suffix = self.out_suffix,
- script_files = self.script_files,
- language = self.config.language,
- css_files = self.css_files,
- sphinx_version = __display_version__,
- style = stylename,
- rellinks = rellinks,
- builder = self.name,
- parents = [],
- logo = logo,
- favicon = favicon,
- html5_doctype = self.config.html_experimental_html5_writer and html5_ready,
- ) # type: Dict[unicode, Any]
+ self.globalcontext = {
+ 'embedded': self.embedded,
+ 'project': self.config.project,
+ 'release': return_codes_re.sub('', self.config.release),
+ 'version': self.config.version,
+ 'last_updated': self.last_updated,
+ 'copyright': self.config.copyright,
+ 'master_doc': self.config.master_doc,
+ 'use_opensearch': self.config.html_use_opensearch,
+ 'docstitle': self.config.html_title,
+ 'shorttitle': self.config.html_short_title,
+ 'show_copyright': self.config.html_show_copyright,
+ 'show_sphinx': self.config.html_show_sphinx,
+ 'has_source': self.config.html_copy_source,
+ 'show_source': self.config.html_show_sourcelink,
+ 'sourcelink_suffix': self.config.html_sourcelink_suffix,
+ 'file_suffix': self.out_suffix,
+ 'script_files': self.script_files,
+ 'language': self.config.language,
+ 'css_files': self.css_files,
+ 'sphinx_version': __display_version__,
+ 'style': stylename,
+ 'rellinks': rellinks,
+ 'builder': self.name,
+ 'parents': [],
+ 'logo': logo,
+ 'favicon': favicon,
+ 'html5_doctype': self.config.html_experimental_html5_writer and html5_ready,
+ }
if self.theme:
self.globalcontext.update(
('theme_' + key, val) for (key, val) in
- iteritems(self.theme.get_options(self.theme_options)))
+ self.theme.get_options(self.theme_options).items())
self.globalcontext.update(self.config.html_context)
def get_doc_context(self, docname, body, metatags):
- # type: (unicode, unicode, Dict) -> Dict[unicode, Any]
+ # type: (str, str, str) -> Dict[str, Any]
"""Collect items for the template context of a page."""
# find out relations
prev = next = None
@@ -681,8 +628,8 @@ class StandaloneHTMLBuilder(Builder):
parents.reverse()
# title rendered as HTML
- title = self.env.longtitles.get(docname)
- title = title and self.render_partial(title)['title'] or ''
+ title_node = self.env.longtitles.get(docname)
+ title = title_node and self.render_partial(title_node)['title'] or ''
# Suffix for the document
source_suffix = path.splitext(self.env.doc2path(docname))[1]
@@ -702,31 +649,31 @@ class StandaloneHTMLBuilder(Builder):
self_toc = TocTree(self.env).get_toc_for(docname, self)
toc = self.render_partial(self_toc)['fragment']
- return dict(
- parents = parents,
- prev = prev,
- next = next,
- title = title,
- meta = meta,
- body = body,
- metatags = metatags,
- rellinks = rellinks,
- sourcename = sourcename,
- toc = toc,
+ return {
+ 'parents': parents,
+ 'prev': prev,
+ 'next': next,
+ 'title': title,
+ 'meta': meta,
+ 'body': body,
+ 'metatags': metatags,
+ 'rellinks': rellinks,
+ 'sourcename': sourcename,
+ 'toc': toc,
# only display a TOC if there's more than one item to show
- display_toc = (self.env.toc_num_entries[docname] > 1),
- page_source_suffix = source_suffix,
- )
+ 'display_toc': (self.env.toc_num_entries[docname] > 1),
+ 'page_source_suffix': source_suffix,
+ }
def write_doc(self, docname, doctree):
- # type: (unicode, nodes.Node) -> None
+ # type: (str, nodes.document) -> None
destination = StringOutput(encoding='utf-8')
doctree.settings = self.docsettings
self.secnumbers = self.env.toc_secnumbers.get(docname, {})
- self.fignumbers = self.env.toc_fignumbers.get(docname, {}) # type: Dict[unicode, Dict[unicode, Tuple[int, ...]]] # NOQA
+ self.fignumbers = self.env.toc_fignumbers.get(docname, {})
self.imgpath = relative_uri(self.get_target_uri(docname), '_images')
- self.dlpath = relative_uri(self.get_target_uri(docname), '_downloads') # type: unicode
+ self.dlpath = relative_uri(self.get_target_uri(docname), '_downloads')
self.current_docname = docname
self.docwriter.write(doctree, destination)
self.docwriter.assemble_parts()
@@ -737,11 +684,11 @@ class StandaloneHTMLBuilder(Builder):
self.handle_page(docname, ctx, event_arg=doctree)
def write_doc_serialized(self, docname, doctree):
- # type: (unicode, nodes.Node) -> None
+ # type: (str, nodes.document) -> None
self.imgpath = relative_uri(self.get_target_uri(docname), self.imagedir)
self.post_process_images(doctree)
- title = self.env.longtitles.get(docname)
- title = title and self.render_partial(title)['title'] or ''
+ title_node = self.env.longtitles.get(docname)
+ title = title_node and self.render_partial(title_node)['title'] or ''
self.index_page(docname, doctree, title)
def finish(self):
@@ -807,11 +754,11 @@ class StandaloneHTMLBuilder(Builder):
indexcounts.append(sum(1 + len(subitems)
for _, (_, subitems, _) in entries))
- genindexcontext = dict(
- genindexentries = genindex,
- genindexcounts = indexcounts,
- split_index = self.config.html_split_index,
- )
+ genindexcontext = {
+ 'genindexentries': genindex,
+ 'genindexcounts': indexcounts,
+ 'split_index': self.config.html_split_index,
+ }
logger.info(' genindex', nonl=1)
if self.config.html_split_index:
@@ -830,11 +777,11 @@ class StandaloneHTMLBuilder(Builder):
def write_domain_indices(self):
# type: () -> None
for indexname, indexcls, content, collapse in self.domain_indices:
- indexcontext = dict(
- indextitle = indexcls.localname,
- content = content,
- collapse_index = collapse,
- )
+ indexcontext = {
+ 'indextitle': indexcls.localname,
+ 'content': content,
+ 'collapse_index': collapse,
+ }
logger.info(' ' + indexname, nonl=1)
self.handle_page(indexname, indexcontext, 'domainindex.html')
@@ -857,7 +804,7 @@ class StandaloneHTMLBuilder(Builder):
def copy_download_files(self):
# type: () -> None
def to_relpath(f):
- # type: (unicode) -> unicode
+ # type: (str) -> str
return relative_path(self.srcdir, f)
# copy downloadable files
if self.env.dlfiles:
@@ -869,7 +816,7 @@ class StandaloneHTMLBuilder(Builder):
dest = path.join(self.outdir, '_downloads', self.env.dlfiles[src][1])
ensuredir(path.dirname(dest))
copyfile(path.join(self.srcdir, src), dest)
- except EnvironmentError as err:
+ except OSError as err:
logger.warning(__('cannot copy downloadable file %r: %s'),
path.join(self.srcdir, src), err)
@@ -881,7 +828,7 @@ class StandaloneHTMLBuilder(Builder):
ensuredir(path.join(self.outdir, '_static'))
# first, create pygments style file
with open(path.join(self.outdir, '_static', 'pygments.css'), 'w') as f:
- f.write(self.highlighter.get_stylesheet()) # type: ignore
+ f.write(self.highlighter.get_stylesheet())
# then, copy translations JavaScript file
if self.config.language is not None:
jsfile = self._get_translations_js()
@@ -935,9 +882,7 @@ class StandaloneHTMLBuilder(Builder):
copyfile(path.join(self.confdir, self.config.html_favicon),
icontarget)
logger.info('done')
- except EnvironmentError as err:
- # TODO: In py3, EnvironmentError (and IOError) was merged into OSError.
- # So it should be replaced by IOError on dropping py2 support
+ except OSError as err:
logger.warning(__('cannot copy static file %r'), err)
def copy_extra_files(self):
@@ -955,7 +900,7 @@ class StandaloneHTMLBuilder(Builder):
copy_asset(entry, self.outdir, excluded)
logger.info(__('done'))
- except EnvironmentError as err:
+ except OSError as err:
logger.warning(__('cannot copy extra file %r'), err)
def write_buildinfo(self):
@@ -963,7 +908,7 @@ class StandaloneHTMLBuilder(Builder):
try:
with open(path.join(self.outdir, '.buildinfo'), 'w') as fp:
self.build_info.dump(fp)
- except IOError as exc:
+ except OSError as exc:
logger.warning(__('Failed to write build info file: %r'), exc)
def cleanup(self):
@@ -999,17 +944,17 @@ class StandaloneHTMLBuilder(Builder):
reference.append(node)
def load_indexer(self, docnames):
- # type: (Iterable[unicode]) -> None
+ # type: (Iterable[str]) -> None
keep = set(self.env.all_docs) - set(docnames)
try:
searchindexfn = path.join(self.outdir, self.searchindex_filename)
if self.indexer_dumps_unicode:
- f = codecs.open(searchindexfn, 'r', encoding='utf-8') # type: ignore
+ with open(searchindexfn, encoding='utf-8') as ft:
+ self.indexer.load(ft, self.indexer_format)
else:
- f = open(searchindexfn, 'rb') # type: ignore
- with f:
- self.indexer.load(f, self.indexer_format)
- except (IOError, OSError, ValueError):
+ with open(searchindexfn, 'rb') as fb:
+ self.indexer.load(fb, self.indexer_format)
+ except (OSError, ValueError):
if keep:
logger.warning(__('search index couldn\'t be loaded, but not all '
'documents will be built: the index will be '
@@ -1018,7 +963,7 @@ class StandaloneHTMLBuilder(Builder):
self.indexer.prune(keep)
def index_page(self, pagename, doctree, title):
- # type: (unicode, nodes.Node, unicode) -> None
+ # type: (str, nodes.document, str) -> None
# only index pages with title
if self.indexer is not None and title:
filename = self.env.doc2path(pagename, base=None)
@@ -1027,22 +972,28 @@ class StandaloneHTMLBuilder(Builder):
except TypeError:
# fallback for old search-adapters
self.indexer.feed(pagename, title, doctree) # type: ignore
+ indexer_name = self.indexer.__class__.__name__
+ warnings.warn(
+ 'The %s.feed() method signature is deprecated. Update to '
+ '%s.feed(docname, filename, title, doctree).' % (
+ indexer_name, indexer_name),
+ RemovedInSphinx40Warning)
def _get_local_toctree(self, docname, collapse=True, **kwds):
- # type: (unicode, bool, Any) -> unicode
+ # type: (str, bool, Any) -> str
if 'includehidden' not in kwds:
kwds['includehidden'] = False
return self.render_partial(TocTree(self.env).get_toctree_for(
docname, self, collapse, **kwds))['fragment']
def get_outfilename(self, pagename):
- # type: (unicode) -> unicode
+ # type: (str) -> str
return path.join(self.outdir, os_path(pagename) + self.out_suffix)
def add_sidebars(self, pagename, ctx):
- # type: (unicode, Dict) -> None
+ # type: (str, Dict) -> None
def has_wildcard(pattern):
- # type: (unicode) -> bool
+ # type: (str) -> bool
return any(char in pattern for char in '*?[')
sidebars = None
matched = None
@@ -1068,7 +1019,7 @@ class StandaloneHTMLBuilder(Builder):
# user sidebar settings
html_sidebars = self.get_builder_config('sidebars', 'html')
- for pattern, patsidebars in iteritems(html_sidebars):
+ for pattern, patsidebars in html_sidebars.items():
if patmatch(pagename, pattern):
if matched:
if has_wildcard(pattern):
@@ -1086,14 +1037,6 @@ class StandaloneHTMLBuilder(Builder):
if sidebars is None:
# keep defaults
pass
- elif isinstance(sidebars, string_types):
- # 0.x compatible mode: insert custom sidebar before searchbox
- customsidebar = sidebars
- sidebars = None
- warnings.warn('Now html_sidebars only allows list of sidebar '
- 'templates as a value. Support for a string value '
- 'will be removed at Sphinx-2.0.',
- RemovedInSphinx20Warning, stacklevel=2)
ctx['sidebars'] = sidebars
ctx['customsidebar'] = customsidebar
@@ -1101,12 +1044,12 @@ class StandaloneHTMLBuilder(Builder):
# --------- these are overwritten by the serialization builder
def get_target_uri(self, docname, typ=None):
- # type: (unicode, unicode) -> unicode
+ # type: (str, str) -> str
return docname + self.link_suffix
def handle_page(self, pagename, addctx, templatename='page.html',
outfilename=None, event_arg=None):
- # type: (unicode, Dict, unicode, unicode, Any) -> None
+ # type: (str, Dict, str, str, Any) -> None
ctx = self.globalcontext.copy()
# current_page_name is backwards compatibility
ctx['pagename'] = ctx['current_page_name'] = pagename
@@ -1123,7 +1066,7 @@ class StandaloneHTMLBuilder(Builder):
ctx['pageurl'] = None
def pathto(otheruri, resource=False, baseuri=default_baseuri):
- # type: (unicode, bool, unicode) -> unicode
+ # type: (str, bool, str) -> str
if resource and '://' in otheruri:
# allow non-local resources given by scheme
return otheruri
@@ -1136,18 +1079,18 @@ class StandaloneHTMLBuilder(Builder):
ctx['pathto'] = pathto
def css_tag(css):
- # type: (Stylesheet) -> unicode
+ # type: (Stylesheet) -> str
attrs = []
for key in sorted(css.attributes):
value = css.attributes[key]
if value is not None:
- attrs.append('%s="%s"' % (key, htmlescape(value, True)))
+ attrs.append('%s="%s"' % (key, html.escape(value, True)))
attrs.append('href="%s"' % pathto(css.filename, resource=True))
return '<link %s />' % ' '.join(attrs)
ctx['css_tag'] = css_tag
def hasdoc(name):
- # type: (unicode) -> bool
+ # type: (str) -> bool
if name in self.env.all_docs:
return True
elif name == 'search' and self.search:
@@ -1158,12 +1101,12 @@ class StandaloneHTMLBuilder(Builder):
ctx['hasdoc'] = hasdoc
def warn(*args, **kwargs):
- # type: (Any, Any) -> unicode
+ # type: (Any, Any) -> str
"""Simple warn() wrapper for themes."""
warnings.warn('The template function warn() was deprecated. '
'Use warning() instead.',
RemovedInSphinx30Warning, stacklevel=2)
- self.warn(*args, **kwargs)
+ logger.warning(*args, **kwargs)
return '' # return empty string
ctx['warn'] = warn
@@ -1193,9 +1136,10 @@ class StandaloneHTMLBuilder(Builder):
# outfilename's path is in general different from self.outdir
ensuredir(path.dirname(outfilename))
try:
- with codecs.open(outfilename, 'w', ctx['encoding'], 'xmlcharrefreplace') as f: # type: ignore # NOQA
+ with open(outfilename, 'w', encoding=ctx['encoding'],
+ errors='xmlcharrefreplace') as f:
f.write(output)
- except (IOError, OSError) as err:
+ except OSError as err:
logger.warning(__("error writing file %s: %s"), outfilename, err)
if self.copysource and ctx.get('sourcename'):
# copy the source file for the "show source" link
@@ -1205,7 +1149,7 @@ class StandaloneHTMLBuilder(Builder):
copyfile(self.env.doc2path(pagename), source_name)
def update_page_context(self, pagename, templatename, ctx, event_arg):
- # type: (unicode, unicode, Dict, Any) -> None
+ # type: (str, str, Dict, Any) -> None
pass
def handle_finish(self):
@@ -1230,11 +1174,11 @@ class StandaloneHTMLBuilder(Builder):
# first write to a temporary file, so that if dumping fails,
# the existing index won't be overwritten
if self.indexer_dumps_unicode:
- f = codecs.open(searchindexfn + '.tmp', 'w', encoding='utf-8') # type: ignore
+ with open(searchindexfn + '.tmp', 'w', encoding='utf-8') as ft:
+ self.indexer.dump(ft, self.indexer_format)
else:
- f = open(searchindexfn + '.tmp', 'wb') # type: ignore
- with f:
- self.indexer.dump(f, self.indexer_format)
+ with open(searchindexfn + '.tmp', 'wb') as fb:
+ self.indexer.dump(fb, self.indexer_format)
movefile(searchindexfn + '.tmp', searchindexfn)
logger.info(__('done'))
@@ -1248,7 +1192,7 @@ class DirectoryHTMLBuilder(StandaloneHTMLBuilder):
name = 'dirhtml'
def get_target_uri(self, docname, typ=None):
- # type: (unicode, unicode) -> unicode
+ # type: (str, str) -> str
if docname == 'index':
return ''
if docname.endswith(SEP + 'index'):
@@ -1256,7 +1200,7 @@ class DirectoryHTMLBuilder(StandaloneHTMLBuilder):
return docname + SEP
def get_outfilename(self, pagename):
- # type: (unicode) -> unicode
+ # type: (str) -> str
if pagename == 'index' or pagename.endswith(SEP + 'index'):
outfilename = path.join(self.outdir, os_path(pagename) +
self.out_suffix)
@@ -1267,8 +1211,8 @@ class DirectoryHTMLBuilder(StandaloneHTMLBuilder):
return outfilename
def prepare_writing(self, docnames):
- # type: (Iterable[unicode]) -> None
- StandaloneHTMLBuilder.prepare_writing(self, docnames)
+ # type: (Set[str]) -> None
+ super().prepare_writing(docnames)
self.globalcontext['no_search_suffix'] = True
@@ -1283,11 +1227,11 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
copysource = False
def get_outdated_docs(self): # type: ignore
- # type: () -> Union[unicode, List[unicode]]
+ # type: () -> Union[str, List[str]]
return 'all documents'
def get_target_uri(self, docname, typ=None):
- # type: (unicode, unicode) -> unicode
+ # type: (str, str) -> str
if docname in self.env.all_docs:
# all references are on the same page...
return self.config.master_doc + self.out_suffix + \
@@ -1297,7 +1241,7 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
return docname + self.out_suffix
def get_relative_uri(self, from_, to, typ=None):
- # type: (unicode, unicode, unicode) -> unicode
+ # type: (str, str, str) -> str
# ignore source
return self.get_target_uri(to, typ)
@@ -1317,7 +1261,7 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
refnode['refuri'] = fname + refuri[hashindex:]
def _get_local_toctree(self, docname, collapse=True, **kwds):
- # type: (unicode, bool, Any) -> unicode
+ # type: (str, bool, Any) -> str
if 'includehidden' not in kwds:
kwds['includehidden'] = False
toctree = TocTree(self.env).get_toctree_for(docname, self, collapse, **kwds)
@@ -1326,7 +1270,7 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
return self.render_partial(toctree)['fragment']
def assemble_doctree(self):
- # type: () -> nodes.Node
+ # type: () -> nodes.document
master = self.config.master_doc
tree = self.env.get_doctree(master)
tree = inline_all_toctrees(self, set(), master, tree, darkgreen, [master])
@@ -1336,7 +1280,7 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
return tree
def assemble_toc_secnumbers(self):
- # type: () -> Dict[unicode, Dict[unicode, Tuple[int, ...]]]
+ # type: () -> Dict[str, Dict[str, Tuple[int, ...]]]
# Assemble toc_secnumbers to resolve section numbers on SingleHTML.
# Merge all secnumbers to single secnumber.
#
@@ -1346,16 +1290,16 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
#
# There are related codes in inline_all_toctres() and
# HTMLTranslter#add_secnumber().
- new_secnumbers = {} # type: Dict[unicode, Tuple[int, ...]]
- for docname, secnums in iteritems(self.env.toc_secnumbers):
- for id, secnum in iteritems(secnums):
+ new_secnumbers = {} # type: Dict[str, Tuple[int, ...]]
+ for docname, secnums in self.env.toc_secnumbers.items():
+ for id, secnum in secnums.items():
alias = "%s/%s" % (docname, id)
new_secnumbers[alias] = secnum
return {self.config.master_doc: new_secnumbers}
def assemble_toc_fignumbers(self):
- # type: () -> Dict[unicode, Dict[unicode, Dict[unicode, Tuple[int, ...]]]] # NOQA
+ # type: () -> Dict[str, Dict[str, Dict[str, Tuple[int, ...]]]]
# Assemble toc_fignumbers to resolve figure numbers on SingleHTML.
# Merge all fignumbers to single fignumber.
#
@@ -1365,51 +1309,50 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
#
# There are related codes in inline_all_toctres() and
# HTMLTranslter#add_fignumber().
- new_fignumbers = {} # type: Dict[unicode, Dict[unicode, Tuple[int, ...]]]
- # {u'foo': {'figure': {'id2': (2,), 'id1': (1,)}}, u'bar': {'figure': {'id1': (3,)}}}
- for docname, fignumlist in iteritems(self.env.toc_fignumbers):
- for figtype, fignums in iteritems(fignumlist):
+ new_fignumbers = {} # type: Dict[str, Dict[str, Tuple[int, ...]]]
+ # {'foo': {'figure': {'id2': (2,), 'id1': (1,)}}, 'bar': {'figure': {'id1': (3,)}}}
+ for docname, fignumlist in self.env.toc_fignumbers.items():
+ for figtype, fignums in fignumlist.items():
alias = "%s/%s" % (docname, figtype)
new_fignumbers.setdefault(alias, {})
- for id, fignum in iteritems(fignums):
+ for id, fignum in fignums.items():
new_fignumbers[alias][id] = fignum
return {self.config.master_doc: new_fignumbers}
def get_doc_context(self, docname, body, metatags):
- # type: (unicode, unicode, Dict) -> Dict
+ # type: (str, str, str) -> Dict
# no relation links...
- toc = TocTree(self.env).get_toctree_for(self.config.master_doc,
- self, False)
+ toctree = TocTree(self.env).get_toctree_for(self.config.master_doc, self, False)
# if there is no toctree, toc is None
- if toc:
- self.fix_refuris(toc)
- toc = self.render_partial(toc)['fragment']
+ if toctree:
+ self.fix_refuris(toctree)
+ toc = self.render_partial(toctree)['fragment']
display_toc = True
else:
toc = ''
display_toc = False
- return dict(
- parents = [],
- prev = None,
- next = None,
- docstitle = None,
- title = self.config.html_title,
- meta = None,
- body = body,
- metatags = metatags,
- rellinks = [],
- sourcename = '',
- toc = toc,
- display_toc = display_toc,
- )
+ return {
+ 'parents': [],
+ 'prev': None,
+ 'next': None,
+ 'docstitle': None,
+ 'title': self.config.html_title,
+ 'meta': None,
+ 'body': body,
+ 'metatags': metatags,
+ 'rellinks': [],
+ 'sourcename': '',
+ 'toc': toc,
+ 'display_toc': display_toc,
+ }
def write(self, *ignored):
# type: (Any) -> None
docnames = self.env.all_docs
logger.info(bold(__('preparing documents... ')), nonl=True)
- self.prepare_writing(docnames)
+ self.prepare_writing(docnames) # type: ignore
logger.info(__('done'))
logger.info(bold(__('assembling single document... ')), nonl=True)
@@ -1460,7 +1403,7 @@ class SerializingHTMLBuilder(StandaloneHTMLBuilder):
additional_dump_args = () # type: Tuple
#: the filename for the global context file
- globalcontext_filename = None # type: unicode
+ globalcontext_filename = None # type: str
supported_image_types = ['image/svg+xml', 'image/png',
'image/gif', 'image/jpeg']
@@ -1479,7 +1422,7 @@ class SerializingHTMLBuilder(StandaloneHTMLBuilder):
self.use_index = self.get_builder_config('use_index', 'html')
def get_target_uri(self, docname, typ=None):
- # type: (unicode, unicode) -> unicode
+ # type: (str, str) -> str
if docname == 'index':
return ''
if docname.endswith(SEP + 'index'):
@@ -1487,17 +1430,17 @@ class SerializingHTMLBuilder(StandaloneHTMLBuilder):
return docname + SEP
def dump_context(self, context, filename):
- # type: (Dict, unicode) -> None
+ # type: (Dict, str) -> None
if self.implementation_dumps_unicode:
- f = codecs.open(filename, 'w', encoding='utf-8') # type: ignore
+ with open(filename, 'w', encoding='utf-8') as ft:
+ self.implementation.dump(context, ft, *self.additional_dump_args)
else:
- f = open(filename, 'wb') # type: ignore
- with f:
- self.implementation.dump(context, f, *self.additional_dump_args)
+ with open(filename, 'wb') as fb:
+ self.implementation.dump(context, fb, *self.additional_dump_args)
def handle_page(self, pagename, ctx, templatename='page.html',
outfilename=None, event_arg=None):
- # type: (unicode, Dict, unicode, unicode, Any) -> None
+ # type: (str, Dict, str, str, Any) -> None
ctx['current_page_name'] = pagename
self.add_sidebars(pagename, ctx)
@@ -1532,7 +1475,7 @@ class SerializingHTMLBuilder(StandaloneHTMLBuilder):
self.dump_context(self.globalcontext, outfilename)
# super here to dump the search index
- StandaloneHTMLBuilder.handle_finish(self)
+ super().handle_finish()
# copy the environment file from the doctree dir to the output dir
# as needed by the web app
@@ -1580,17 +1523,13 @@ class JSONHTMLBuilder(SerializingHTMLBuilder):
globalcontext_filename = 'globalcontext.json'
searchindex_filename = 'searchindex.json'
- def init(self):
- # type: () -> None
- SerializingHTMLBuilder.init(self)
-
def convert_html_css_files(app, config):
# type: (Sphinx, Config) -> None
"""This converts string styled html_css_files to tuple styled one."""
- html_css_files = [] # type: List[Tuple[unicode, Dict]]
+ html_css_files = [] # type: List[Tuple[str, Dict]]
for entry in config.html_css_files:
- if isinstance(entry, string_types):
+ if isinstance(entry, str):
html_css_files.append((entry, {}))
else:
try:
@@ -1606,9 +1545,9 @@ def convert_html_css_files(app, config):
def convert_html_js_files(app, config):
# type: (Sphinx, Config) -> None
"""This converts string styled html_js_files to tuple styled one."""
- html_js_files = [] # type: List[Tuple[unicode, Dict]]
+ html_js_files = [] # type: List[Tuple[str, Dict]]
for entry in config.html_js_files:
- if isinstance(entry, string_types):
+ if isinstance(entry, str):
html_js_files.append((entry, {}))
else:
try:
@@ -1622,7 +1561,7 @@ def convert_html_js_files(app, config):
def setup_js_tag_helper(app, pagename, templatexname, context, doctree):
- # type: (Sphinx, unicode, unicode, Dict, nodes.Node) -> None
+ # type: (Sphinx, str, str, Dict, nodes.Node) -> None
"""Set up js_tag() template helper.
.. note:: This set up function is added to keep compatibility with webhelper.
@@ -1630,9 +1569,9 @@ def setup_js_tag_helper(app, pagename, templatexname, context, doctree):
pathto = context.get('pathto')
def js_tag(js):
- # type: (JavaScript) -> unicode
+ # type: (JavaScript) -> str
attrs = []
- body = '' # type: unicode
+ body = ''
if isinstance(js, JavaScript):
for key in sorted(js.attributes):
value = js.attributes[key]
@@ -1640,7 +1579,7 @@ def setup_js_tag_helper(app, pagename, templatexname, context, doctree):
if key == 'body':
body = value
else:
- attrs.append('%s="%s"' % (key, htmlescape(value, True)))
+ attrs.append('%s="%s"' % (key, html.escape(value, True)))
if js.filename:
attrs.append('src="%s"' % pathto(js.filename, resource=True))
else:
@@ -1666,7 +1605,7 @@ def validate_math_renderer(app):
def setup(app):
- # type: (Sphinx) -> Dict[unicode, Any]
+ # type: (Sphinx) -> Dict[str, Any]
# builders
app.add_builder(StandaloneHTMLBuilder)
app.add_builder(DirectoryHTMLBuilder)
@@ -1680,35 +1619,35 @@ def setup(app):
app.add_config_value('html_theme_options', {}, 'html')
app.add_config_value('html_title',
lambda self: _('%s %s documentation') % (self.project, self.release),
- 'html', string_classes)
+ 'html', [str])
app.add_config_value('html_short_title', lambda self: self.html_title, 'html')
- app.add_config_value('html_style', None, 'html', string_classes)
- app.add_config_value('html_logo', None, 'html', string_classes)
- app.add_config_value('html_favicon', None, 'html', string_classes)
+ app.add_config_value('html_style', None, 'html', [str])
+ app.add_config_value('html_logo', None, 'html', [str])
+ app.add_config_value('html_favicon', None, 'html', [str])
app.add_config_value('html_css_files', [], 'html')
app.add_config_value('html_js_files', [], 'html')
app.add_config_value('html_static_path', [], 'html')
app.add_config_value('html_extra_path', [], 'html')
- app.add_config_value('html_last_updated_fmt', None, 'html', string_classes)
+ app.add_config_value('html_last_updated_fmt', None, 'html', [str])
app.add_config_value('html_sidebars', {}, 'html')
app.add_config_value('html_additional_pages', {}, 'html')
app.add_config_value('html_domain_indices', True, 'html', [list])
- app.add_config_value('html_add_permalinks', u'\u00B6', 'html')
+ app.add_config_value('html_add_permalinks', 'ΒΆ', 'html')
app.add_config_value('html_use_index', True, 'html')
app.add_config_value('html_split_index', False, 'html')
app.add_config_value('html_copy_source', True, 'html')
app.add_config_value('html_show_sourcelink', True, 'html')
app.add_config_value('html_sourcelink_suffix', '.txt', 'html')
app.add_config_value('html_use_opensearch', '', 'html')
- app.add_config_value('html_file_suffix', None, 'html', string_classes)
- app.add_config_value('html_link_suffix', None, 'html', string_classes)
+ app.add_config_value('html_file_suffix', None, 'html', [str])
+ app.add_config_value('html_link_suffix', None, 'html', [str])
app.add_config_value('html_show_copyright', True, 'html')
app.add_config_value('html_show_sphinx', True, 'html')
app.add_config_value('html_context', {}, 'html')
app.add_config_value('html_output_encoding', 'utf-8', 'html')
app.add_config_value('html_compact_lists', True, 'html')
app.add_config_value('html_secnumber_suffix', '. ', 'html')
- app.add_config_value('html_search_language', None, 'html', string_classes)
+ app.add_config_value('html_search_language', None, 'html', [str])
app.add_config_value('html_search_options', {}, 'html')
app.add_config_value('html_search_scorer', '', None)
app.add_config_value('html_scaled_image_link', True, 'html')