From f3b50ebef0357f49b779784f4e0ab8d8d163ec4a Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 18 Mar 2018 12:32:22 +0900 Subject: [PATCH 1/7] Add testcase for qthelp --- tests/test_build_qthelp.py | 46 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/tests/test_build_qthelp.py b/tests/test_build_qthelp.py index de676e6e0..e2e2322d4 100644 --- a/tests/test_build_qthelp.py +++ b/tests/test_build_qthelp.py @@ -14,15 +14,61 @@ import pytest +@pytest.mark.sphinx('qthelp', testroot='basic') +def test_qthelp_basic(app, status, warning): + app.builder.build_all() + + qhcp = (app.outdir / 'Python.qhcp').text() + assert 'Python documentation' in qhcp + assert 'qthelp://org.sphinx.python/doc/index.html' in qhcp + assert 'qthelp://org.sphinx.python/doc/index.html' in qhcp + assert 'Python.qhp' in qhcp + assert 'Python.qch' in qhcp + assert 'Python.qch' in qhcp + + @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 'org.sphinx.python' in qhp + qhcp = (app.outdir / 'Python.qhcp').text() + assert 'qthelp://org.sphinx.python/doc/index.html' in qhcp + assert 'qthelp://org.sphinx.python/doc/index.html' in qhcp + # give a namespace app.config.qthelp_namespace = 'org.sphinx-doc.sphinx' app.builder.build_all() + qhp = (app.outdir / 'Python.qhp').text() assert 'org.sphinxdoc.sphinx' in qhp + + qhcp = (app.outdir / 'Python.qhcp').text() + assert 'qthelp://org.sphinxdoc.sphinx/doc/index.html' in qhcp + assert 'qthelp://org.sphinxdoc.sphinx/doc/index.html' 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 '
' in qhp + + qhcp = (app.outdir / 'Python.qhcp').text() + assert 'Python documentation' in qhcp + + # give a title + app.config.html_title = 'Sphinx "full" title' + app.config.html_short_title = 'Sphinx "short" title' + app.builder.build_all() + + qhp = (app.outdir / 'Python.qhp').text() + assert '
' in qhp + + qhcp = (app.outdir / 'Python.qhcp').text() + assert 'Sphinx <b>"short"</b> title' in qhcp From dfd550eca6c1fe66d9801f90be509fc869c4b8a5 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 18 Mar 2018 12:32:22 +0900 Subject: [PATCH 2/7] builder: Use template for generating .qhcp file --- sphinx/builders/qthelp.py | 45 ++++++++-------------------- sphinx/templates/qthelp/project.qhcp | 19 ++++++++++++ tests/test_build_qthelp.py | 2 +- 3 files changed, 32 insertions(+), 34 deletions(-) create mode 100644 sphinx/templates/qthelp/project.qhcp diff --git a/sphinx/builders/qthelp.py b/sphinx/builders/qthelp.py index 776a2d142..cf9776131 100644 --- a/sphinx/builders/qthelp.py +++ b/sphinx/builders/qthelp.py @@ -19,12 +19,14 @@ from docutils import nodes from six import text_type from sphinx import addnodes +from sphinx import package_dir from sphinx.builders.html import StandaloneHTMLBuilder from sphinx.config import string_classes from sphinx.environment.adapters.indexentries import IndexEntries from sphinx.util import force_decode, logging from sphinx.util.osutil import make_filename from sphinx.util.pycompat import htmlescape +from sphinx.util.template import SphinxRenderer if False: # For type annotation @@ -38,34 +40,6 @@ logger = logging.getLogger(__name__) _idpattern = re.compile( r'(?P.+) (\((class in )?(?P<id>[\w\.]+)( (?P<descr>\w+))?\))$') - -# Qt Help Collection Project (.qhcp). -# Is the input file for the help collection generator. -# It contains references to compressed help files which should be -# included in the collection. -# It may contain various other information for customizing Qt Assistant. -collection_template = u'''\ -<?xml version="1.0" encoding="utf-8" ?> -<QHelpCollectionProject version="1.0"> - <assistant> - <title>%(title)s - %(homepage)s - %(startpage)s - - - - - %(outname)s.qhp - %(outname)s.qch - - - - %(outname)s.qch - - - -''' - # Qt Help Project (.qhp) # This is the input file for the help generator. # It contains the table of contents, indices and references to the @@ -102,6 +76,12 @@ section_template = '
' file_template = ' ' * 12 + '%(filename)s' +def render_file(filename, **kwargs): + # type: (unicode, Any) -> unicode + 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. @@ -232,11 +212,10 @@ class QtHelpBuilder(StandaloneHTMLBuilder): logger.info('writing collection project file...') with codecs.open(path.join(outdir, outname + '.qhcp'), 'w', 'utf-8') as f: # type: ignore # NOQA - f.write(collection_template % { - 'outname': htmlescape(outname), - 'title': htmlescape(self.config.html_short_title), - 'homepage': htmlescape(homepage), - 'startpage': htmlescape(startpage)}) + content = render_file('project.qhcp', outname=outname, + title=self.config.html_short_title, + homepage=homepage, startpage=startpage) + f.write(content) def isdocnode(self, node): # type: (nodes.Node) -> bool diff --git a/sphinx/templates/qthelp/project.qhcp b/sphinx/templates/qthelp/project.qhcp new file mode 100644 index 000000000..fe12eaa14 --- /dev/null +++ b/sphinx/templates/qthelp/project.qhcp @@ -0,0 +1,19 @@ + + + + {{ title|e }} + {{ homepage|e }} + {{ startpage|e }} + + + + + {{ outname|e }}.qhp + {{ outname|e }}.qch + + + + {{ outname|e }}.qch + + + diff --git a/tests/test_build_qthelp.py b/tests/test_build_qthelp.py index e2e2322d4..541e03b75 100644 --- a/tests/test_build_qthelp.py +++ b/tests/test_build_qthelp.py @@ -71,4 +71,4 @@ def test_qthelp_title(app, status, warning): assert '
' in qhp qhcp = (app.outdir / 'Python.qhcp').text() - assert 'Sphinx <b>"short"</b> title' in qhcp + assert 'Sphinx <b>"short"</b> title' in qhcp From dc3faa57b47c0c0e1ded6740d9a8912d26ded76c Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 18 Mar 2018 12:32:23 +0900 Subject: [PATCH 3/7] Add testcase for qthelp (.qhp files) --- tests/test_build_qthelp.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/test_build_qthelp.py b/tests/test_build_qthelp.py index 541e03b75..3beddf554 100644 --- a/tests/test_build_qthelp.py +++ b/tests/test_build_qthelp.py @@ -13,11 +13,23 @@ 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 '' in qhp + assert 'Python' in qhp + assert '' in qhp + assert '
' in qhp + assert 'genindex.html' in qhp + assert 'index.html' in qhp + assert '_static/basic.css' in qhp + assert '_static/down.png' in qhp + qhcp = (app.outdir / 'Python.qhcp').text() assert 'Python documentation' in qhcp assert 'qthelp://org.sphinx.python/doc/index.html' in qhcp @@ -27,6 +39,27 @@ def test_qthelp_basic(app, status, warning): assert 'Python.qch' in qhcp +@pytest.mark.sphinx('qthelp', testroot='toctree') +def test_qthelp_toctree(app, status, warning): + app.builder.build_all() + + et = etree_parse(app.outdir / 'Python.qhp') + toc = et.find('.//toc') + assert len(toc) == 1 + assert toc[0].attrib == {'title': 'Python documentation', + 'ref': 'index.html'} + assert len(toc[0]) == 4 + assert toc[0][0].attrib == {'title': 'foo', 'ref': 'foo.html'} + assert toc[0][1].attrib == {'title': 'bar', 'ref': 'bar.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][2].attrib == {'title': 'http://sphinx-doc.org/', + 'ref': 'http://sphinx-doc.org/'} + assert toc[0][3].attrib == {'title': 'baz', 'ref': 'baz.html'} + + @pytest.mark.sphinx('qthelp', testroot='basic') def test_qthelp_namespace(app, status, warning): # default namespace From c271cc45424bdc75b280fb6311cfcb439ab27e73 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 18 Mar 2018 12:32:23 +0900 Subject: [PATCH 4/7] Use template for generating .qhp file --- sphinx/builders/qthelp.py | 53 +++++-------------------- sphinx/templates/qthelp/project.qhp | 24 +++++++++++ tests/roots/test-need-escaped/bar.rst | 2 + tests/roots/test-need-escaped/baz.rst | 2 + tests/roots/test-need-escaped/conf.py | 5 +++ tests/roots/test-need-escaped/foo.rst | 15 +++++++ tests/roots/test-need-escaped/index.rst | 30 ++++++++++++++ tests/roots/test-need-escaped/quux.rst | 2 + tests/roots/test-need-escaped/qux.rst | 1 + tests/test_build_qthelp.py | 28 +++++++++---- 10 files changed, 110 insertions(+), 52 deletions(-) create mode 100644 sphinx/templates/qthelp/project.qhp create mode 100644 tests/roots/test-need-escaped/bar.rst create mode 100644 tests/roots/test-need-escaped/baz.rst create mode 100644 tests/roots/test-need-escaped/conf.py create mode 100644 tests/roots/test-need-escaped/foo.rst create mode 100644 tests/roots/test-need-escaped/index.rst create mode 100644 tests/roots/test-need-escaped/quux.rst create mode 100644 tests/roots/test-need-escaped/qux.rst diff --git a/sphinx/builders/qthelp.py b/sphinx/builders/qthelp.py index cf9776131..ef729e420 100644 --- a/sphinx/builders/qthelp.py +++ b/sphinx/builders/qthelp.py @@ -40,37 +40,6 @@ logger = logging.getLogger(__name__) _idpattern = re.compile( r'(?P.+) (\((class in )?(?P<id>[\w\.]+)( (?P<descr>\w+))?\))$') -# Qt Help Project (.qhp) -# This is the input file for the help generator. -# It contains the table of contents, indices and references to the -# actual documentation files (*.html). -# In addition it defines a unique namespace for the documentation. -project_template = u'''\ -<?xml version="1.0" encoding="utf-8" ?> -<QtHelpProject version="1.0"> - <namespace>%(namespace)s</namespace> - <virtualFolder>doc</virtualFolder> - <customFilter name="%(project)s %(version)s"> - <filterAttribute>%(outname)s</filterAttribute> - <filterAttribute>%(version)s</filterAttribute> - </customFilter> - <filterSection> - <filterAttribute>%(outname)s</filterAttribute> - <filterAttribute>%(version)s</filterAttribute> - <toc> - <section title="%(title)s" ref="%(masterdoc)s.html"> -%(sections)s - </section> - </toc> - <keywords> -%(keywords)s - </keywords> - <files> -%(files)s - </files> - </filterSection> -</QtHelpProject> -''' section_template = '<section title="%(title)s" ref="%(ref)s"/>' file_template = ' ' * 12 + '<file>%(filename)s</file>' @@ -195,16 +164,12 @@ class QtHelpBuilder(StandaloneHTMLBuilder): # write the project file with codecs.open(path.join(outdir, outname + '.qhp'), 'w', 'utf-8') as f: # type: ignore # NOQA - f.write(project_template % { - 'outname': htmlescape(outname), - 'title': htmlescape(self.config.html_title), - 'version': htmlescape(self.config.version), - 'project': htmlescape(self.config.project), - 'namespace': htmlescape(nspace), - 'masterdoc': htmlescape(self.config.master_doc), - 'sections': sections, - 'keywords': keywords, - 'files': projectfiles}) + content = 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=projectfiles) + f.write(content) homepage = 'qthelp://' + posixpath.join( nspace, 'doc', self.get_target_uri(self.config.master_doc)) @@ -279,9 +244,9 @@ class QtHelpBuilder(StandaloneHTMLBuilder): if id: item = ' ' * 12 + '<keyword name="%s" id="%s" ref="%s"/>' % ( - name, id, ref[1]) + name, id, htmlescape(ref[1])) else: - item = ' ' * 12 + '<keyword name="%s" ref="%s"/>' % (name, ref[1]) + item = ' ' * 12 + '<keyword name="%s" ref="%s"/>' % (name, htmlescape(ref[1])) item.encode('ascii', 'xmlcharrefreplace') return item @@ -289,7 +254,7 @@ class QtHelpBuilder(StandaloneHTMLBuilder): # type: (unicode, List[Any], Any) -> List[unicode] keywords = [] # type: List[unicode] - title = htmlescape(title) + title = htmlescape(title, quote=True) # if len(refs) == 0: # XXX # write_param('See Also', title) if len(refs) == 1: diff --git a/sphinx/templates/qthelp/project.qhp b/sphinx/templates/qthelp/project.qhp new file mode 100644 index 000000000..92a7af0e8 --- /dev/null +++ b/sphinx/templates/qthelp/project.qhp @@ -0,0 +1,24 @@ +<?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> +{{ files }} + </files> + </filterSection> +</QtHelpProject> diff --git a/tests/roots/test-need-escaped/bar.rst b/tests/roots/test-need-escaped/bar.rst new file mode 100644 index 000000000..1cccd3cb7 --- /dev/null +++ b/tests/roots/test-need-escaped/bar.rst @@ -0,0 +1,2 @@ +bar +=== diff --git a/tests/roots/test-need-escaped/baz.rst b/tests/roots/test-need-escaped/baz.rst new file mode 100644 index 000000000..52e2e72ac --- /dev/null +++ b/tests/roots/test-need-escaped/baz.rst @@ -0,0 +1,2 @@ +baz +=== diff --git a/tests/roots/test-need-escaped/conf.py b/tests/roots/test-need-escaped/conf.py new file mode 100644 index 000000000..d65a22e07 --- /dev/null +++ b/tests/roots/test-need-escaped/conf.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- + +master_doc = 'index' +project = 'need <b>"escaped"</b> project' +smartquotes = False diff --git a/tests/roots/test-need-escaped/foo.rst b/tests/roots/test-need-escaped/foo.rst new file mode 100644 index 000000000..70859b3fc --- /dev/null +++ b/tests/roots/test-need-escaped/foo.rst @@ -0,0 +1,15 @@ +<foo> +===== + +.. toctree:: + + quux + +foo "1" +------- + +foo.1-1 +^^^^^^^ + +foo.2 +----- diff --git a/tests/roots/test-need-escaped/index.rst b/tests/roots/test-need-escaped/index.rst new file mode 100644 index 000000000..9ef74e00a --- /dev/null +++ b/tests/roots/test-need-escaped/index.rst @@ -0,0 +1,30 @@ +.. Sphinx Tests documentation master file, created by sphinx-quickstart on Wed Jun 4 23:49:58 2008. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to Sphinx Tests's documentation! +======================================== + +Contents: + +.. toctree:: + :maxdepth: 2 + :numbered: + :caption: Table of Contents + :name: mastertoc + + foo + bar + http://sphinx-doc.org/ + baz + qux + +.. index:: + pair: "subsection"; <subsection> + +---------- +subsection +---------- + +subsubsection +------------- diff --git a/tests/roots/test-need-escaped/quux.rst b/tests/roots/test-need-escaped/quux.rst new file mode 100644 index 000000000..07dd0a0a3 --- /dev/null +++ b/tests/roots/test-need-escaped/quux.rst @@ -0,0 +1,2 @@ +quux +==== diff --git a/tests/roots/test-need-escaped/qux.rst b/tests/roots/test-need-escaped/qux.rst new file mode 100644 index 000000000..26176b947 --- /dev/null +++ b/tests/roots/test-need-escaped/qux.rst @@ -0,0 +1 @@ +qux.rst has no section title diff --git a/tests/test_build_qthelp.py b/tests/test_build_qthelp.py index 3beddf554..a0d4fcf2a 100644 --- a/tests/test_build_qthelp.py +++ b/tests/test_build_qthelp.py @@ -39,26 +39,37 @@ def test_qthelp_basic(app, status, warning): assert '<file>Python.qch</file>' in qhcp -@pytest.mark.sphinx('qthelp', testroot='toctree') -def test_qthelp_toctree(app, status, warning): +@pytest.mark.sphinx('qthelp', testroot='need-escaped') +def test_qthelp_escaped(app, status, warning): app.builder.build_all() - et = etree_parse(app.outdir / 'Python.qhp') + 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': 'Python documentation', + 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][1].attrib == {'title': 'bar', 'ref': 'bar.html'} + 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].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): @@ -101,7 +112,8 @@ def test_qthelp_title(app, status, warning): app.builder.build_all() qhp = (app.outdir / 'Python.qhp').text() - assert '<section title="Sphinx <b>"full"</b> title" ref="index.html">' in qhp + 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' in qhcp From ba8569f131f0031b3785657751c684e5df59948e Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 18 Mar 2018 12:32:23 +0900 Subject: [PATCH 5/7] Do not construct tag by python code --- sphinx/builders/qthelp.py | 39 ++++++++++++++--------------- sphinx/templates/qthelp/project.qhp | 4 ++- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/sphinx/builders/qthelp.py b/sphinx/builders/qthelp.py index ef729e420..4c1016708 100644 --- a/sphinx/builders/qthelp.py +++ b/sphinx/builders/qthelp.py @@ -42,7 +42,6 @@ _idpattern = re.compile( section_template = '
' -file_template = ' ' * 12 + '%(filename)s' def render_file(filename, **kwargs): @@ -132,24 +131,6 @@ class QtHelpBuilder(StandaloneHTMLBuilder): keywords.extend(self.build_keywords(title, refs, subitems)) keywords = u'\n'.join(keywords) # type: ignore - # files - if not outdir.endswith(os.sep): - outdir += os.sep - olen = len(outdir) - projectfiles = [] - staticdir = path.join(outdir, '_static') - imagesdir = path.join(outdir, self.imagedir) - for root, dirs, files in os.walk(outdir): - resourcedir = root.startswith(staticdir) or \ - root.startswith(imagesdir) - for fn in sorted(files): - if (resourcedir and not fn.endswith('.js')) or \ - fn.endswith('.html'): - filename = path.join(root, fn)[olen:] - projectfiles.append(file_template % - {'filename': htmlescape(filename)}) - projectfiles = '\n'.join(projectfiles) # 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 @@ -168,7 +149,8 @@ class QtHelpBuilder(StandaloneHTMLBuilder): 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=projectfiles) + sections=sections, keywords=keywords, + files=self.get_project_files(outdir)) f.write(content) homepage = 'qthelp://' + posixpath.join( @@ -274,6 +256,23 @@ class QtHelpBuilder(StandaloneHTMLBuilder): return keywords + def get_project_files(self, outdir): + # type: (unicode) -> List[unicode] + 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(filename) + + return project_files + def setup(app): # type: (Sphinx) -> Dict[unicode, Any] diff --git a/sphinx/templates/qthelp/project.qhp b/sphinx/templates/qthelp/project.qhp index 92a7af0e8..53f999043 100644 --- a/sphinx/templates/qthelp/project.qhp +++ b/sphinx/templates/qthelp/project.qhp @@ -18,7 +18,9 @@ {{ keywords }} -{{ files }} + {%- for filename in files %} + {{ filename|e }} + {%- endfor %} From a709adf23338c58588440dde98b63970b616c864 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 18 Mar 2018 12:32:02 +0900 Subject: [PATCH 6/7] qthelp: escape keywords in .qhp file --- CHANGES | 2 ++ sphinx/builders/qthelp.py | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index a105fa084..f1e0d3c7b 100644 --- a/CHANGES +++ b/CHANGES @@ -28,6 +28,8 @@ Bugs fixed * #4716: Generation PDF file with TexLive on Windows, file not found error * #4574: vertical space before equation in latex * #4720: message when an image is mismatched for builder is not clear +* #1435: qthelp builder should htmlescape keywords + Testing -------- diff --git a/sphinx/builders/qthelp.py b/sphinx/builders/qthelp.py index 4c1016708..7a5c6c0d6 100644 --- a/sphinx/builders/qthelp.py +++ b/sphinx/builders/qthelp.py @@ -224,11 +224,12 @@ class QtHelpBuilder(StandaloneHTMLBuilder): else: id = None + nameattr = htmlescape(name, quote=True) + refattr = htmlescape(ref[1], quote=True) if id: - item = ' ' * 12 + '' % ( - name, id, htmlescape(ref[1])) + item = ' ' * 12 + '' % (nameattr, id, refattr) else: - item = ' ' * 12 + '' % (name, htmlescape(ref[1])) + item = ' ' * 12 + '' % (nameattr, refattr) item.encode('ascii', 'xmlcharrefreplace') return item @@ -236,7 +237,6 @@ class QtHelpBuilder(StandaloneHTMLBuilder): # type: (unicode, List[Any], Any) -> List[unicode] keywords = [] # type: List[unicode] - title = htmlescape(title, quote=True) # if len(refs) == 0: # XXX # write_param('See Also', title) if len(refs) == 1: From 00c2a90cf89cfa85cfe21537e646eeddbe8556ac Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 18 Mar 2018 12:58:31 +0900 Subject: [PATCH 7/7] Fix mypy violations --- sphinx/builders/qthelp.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/sphinx/builders/qthelp.py b/sphinx/builders/qthelp.py index 7a5c6c0d6..13ea256da 100644 --- a/sphinx/builders/qthelp.py +++ b/sphinx/builders/qthelp.py @@ -145,13 +145,13 @@ class QtHelpBuilder(StandaloneHTMLBuilder): # write the project file with codecs.open(path.join(outdir, outname + '.qhp'), 'w', 'utf-8') as f: # type: ignore # NOQA - content = 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(content) + 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)) @@ -159,10 +159,10 @@ class QtHelpBuilder(StandaloneHTMLBuilder): logger.info('writing collection project file...') with codecs.open(path.join(outdir, outname + '.qhcp'), 'w', 'utf-8') as f: # type: ignore # NOQA - content = render_file('project.qhcp', outname=outname, - title=self.config.html_short_title, - homepage=homepage, startpage=startpage) - f.write(content) + 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