mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2026-09-03 20:52:55 -05:00
Merge pull request #5968 from tk0miya/separate_qthelp
Separate qthelp to sphinxcontrib package
This commit is contained in:
@@ -22,6 +22,7 @@ Dependencies
|
||||
* Some packages are separated to sub packages:
|
||||
|
||||
- sphinxcontrib.jsmath
|
||||
- sphinxcontrib.qthelp
|
||||
|
||||
Incompatible changes
|
||||
--------------------
|
||||
|
||||
@@ -290,6 +290,11 @@ The following is a list of deprecated interfaces.
|
||||
- 4.0
|
||||
- ``open()``
|
||||
|
||||
* - ``sphinx.builders.qthelp``
|
||||
- 2.0
|
||||
- 4.0
|
||||
- ``sphinxcontrib.qthelp``
|
||||
|
||||
* - ``sphinx.cmd.quickstart.term_decode()``
|
||||
- 2.0
|
||||
- 4.0
|
||||
|
||||
@@ -72,13 +72,17 @@ The builder's "name" must be given to the **-b** command-line option of
|
||||
|
||||
.. autoattribute:: supported_image_types
|
||||
|
||||
.. module:: sphinx.builders.qthelp
|
||||
.. module:: sphinxcontrib.qthelp
|
||||
.. class:: QtHelpBuilder
|
||||
|
||||
This builder produces the same output as the standalone HTML builder, but
|
||||
also generates `Qt help`_ collection support files that allow the Qt
|
||||
collection generator to compile them.
|
||||
|
||||
.. versionchanged:: 2.0
|
||||
|
||||
Moved to sphinxcontrib.qthelp from sphinx.builders package.
|
||||
|
||||
.. autoattribute:: name
|
||||
|
||||
.. autoattribute:: format
|
||||
|
||||
@@ -16,6 +16,7 @@ if sys.version_info < (3, 5):
|
||||
|
||||
install_requires = [
|
||||
'sphinxcontrib-jsmath',
|
||||
'sphinxcontrib-qthelp',
|
||||
'Jinja2>=2.3',
|
||||
'Pygments>=2.0',
|
||||
'docutils>=0.12',
|
||||
|
||||
@@ -73,7 +73,6 @@ builtin_extensions = (
|
||||
'sphinx.builders.latex',
|
||||
'sphinx.builders.linkcheck',
|
||||
'sphinx.builders.manpage',
|
||||
'sphinx.builders.qthelp',
|
||||
'sphinx.builders.singlehtml',
|
||||
'sphinx.builders.texinfo',
|
||||
'sphinx.builders.text',
|
||||
@@ -107,6 +106,8 @@ builtin_extensions = (
|
||||
'sphinx.environment.collectors.title',
|
||||
'sphinx.environment.collectors.toctree',
|
||||
'sphinx.environment.collectors.indexentries',
|
||||
# 1st party extensions
|
||||
'sphinxcontrib.qthelp',
|
||||
# Strictly, alabaster theme is not a builtin extension,
|
||||
# but it is loaded automatically to use it as default theme.
|
||||
'alabaster',
|
||||
|
||||
+15
-254
@@ -8,275 +8,36 @@
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
import html
|
||||
import os
|
||||
import posixpath
|
||||
import re
|
||||
from os import path
|
||||
from typing import Iterable, cast
|
||||
import warnings
|
||||
|
||||
from docutils import nodes
|
||||
from sphinxcontrib.qthelp import QtHelpBuilder, render_file
|
||||
|
||||
from sphinx import addnodes
|
||||
from sphinx import package_dir
|
||||
from sphinx.builders.html import StandaloneHTMLBuilder
|
||||
from sphinx.environment.adapters.indexentries import IndexEntries
|
||||
from sphinx.locale import __
|
||||
from sphinx.util import logging
|
||||
from sphinx.util.nodes import NodeMatcher
|
||||
from sphinx.util.osutil import canon_path, make_filename
|
||||
from sphinx.util.template import SphinxRenderer
|
||||
import sphinx
|
||||
from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
from typing import Any, Dict, List, Tuple # NOQA
|
||||
from typing import Any, Dict # NOQA
|
||||
from sphinx.application import Sphinx # NOQA
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
_idpattern = re.compile(
|
||||
r'(?P<title>.+) (\((class in )?(?P<id>[\w\.]+)( (?P<descr>\w+))?\))$')
|
||||
|
||||
|
||||
section_template = '<section title="%(title)s" ref="%(ref)s"/>'
|
||||
|
||||
|
||||
def render_file(filename, **kwargs):
|
||||
# type: (str, Any) -> str
|
||||
pathname = os.path.join(package_dir, 'templates', 'qthelp', filename)
|
||||
return SphinxRenderer.render_from_file(pathname, kwargs)
|
||||
|
||||
|
||||
class QtHelpBuilder(StandaloneHTMLBuilder):
|
||||
"""
|
||||
Builder that also outputs Qt help project, contents and index files.
|
||||
"""
|
||||
name = 'qthelp'
|
||||
epilog = __('You can now run "qcollectiongenerator" with the .qhcp '
|
||||
'project file in %(outdir)s, like this:\n'
|
||||
'$ qcollectiongenerator %(outdir)s/%(project)s.qhcp\n'
|
||||
'To view the help file:\n'
|
||||
'$ assistant -collectionFile %(outdir)s/%(project)s.qhc')
|
||||
|
||||
# don't copy the reST source
|
||||
copysource = False
|
||||
supported_image_types = ['image/svg+xml', 'image/png', 'image/gif',
|
||||
'image/jpeg']
|
||||
|
||||
# don't add links
|
||||
add_permalinks = False
|
||||
|
||||
# don't add sidebar etc.
|
||||
embedded = True
|
||||
# disable download role
|
||||
download_support = False
|
||||
|
||||
# don't generate the search index or include the search page
|
||||
search = False
|
||||
|
||||
def init(self):
|
||||
# type: () -> None
|
||||
super().init()
|
||||
# the output files for HTML help must be .html only
|
||||
self.out_suffix = '.html'
|
||||
self.link_suffix = '.html'
|
||||
# self.config.html_style = 'traditional.css'
|
||||
|
||||
def get_theme_config(self):
|
||||
# type: () -> Tuple[str, Dict]
|
||||
return self.config.qthelp_theme, self.config.qthelp_theme_options
|
||||
|
||||
def handle_finish(self):
|
||||
# type: () -> None
|
||||
self.build_qhp(self.outdir, self.config.qthelp_basename)
|
||||
|
||||
def build_qhp(self, outdir, outname):
|
||||
# type: (str, str) -> None
|
||||
logger.info(__('writing project file...'))
|
||||
|
||||
# sections
|
||||
tocdoc = self.env.get_and_resolve_doctree(self.config.master_doc, self,
|
||||
prune_toctrees=False)
|
||||
|
||||
sections = []
|
||||
matcher = NodeMatcher(addnodes.compact_paragraph, toctree=True)
|
||||
for node in tocdoc.traverse(matcher): # type: addnodes.compact_paragraph
|
||||
sections.extend(self.write_toc(node))
|
||||
|
||||
for indexname, indexcls, content, collapse in self.domain_indices:
|
||||
item = section_template % {'title': indexcls.localname,
|
||||
'ref': '%s.html' % indexname}
|
||||
sections.append(' ' * 4 * 4 + item)
|
||||
sections = '\n'.join(sections) # type: ignore
|
||||
|
||||
# keywords
|
||||
keywords = []
|
||||
index = IndexEntries(self.env).create_index(self, group_entries=False)
|
||||
for (key, group) in index:
|
||||
for title, (refs, subitems, key_) in group:
|
||||
keywords.extend(self.build_keywords(title, refs, subitems))
|
||||
keywords = '\n'.join(keywords) # type: ignore
|
||||
|
||||
# it seems that the "namespace" may not contain non-alphanumeric
|
||||
# characters, and more than one successive dot, or leading/trailing
|
||||
# dots, are also forbidden
|
||||
if self.config.qthelp_namespace:
|
||||
nspace = self.config.qthelp_namespace
|
||||
else:
|
||||
nspace = 'org.sphinx.%s.%s' % (outname, self.config.version)
|
||||
|
||||
nspace = re.sub(r'[^a-zA-Z0-9.\-]', '', nspace)
|
||||
nspace = re.sub(r'\.+', '.', nspace).strip('.')
|
||||
nspace = nspace.lower()
|
||||
|
||||
# write the project file
|
||||
with open(path.join(outdir, outname + '.qhp'), 'w', encoding='utf-8') as f:
|
||||
body = render_file('project.qhp', outname=outname,
|
||||
title=self.config.html_title, version=self.config.version,
|
||||
project=self.config.project, namespace=nspace,
|
||||
master_doc=self.config.master_doc,
|
||||
sections=sections, keywords=keywords,
|
||||
files=self.get_project_files(outdir))
|
||||
f.write(body)
|
||||
|
||||
homepage = 'qthelp://' + posixpath.join(
|
||||
nspace, 'doc', self.get_target_uri(self.config.master_doc))
|
||||
startpage = 'qthelp://' + posixpath.join(nspace, 'doc', 'index.html')
|
||||
|
||||
logger.info(__('writing collection project file...'))
|
||||
with open(path.join(outdir, outname + '.qhcp'), 'w', encoding='utf-8') as f:
|
||||
body = render_file('project.qhcp', outname=outname,
|
||||
title=self.config.html_short_title,
|
||||
homepage=homepage, startpage=startpage)
|
||||
f.write(body)
|
||||
|
||||
def isdocnode(self, node):
|
||||
# type: (nodes.Node) -> bool
|
||||
if not isinstance(node, nodes.list_item):
|
||||
return False
|
||||
if len(node.children) != 2:
|
||||
return False
|
||||
if not isinstance(node[0], addnodes.compact_paragraph):
|
||||
return False
|
||||
if not isinstance(node[0][0], nodes.reference):
|
||||
return False
|
||||
if not isinstance(node[1], nodes.bullet_list):
|
||||
return False
|
||||
return True
|
||||
|
||||
def write_toc(self, node, indentlevel=4):
|
||||
# type: (nodes.Node, int) -> List[str]
|
||||
parts = [] # type: List[str]
|
||||
if isinstance(node, nodes.list_item) and self.isdocnode(node):
|
||||
compact_paragraph = cast(addnodes.compact_paragraph, node[0])
|
||||
reference = cast(nodes.reference, compact_paragraph[0])
|
||||
link = reference['refuri']
|
||||
title = html.escape(reference.astext()).replace('"', '"')
|
||||
item = '<section title="%(title)s" ref="%(ref)s">' % \
|
||||
{'title': title, 'ref': link}
|
||||
parts.append(' ' * 4 * indentlevel + item)
|
||||
|
||||
bullet_list = cast(nodes.bullet_list, node[1])
|
||||
list_items = cast(Iterable[nodes.list_item], bullet_list)
|
||||
for list_item in list_items:
|
||||
parts.extend(self.write_toc(list_item, indentlevel + 1))
|
||||
parts.append(' ' * 4 * indentlevel + '</section>')
|
||||
elif isinstance(node, nodes.list_item):
|
||||
for subnode in node:
|
||||
parts.extend(self.write_toc(subnode, indentlevel))
|
||||
elif isinstance(node, nodes.reference):
|
||||
link = node['refuri']
|
||||
title = html.escape(node.astext()).replace('"', '"')
|
||||
item = section_template % {'title': title, 'ref': link}
|
||||
item = ' ' * 4 * indentlevel + item
|
||||
parts.append(item.encode('ascii', 'xmlcharrefreplace').decode())
|
||||
elif isinstance(node, nodes.bullet_list):
|
||||
for subnode in node:
|
||||
parts.extend(self.write_toc(subnode, indentlevel))
|
||||
elif isinstance(node, addnodes.compact_paragraph):
|
||||
for subnode in node:
|
||||
parts.extend(self.write_toc(subnode, indentlevel))
|
||||
|
||||
return parts
|
||||
|
||||
def keyword_item(self, name, ref):
|
||||
# type: (str, Any) -> str
|
||||
matchobj = _idpattern.match(name)
|
||||
if matchobj:
|
||||
groupdict = matchobj.groupdict()
|
||||
shortname = groupdict['title']
|
||||
id = groupdict.get('id')
|
||||
# descr = groupdict.get('descr')
|
||||
if shortname.endswith('()'):
|
||||
shortname = shortname[:-2]
|
||||
id = '%s.%s' % (id, shortname)
|
||||
else:
|
||||
id = None
|
||||
|
||||
nameattr = html.escape(name, quote=True)
|
||||
refattr = html.escape(ref[1], quote=True)
|
||||
if id:
|
||||
item = ' ' * 12 + '<keyword name="%s" id="%s" ref="%s"/>' % (nameattr, id, refattr)
|
||||
else:
|
||||
item = ' ' * 12 + '<keyword name="%s" ref="%s"/>' % (nameattr, refattr)
|
||||
item.encode('ascii', 'xmlcharrefreplace')
|
||||
return item
|
||||
|
||||
def build_keywords(self, title, refs, subitems):
|
||||
# type: (str, List[Any], Any) -> List[str]
|
||||
keywords = [] # type: List[str]
|
||||
|
||||
# if len(refs) == 0: # XXX
|
||||
# write_param('See Also', title)
|
||||
if len(refs) == 1:
|
||||
keywords.append(self.keyword_item(title, refs[0]))
|
||||
elif len(refs) > 1:
|
||||
for i, ref in enumerate(refs): # XXX
|
||||
# item = (' '*12 +
|
||||
# '<keyword name="%s [%d]" ref="%s"/>' % (
|
||||
# title, i, ref))
|
||||
# item.encode('ascii', 'xmlcharrefreplace')
|
||||
# keywords.append(item)
|
||||
keywords.append(self.keyword_item(title, ref))
|
||||
|
||||
if subitems:
|
||||
for subitem in subitems:
|
||||
keywords.extend(self.build_keywords(subitem[0], subitem[1], []))
|
||||
|
||||
return keywords
|
||||
|
||||
def get_project_files(self, outdir):
|
||||
# type: (str) -> List[str]
|
||||
if not outdir.endswith(os.sep):
|
||||
outdir += os.sep
|
||||
olen = len(outdir)
|
||||
project_files = []
|
||||
staticdir = path.join(outdir, '_static')
|
||||
imagesdir = path.join(outdir, self.imagedir)
|
||||
for root, dirs, files in os.walk(outdir):
|
||||
resourcedir = root.startswith((staticdir, imagesdir))
|
||||
for fn in sorted(files):
|
||||
if (resourcedir and not fn.endswith('.js')) or fn.endswith('.html'):
|
||||
filename = path.join(root, fn)[olen:]
|
||||
project_files.append(canon_path(filename))
|
||||
|
||||
return project_files
|
||||
deprecated_alias('sphinx.builders.qthelp',
|
||||
{
|
||||
'render_file': render_file,
|
||||
'QtHelpBuilder': QtHelpBuilder,
|
||||
},
|
||||
RemovedInSphinx40Warning)
|
||||
|
||||
|
||||
def setup(app):
|
||||
# type: (Sphinx) -> Dict[str, Any]
|
||||
app.setup_extension('sphinx.builders.html')
|
||||
app.add_builder(QtHelpBuilder)
|
||||
warnings.warn('sphinx.builders.qthelp has been moved to sphinxcontrib-qthelp.',
|
||||
RemovedInSphinx40Warning)
|
||||
|
||||
app.add_config_value('qthelp_basename', lambda self: make_filename(self.project), None)
|
||||
app.add_config_value('qthelp_namespace', None, 'html', [str])
|
||||
app.add_config_value('qthelp_theme', 'nonav', 'html')
|
||||
app.add_config_value('qthelp_theme_options', {}, 'html')
|
||||
app.setup_extension('sphinxcontrib.qthelp')
|
||||
|
||||
return {
|
||||
'version': 'builtin',
|
||||
'version': sphinx.__display_version__,
|
||||
'parallel_read_safe': True,
|
||||
'parallel_write_safe': True,
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<QHelpCollectionProject version="1.0">
|
||||
<assistant>
|
||||
<title>{{ title|e }}</title>
|
||||
<homePage>{{ homepage|e }}</homePage>
|
||||
<startPage>{{ startpage|e }}</startPage>
|
||||
</assistant>
|
||||
<docFiles>
|
||||
<generate>
|
||||
<file>
|
||||
<input>{{ outname|e }}.qhp</input>
|
||||
<output>{{ outname|e }}.qch</output>
|
||||
</file>
|
||||
</generate>
|
||||
<register>
|
||||
<file>{{ outname|e }}.qch</file>
|
||||
</register>
|
||||
</docFiles>
|
||||
</QHelpCollectionProject>
|
||||
@@ -1,26 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<QtHelpProject version="1.0">
|
||||
<namespace>{{ namespace|e }}</namespace>
|
||||
<virtualFolder>doc</virtualFolder>
|
||||
<customFilter name="{{ project|e }} {{ version|e }}">
|
||||
<filterAttribute>{{ outname|e }}</filterAttribute>
|
||||
<filterAttribute>{{ version|e }}</filterAttribute>
|
||||
</customFilter>
|
||||
<filterSection>
|
||||
<filterAttribute>{{ outname|e }}</filterAttribute>
|
||||
<filterAttribute>{{ version|e }}</filterAttribute>
|
||||
<toc>
|
||||
<section title="{{ title|e }}" ref="{{ master_doc|e }}.html">
|
||||
{{ sections }}
|
||||
</section>
|
||||
</toc>
|
||||
<keywords>
|
||||
{{ keywords }}
|
||||
</keywords>
|
||||
<files>
|
||||
{%- for filename in files %}
|
||||
<file>{{ filename|e }}</file>
|
||||
{%- endfor %}
|
||||
</files>
|
||||
</filterSection>
|
||||
</QtHelpProject>
|
||||
+1
-1
@@ -60,7 +60,7 @@ def nonascii_srcdir(request, rootdir, sphinx_test_tempdir):
|
||||
"buildername",
|
||||
[
|
||||
# note: no 'html' - if it's ok with dirhtml it's ok with html
|
||||
'dirhtml', 'singlehtml', 'pickle', 'json', 'text', 'htmlhelp', 'qthelp',
|
||||
'dirhtml', 'singlehtml', 'pickle', 'json', 'text', 'htmlhelp',
|
||||
'applehelp', 'changes', 'xml', 'pseudoxml', 'linkcheck',
|
||||
],
|
||||
)
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
"""
|
||||
test_build_qthelp
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Test the Qt Help builder and check its output. We don't need to
|
||||
test the HTML itself; that's already handled by
|
||||
:file:`test_build_html.py`.
|
||||
|
||||
:copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
from sphinx.testing.util import etree_parse
|
||||
|
||||
|
||||
@pytest.mark.sphinx('qthelp', testroot='basic')
|
||||
def test_qthelp_basic(app, status, warning):
|
||||
app.builder.build_all()
|
||||
|
||||
qhp = (app.outdir / 'Python.qhp').text()
|
||||
assert '<customFilter name="Python ">' in qhp
|
||||
assert '<filterAttribute>Python</filterAttribute>' in qhp
|
||||
assert '<filterAttribute></filterAttribute>' in qhp
|
||||
assert '<section title="Python documentation" ref="index.html">' in qhp
|
||||
assert '<file>genindex.html</file>' in qhp
|
||||
assert '<file>index.html</file>' in qhp
|
||||
assert '<file>_static/basic.css</file>' in qhp
|
||||
|
||||
qhcp = (app.outdir / 'Python.qhcp').text()
|
||||
assert '<title>Python documentation</title>' in qhcp
|
||||
assert '<homePage>qthelp://org.sphinx.python/doc/index.html</homePage>' in qhcp
|
||||
assert '<startPage>qthelp://org.sphinx.python/doc/index.html</startPage>' in qhcp
|
||||
assert '<input>Python.qhp</input>' in qhcp
|
||||
assert '<output>Python.qch</output>' in qhcp
|
||||
assert '<file>Python.qch</file>' in qhcp
|
||||
|
||||
|
||||
@pytest.mark.sphinx('qthelp', testroot='need-escaped')
|
||||
def test_qthelp_escaped(app, status, warning):
|
||||
app.builder.build_all()
|
||||
|
||||
et = etree_parse(app.outdir / 'needbescapedbproject.qhp')
|
||||
customFilter = et.find('.//customFilter')
|
||||
assert len(customFilter) == 2
|
||||
assert customFilter.attrib == {'name': 'need <b>"escaped"</b> project '}
|
||||
assert customFilter[0].text == 'needbescapedbproject'
|
||||
assert customFilter[1].text is None
|
||||
|
||||
toc = et.find('.//toc')
|
||||
assert len(toc) == 1
|
||||
assert toc[0].attrib == {'title': 'need <b>"escaped"</b> project documentation',
|
||||
'ref': 'index.html'}
|
||||
assert len(toc[0]) == 4
|
||||
assert toc[0][0].attrib == {'title': '<foo>', 'ref': 'foo.html'}
|
||||
assert toc[0][0][0].attrib == {'title': 'quux', 'ref': 'quux.html'}
|
||||
assert toc[0][0][1].attrib == {'title': 'foo "1"', 'ref': 'foo.html#foo-1'}
|
||||
assert toc[0][0][1][0].attrib == {'title': 'foo.1-1', 'ref': 'foo.html#foo-1-1'}
|
||||
assert toc[0][0][2].attrib == {'title': 'foo.2', 'ref': 'foo.html#foo-2'}
|
||||
assert toc[0][1].attrib == {'title': 'bar', 'ref': 'bar.html'}
|
||||
assert toc[0][2].attrib == {'title': 'http://sphinx-doc.org/',
|
||||
'ref': 'http://sphinx-doc.org/'}
|
||||
assert toc[0][3].attrib == {'title': 'baz', 'ref': 'baz.html'}
|
||||
|
||||
keywords = et.find('.//keywords')
|
||||
assert len(keywords) == 2
|
||||
assert keywords[0].attrib == {'name': '<subsection>', 'ref': 'index.html#index-0'}
|
||||
assert keywords[1].attrib == {'name': '"subsection"', 'ref': 'index.html#index-0'}
|
||||
|
||||
|
||||
@pytest.mark.sphinx('qthelp', testroot='basic')
|
||||
def test_qthelp_namespace(app, status, warning):
|
||||
# default namespace
|
||||
app.builder.build_all()
|
||||
|
||||
qhp = (app.outdir / 'Python.qhp').text()
|
||||
assert '<namespace>org.sphinx.python</namespace>' in qhp
|
||||
|
||||
qhcp = (app.outdir / 'Python.qhcp').text()
|
||||
assert '<homePage>qthelp://org.sphinx.python/doc/index.html</homePage>' in qhcp
|
||||
assert '<startPage>qthelp://org.sphinx.python/doc/index.html</startPage>' in qhcp
|
||||
|
||||
# give a namespace
|
||||
app.config.qthelp_namespace = 'org.sphinx-doc.sphinx'
|
||||
app.builder.build_all()
|
||||
|
||||
qhp = (app.outdir / 'Python.qhp').text()
|
||||
assert '<namespace>org.sphinx-doc.sphinx</namespace>' in qhp
|
||||
|
||||
qhcp = (app.outdir / 'Python.qhcp').text()
|
||||
assert '<homePage>qthelp://org.sphinx-doc.sphinx/doc/index.html</homePage>' in qhcp
|
||||
assert '<startPage>qthelp://org.sphinx-doc.sphinx/doc/index.html</startPage>' in qhcp
|
||||
|
||||
|
||||
@pytest.mark.sphinx('qthelp', testroot='basic')
|
||||
def test_qthelp_title(app, status, warning):
|
||||
# default title
|
||||
app.builder.build_all()
|
||||
|
||||
qhp = (app.outdir / 'Python.qhp').text()
|
||||
assert '<section title="Python documentation" ref="index.html">' in qhp
|
||||
|
||||
qhcp = (app.outdir / 'Python.qhcp').text()
|
||||
assert '<title>Python documentation</title>' in qhcp
|
||||
|
||||
# give a title
|
||||
app.config.html_title = 'Sphinx <b>"full"</b> title'
|
||||
app.config.html_short_title = 'Sphinx <b>"short"</b> title'
|
||||
app.builder.build_all()
|
||||
|
||||
qhp = (app.outdir / 'Python.qhp').text()
|
||||
assert ('<section title="Sphinx <b>"full"</b> title" ref="index.html">'
|
||||
in qhp)
|
||||
|
||||
qhcp = (app.outdir / 'Python.qhcp').text()
|
||||
assert '<title>Sphinx <b>"short"</b> title</title>' in qhcp
|
||||
Reference in New Issue
Block a user