From 5df7228cde0b81507f57a00cff3c11c0b2b7d37c Mon Sep 17 00:00:00 2001 From: dombre Date: Wed, 13 Jun 2018 15:32:41 +0200 Subject: [PATCH 01/16] Update intl.rst Fixes #5081 --- doc/intl.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/intl.rst b/doc/intl.rst index 43f620bd0..129665dde 100644 --- a/doc/intl.rst +++ b/doc/intl.rst @@ -123,14 +123,14 @@ This section describe an easy way to translate with sphinx-intl. .. code-block:: console - > set SPHINXOPTS=-D language='de' + > set SPHINXOPTS=-D language=de > .\make.bat html command line (for PowerShell): .. code-block:: console - > Set-Item env:SPHINXOPTS "-D language='de'" + > Set-Item env:SPHINXOPTS "-D language=de" > .\make.bat html From 34126021d9cdff90acd73131e9d7d7132c7f1eaa Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 13 Jun 2018 23:24:14 +0900 Subject: [PATCH 02/16] Close #3784: mathjax: Add :confval:`mathjax_options` --- CHANGES | 2 ++ doc/ext/math.rst | 10 ++++++++++ sphinx/ext/mathjax.py | 5 ++++- tests/test_ext_math.py | 12 ++++++++++++ 4 files changed, 28 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index c2c0bd45c..a4beb3c7d 100644 --- a/CHANGES +++ b/CHANGES @@ -137,6 +137,8 @@ Features added * html: Output ``canonical_url`` metadata if :confval:`html_baseurl` set (refs: #4193) * #5029: autosummary: expose ``inherited_members`` to template +* #3784: mathjax: Add :confval:`mathjax_options` to give options to script tag + for mathjax Bugs fixed ---------- diff --git a/doc/ext/math.rst b/doc/ext/math.rst index c00713a3d..c299b7bee 100644 --- a/doc/ext/math.rst +++ b/doc/ext/math.rst @@ -260,6 +260,16 @@ Sphinx. You can also give a full ``https://`` URL different from the CDN URL. +.. confval:: mathjax_options + + The options to script tag for mathjax. For example, you can set integrity + option with following setting:: + + mathjax_options = { + 'integrity': 'sha384-......', + } + + The default is empty (``{}``). :mod:`sphinx.ext.jsmath` -- Render math via JavaScript ------------------------------------------------------ diff --git a/sphinx/ext/mathjax.py b/sphinx/ext/mathjax.py index 8aeafefc7..0d73f3d3d 100644 --- a/sphinx/ext/mathjax.py +++ b/sphinx/ext/mathjax.py @@ -74,6 +74,8 @@ def builder_inited(app): 'mathjax extension to work') if app.builder.format == 'html': options = {'async': 'async'} + if app.config.mathjax_options: + options.update(app.config.mathjax_options) app.builder.add_js_file(app.config.mathjax_path, **options) # type: ignore @@ -88,7 +90,8 @@ def setup(app): # https://docs.mathjax.org/en/latest/start.html#secure-access-to-the-cdn app.add_config_value('mathjax_path', 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?' - 'config=TeX-AMS-MML_HTMLorMML', False) + 'config=TeX-AMS-MML_HTMLorMML', 'html') + app.add_config_value('mathjax_options', {}, 'html') app.add_config_value('mathjax_inline', [r'\(', r'\)'], 'html') app.add_config_value('mathjax_display', [r'\[', r'\]'], 'html') app.connect('builder-inited', builder_inited) diff --git a/tests/test_ext_math.py b/tests/test_ext_math.py index 47465f07e..99940977b 100644 --- a/tests/test_ext_math.py +++ b/tests/test_ext_math.py @@ -89,6 +89,18 @@ def test_imgmath_svg(app, status, warning): assert re.search(html, content, re.S) +@pytest.mark.sphinx('html', testroot='ext-math', + confoverrides={'extensions': ['sphinx.ext.mathjax'], + 'mathjax_options': {'integrity': 'sha384-0123456789'}}) +def test_mathjax_options(app, status, warning): + app.builder.build_all() + + content = (app.outdir / 'index.html').text() + assert ('' in content) + + @pytest.mark.sphinx('html', testroot='ext-math', confoverrides={'extensions': ['sphinx.ext.mathjax']}) def test_mathjax_align(app, status, warning): From 20f625b4d39d0e981b4922f629212acc74271665 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Mon, 11 Jun 2018 02:27:27 +0200 Subject: [PATCH 03/16] Builder.build: save env also with only new documents Saving/pickling the environment is necessary for intersphinx to have its cache saved. Currently a new build would fetch the inventories, but the cache would not get saved. This appears to break no existing test, but should probably get a new one then? --- sphinx/builders/__init__.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py index 7cfacbd3c..e446ca94a 100644 --- a/sphinx/builders/__init__.py +++ b/sphinx/builders/__init__.py @@ -372,14 +372,14 @@ class Builder(object): else: logger.info(__('none found')) - if updated_docnames: - # save the environment - from sphinx.application import ENV_PICKLE_FILENAME - logger.info(bold(__('pickling environment... ')), nonl=True) - with open(path.join(self.doctreedir, ENV_PICKLE_FILENAME), 'wb') as f: - pickle.dump(self.env, f, pickle.HIGHEST_PROTOCOL) - logger.info(__('done')) + # save the environment + from sphinx.application import ENV_PICKLE_FILENAME + logger.info(bold(__('pickling environment... ')), nonl=True) + with open(path.join(self.doctreedir, ENV_PICKLE_FILENAME), 'wb') as f: + pickle.dump(self.env, f, pickle.HIGHEST_PROTOCOL) + logger.info(__('done')) + if updated_docnames: # global actions self.app.phase = BuildPhase.CONSISTENCY_CHECK logger.info(bold(__('checking consistency... ')), nonl=True) From 28fe6dadf498bb74158c7fd9f41b28228fc127de Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 13 Jun 2018 23:26:42 +0900 Subject: [PATCH 04/16] Fix #4362: latex: Don't overwrite .tex file if document not changed --- CHANGES | 1 + sphinx/builders/latex/__init__.py | 8 +++----- sphinx/util/docutils.py | 22 +++++++++++++++++++++ tests/test_util_docutils.py | 32 ++++++++++++++++++++++++++++++- 4 files changed, 57 insertions(+), 6 deletions(-) diff --git a/CHANGES b/CHANGES index c2c0bd45c..948b3dd6e 100644 --- a/CHANGES +++ b/CHANGES @@ -137,6 +137,7 @@ Features added * html: Output ``canonical_url`` metadata if :confval:`html_baseurl` set (refs: #4193) * #5029: autosummary: expose ``inherited_members`` to template +* #4362: latex: Don't overwrite .tex file if document not changed Bugs fixed ---------- diff --git a/sphinx/builders/latex/__init__.py b/sphinx/builders/latex/__init__.py index 32e813241..382914731 100644 --- a/sphinx/builders/latex/__init__.py +++ b/sphinx/builders/latex/__init__.py @@ -13,7 +13,6 @@ import os from os import path from docutils.frontend import OptionParser -from docutils.io import FileOutput from six import text_type from sphinx import package_dir, addnodes, highlighting @@ -31,7 +30,7 @@ from sphinx.locale import _, __ from sphinx.transforms import SphinxTransformer from sphinx.util import texescape, logging, status_iterator from sphinx.util.console import bold, darkgreen # type: ignore -from sphinx.util.docutils import new_document +from sphinx.util.docutils import SphinxFileOutput, new_document from sphinx.util.fileutil import copy_asset_file from sphinx.util.nodes import inline_all_toctrees from sphinx.util.osutil import SEP, make_filename @@ -134,9 +133,8 @@ class LaTeXBuilder(Builder): toctree_only = False if len(entry) > 5: toctree_only = entry[5] - destination = FileOutput( - destination_path=path.join(self.outdir, targetname), - encoding='utf-8') + destination = SphinxFileOutput(destination_path=path.join(self.outdir, targetname), + encoding='utf-8', overwrite_if_changed=True) logger.info(__("processing %s..."), targetname, nonl=1) toctrees = self.env.get_doctree(docname).traverse(addnodes.toctree) if toctrees: diff --git a/sphinx/util/docutils.py b/sphinx/util/docutils.py index d96ab843c..547d74c17 100644 --- a/sphinx/util/docutils.py +++ b/sphinx/util/docutils.py @@ -10,6 +10,7 @@ """ from __future__ import absolute_import +import codecs import os import re import types @@ -21,6 +22,7 @@ from os import path import docutils from docutils import nodes +from docutils.io import FileOutput from docutils.parsers.rst import Directive, directives, roles, convert_directive_function from docutils.statemachine import StateMachine from docutils.utils import Reporter @@ -300,6 +302,26 @@ def switch_source_input(state, content): state.memo.reporter.get_source_and_line = get_source_and_line +class SphinxFileOutput(FileOutput): + """Better FileOutput class for Sphinx.""" + + def __init__(self, **kwargs): + # type: (Any) -> None + self.overwrite_if_changed = kwargs.pop('overwrite_if_changed', False) + FileOutput.__init__(self, **kwargs) + + def write(self, data): + # type: (unicode) -> unicode + if (self.destination_path and self.autoclose and 'b' not in self.mode and + self.overwrite_if_changed and os.path.exists(self.destination_path)): + with codecs.open(self.destination_path, encoding=self.encoding) as f: + # skip writing: content not changed + if f.read() == data: + return data + + return FileOutput.write(self, data) + + class SphinxDirective(Directive): """A base class for Sphinx directives. diff --git a/tests/test_util_docutils.py b/tests/test_util_docutils.py index 31a1d9bd2..9319863e0 100644 --- a/tests/test_util_docutils.py +++ b/tests/test_util_docutils.py @@ -9,9 +9,11 @@ :license: BSD, see LICENSE for details. """ +import os + from docutils import nodes -from sphinx.util.docutils import docutils_namespace, register_node +from sphinx.util.docutils import SphinxFileOutput, docutils_namespace, register_node def test_register_node(): @@ -32,3 +34,31 @@ def test_register_node(): assert not hasattr(nodes.GenericNodeVisitor, 'depart_custom_node') assert not hasattr(nodes.SparseNodeVisitor, 'visit_custom_node') assert not hasattr(nodes.SparseNodeVisitor, 'depart_custom_node') + + +def test_SphinxFileOutput(tmpdir): + content = 'Hello Sphinx World' + + # write test.txt at first + filename = str(tmpdir / 'test.txt') + output = SphinxFileOutput(destination_path=filename) + output.write(content) + os.utime(filename, (0, 0)) + + # overrite it again + output.write(content) + assert os.stat(filename).st_mtime != 0 # updated + + # write test2.txt at first + filename = str(tmpdir / 'test2.txt') + output = SphinxFileOutput(destination_path=filename, overwrite_if_changed=True) + output.write(content) + os.utime(filename, (0, 0)) + + # overrite it again + output.write(content) + assert os.stat(filename).st_mtime == 0 # not updated + + # overrite it again (content changed) + output.write(content + "; content change") + assert os.stat(filename).st_mtime != 0 # updated From 285ae7e881c31043592c8aeb62770ac362d57098 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 16 Jun 2018 00:51:21 +0900 Subject: [PATCH 05/16] Fix #5066: html: "relations" sidebar is not shown by default --- CHANGES | 1 + sphinx/builders/html.py | 22 +++++++++++++++------- tests/test_build_html.py | 37 +++++++++++++++++++++++++++++++++---- 3 files changed, 49 insertions(+), 11 deletions(-) diff --git a/CHANGES b/CHANGES index 2074d06ea..2d896a3e8 100644 --- a/CHANGES +++ b/CHANGES @@ -29,6 +29,7 @@ Bugs fixed * #5019: autodoc: crashed by Form Feed Character * #5032: autodoc: loses the first staticmethod parameter for old styled classes * #5036: quickstart: Typing Ctrl-U clears the whole of line +* #5066: html: "relations" sidebar is not shown by default Testing -------- diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index fad49486b..f43ac0e4f 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -906,19 +906,27 @@ class StandaloneHTMLBuilder(Builder): def has_wildcard(pattern): # type: (unicode) -> bool return any(char in pattern for char in '*?[') - sidebars = self.theme.get_config('theme', 'sidebars', None) + sidebars = None matched = None customsidebar = None # default sidebars settings for selected theme - theme_default_sidebars = self.theme.get_config('theme', 'sidebars', None) - if theme_default_sidebars: - sidebars = [name.strip() for name in theme_default_sidebars.split(',')] - elif self.theme.name == 'alabaster': + if self.theme.name == 'alabaster': # provide default settings for alabaster (for compatibility) # Note: this will be removed before Sphinx-2.0 - sidebars = ['about.html', 'navigation.html', 'relation.html', - 'searchbox.html', 'donate.html'] + try: + # get default sidebars settings from alabaster (if defined) + theme_default_sidebars = self.theme.config.get('theme', 'sidebars') + if theme_default_sidebars: + sidebars = [name.strip() for name in theme_default_sidebars.split(',')] + except Exception: + # fallback to better default settings + sidebars = ['about.html', 'navigation.html', 'relations.html', + 'searchbox.html', 'donate.html'] + else: + theme_default_sidebars = self.theme.get_config('theme', 'sidebars', None) + if theme_default_sidebars: + sidebars = [name.strip() for name in theme_default_sidebars.split(',')] # user sidebar settings for pattern, patsidebars in iteritems(self.config.html_sidebars): diff --git a/tests/test_build_html.py b/tests/test_build_html.py index bbfd0bc90..fd599c703 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -1230,21 +1230,50 @@ def test_html_remote_images(app, status, warning): @pytest.mark.sphinx('html', testroot='basic') def test_html_sidebar(app, status, warning): + ctx = {} + + # default for alabaster app.builder.build_all() result = (app.outdir / 'index.html').text(encoding='utf8') - assert '

Table Of Contents

' in result + assert ('