mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge branch '2.0' into 5602_apidoc_templating
This commit is contained in:
commit
45027677d1
4
CHANGES
4
CHANGES
@ -27,12 +27,16 @@ 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
|
||||
* #5602: apidoc: Add ``--templatedir`` option
|
||||
|
||||
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
|
||||
|
@ -783,29 +783,15 @@ 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
|
||||
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):
|
||||
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(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):
|
||||
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)
|
||||
@ -818,10 +804,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:
|
||||
@ -1166,6 +1148,44 @@ 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[:]:
|
||||
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[:]:
|
||||
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:
|
||||
"""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
|
||||
|
||||
|
||||
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
|
||||
@ -1221,6 +1241,10 @@ 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('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)
|
||||
|
||||
|
@ -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']
|
||||
|
@ -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')
|
||||
|
Loading…
Reference in New Issue
Block a user