diff --git a/CHANGES b/CHANGES index 465faf1a3..0355e9e4c 100644 --- a/CHANGES +++ b/CHANGES @@ -48,6 +48,12 @@ Bugs fixed * #2134: Fix figure caption with reference causes latex build error * #2094: Fix rubric with reference not working in Latex * #2147: Fix literalinclude code in latex does not break in pages +* #1833: Fix email addresses is showed again if latex_show_urls is not None +* #2176: sphinx.ext.graphviz: use instead of to embed svg +* #967: Fix SVG inheritance diagram is not hyperlinked (clickable) +* #1237: Fix footnotes not working in definition list in LaTeX +* #2168: Fix raw directive does not work for text writer +* #2171: Fix cannot linkcheck url with unicode Release 1.3.3 (released Dec 2, 2015) diff --git a/doc/ext/graphviz.rst b/doc/ext/graphviz.rst index d5a5969f4..ef57da4fd 100644 --- a/doc/ext/graphviz.rst +++ b/doc/ext/graphviz.rst @@ -111,7 +111,18 @@ There are also these new config values: .. confval:: graphviz_output_format The output format for Graphviz when building HTML files. This must be either - ``'png'`` or ``'svg'``; the default is ``'png'``. + ``'png'`` or ``'svg'``; the default is ``'png'``. If ``'svg'`` is used, in + order to make the URL links work properly, an appropriate ``target`` + attribute must be set, such as ``"_top"`` and ``"_blank"``. For example, the + link in the following graph should work in the svg output: :: + + .. graphviz:: + + digraph example { + a [label="sphinx", href="http://sphinx-doc.org", target="_top"]; + b [label="other"]; + a -> b; + } .. versionadded:: 1.0 Previously, output always was PNG. diff --git a/sphinx/builders/linkcheck.py b/sphinx/builders/linkcheck.py index b05c5b2e0..15b22d9e1 100644 --- a/sphinx/builders/linkcheck.py +++ b/sphinx/builders/linkcheck.py @@ -17,7 +17,7 @@ from os import path from six.moves import queue from six.moves.urllib.request import build_opener, Request, HTTPRedirectHandler -from six.moves.urllib.parse import unquote, urlsplit, quote +from six.moves.urllib.parse import unquote from six.moves.urllib.error import HTTPError from six.moves.html_parser import HTMLParser from docutils import nodes @@ -33,6 +33,7 @@ except ImportError: pass from sphinx.builders import Builder +from sphinx.util import encode_uri from sphinx.util.console import purple, red, darkgreen, darkgray, \ darkred, turquoise from sphinx.util.pycompat import TextIOWrapper @@ -153,15 +154,7 @@ class CheckExternalLinksBuilder(Builder): try: req_url.encode('ascii') except UnicodeError: - split = urlsplit(req_url) - req_url = (split[0].encode() + '://' + # scheme - split[1].encode('idna') + # netloc - quote(split[2].encode('utf-8'))) # path - if split[3]: # query - req_url += '?' + quote(split[3].encode('utf-8')) - # go back to Unicode strings which is required by Python 3 - # (but now all parts are pure ascii) - req_url = req_url.decode('ascii') + req_url = encode_uri(req_url) # need to actually check the URI try: diff --git a/sphinx/ext/graphviz.py b/sphinx/ext/graphviz.py index 3970edc07..e0c73ecd8 100644 --- a/sphinx/ext/graphviz.py +++ b/sphinx/ext/graphviz.py @@ -223,7 +223,8 @@ def render_dot_html(self, node, code, options, prefix='graphviz', alt = node.get('alt', self.encode(code).strip()) imgcss = imgcls and 'class="%s"' % imgcls or '' if format == 'svg': - svgtag = '%s\n' % (fname, alt, imgcss) + svgtag = ''' +

%s

\n''' % (fname, alt) self.body.append(svgtag) else: mapfile = open(outfn + '.map', 'rb') diff --git a/sphinx/ext/inheritance_diagram.py b/sphinx/ext/inheritance_diagram.py index f618aaf15..0f4a18c63 100644 --- a/sphinx/ext/inheritance_diagram.py +++ b/sphinx/ext/inheritance_diagram.py @@ -264,6 +264,7 @@ class InheritanceGraph(object): this_node_attrs = n_attrs.copy() if fullname in urls: this_node_attrs['URL'] = '"%s"' % urls[fullname] + this_node_attrs['target'] = '"_top"' if tooltip: this_node_attrs['tooltip'] = tooltip res.append(' "%s" [%s];\n' % @@ -348,12 +349,20 @@ def html_visit_inheritance_diagram(self, node): name = 'inheritance%s' % graph_hash # Create a mapping from fully-qualified class names to URLs. + graphviz_output_format = self.builder.env.config.graphviz_output_format.upper() + current_filename = self.builder.current_docname + self.builder.out_suffix urls = {} for child in node: if child.get('refuri') is not None: - urls[child['reftitle']] = child.get('refuri') + if graphviz_output_format == 'SVG': + urls[child['reftitle']] = "../" + child.get('refuri') + else: + urls[child['reftitle']] = child.get('refuri') elif child.get('refid') is not None: - urls[child['reftitle']] = '#' + child.get('refid') + if graphviz_output_format == 'SVG': + urls[child['reftitle']] = '../' + current_filename + '#' + child.get('refid') + else: + urls[child['reftitle']] = '#' + child.get('refid') dotcode = graph.generate_dot(name, urls, env=self.builder.env) render_dot_html(self, node, dotcode, [], 'inheritance', 'inheritance', diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py index e23539fdc..bd7f564f7 100644 --- a/sphinx/util/__init__.py +++ b/sphinx/util/__init__.py @@ -23,6 +23,7 @@ from collections import deque from six import iteritems, text_type, binary_type from six.moves import range +from six.moves.urllib.parse import urlsplit, urlunsplit, quote_plus, parse_qsl, urlencode import docutils from docutils.utils import relative_path @@ -523,3 +524,13 @@ def import_object(objname, source=None): raise ExtensionError('Could not find %s' % objname + (source and ' (needed for %s)' % source or ''), err) + + +def encode_uri(uri): + split = list(urlsplit(uri)) + split[1] = split[1].encode('idna').decode('ascii') + split[2] = quote_plus(split[2].encode('utf-8'), '/').decode('ascii') + query = list((q, quote_plus(v.encode('utf-8'))) + for (q, v) in parse_qsl(split[3])) + split[3] = urlencode(query).decode('ascii') + return urlunsplit(split) diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 746797a70..6f56c5fb0 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -69,6 +69,8 @@ FOOTER = r''' \end{document} ''' +URI_SCHEMES = ('mailto:', 'http:', 'https:', 'ftp:') + class collected_footnote(nodes.footnote): """Footnotes that are collected are assigned this class.""" @@ -98,6 +100,8 @@ class LaTeXWriter(writers.Writer): self.builder.translator_class or LaTeXTranslator) def translate(self): + transform = ShowUrlsTransform(self.document) + transform.apply() visitor = self.translator_class(self.document, self.builder) self.document.walkabout(visitor) self.output = visitor.astext() @@ -126,6 +130,99 @@ if hasattr(Babel, '_ISO639_TO_BABEL'): Babel._ISO639_TO_BABEL['sl'] = 'slovene' +class ShowUrlsTransform(object): + expanded = False + + def __init__(self, document): + self.document = document + + def apply(self): + # replace id_prefix temporarily + id_prefix = self.document.settings.id_prefix + self.document.settings.id_prefix = 'show_urls' + + self.expand_show_urls() + if self.expanded: + self.renumber_footnotes() + + # restore id_prefix + self.document.settings.id_prefix = id_prefix + + def expand_show_urls(self): + show_urls = self.document.settings.env.config.latex_show_urls + if show_urls is False or show_urls == 'no': + return + + for node in self.document.traverse(nodes.reference): + uri = node.get('refuri', '') + if uri.startswith(URI_SCHEMES): + if uri.startswith('mailto:'): + uri = uri[7:] + if node.astext() != uri: + index = node.parent.index(node) + if show_urls == 'footnote': + footnote_nodes = self.create_footnote(uri) + for i, fn in enumerate(footnote_nodes): + node.parent.insert(index + i + 1, fn) + + self.expanded = True + else: # all other true values (b/w compat) + textnode = nodes.Text(" (%s)" % uri) + node.parent.insert(index + 1, textnode) + + def create_footnote(self, uri): + label = nodes.label('', '#') + para = nodes.paragraph() + para.append(nodes.Text(uri)) + footnote = nodes.footnote(uri, label, para, auto=1) + footnote['names'].append('#') + self.document.note_autofootnote(footnote) + + label = nodes.Text('#') + footnote_ref = nodes.footnote_reference('[#]_', label, auto=1, + refid=footnote['ids'][0]) + self.document.note_autofootnote_ref(footnote_ref) + footnote.add_backref(footnote_ref['ids'][0]) + + return [footnote, footnote_ref] + + def renumber_footnotes(self): + def is_used_number(number): + for node in self.document.traverse(nodes.footnote): + if not node.get('auto') and number in node['names']: + return True + + return False + + def is_auto_footnote(node): + return isinstance(node, nodes.footnote) and node.get('auto') + + def footnote_ref_by(ids): + def is_footnote_ref(node): + return isinstance(node, nodes.footnote_reference) and ids[0] == node['refid'] + + return is_footnote_ref + + startnum = 1 + for footnote in self.document.traverse(is_auto_footnote): + while True: + label = str(startnum) + startnum += 1 + if not is_used_number(label): + break + + old_label = footnote[0].astext() + footnote.remove(footnote[0]) + footnote.insert(0, nodes.label('', label)) + if old_label in footnote['names']: + footnote['names'].remove(old_label) + footnote['names'].append(label) + + for footnote_ref in self.document.traverse(footnote_ref_by(footnote['ids'])): + footnote_ref.remove(footnote_ref[0]) + footnote_ref += nodes.Text(label) + + class Table(object): def __init__(self): self.col = 0 @@ -278,6 +375,7 @@ class LaTeXTranslator(nodes.NodeVisitor): sys.maxsize]] self.bodystack = [] self.footnotestack = [] + self.termfootnotestack = [] self.curfilestack = [] self.handled_abbrs = set() if document.settings.docclass == 'howto': @@ -297,6 +395,7 @@ class LaTeXTranslator(nodes.NodeVisitor): self.in_footnote = 0 self.in_caption = 0 self.in_container_literal_block = 0 + self.in_term = 0 self.first_document = 1 self.this_is_the_title = 1 self.literal_whitespace = 0 @@ -761,7 +860,7 @@ class LaTeXTranslator(nodes.NodeVisitor): def visit_collected_footnote(self, node): self.in_footnote += 1 - if 'in_table' in node: + if 'footnotetext' in node: self.body.append('\\footnotetext[%s]{' % node['number']) else: self.body.append('\\footnote[%s]{' % node['number']) @@ -864,7 +963,7 @@ class LaTeXTranslator(nodes.NodeVisitor): self.body.append('\\end{threeparttable}\n\n') if self.table.footnotes: for footnode in self.table.footnotes: - footnode['in_table'] = True + footnode['footnotetext'] = True footnode.walkabout(self) self.table = None self.tablebody = None @@ -1031,14 +1130,22 @@ class LaTeXTranslator(nodes.NodeVisitor): pass def visit_term(self, node): + self.in_term += 1 ctx = '}] \\leavevmode' if node.get('ids'): ctx += self.hypertarget(node['ids'][0]) self.body.append('\\item[{') + self.termfootnotestack.append([]) self.context.append(ctx) def depart_term(self, node): self.body.append(self.context.pop()) + footnotes = self.termfootnotestack.pop() + for footnode in footnotes: + footnode['footnotetext'] = True + footnode.walkabout(self) + + self.in_term -= 1 def visit_termsep(self, node): self.body.append(', ') @@ -1399,23 +1506,9 @@ class LaTeXTranslator(nodes.NodeVisitor): uri = '%' + self.curfilestack[-1] + '#' + node['refid'] if self.in_title or not uri: self.context.append('') - elif uri.startswith('mailto:') or uri.startswith('http:') or \ - uri.startswith('https:') or uri.startswith('ftp:'): + elif uri.startswith(URI_SCHEMES): self.body.append('\\href{%s}{' % self.encode_uri(uri)) - # if configured, put the URL after the link - show_urls = self.builder.config.latex_show_urls - if node.astext() != uri and show_urls and show_urls != 'no': - if uri.startswith('mailto:'): - uri = uri[7:] - if show_urls == 'footnote' and not \ - (self.in_footnote or self.in_caption): - # obviously, footnotes in footnotes are not going to work - self.context.append( - r'}\footnote{%s}' % self.encode_uri(uri)) - else: # all other true values (b/w compat) - self.context.append('} (%s)' % self.encode_uri(uri)) - else: - self.context.append('}') + self.context.append('}') elif uri.startswith('#'): # references to labels in the same document id = self.curfilestack[-1] + ':' + uri[1:] @@ -1566,7 +1659,7 @@ class LaTeXTranslator(nodes.NodeVisitor): # if a footnote has been inserted once, it shouldn't be repeated # by the next reference if used: - if self.table: + if self.table or self.in_term: self.body.append('\\protect\\footnotemark[%s]' % num) else: self.body.append('\\footnotemark[%s]' % num) @@ -1574,6 +1667,9 @@ class LaTeXTranslator(nodes.NodeVisitor): self.footnotestack[-1][num][1] = True self.body.append('\\protect\\footnotemark[%s]' % num) self.table.footnotes.append(footnode) + elif self.in_term: + self.body.append('\\footnotemark[%s]' % num) + self.termfootnotestack[-1].append(footnode) else: if self.in_caption: raise UnsupportedError('%s:%s: footnotes in float captions ' diff --git a/sphinx/writers/text.py b/sphinx/writers/text.py index a4785a980..8e5fd237d 100644 --- a/sphinx/writers/text.py +++ b/sphinx/writers/text.py @@ -940,7 +940,9 @@ class TextTranslator(nodes.NodeVisitor): def visit_raw(self, node): if 'text' in node.get('format', '').split(): - self.body.append(node.astext()) + self.new_state(0) + self.add_text(node.astext()) + self.end_state(wrap = False) raise nodes.SkipNode def visit_math(self, node): diff --git a/tests/path.py b/tests/path.py index 2f0cdc062..68984e0ae 100755 --- a/tests/path.py +++ b/tests/path.py @@ -10,7 +10,7 @@ import os import sys import shutil -from codecs import open +from io import open from six import PY2, text_type @@ -126,35 +126,31 @@ class path(text_type): def utime(self, arg): os.utime(self, arg) - def write_text(self, text, **kwargs): + def open(self, mode='r', **kwargs): + return open(self, mode, **kwargs) + + def write_text(self, text, encoding='utf-8', **kwargs): """ Writes the given `text` to the file. """ - f = open(self, 'w', **kwargs) - try: + if isinstance(text, bytes): + text = text.decode(encoding) + with open(self, 'w', encoding=encoding, **kwargs) as f: f.write(text) - finally: - f.close() - def text(self, **kwargs): + def text(self, encoding='utf-8', **kwargs): """ Returns the text in the file. """ - f = open(self, mode='U', **kwargs) - try: + with open(self, mode='U', encoding=encoding, **kwargs) as f: return f.read() - finally: - f.close() def bytes(self): """ Returns the bytes in the file. """ - f = open(self, mode='rb') - try: + with open(self, mode='rb') as f: return f.read() - finally: - f.close() def write_bytes(self, bytes, append=False): """ @@ -167,11 +163,8 @@ class path(text_type): mode = 'ab' else: mode = 'wb' - f = open(self, mode=mode) - try: + with open(self, mode=mode) as f: f.write(bytes) - finally: - f.close() def exists(self): """ diff --git a/tests/roots/test-references-in-caption/conf.py b/tests/roots/test-footnotes/conf.py similarity index 100% rename from tests/roots/test-references-in-caption/conf.py rename to tests/roots/test-footnotes/conf.py diff --git a/tests/roots/test-footnotes/index.rst b/tests/roots/test-footnotes/index.rst new file mode 100644 index 000000000..c7fe38ff9 --- /dev/null +++ b/tests/roots/test-footnotes/index.rst @@ -0,0 +1,45 @@ +=============== +test-footenotes +=============== + +The section with a reference to [AuthorYear]_ +============================================= + +.. figure:: rimg.png + + This is the figure caption with a reference to [AuthorYear]_. + +.. list-table:: The table title with a reference to [AuthorYear]_ + :header-rows: 1 + + * - Header1 + - Header2 + * - Content + - Content + +.. rubric:: The rubric title with a reference to [AuthorYear]_ + +.. [#] First + +* First footnote: [#]_ +* Second footnote: [1]_ +* `Sphinx `_ +* Third footnote: [#]_ +* `URL including tilde `_ +* GitHub Page: `https://github.com/sphinx-doc/sphinx `_ +* Mailing list: `sphinx-dev@googlegroups.com `_ + +.. [AuthorYear] Author, Title, Year +.. [1] Second +.. [#] Third + +`URL in term `_ + Description Description Description ... + +Footnote in term [#]_ + Description Description Description ... + + `Term in deflist `_ + Description2 + +.. [#] Footnote in term diff --git a/tests/roots/test-references-in-caption/rimg.png b/tests/roots/test-footnotes/rimg.png similarity index 100% rename from tests/roots/test-references-in-caption/rimg.png rename to tests/roots/test-footnotes/rimg.png diff --git a/tests/roots/test-references-in-caption/index.rst b/tests/roots/test-references-in-caption/index.rst deleted file mode 100644 index decec1bad..000000000 --- a/tests/roots/test-references-in-caption/index.rst +++ /dev/null @@ -1,22 +0,0 @@ -============== -test-reference -============== - -The section with a reference to [AuthorYear]_ -============================================= - -.. figure:: rimg.png - - This is the figure caption with a reference to [AuthorYear]_. - -.. list-table:: The table title with a reference to [AuthorYear]_ - :header-rows: 1 - - * - Header1 - - Header2 - * - Content - - Content - -.. rubric:: The rubric title with a reference to [AuthorYear]_ - -.. [AuthorYear] Author, Title, Year diff --git a/tests/test_build.py b/tests/test_build.py index ed39f6971..5b9d7a756 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -58,12 +58,12 @@ def test_build_all(): """)) master_doc = srcdir / 'contents.txt' - master_doc.write_bytes((master_doc.text() + dedent(""" + master_doc.write_text(master_doc.text() + dedent(u""" .. toctree:: %(test_name)s/%(test_name)s """ % {'test_name': test_name}) - ).encode('utf-8')) + ) # note: no 'html' - if it's ok with dirhtml it's ok with html for buildername in ['dirhtml', 'singlehtml', 'latex', 'texinfo', 'pickle', diff --git a/tests/test_build_html.py b/tests/test_build_html.py index 4a22aebbd..91bc8e382 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -376,11 +376,8 @@ def test_html_output(app, status, warning): for fname, paths in iteritems(HTML_XPATH): parser = NslessParser() parser.entity.update(html_entities.entitydefs) - fp = open(os.path.join(app.outdir, fname), 'rb') - try: + with (app.outdir / fname).open('rb') as fp: etree = ET.parse(fp, parser) - finally: - fp.close() for path, check in paths: yield check_xpath, etree, fname, path, check @@ -429,11 +426,8 @@ def test_tocdepth(app, status, warning): for fname, paths in iteritems(expects): parser = NslessParser() parser.entity.update(html_entities.entitydefs) - fp = open(os.path.join(app.outdir, fname), 'rb') - try: + with (app.outdir / fname).open('rb') as fp: etree = ET.parse(fp, parser) - finally: - fp.close() for xpath, check, be_found in paths: yield check_xpath, etree, fname, xpath, check, be_found @@ -474,11 +468,8 @@ def test_tocdepth_singlehtml(app, status, warning): for fname, paths in iteritems(expects): parser = NslessParser() parser.entity.update(html_entities.entitydefs) - fp = open(os.path.join(app.outdir, fname), 'rb') - try: + with (app.outdir / fname).open('rb') as fp: etree = ET.parse(fp, parser) - finally: - fp.close() for xpath, check, be_found in paths: yield check_xpath, etree, fname, xpath, check, be_found @@ -531,11 +522,8 @@ def test_numfig_disabled(app, status, warning): for fname, paths in iteritems(expects): parser = NslessParser() parser.entity.update(html_entities.entitydefs) - fp = open(os.path.join(app.outdir, fname), 'rb') - try: + with (app.outdir / fname).open('rb') as fp: etree = ET.parse(fp, parser) - finally: - fp.close() for xpath, check, be_found in paths: yield check_xpath, etree, fname, xpath, check, be_found @@ -633,11 +621,8 @@ def test_numfig_without_numbered_toctree(app, status, warning): for fname, paths in iteritems(expects): parser = NslessParser() parser.entity.update(html_entities.entitydefs) - fp = open(os.path.join(app.outdir, fname), 'rb') - try: + with (app.outdir / fname).open('rb') as fp: etree = ET.parse(fp, parser) - finally: - fp.close() for xpath, check, be_found in paths: yield check_xpath, etree, fname, xpath, check, be_found @@ -731,11 +716,8 @@ def test_numfig_with_numbered_toctree(app, status, warning): for fname, paths in iteritems(expects): parser = NslessParser() parser.entity.update(html_entities.entitydefs) - fp = open(os.path.join(app.outdir, fname), 'rb') - try: + with (app.outdir / fname).open('rb') as fp: etree = ET.parse(fp, parser) - finally: - fp.close() for xpath, check, be_found in paths: yield check_xpath, etree, fname, xpath, check, be_found @@ -832,11 +814,8 @@ def test_numfig_with_prefix(app, status, warning): for fname, paths in iteritems(expects): parser = NslessParser() parser.entity.update(html_entities.entitydefs) - fp = open(os.path.join(app.outdir, fname), 'rb') - try: + with (app.outdir / fname).open('rb') as fp: etree = ET.parse(fp, parser) - finally: - fp.close() for xpath, check, be_found in paths: yield check_xpath, etree, fname, xpath, check, be_found @@ -930,11 +909,8 @@ def test_numfig_with_secnum_depth(app, status, warning): for fname, paths in iteritems(expects): parser = NslessParser() parser.entity.update(html_entities.entitydefs) - fp = open(os.path.join(app.outdir, fname), 'rb') - try: + with (app.outdir / fname).open('rb') as fp: etree = ET.parse(fp, parser) - finally: - fp.close() for xpath, check, be_found in paths: yield check_xpath, etree, fname, xpath, check, be_found diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py index 4b4eadbf5..d4f9f8451 100644 --- a/tests/test_build_latex.py +++ b/tests/test_build_latex.py @@ -320,7 +320,7 @@ def test_footnote(app, status, warning): '\\footnotetext[5]{\nfootnotes in table\n}' in result) -@with_app(buildername='latex', testroot='references-in-caption') +@with_app(buildername='latex', testroot='footnotes') def test_reference_in_caption(app, status, warning): app.builder.build_all() result = (app.outdir / 'Python.tex').text(encoding='utf8') @@ -332,3 +332,81 @@ def test_reference_in_caption(app, status, warning): assert '\\chapter{The section with a reference to {[}AuthorYear{]}}' in result assert '\\caption{The table title with a reference to {[}AuthorYear{]}}' in result assert '\\paragraph{The rubric title with a reference to {[}AuthorYear{]}}' in result + + +@with_app(buildername='latex', testroot='footnotes', + confoverrides={'latex_show_urls': 'inline'}) +def test_latex_show_urls_is_inline(app, status, warning): + app.builder.build_all() + result = (app.outdir / 'Python.tex').text(encoding='utf8') + print(result) + print(status.getvalue()) + print(warning.getvalue()) + assert 'First footnote: \\footnote[2]{\nFirst\n}' in result + assert 'Second footnote: \\footnote[1]{\nSecond\n}' in result + assert '\\href{http://sphinx-doc.org/}{Sphinx} (http://sphinx-doc.org/)' in result + assert 'Third footnote: \\footnote[3]{\nThird\n}' in result + assert ('\\href{http://sphinx-doc.org/~test/}{URL including tilde} ' + '(http://sphinx-doc.org/\\textasciitilde{}test/)' in result) + assert ('\\item[{\\href{http://sphinx-doc.org/}{URL in term} (http://sphinx-doc.org/)}] ' + '\\leavevmode\nDescription' in result) + assert ('\\item[{Footnote in term \\footnotemark[4]}] ' + '\\leavevmode\\footnotetext[4]{\nFootnote in term\n}\nDescription' in result) + assert ('\\item[{\\href{http://sphinx-doc.org/}{Term in deflist} ' + '(http://sphinx-doc.org/)}] \\leavevmode\nDescription' in result) + assert ('\\href{https://github.com/sphinx-doc/sphinx}' + '{https://github.com/sphinx-doc/sphinx}\n' in result) + assert ('\\href{mailto:sphinx-dev@googlegroups.com}' + '{sphinx-dev@googlegroups.com}' in result) + + +@with_app(buildername='latex', testroot='footnotes', + confoverrides={'latex_show_urls': 'footnote'}) +def test_latex_show_urls_is_footnote(app, status, warning): + app.builder.build_all() + result = (app.outdir / 'Python.tex').text(encoding='utf8') + print(result) + print(status.getvalue()) + print(warning.getvalue()) + assert 'First footnote: \\footnote[2]{\nFirst\n}' in result + assert 'Second footnote: \\footnote[1]{\nSecond\n}' in result + assert ('\\href{http://sphinx-doc.org/}{Sphinx}' + '\\footnote[3]{\nhttp://sphinx-doc.org/\n}' in result) + assert 'Third footnote: \\footnote[5]{\nThird\n}' in result + assert ('\\href{http://sphinx-doc.org/~test/}{URL including tilde}' + '\\footnote[4]{\nhttp://sphinx-doc.org/\\textasciitilde{}test/\n}' in result) + assert ('\\item[{\\href{http://sphinx-doc.org/}{URL in term}\\footnotemark[6]}] ' + '\\leavevmode\\footnotetext[6]{\nhttp://sphinx-doc.org/\n}\nDescription' in result) + assert ('\\item[{Footnote in term \\footnotemark[8]}] ' + '\\leavevmode\\footnotetext[8]{\nFootnote in term\n}\nDescription' in result) + assert ('\\item[{\\href{http://sphinx-doc.org/}{Term in deflist}\\footnotemark[7]}] ' + '\\leavevmode\\footnotetext[7]{\nhttp://sphinx-doc.org/\n}\nDescription' in result) + assert ('\\href{https://github.com/sphinx-doc/sphinx}' + '{https://github.com/sphinx-doc/sphinx}\n' in result) + assert ('\\href{mailto:sphinx-dev@googlegroups.com}' + '{sphinx-dev@googlegroups.com}\n' in result) + + +@with_app(buildername='latex', testroot='footnotes', + confoverrides={'latex_show_urls': 'no'}) +def test_latex_show_urls_is_no(app, status, warning): + app.builder.build_all() + result = (app.outdir / 'Python.tex').text(encoding='utf8') + print(result) + print(status.getvalue()) + print(warning.getvalue()) + assert 'First footnote: \\footnote[2]{\nFirst\n}' in result + assert 'Second footnote: \\footnote[1]{\nSecond\n}' in result + assert '\\href{http://sphinx-doc.org/}{Sphinx}' in result + assert 'Third footnote: \\footnote[3]{\nThird\n}' in result + assert '\\href{http://sphinx-doc.org/~test/}{URL including tilde}' in result + assert ('\\item[{\\href{http://sphinx-doc.org/}{URL in term}}] ' + '\\leavevmode\nDescription' in result) + assert ('\\item[{Footnote in term \\footnotemark[4]}] ' + '\\leavevmode\\footnotetext[4]{\nFootnote in term\n}\nDescription' in result) + assert ('\\item[{\\href{http://sphinx-doc.org/}{Term in deflist}}] ' + '\\leavevmode\nDescription' in result) + assert ('\\href{https://github.com/sphinx-doc/sphinx}' + '{https://github.com/sphinx-doc/sphinx}\n' in result) + assert ('\\href{mailto:sphinx-dev@googlegroups.com}' + '{sphinx-dev@googlegroups.com}\n' in result) diff --git a/tests/test_util.py b/tests/test_util.py new file mode 100644 index 000000000..3d30b6fe0 --- /dev/null +++ b/tests/test_util.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +""" + test_util + ~~~~~~~~~~~~~~~ + + Tests util functions. + + :copyright: Copyright 2007-2015 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" +from sphinx.util import encode_uri + + +def test_encode_uri(): + expected = (u'https://ru.wikipedia.org/wiki/%D0%A1%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%B0_' + u'%D1%83%D0%BF%D1%80%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D1%8F_' + u'%D0%B1%D0%B0%D0%B7%D0%B0%D0%BC%D0%B8_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85') + uri = u'https://ru.wikipedia.org/wiki/Система_управления_базами_данных' + assert expected, encode_uri(uri) + + expected = (u'https://github.com/search?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+is%3A' + u'sprint-friendly+user%3Ajupyter&type=Issues&ref=searchresults') + uri = (u'https://github.com/search?utf8=✓&q=is%3Aissue+is%3Aopen+is%3A' + u'sprint-friendly+user%3Ajupyter&type=Issues&ref=searchresults') + assert expected, encode_uri(uri)