mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge pull request #4849 from tk0miya/fix_toctree
Fix toctree directive tries to glob for URL having query_string
This commit is contained in:
commit
ec47b66887
1
CHANGES
1
CHANGES
@ -29,6 +29,7 @@ Bugs fixed
|
||||
* #4837: latex with class memoir Error: Font command ``\sf`` is not supported
|
||||
* #4803: latex: too slow in proportion to number of auto numbered footnotes
|
||||
* #4838: htmlhelp: The entries in .hhp file is not ordered
|
||||
* toctree directive tries to glob for URL having query_string
|
||||
|
||||
Testing
|
||||
--------
|
||||
|
@ -7,6 +7,8 @@
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.parsers.rst import Directive, directives
|
||||
from docutils.parsers.rst.directives.admonitions import BaseAdmonition
|
||||
@ -27,6 +29,9 @@ if False:
|
||||
from sphinx.application import Sphinx # NOQA
|
||||
|
||||
|
||||
glob_re = re.compile('.*[*?\[].*')
|
||||
|
||||
|
||||
def int_or_nothing(argument):
|
||||
# type: (unicode) -> int
|
||||
if not argument:
|
||||
@ -57,30 +62,51 @@ class TocTree(Directive):
|
||||
|
||||
def run(self):
|
||||
# type: () -> List[nodes.Node]
|
||||
env = self.state.document.settings.env
|
||||
suffixes = env.config.source_suffix
|
||||
glob = 'glob' in self.options
|
||||
subnode = addnodes.toctree()
|
||||
subnode['parent'] = self.state.document.settings.env.docname
|
||||
|
||||
ret = []
|
||||
# (title, ref) pairs, where ref may be a document, or an external link,
|
||||
# and title may be None if the document's title is to be used
|
||||
entries = [] # type: List[Tuple[unicode, unicode]]
|
||||
includefiles = []
|
||||
subnode['entries'] = []
|
||||
subnode['includefiles'] = []
|
||||
subnode['maxdepth'] = self.options.get('maxdepth', -1)
|
||||
subnode['caption'] = self.options.get('caption')
|
||||
subnode['glob'] = 'glob' in self.options
|
||||
subnode['hidden'] = 'hidden' in self.options
|
||||
subnode['includehidden'] = 'includehidden' in self.options
|
||||
subnode['numbered'] = self.options.get('numbered', 0)
|
||||
subnode['titlesonly'] = 'titlesonly' in self.options
|
||||
set_source_info(self, subnode)
|
||||
wrappernode = nodes.compound(classes=['toctree-wrapper'])
|
||||
wrappernode.append(subnode)
|
||||
self.add_name(wrappernode)
|
||||
|
||||
ret = self.parse_content(subnode)
|
||||
ret.append(wrappernode)
|
||||
return ret
|
||||
|
||||
def parse_content(self, toctree):
|
||||
env = self.state.document.settings.env
|
||||
suffixes = env.config.source_suffix
|
||||
|
||||
# glob target documents
|
||||
all_docnames = env.found_docs.copy()
|
||||
# don't add the currently visited file in catch-all patterns
|
||||
all_docnames.remove(env.docname)
|
||||
all_docnames.remove(env.docname) # remove current document
|
||||
|
||||
ret = []
|
||||
for entry in self.content:
|
||||
if not entry:
|
||||
continue
|
||||
# look for explicit titles ("Some Title <document>")
|
||||
explicit = explicit_title_re.match(entry)
|
||||
if glob and ('*' in entry or '?' in entry or '[' in entry) and not explicit:
|
||||
if (toctree['glob'] and glob_re.match(entry) and
|
||||
not explicit and not url_re.match(entry)):
|
||||
patname = docname_join(env.docname, entry)
|
||||
docnames = sorted(patfilter(all_docnames, patname))
|
||||
for docname in docnames:
|
||||
all_docnames.remove(docname) # don't include it again
|
||||
entries.append((None, docname))
|
||||
includefiles.append(docname)
|
||||
toctree['entries'].append((None, docname))
|
||||
toctree['includefiles'].append(docname)
|
||||
if not docnames:
|
||||
ret.append(self.state.document.reporter.warning(
|
||||
'toctree glob pattern %r didn\'t match any documents'
|
||||
@ -101,7 +127,7 @@ class TocTree(Directive):
|
||||
# absolutize filenames
|
||||
docname = docname_join(env.docname, docname)
|
||||
if url_re.match(ref) or ref == 'self':
|
||||
entries.append((title, ref))
|
||||
toctree['entries'].append((title, ref))
|
||||
elif docname not in env.found_docs:
|
||||
ret.append(self.state.document.reporter.warning(
|
||||
'toctree contains reference to nonexisting '
|
||||
@ -109,28 +135,13 @@ class TocTree(Directive):
|
||||
env.note_reread()
|
||||
else:
|
||||
all_docnames.discard(docname)
|
||||
entries.append((title, docname))
|
||||
includefiles.append(docname)
|
||||
subnode = addnodes.toctree()
|
||||
subnode['parent'] = env.docname
|
||||
toctree['entries'].append((title, docname))
|
||||
toctree['includefiles'].append(docname)
|
||||
|
||||
# entries contains all entries (self references, external links etc.)
|
||||
if 'reversed' in self.options:
|
||||
entries.reverse()
|
||||
subnode['entries'] = entries
|
||||
# includefiles only entries that are documents
|
||||
subnode['includefiles'] = includefiles
|
||||
subnode['maxdepth'] = self.options.get('maxdepth', -1)
|
||||
subnode['caption'] = self.options.get('caption')
|
||||
subnode['glob'] = glob
|
||||
subnode['hidden'] = 'hidden' in self.options
|
||||
subnode['includehidden'] = 'includehidden' in self.options
|
||||
subnode['numbered'] = self.options.get('numbered', 0)
|
||||
subnode['titlesonly'] = 'titlesonly' in self.options
|
||||
set_source_info(self, subnode)
|
||||
wrappernode = nodes.compound(classes=['toctree-wrapper'])
|
||||
wrappernode.append(subnode)
|
||||
self.add_name(wrappernode)
|
||||
ret.append(wrappernode)
|
||||
toctree['entries'] = list(reversed(toctree['entries']))
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
|
@ -15,7 +15,7 @@ normal order
|
||||
hyperref <https://sphinx-doc.org/?q=sphinx>
|
||||
|
||||
reversed order
|
||||
-------------
|
||||
--------------
|
||||
|
||||
.. toctree::
|
||||
:glob:
|
||||
|
153
tests/test_directive_other.py
Normal file
153
tests/test_directive_other.py
Normal file
@ -0,0 +1,153 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
test_directive_other
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Test the other directives.
|
||||
|
||||
:copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from docutils import nodes
|
||||
from docutils.core import publish_doctree
|
||||
|
||||
from sphinx import addnodes
|
||||
from sphinx.io import SphinxStandaloneReader
|
||||
from sphinx.parsers import RSTParser
|
||||
from sphinx.testing.util import assert_node
|
||||
|
||||
|
||||
def parse(app, docname, text):
|
||||
app.env.temp_data['docname'] = docname
|
||||
return publish_doctree(text, app.srcdir / docname + '.rst',
|
||||
reader=SphinxStandaloneReader(app),
|
||||
parser=RSTParser(),
|
||||
settings_overrides={'env': app.env,
|
||||
'gettext_compact': True})
|
||||
|
||||
|
||||
@pytest.mark.sphinx(testroot='toctree-glob')
|
||||
def test_toctree(app):
|
||||
text = (".. toctree::\n"
|
||||
"\n"
|
||||
" foo\n"
|
||||
" bar/index\n"
|
||||
" baz\n")
|
||||
|
||||
app.env.find_files(app.config, app.builder)
|
||||
doctree = parse(app, 'index', text)
|
||||
assert_node(doctree, [nodes.document, nodes.compound, addnodes.toctree])
|
||||
assert_node(doctree[0][0],
|
||||
entries=[(None, 'foo'), (None, 'bar/index'), (None, 'baz')],
|
||||
includefiles=['foo', 'bar/index', 'baz'])
|
||||
|
||||
|
||||
@pytest.mark.sphinx(testroot='toctree-glob')
|
||||
def test_relative_toctree(app):
|
||||
text = (".. toctree::\n"
|
||||
"\n"
|
||||
" bar_1\n"
|
||||
" bar_2\n"
|
||||
" bar_3\n"
|
||||
" ../quux\n")
|
||||
|
||||
app.env.find_files(app.config, app.builder)
|
||||
doctree = parse(app, 'bar/index', text)
|
||||
assert_node(doctree, [nodes.document, nodes.compound, addnodes.toctree])
|
||||
assert_node(doctree[0][0],
|
||||
entries=[(None, 'bar/bar_1'), (None, 'bar/bar_2'), (None, 'bar/bar_3'),
|
||||
(None, 'quux')],
|
||||
includefiles=['bar/bar_1', 'bar/bar_2', 'bar/bar_3', 'quux'])
|
||||
|
||||
|
||||
@pytest.mark.sphinx(testroot='toctree-glob')
|
||||
def test_toctree_urls_and_titles(app):
|
||||
text = (".. toctree::\n"
|
||||
"\n"
|
||||
" Sphinx <https://www.sphinx-doc.org/>\n"
|
||||
" https://readthedocs.org/\n"
|
||||
" The BAR <bar/index>\n")
|
||||
|
||||
app.env.find_files(app.config, app.builder)
|
||||
doctree = parse(app, 'index', text)
|
||||
assert_node(doctree, [nodes.document, nodes.compound, addnodes.toctree])
|
||||
assert_node(doctree[0][0],
|
||||
entries=[('Sphinx', 'https://www.sphinx-doc.org/'),
|
||||
(None, 'https://readthedocs.org/'),
|
||||
('The BAR', 'bar/index')],
|
||||
includefiles=['bar/index'])
|
||||
|
||||
|
||||
@pytest.mark.sphinx(testroot='toctree-glob')
|
||||
def test_toctree_glob(app):
|
||||
text = (".. toctree::\n"
|
||||
" :glob:\n"
|
||||
"\n"
|
||||
" *\n")
|
||||
|
||||
app.env.find_files(app.config, app.builder)
|
||||
doctree = parse(app, 'index', text)
|
||||
assert_node(doctree, [nodes.document, nodes.compound, addnodes.toctree])
|
||||
assert_node(doctree[0][0],
|
||||
entries=[(None, 'baz'), (None, 'foo'), (None, 'quux')],
|
||||
includefiles=['baz', 'foo', 'quux'])
|
||||
|
||||
# give both docname and glob (case1)
|
||||
text = (".. toctree::\n"
|
||||
" :glob:\n"
|
||||
"\n"
|
||||
" foo\n"
|
||||
" *\n")
|
||||
|
||||
app.env.find_files(app.config, app.builder)
|
||||
doctree = parse(app, 'index', text)
|
||||
assert_node(doctree, [nodes.document, nodes.compound, addnodes.toctree])
|
||||
assert_node(doctree[0][0],
|
||||
entries=[(None, 'foo'), (None, 'baz'), (None, 'quux')],
|
||||
includefiles=['foo', 'baz', 'quux'])
|
||||
|
||||
# give both docname and glob (case2)
|
||||
text = (".. toctree::\n"
|
||||
" :glob:\n"
|
||||
"\n"
|
||||
" *\n"
|
||||
" foo\n")
|
||||
|
||||
app.env.find_files(app.config, app.builder)
|
||||
doctree = parse(app, 'index', text)
|
||||
assert_node(doctree, [nodes.document, nodes.compound, addnodes.toctree])
|
||||
assert_node(doctree[0][0],
|
||||
entries=[(None, 'baz'), (None, 'foo'), (None, 'quux'), (None, 'foo')],
|
||||
includefiles=['baz', 'foo', 'quux', 'foo'])
|
||||
|
||||
|
||||
@pytest.mark.sphinx(testroot='toctree-glob')
|
||||
def test_toctree_glob_and_url(app):
|
||||
text = (".. toctree::\n"
|
||||
" :glob:\n"
|
||||
"\n"
|
||||
" https://example.com/?q=sphinx\n")
|
||||
|
||||
app.env.find_files(app.config, app.builder)
|
||||
doctree = parse(app, 'index', text)
|
||||
assert_node(doctree, [nodes.document, nodes.compound, addnodes.toctree])
|
||||
assert_node(doctree[0][0],
|
||||
entries=[(None, 'https://example.com/?q=sphinx')],
|
||||
includefiles=[])
|
||||
|
||||
|
||||
@pytest.mark.sphinx(testroot='toctree-glob')
|
||||
def test_toctree_twice(app):
|
||||
text = (".. toctree::\n"
|
||||
"\n"
|
||||
" foo\n"
|
||||
" foo\n")
|
||||
|
||||
app.env.find_files(app.config, app.builder)
|
||||
doctree = parse(app, 'index', text)
|
||||
assert_node(doctree, [nodes.document, nodes.compound, addnodes.toctree])
|
||||
assert_node(doctree[0][0],
|
||||
entries=[(None, 'foo'), (None, 'foo')],
|
||||
includefiles=['foo', 'foo'])
|
Loading…
Reference in New Issue
Block a user