From 09610fb77ecd9b59557489ecd67fc410307d44f8 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 16 Jun 2019 16:36:54 +0900 Subject: [PATCH 1/7] Add testcase for option directive --- tests/test_domain_std.py | 43 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/tests/test_domain_std.py b/tests/test_domain_std.py index dd77c11dd..b4528a6c9 100644 --- a/tests/test_domain_std.py +++ b/tests/test_domain_std.py @@ -13,7 +13,10 @@ from unittest import mock from docutils import nodes from docutils.nodes import definition, definition_list, definition_list_item, term -from sphinx.addnodes import glossary, index +from sphinx import addnodes +from sphinx.addnodes import ( + desc, desc_addname, desc_content, desc_name, desc_signature, glossary, index +) from sphinx.domains.std import StandardDomain from sphinx.testing import restructuredtext from sphinx.testing.util import assert_node @@ -239,3 +242,41 @@ def test_glossary_sorted(app): [nodes.definition, nodes.paragraph, "description"]) assert_node(doctree[0][0][1][1], [nodes.definition, nodes.paragraph, "description"]) + + +def test_cmdoption(app): + text = (".. program:: ls\n" + "\n" + ".. option:: -l\n") + domain = app.env.get_domain('std') + doctree = restructuredtext.parse(app, text) + assert_node(doctree, (addnodes.index, + [desc, ([desc_signature, ([desc_name, "-l"], + [desc_addname, ()])], + [desc_content, ()])])) + assert_node(doctree[0], addnodes.index, + entries=[('pair', 'ls command line option; -l', 'cmdoption-ls-l', '', None)]) + assert ('ls', '-l') in domain.progoptions + assert domain.progoptions[('ls', '-l')] == ('index', 'cmdoption-ls-l') + + +def test_multiple_cmdoptions(app): + text = (".. program:: ls\n" + "\n" + ".. option:: -h, --help\n") + domain = app.env.get_domain('std') + doctree = restructuredtext.parse(app, text) + assert_node(doctree, (addnodes.index, + [desc, ([desc_signature, ([desc_name, "-h"], + [desc_addname, ()], + [desc_addname, ", "], + [desc_name, "--help"], + [desc_addname, ()])], + [desc_content, ()])])) + assert_node(doctree[0], addnodes.index, + entries=[('pair', 'ls command line option; -h, --help', + 'cmdoption-ls-h', '', None)]) + assert ('ls', '-h') in domain.progoptions + assert ('ls', '--help') in domain.progoptions + assert domain.progoptions[('ls', '-h')] == ('index', 'cmdoption-ls-h') + assert domain.progoptions[('ls', '--help')] == ('index', 'cmdoption-ls-h') From 9bef665cb84d4a7ee17fa7b3f6544dae825b170d Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 17 Jun 2019 02:09:02 +0900 Subject: [PATCH 2/7] validate html_static_path on config-inited --- sphinx/builders/html.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index 1fd9e6cac..a21aeb798 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -783,9 +783,6 @@ class StandaloneHTMLBuilder(Builder): excluded = Matcher(self.config.exclude_patterns + ["**/.*"]) for static_path in self.config.html_static_path: entry = path.join(self.confdir, static_path) - if not path.exists(entry): - logger.warning(__('html_static_path entry %r does not exist'), entry) - continue copy_asset(entry, path.join(self.outdir, '_static'), excluded, context=ctx, renderer=self.templates) # copy logo and favicon files if not already in static path @@ -1166,6 +1163,14 @@ def validate_math_renderer(app: Sphinx) -> None: raise ConfigError(__('Unknown math_renderer %r is given.') % name) +def validate_html_static_path(app: Sphinx, config: Config) -> None: + """Check html_static_paths setting.""" + for entry in config.html_static_path[:]: + if not path.exists(path.join(app.confdir, entry)): + logger.warning(__('html_static_path entry %r does not exist'), entry) + config.html_static_path.remove(entry) + + # for compatibility import sphinx.builders.dirhtml # NOQA import sphinx.builders.singlehtml # NOQA @@ -1221,6 +1226,7 @@ def setup(app: Sphinx) -> Dict[str, Any]: # event handlers app.connect('config-inited', convert_html_css_files) app.connect('config-inited', convert_html_js_files) + app.connect('config-inited', validate_html_static_path) app.connect('builder-inited', validate_math_renderer) app.connect('html-page-context', setup_js_tag_helper) From eb9bd50d689b3982d6efdb434091760cd04f979d Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 17 Jun 2019 02:11:06 +0900 Subject: [PATCH 3/7] validate html_extra_path on config-inited --- sphinx/builders/html.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index a21aeb798..1d52419d2 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -815,10 +815,6 @@ class StandaloneHTMLBuilder(Builder): for extra_path in self.config.html_extra_path: entry = path.join(self.confdir, extra_path) - if not path.exists(entry): - logger.warning(__('html_extra_path entry %r does not exist'), entry) - continue - copy_asset(entry, self.outdir, excluded) logger.info(__('done')) except OSError as err: @@ -1163,6 +1159,14 @@ def validate_math_renderer(app: Sphinx) -> None: raise ConfigError(__('Unknown math_renderer %r is given.') % name) +def validate_html_extra_path(app: Sphinx, config: Config) -> None: + """Check html_extra_paths setting.""" + for entry in config.html_extra_path[:]: + if not path.exists(path.join(app.confdir, entry)): + logger.warning(__('html_extra_path entry %r does not exist'), entry) + config.html_extra_path.remove(entry) + + def validate_html_static_path(app: Sphinx, config: Config) -> None: """Check html_static_paths setting.""" for entry in config.html_static_path[:]: @@ -1226,6 +1230,7 @@ def setup(app: Sphinx) -> Dict[str, Any]: # event handlers app.connect('config-inited', convert_html_css_files) app.connect('config-inited', convert_html_js_files) + app.connect('config-inited', validate_html_extra_path) app.connect('config-inited', validate_html_static_path) app.connect('builder-inited', validate_math_renderer) app.connect('html-page-context', setup_js_tag_helper) From dd8ae7e2edbe820ff76f3a836da620f0f0712516 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 17 Jun 2019 22:27:13 +0900 Subject: [PATCH 4/7] validate html_logo on config-inited --- sphinx/builders/html.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index 1d52419d2..6e63a22d2 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -789,9 +789,7 @@ class StandaloneHTMLBuilder(Builder): if self.config.html_logo: logobase = path.basename(self.config.html_logo) logotarget = path.join(self.outdir, '_static', logobase) - if not path.isfile(path.join(self.confdir, self.config.html_logo)): - logger.warning(__('logo file %r does not exist'), self.config.html_logo) - elif not path.isfile(logotarget): + if not path.isfile(logotarget): copyfile(path.join(self.confdir, self.config.html_logo), logotarget) if self.config.html_favicon: @@ -1175,6 +1173,13 @@ def validate_html_static_path(app: Sphinx, config: Config) -> None: config.html_static_path.remove(entry) +def validate_html_logo(app: Sphinx, config: Config) -> None: + """Check html_logo setting.""" + if config.html_logo and not path.isfile(path.join(app.confdir, config.html_logo)): + logger.warning(__('logo file %r does not exist'), config.html_logo) + config.html_logo = None # type: ignore + + # for compatibility import sphinx.builders.dirhtml # NOQA import sphinx.builders.singlehtml # NOQA @@ -1232,6 +1237,7 @@ def setup(app: Sphinx) -> Dict[str, Any]: app.connect('config-inited', convert_html_js_files) app.connect('config-inited', validate_html_extra_path) app.connect('config-inited', validate_html_static_path) + app.connect('config-inited', validate_html_logo) app.connect('builder-inited', validate_math_renderer) app.connect('html-page-context', setup_js_tag_helper) From fe0f83daf8f00a2838711d988cb6bd1a89242a20 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 17 Jun 2019 22:27:15 +0900 Subject: [PATCH 5/7] validate html_favicon on config-inited --- sphinx/builders/html.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index 6e63a22d2..c8d330d2d 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -795,10 +795,7 @@ class StandaloneHTMLBuilder(Builder): if self.config.html_favicon: iconbase = path.basename(self.config.html_favicon) icontarget = path.join(self.outdir, '_static', iconbase) - if not path.isfile(path.join(self.confdir, self.config.html_favicon)): - logger.warning(__('favicon file %r does not exist'), - self.config.html_favicon) - elif not path.isfile(icontarget): + if not path.isfile(icontarget): copyfile(path.join(self.confdir, self.config.html_favicon), icontarget) logger.info(__('done')) @@ -1180,6 +1177,13 @@ def validate_html_logo(app: Sphinx, config: Config) -> None: config.html_logo = None # type: ignore +def validate_html_favicon(app: Sphinx, config: Config) -> None: + """Check html_favicon setting.""" + if config.html_favicon and not path.isfile(path.join(app.confdir, config.html_favicon)): + logger.warning(__('favicon file %r does not exist'), config.html_favicon) + config.html_favicon = None # type: ignore + + # for compatibility import sphinx.builders.dirhtml # NOQA import sphinx.builders.singlehtml # NOQA @@ -1238,6 +1242,7 @@ def setup(app: Sphinx) -> Dict[str, Any]: app.connect('config-inited', validate_html_extra_path) app.connect('config-inited', validate_html_static_path) app.connect('config-inited', validate_html_logo) + app.connect('config-inited', validate_html_favicon) app.connect('builder-inited', validate_math_renderer) app.connect('html-page-context', setup_js_tag_helper) From d8208d406a0c470b34de79c4b054ba05f50ae34c Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Tue, 18 Jun 2019 00:17:15 +0900 Subject: [PATCH 6/7] Close #1464: html: warn html_static_path is inside outdir --- CHANGES | 2 ++ sphinx/builders/html.py | 12 ++++++++++-- tests/test_build_html.py | 27 +++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 6e38020e8..faa12bb10 100644 --- a/CHANGES +++ b/CHANGES @@ -25,6 +25,8 @@ Features added -------------- * #5124: graphviz: ``:graphviz_dot:`` option is renamed to ``:layout:`` +* #1464: html: emit a warning if :confval:`html_static_path` and + :confval:`html_extra_path` directories are inside output directory Bugs fixed ---------- diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index c8d330d2d..7c72ed785 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -1157,17 +1157,25 @@ def validate_math_renderer(app: Sphinx) -> None: def validate_html_extra_path(app: Sphinx, config: Config) -> None: """Check html_extra_paths setting.""" for entry in config.html_extra_path[:]: - if not path.exists(path.join(app.confdir, entry)): + extra_path = path.normpath(path.join(app.confdir, entry)) + if not path.exists(extra_path): logger.warning(__('html_extra_path entry %r does not exist'), entry) config.html_extra_path.remove(entry) + elif path.commonpath([app.outdir, extra_path]) == app.outdir: + logger.warning(__('html_extra_path entry %r is placed inside outdir'), entry) + config.html_extra_path.remove(entry) def validate_html_static_path(app: Sphinx, config: Config) -> None: """Check html_static_paths setting.""" for entry in config.html_static_path[:]: - if not path.exists(path.join(app.confdir, entry)): + static_path = path.normpath(path.join(app.confdir, entry)) + if not path.exists(static_path): logger.warning(__('html_static_path entry %r does not exist'), entry) config.html_static_path.remove(entry) + elif path.commonpath([app.outdir, static_path]) == app.outdir: + logger.warning(__('html_static_path entry %r is placed inside outdir'), entry) + config.html_static_path.remove(entry) def validate_html_logo(app: Sphinx, config: Config) -> None: diff --git a/tests/test_build_html.py b/tests/test_build_html.py index 677ca9de0..352166d94 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -16,6 +16,7 @@ from itertools import cycle, chain import pytest from html5lib import HTMLParser +from sphinx.builders.html import validate_html_extra_path, validate_html_static_path from sphinx.errors import ConfigError from sphinx.testing.util import strip_escseq from sphinx.util import docutils @@ -1496,3 +1497,29 @@ def test_html_pygments_style_manually(app): def test_html_pygments_for_classic_theme(app): style = app.builder.highlighter.formatter_args.get('style') assert style.__name__ == 'SphinxStyle' + + +@pytest.mark.sphinx(testroot='basic', srcdir='validate_html_extra_path') +def test_validate_html_extra_path(app): + (app.confdir / '_static').makedirs() + app.config.html_extra_path = [ + '/path/to/not_found', # not found + '_static', + app.outdir, # outdir + app.outdir / '_static', # inside outdir + ] + validate_html_extra_path(app, app.config) + assert app.config.html_extra_path == ['_static'] + + +@pytest.mark.sphinx(testroot='basic', srcdir='validate_html_static_path') +def test_validate_html_static_path(app): + (app.confdir / '_static').makedirs() + app.config.html_static_path = [ + '/path/to/not_found', # not found + '_static', + app.outdir, # outdir + app.outdir / '_static', # inside outdir + ] + validate_html_static_path(app, app.config) + assert app.config.html_static_path == ['_static'] From 83c59bba6d5a7edcfb139a8218f6b3aa9a35c780 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Tue, 18 Jun 2019 01:27:44 +0900 Subject: [PATCH 7/7] Fix #6499: html: Sphinx never updates a copy of html_logo --- CHANGES | 2 ++ sphinx/builders/html.py | 14 ++++---------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/CHANGES b/CHANGES index 6e38020e8..f09413c62 100644 --- a/CHANGES +++ b/CHANGES @@ -30,6 +30,8 @@ Bugs fixed ---------- * py domain: duplicated warning does not point the location of source code +* #6499: html: Sphinx never updates a copy of :confval:`html_logo` even if + original file has changed * #1125: html theme: scrollbar is hard to see on classic theme and macOS * #5502: linkcheck: Consider HTTP 503 response as not an error * #6439: Make generated download links reproducible diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index c8d330d2d..147ac14a9 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -787,17 +787,11 @@ class StandaloneHTMLBuilder(Builder): context=ctx, renderer=self.templates) # copy logo and favicon files if not already in static path if self.config.html_logo: - logobase = path.basename(self.config.html_logo) - logotarget = path.join(self.outdir, '_static', logobase) - if not path.isfile(logotarget): - copyfile(path.join(self.confdir, self.config.html_logo), - logotarget) + entry = path.join(self.confdir, self.config.html_logo) + copy_asset(entry, path.join(self.outdir, '_static')) if self.config.html_favicon: - iconbase = path.basename(self.config.html_favicon) - icontarget = path.join(self.outdir, '_static', iconbase) - if not path.isfile(icontarget): - copyfile(path.join(self.confdir, self.config.html_favicon), - icontarget) + entry = path.join(self.confdir, self.config.html_favicon) + copy_asset(entry, path.join(self.outdir, '_static')) logger.info(__('done')) except OSError as err: logger.warning(__('cannot copy static file %r'), err)