diff --git a/CHANGES b/CHANGES index ec4ba5c69..335cb1d00 100644 --- a/CHANGES +++ b/CHANGES @@ -27,11 +27,26 @@ Release 1.4.2 (in development) Features added -------------- -* Now :confval:`suppress_warnings` accepts following configurations: ``app.add_node``, ``app.add_directive``, ``app.add_role`` and ``app.add_generic_role`` (ref: #2451) -* LaTeX writer allows page breaks in topic contents; and their horizontal - extent now fits in the line width (shadow in margin). Warning-type - admonitions allow page breaks (if very long) and their vertical spacing - has been made more coherent with the one for Hint-type notices. +* Now :confval:`suppress_warnings` accepts following configurations (ref: #2451, #2466): + + - ``app.add_node`` + - ``app.add_directive`` + - ``app.add_role`` + - ``app.add_generic_role`` + - ``app.add_source_parser`` + - ``image.data_uri`` + - ``image.nonlocal_uri`` + +* #2453: LaTeX writer allows page breaks in topic contents; and their + horizontal extent now fits in the line width (with shadow in margin). Also + warning-type admonitions allow page breaks and their vertical spacing has + been made more coherent with the one for hint-type notices (ref #2446). + +* #2459: the framing of literal code-blocks in LaTeX output (and not only the + code lines themselves) obey the indentation in lists or quoted blocks. + +* #2343: the long source lines in code-blocks are wrapped (without modifying + the line numbering) in LaTeX output (ref #1534, #2304). * Convert linkcheck builder to requests for better encoding handling Bugs fixed @@ -45,6 +60,22 @@ Bugs fixed * #2447: VerbatimBorderColor wrongly used also for captions of PDF * #2456: C++, fix crash related to document merging (e.g., singlehtml and Latex builders). * #2446: latex(pdf) sets local tables of contents (or more generally topic nodes) in unbreakable boxes, causes overflow at bottom +* #2476: Omit MathJax markers if :nowrap: is given +* #2465: latex builder fails in case no caption option is provided to toctree directive +* Sphinx crashes if self referenced toctree found +* #2481: spelling mistake for mecab search splitter. Thanks to Naoki Sato. +* #2309: Fix could not refer "indirect hyperlink targets" by ref-role +* intersphinx fails if mapping URL contains any port +* #2088: intersphinx crashes if the mapping URL requires basic auth +* #2304: auto line breaks in latexpdf codeblocks +* #1534: Word wrap long lines in Latex verbatim blocks +* #2460: too much white space on top of captioned literal blocks in PDF output +* Show error reason when multiple math extensions are loaded (ref: #2499) +* #2483: any figure number was not assigned if figure title contains only non text objects +* #2501: Unicode subscript numbers are normalized in LaTeX +* #2492: Figure directive with :figwidth: generates incorrect Latex-code +* The caption of figure is always put on center even if ``:align:`` was specified +* #2526: LaTeX writer crashes if the section having only images Release 1.4.1 (released Apr 12, 2016) diff --git a/doc/config.rst b/doc/config.rst index 1d9fd2a6f..d4c2784aa 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -222,6 +222,9 @@ General configuration * app.add_directive * app.add_role * app.add_generic_role + * app.add_source_parser + * image.data_uri + * image.nonlocal_uri * ref.term * ref.ref * ref.numref diff --git a/doc/ext/doctest.rst b/doc/ext/doctest.rst index f3086f398..3d4f66a9a 100644 --- a/doc/ext/doctest.rst +++ b/doc/ext/doctest.rst @@ -33,11 +33,14 @@ There are two kinds of test blocks: * *code-output-style* blocks consist of an ordinary piece of Python code, and optionally, a piece of output for that code. -The doctest extension provides four directives. The *group* argument is -interpreted as follows: if it is empty, the block is assigned to the group named -``default``. If it is ``*``, the block is assigned to all groups (including the -``default`` group). Otherwise, it must be a comma-separated list of group -names. + +Directives +---------- + +The *group* argument below is interpreted as follows: if it is empty, the block +is assigned to the group named ``default``. If it is ``*``, the block is +assigned to all groups (including the ``default`` group). Otherwise, it must be +a comma-separated list of group names. .. rst:directive:: .. testsetup:: [group] @@ -171,7 +174,10 @@ The following is an example for the usage of the directives. The test via This parrot wouldn't voom if you put 3000 volts through it! -There are also these config values for customizing the doctest extension: +Configuration +------------- + +The doctest extension uses the following configuration values: .. confval:: doctest_default_flags diff --git a/doc/theming.rst b/doc/theming.rst index 86960d27c..df0755f1f 100644 --- a/doc/theming.rst +++ b/doc/theming.rst @@ -51,7 +51,7 @@ The third form provides your theme path dynamically to Sphinx if the called ``sphinx_themes`` in your setup.py file and write a ``get_path`` function that has to return the directory with themes in it:: - // in your 'setup.py' + # 'setup.py' setup( ... @@ -63,7 +63,7 @@ that has to return the directory with themes in it:: ... ) - // in 'your_package.py' + # 'your_package.py' from os import path package_dir = path.abspath(path.dirname(__file__)) diff --git a/sphinx/application.py b/sphinx/application.py index 5ec129d4a..a833816f1 100644 --- a/sphinx/application.py +++ b/sphinx/application.py @@ -788,6 +788,11 @@ class Sphinx(object): def add_source_parser(self, suffix, parser): self.debug('[app] adding search source_parser: %r, %r', (suffix, parser)) + if suffix in self._additional_source_parsers: + self.warn('while setting up extension %s: source_parser for %r is ' + 'already registered, it will be overridden' % + (self._setting_up_extension[-1], suffix), + type='app', subtype='add_source_parser') self._additional_source_parsers[suffix] = parser diff --git a/sphinx/builders/gettext.py b/sphinx/builders/gettext.py index fce6c8cc1..1c4789392 100644 --- a/sphinx/builders/gettext.py +++ b/sphinx/builders/gettext.py @@ -11,7 +11,7 @@ from __future__ import unicode_literals -from os import path, walk +from os import path, walk, getenv from codecs import open from time import time from datetime import datetime, tzinfo, timedelta @@ -130,6 +130,12 @@ class I18nBuilder(Builder): timestamp = time() tzdelta = datetime.fromtimestamp(timestamp) - \ datetime.utcfromtimestamp(timestamp) +# set timestamp from SOURCE_DATE_EPOCH if set +# see https://reproducible-builds.org/specs/source-date-epoch/ +source_date_epoch = getenv('SOURCE_DATE_EPOCH') +if source_date_epoch is not None: + timestamp = float(source_date_epoch) + tzdelta = 0 class LocalTimeZone(tzinfo): diff --git a/sphinx/builders/latex.py b/sphinx/builders/latex.py index f3e64bcb1..ac26e33c9 100644 --- a/sphinx/builders/latex.py +++ b/sphinx/builders/latex.py @@ -137,7 +137,7 @@ class LaTeXBuilder(Builder): tree = self.env.get_doctree(indexfile) contentsname = None for toctree in tree.traverse(addnodes.toctree): - if toctree['caption']: + if 'caption' in toctree: contentsname = toctree['caption'] break diff --git a/sphinx/config.py b/sphinx/config.py index b7075caa4..ef57334fe 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -10,7 +10,7 @@ """ import re -from os import path, environ +from os import path, environ, getenv import shlex from six import PY2, PY3, iteritems, string_types, binary_type, text_type, integer_types @@ -19,8 +19,10 @@ from sphinx.errors import ConfigError from sphinx.locale import l_ from sphinx.util.osutil import make_filename, cd from sphinx.util.pycompat import execfile_, NoneType +from sphinx.util.i18n import format_date nonascii_re = re.compile(br'[\x80-\xff]') +copyright_year_re = re.compile(r'^((\d{4}-)?)(\d{4})(?=[ ,])') CONFIG_SYNTAX_ERROR = "There is a syntax error in your configuration file: %s" if PY3: @@ -298,6 +300,15 @@ class Config(object): self.setup = config.get('setup', None) self.extensions = config.get('extensions', []) + # correct values of copyright year that are not coherent with + # the SOURCE_DATE_EPOCH environment variable (if set) + # See https://reproducible-builds.org/specs/source-date-epoch/ + if getenv('SOURCE_DATE_EPOCH') is not None: + for k in ('copyright', 'epub_copyright'): + if k in config: + config[k] = copyright_year_re.sub('\g<1>%s' % format_date('%Y'), + config[k]) + def check_types(self, warn): # check all values for deviation from the default value's type, since # that can result in TypeErrors all over the place diff --git a/sphinx/directives/patches.py b/sphinx/directives/patches.py index dcdc41a17..4f6f3729f 100644 --- a/sphinx/directives/patches.py +++ b/sphinx/directives/patches.py @@ -28,6 +28,10 @@ class Figure(images.Figure): self.options['name'] = name self.add_name(figure_node) + # fill lineno using image node + if figure_node.line is None and len(figure_node) == 2: + figure_node.line = figure_node[1].line + return [figure_node] diff --git a/sphinx/domains/std.py b/sphinx/domains/std.py index e5f9ab557..997d95ba3 100644 --- a/sphinx/domains/std.py +++ b/sphinx/domains/std.py @@ -532,6 +532,9 @@ class StandardDomain(Domain): if labelid is None: continue node = document.ids[labelid] + if node.tagname == 'target' and 'refid' in node: # indirect hyperlink targets + node = document.ids.get(node['refid']) + labelid = node['names'][0] if name.isdigit() or 'refuri' in node or \ node.tagname.startswith('desc_'): # ignore footnote labels, labels automatically generated from a @@ -545,7 +548,7 @@ class StandardDomain(Domain): sectname = clean_astext(node[0]) # node[0] == title node elif self.is_enumerable_node(node): sectname = self.get_numfig_title(node) - if sectname is None: + if not sectname: continue elif node.traverse(addnodes.toctree): n = node.traverse(addnodes.toctree)[0] diff --git a/sphinx/environment.py b/sphinx/environment.py index af6eb4d0d..d5848a429 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -75,7 +75,7 @@ default_settings = { # or changed to properly invalidate pickle files. # # NOTE: increase base version by 2 to have distinct numbers for Py2 and 3 -ENV_VERSION = 47 + (sys.version_info[0] - 2) +ENV_VERSION = 48 + (sys.version_info[0] - 2) dummy_reporter = Reporter('', 4, 4) @@ -908,11 +908,13 @@ class BuildEnvironment: node['candidates'] = candidates = {} imguri = node['uri'] if imguri.startswith('data:'): - self.warn_node('image data URI found. some builders might not support', node) + self.warn_node('image data URI found. some builders might not support', node, + type='image', subtype='data_uri') candidates['?'] = imguri continue elif imguri.find('://') != -1: - self.warn_node('nonlocal image URI found: %s' % imguri, node) + self.warn_node('nonlocal image URI found: %s' % imguri, node, + type='image', subtype='nonlocal_uri') candidates['?'] = imguri continue rel_imgpath, full_imgpath = self.relfn2path(imguri, docname) @@ -1909,6 +1911,10 @@ class BuildEnvironment: traversed = set() def traverse_toctree(parent, docname): + if parent == docname: + self.warn(docname, 'self referenced toctree found. Ignored.') + return + # traverse toctree by pre-order yield parent, docname traversed.add(docname) diff --git a/sphinx/ext/imgmath.py b/sphinx/ext/imgmath.py index 40a9c1402..307145adb 100644 --- a/sphinx/ext/imgmath.py +++ b/sphinx/ext/imgmath.py @@ -22,7 +22,7 @@ from six import text_type from docutils import nodes import sphinx -from sphinx.errors import SphinxError +from sphinx.errors import SphinxError, ExtensionError from sphinx.util.png import read_png_depth, write_png_depth from sphinx.util.osutil import ensuredir, ENOENT, cd from sphinx.util.pycompat import sys_encoding @@ -267,7 +267,11 @@ def html_visit_displaymath(self, node): def setup(app): - mathbase_setup(app, (html_visit_math, None), (html_visit_displaymath, None)) + try: + mathbase_setup(app, (html_visit_math, None), (html_visit_displaymath, None)) + except ExtensionError: + raise ExtensionError('sphinx.ext.imgmath: other math package is already loaded') + app.add_config_value('imgmath_image_format', 'png', 'html') app.add_config_value('imgmath_dvipng', 'dvipng', 'html') app.add_config_value('imgmath_dvisvgm', 'dvisvgm', 'html') diff --git a/sphinx/ext/intersphinx.py b/sphinx/ext/intersphinx.py index 9fe2f765c..b755253c7 100644 --- a/sphinx/ext/intersphinx.py +++ b/sphinx/ext/intersphinx.py @@ -150,7 +150,10 @@ def _strip_basic_auth(url): password = url_parts.password frags = list(url_parts) # swap out "user[:pass]@hostname" for "hostname" - frags[1] = url_parts.hostname + if url_parts.port: + frags[1] = "%s:%s" % (url_parts.hostname, url_parts.port) + else: + frags[1] = url_parts.hostname url = parse.urlunsplit(frags) return (url, username, password) @@ -177,7 +180,7 @@ def _read_from_url(url): password_mgr = request.HTTPPasswordMgrWithDefaultRealm() password_mgr.add_password(None, url, username, password) handler = request.HTTPBasicAuthHandler(password_mgr) - opener = request.build_opener(default_handlers + [handler]) + opener = request.build_opener(*(default_handlers + [handler])) else: opener = default_opener diff --git a/sphinx/ext/jsmath.py b/sphinx/ext/jsmath.py index ff77c2d1b..f36e12fed 100644 --- a/sphinx/ext/jsmath.py +++ b/sphinx/ext/jsmath.py @@ -56,7 +56,11 @@ def builder_inited(app): def setup(app): - mathbase_setup(app, (html_visit_math, None), (html_visit_displaymath, None)) + try: + mathbase_setup(app, (html_visit_math, None), (html_visit_displaymath, None)) + except ExtensionError: + raise ExtensionError('sphinx.ext.jsmath: other math package is already loaded') + app.add_config_value('jsmath_path', '', False) app.connect('builder-inited', builder_inited) return {'version': sphinx.__display_version__, 'parallel_read_safe': True} diff --git a/sphinx/ext/mathjax.py b/sphinx/ext/mathjax.py index 325003d0c..bdaab55e0 100644 --- a/sphinx/ext/mathjax.py +++ b/sphinx/ext/mathjax.py @@ -14,7 +14,7 @@ from docutils import nodes import sphinx -from sphinx.application import ExtensionError +from sphinx.errors import ExtensionError from sphinx.ext.mathbase import setup_math as mathbase_setup @@ -29,9 +29,7 @@ def html_visit_math(self, node): def html_visit_displaymath(self, node): self.body.append(self.starttag(node, 'div', CLASS='math')) if node['nowrap']: - self.body.append(self.builder.config.mathjax_display[0] + - self.encode(node['latex']) + - self.builder.config.mathjax_display[1]) + self.body.append(self.encode(node['latex'])) self.body.append('') raise nodes.SkipNode @@ -65,7 +63,11 @@ def builder_inited(app): def setup(app): - mathbase_setup(app, (html_visit_math, None), (html_visit_displaymath, None)) + try: + mathbase_setup(app, (html_visit_math, None), (html_visit_displaymath, None)) + except ExtensionError: + raise ExtensionError('sphinx.ext.mathjax: other math package is already loaded') + # more information for mathjax secure url is here: # http://docs.mathjax.org/en/latest/start.html#secure-access-to-the-cdn app.add_config_value('mathjax_path', @@ -74,4 +76,5 @@ def setup(app): app.add_config_value('mathjax_inline', [r'\(', r'\)'], 'html') app.add_config_value('mathjax_display', [r'\[', r'\]'], 'html') app.connect('builder-inited', builder_inited) + return {'version': sphinx.__display_version__, 'parallel_read_safe': True} diff --git a/sphinx/ext/pngmath.py b/sphinx/ext/pngmath.py index a2801bd1f..d7660550e 100644 --- a/sphinx/ext/pngmath.py +++ b/sphinx/ext/pngmath.py @@ -23,7 +23,7 @@ from six import text_type from docutils import nodes import sphinx -from sphinx.errors import SphinxError +from sphinx.errors import SphinxError, ExtensionError from sphinx.util.png import read_png_depth, write_png_depth from sphinx.util.osutil import ensuredir, ENOENT, cd from sphinx.util.pycompat import sys_encoding @@ -239,7 +239,11 @@ def html_visit_displaymath(self, node): def setup(app): app.warn('sphinx.ext.pngmath has been deprecated. Please use sphinx.ext.imgmath instead.') - mathbase_setup(app, (html_visit_math, None), (html_visit_displaymath, None)) + try: + mathbase_setup(app, (html_visit_math, None), (html_visit_displaymath, None)) + except ExtensionError: + raise ExtensionError('sphinx.ext.pngmath: other math package is already loaded') + app.add_config_value('pngmath_dvipng', 'dvipng', 'html') app.add_config_value('pngmath_latex', 'latex', 'html') app.add_config_value('pngmath_use_preview', False, 'html') diff --git a/sphinx/search/ja.py b/sphinx/search/ja.py index ba6951f39..9e02926f0 100644 --- a/sphinx/search/ja.py +++ b/sphinx/search/ja.py @@ -533,7 +533,7 @@ class SearchJapanese(SearchLanguage): language_name = 'Japanese' splitters = { 'default': 'sphinx.search.ja.DefaultSplitter', - 'mecab': 'sphinx.sarch.ja.MecabSplitter', + 'mecab': 'sphinx.search.ja.MecabSplitter', 'janome': 'sphinx.search.ja.JanomeSplitter', } diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty index 175344ad9..188e1b345 100644 --- a/sphinx/texinputs/sphinx.sty +++ b/sphinx/texinputs/sphinx.sty @@ -8,12 +8,6 @@ \NeedsTeXFormat{LaTeX2e}[1995/12/01] \ProvidesPackage{sphinx}[2010/01/15 LaTeX package (Sphinx markup)] -\ifx\directlua\undefined\else -% if compiling with lualatex 0.85 or later load compatibility patch issued by -% the LaTeX team for older packages relying on \pdf named primitives. - \IfFileExists{luatex85.sty}{\RequirePackage{luatex85}}{} -\fi - \@ifclassloaded{memoir}{}{\RequirePackage{fancyhdr}} \RequirePackage{textcomp} @@ -166,12 +160,12 @@ \newcommand\Sphinx@colorbox [2]{% % #1 will be \fcolorbox or, for first part of frame: \Sphinx@fcolorbox - #1{VerbatimBorderColor}{VerbatimColor}{% - % adjust width to be able to handle indentation. - \begin{minipage}{\dimexpr\linewidth-\@totalleftmargin\relax}% - #2% - \end{minipage}% - }% +% let the framing obey the current indentation (adapted from framed.sty's code). + \hskip\@totalleftmargin + \hskip-\fboxsep\hskip-\fboxrule + #1{VerbatimBorderColor}{VerbatimColor}{#2}% + \hskip-\fboxsep\hskip-\fboxrule + \hskip-\linewidth \hskip-\@totalleftmargin \hskip\columnwidth } % use of \color@b@x here is compatible with both xcolor.sty and color.sty \def\Sphinx@fcolorbox #1#2% @@ -184,9 +178,11 @@ \newcommand*\SphinxLiteralBlockLabel {} \newcommand*\SphinxSetupCaptionForVerbatim [2] {% - \needspace{\literalblockneedspace}\vspace{\literalblockcaptiontopvspace}% + \needspace{\literalblockneedspace}% % insert a \label via \SphinxLiteralBlockLabel % reset to normal the color for the literal block caption +% the caption inserts \abovecaptionskip whitespace above itself (usually 10pt) +% there is also \belowcaptionskip but it is usually zero, hence the \smallskip \def\SphinxVerbatimTitle {\py@NormalColor\captionof{#1}{\SphinxLiteralBlockLabel #2}\smallskip }% } @@ -198,12 +194,22 @@ \long\def\Sphinx@VerbatimFBox#1{% \leavevmode \begingroup + % framed.sty does some measuring but this macro adds possibly a caption + % use amsmath conditional to inhibit the caption counter stepping after + % first pass \ifSphinx@myfirstframedpass\else\firstchoice@false\fi \setbox\@tempboxa\hbox{\kern\fboxsep{#1}\kern\fboxsep}% \hbox {\lower\dimexpr\fboxrule+\fboxsep+\dp\@tempboxa \hbox{% - \vbox{\ifx\SphinxVerbatimTitle\empty\else{\SphinxVerbatimTitle}\fi + \vbox{\ifx\SphinxVerbatimTitle\empty\else + % add the caption in a centered way above possibly indented frame + % hide its width from framed.sty's measuring step + % note that the caption brings \abovecaptionskip top vertical space + \moveright\dimexpr\fboxrule+.5\wd\@tempboxa + \hb@xt@\z@{\hss\begin{minipage}{\wd\@tempboxa}% + \SphinxVerbatimTitle + \end{minipage}\hss}\fi \hrule\@height\fboxrule\relax \hbox{\vrule\@width\fboxrule\relax \vbox{\vskip\fboxsep\copy\@tempboxa\vskip\fboxsep}% @@ -211,16 +217,70 @@ \hrule\@height\fboxrule\relax}% }}% \endgroup - % amsmath conditional inhibits counter stepping after first pass. \global\Sphinx@myfirstframedpassfalse } +% For linebreaks inside Verbatim environment from package fancyvrb. +\newbox\Sphinxcontinuationbox +\newbox\Sphinxvisiblespacebox +% These are user customizable e.g. from latex_elements's preamble key. +% Use of \textvisiblespace for compatibility with XeTeX/LuaTeX/fontspec. +\newcommand*\Sphinxvisiblespace {\textcolor{red}{\textvisiblespace}} +\newcommand*\Sphinxcontinuationsymbol {\textcolor{red}{\llap{\tiny$\m@th\hookrightarrow$}}} +\newcommand*\Sphinxcontinuationindent {3ex } +\newcommand*\Sphinxafterbreak {\kern\Sphinxcontinuationindent\copy\Sphinxcontinuationbox} + +% Take advantage of the already applied Pygments mark-up to insert +% potential linebreaks for TeX processing. +% {, <, #, %, $, ' and ": go to next line. +% _, }, ^, &, >, - and ~: stay at end of broken line. +% Use of \textquotesingle for straight quote. +\newcommand*\Sphinxbreaksatspecials {% + \def\PYGZus{\discretionary{\char`\_}{\Sphinxafterbreak}{\char`\_}}% + \def\PYGZob{\discretionary{}{\Sphinxafterbreak\char`\{}{\char`\{}}% + \def\PYGZcb{\discretionary{\char`\}}{\Sphinxafterbreak}{\char`\}}}% + \def\PYGZca{\discretionary{\char`\^}{\Sphinxafterbreak}{\char`\^}}% + \def\PYGZam{\discretionary{\char`\&}{\Sphinxafterbreak}{\char`\&}}% + \def\PYGZlt{\discretionary{}{\Sphinxafterbreak\char`\<}{\char`\<}}% + \def\PYGZgt{\discretionary{\char`\>}{\Sphinxafterbreak}{\char`\>}}% + \def\PYGZsh{\discretionary{}{\Sphinxafterbreak\char`\#}{\char`\#}}% + \def\PYGZpc{\discretionary{}{\Sphinxafterbreak\char`\%}{\char`\%}}% + \def\PYGZdl{\discretionary{}{\Sphinxafterbreak\char`\$}{\char`\$}}% + \def\PYGZhy{\discretionary{\char`\-}{\Sphinxafterbreak}{\char`\-}}% + \def\PYGZsq{\discretionary{}{\Sphinxafterbreak\textquotesingle}{\textquotesingle}}% + \def\PYGZdq{\discretionary{}{\Sphinxafterbreak\char`\"}{\char`\"}}% + \def\PYGZti{\discretionary{\char`\~}{\Sphinxafterbreak}{\char`\~}}% +} + +% Some characters . , ; ? ! / are not pygmentized. +% This macro makes them "active" and they will insert potential linebreaks +\newcommand*\Sphinxbreaksatpunct {% + \lccode`\~`\.\lowercase{\def~}{\discretionary{\char`\.}{\Sphinxafterbreak}{\char`\.}}% + \lccode`\~`\,\lowercase{\def~}{\discretionary{\char`\,}{\Sphinxafterbreak}{\char`\,}}% + \lccode`\~`\;\lowercase{\def~}{\discretionary{\char`\;}{\Sphinxafterbreak}{\char`\;}}% + \lccode`\~`\:\lowercase{\def~}{\discretionary{\char`\:}{\Sphinxafterbreak}{\char`\:}}% + \lccode`\~`\?\lowercase{\def~}{\discretionary{\char`\?}{\Sphinxafterbreak}{\char`\?}}% + \lccode`\~`\!\lowercase{\def~}{\discretionary{\char`\!}{\Sphinxafterbreak}{\char`\!}}% + \lccode`\~`\/\lowercase{\def~}{\discretionary{\char`\/}{\Sphinxafterbreak}{\char`\/}}% + \catcode`\.\active + \catcode`\,\active + \catcode`\;\active + \catcode`\:\active + \catcode`\?\active + \catcode`\!\active + \catcode`\/\active + \lccode`\~`\~ +} + \renewcommand{\Verbatim}[1][1]{% + % quit horizontal mode if we are still in a paragraph + \par % list starts new par, but we don't want it to be set apart vertically \parskip\z@skip - \smallskip % first, let's check if there is a caption \ifx\SphinxVerbatimTitle\empty + \addvspace\z@% counteract possible previous negative skip (French lists!) + \smallskip % there was no caption. Check if nevertheless a label was set. \ifx\SphinxLiteralBlockLabel\empty\else % we require some space to be sure hyperlink target from \phantomsection @@ -238,8 +298,36 @@ % for mid pages and last page portion of (long) split frame: \def\MidFrameCommand{\Sphinx@colorbox\fcolorbox }% \let\LastFrameCommand\MidFrameCommand - % The list environement is needed to control perfectly the vertical - % space. + % fancyvrb's Verbatim puts each input line in (unbreakable) horizontal boxes. + % This customization wraps each line from the input in a \vtop, thus + % allowing it to wrap and display on two or more lines in the latex output. + % - The codeline counter will be increased only once. + % - The wrapped material will not break across pages, it is impossible + % to achieve this without extensive rewrite of fancyvrb. + % - The (not used in Sphinx) obeytabs option to Verbatim is + % broken by this change (showtabs and tabspace work). + \sbox\Sphinxcontinuationbox {\Sphinxcontinuationsymbol}% + \sbox\Sphinxvisiblespacebox {\FV@SetupFont\Sphinxvisiblespace}% + \def\FancyVerbFormatLine ##1{\hsize\linewidth + \vtop{\raggedright\hyphenpenalty\z@\exhyphenpenalty\z@ + \doublehyphendemerits\z@\finalhyphendemerits\z@ + \strut ##1\strut}% + }% + % If the linebreak is at a space, the latter will be displayed as visible + % space at end of first line, and a continuation symbol starts next line. + % Stretch/shrink are however usually zero for typewriter font. + \def\FV@Space {% + \nobreak\hskip\z@ plus\fontdimen3\font minus\fontdimen4\font + \discretionary{\copy\Sphinxvisiblespacebox}{\Sphinxafterbreak} + {\kern\fontdimen2\font}% + }% + % Allow breaks at special characters using \PYG... macros. + \Sphinxbreaksatspecials + % The list environment is needed to control perfectly the vertical space. + % Note: \OuterFrameSep used by framed.sty is later set to \topsep hence 0pt. + % - if caption: vertical space above caption = (\abovecaptionskip + D) with + % D = \baselineskip-\FrameHeightAdjust, and then \smallskip above frame. + % - if no caption: (\smallskip + D) above frame. By default D=6pt. \list{}{% \setlength\parskip{0pt}% \setlength\itemsep{0ex}% @@ -251,13 +339,18 @@ \item % use a minipage if we are already inside a framed environment \relax\ifSphinx@inframed\noindent\begin{\minipage}{\linewidth}\fi - \MakeFramed {\FrameRestore}% + \MakeFramed {% adapted over from framed.sty's snugshade environment + \advance\hsize-\width\@totalleftmargin\z@\linewidth\hsize + \@setminipage }% \small - \OriginalVerbatim[#1]% + % For grid placement from \strut's in \FancyVerbFormatLine + \lineskip\z@skip + % Breaks at punctuation characters . , ; ? ! and / need catcode=\active + \OriginalVerbatim[#1,codes*=\Sphinxbreaksatpunct]% } \renewcommand{\endVerbatim}{% \endOriginalVerbatim - \endMakeFramed + \par\unskip\@minipagefalse\endMakeFramed \ifSphinx@inframed\end{minipage}\fi \endlist % LaTeX environments always revert local changes on exit, here e.g. \parskip @@ -344,6 +437,7 @@ \endtrivlist } + % \moduleauthor{name}{email} \newcommand{\moduleauthor}[2]{} @@ -456,8 +550,11 @@ {\parskip\z@skip\noindent}% } \newcommand{\py@endlightbox}{% - \par\nobreak - {\parskip\z@skip\noindent\rule[.4\baselineskip]{\linewidth}{0.5pt}}\par + \par + % counteract previous possible negative skip (French lists!): + % (we can't cancel that any earlier \vskip introduced a potential pagebreak) + \ifdim\lastskip<\z@\vskip-\lastskip\fi + \nobreak\vbox{\noindent\rule[.4\baselineskip]{\linewidth}{0.5pt}}\allowbreak } % Some are quite plain: @@ -721,8 +818,6 @@ % control caption around literal-block \RequirePackage{capt-of} \RequirePackage{needspace} -% if the left page space is less than \literalblockneedsapce, insert page-break +% if the left page space is less than \literalblockneedspace, insert page-break \newcommand{\literalblockneedspace}{5\baselineskip} \newcommand{\literalblockwithoutcaptionneedspace}{1.5\baselineskip} -% margin before the caption of literal-block -\newcommand{\literalblockcaptiontopvspace}{0.5\baselineskip} diff --git a/sphinx/texinputs/sphinxhowto.cls b/sphinx/texinputs/sphinxhowto.cls index 26e63a7ee..8607ef8c4 100644 --- a/sphinx/texinputs/sphinxhowto.cls +++ b/sphinx/texinputs/sphinxhowto.cls @@ -5,6 +5,12 @@ \NeedsTeXFormat{LaTeX2e}[1995/12/01] \ProvidesClass{sphinxhowto}[2009/06/02 Document class (Sphinx HOWTO)] +\ifx\directlua\undefined\else +% if compiling with lualatex 0.85 or later load compatibility patch issued by +% the LaTeX team for older packages relying on \pdf named primitives. + \IfFileExists{luatex85.sty}{\RequirePackage{luatex85}}{} +\fi + % 'oneside' option overriding the 'twoside' default \newif\if@oneside \DeclareOption{oneside}{\@onesidetrue} diff --git a/sphinx/texinputs/sphinxmanual.cls b/sphinx/texinputs/sphinxmanual.cls index a6b9b3928..b576b673e 100644 --- a/sphinx/texinputs/sphinxmanual.cls +++ b/sphinx/texinputs/sphinxmanual.cls @@ -5,6 +5,12 @@ \NeedsTeXFormat{LaTeX2e}[1995/12/01] \ProvidesClass{sphinxmanual}[2009/06/02 Document class (Sphinx manual)] +\ifx\directlua\undefined\else +% if compiling with lualatex 0.85 or later load compatibility patch issued by +% the LaTeX team for older packages relying on \pdf named primitives. + \IfFileExists{luatex85.sty}{\RequirePackage{luatex85}}{} +\fi + % chapters starting at odd pages (overridden by 'openany' document option) \PassOptionsToClass{openright}{\sphinxdocclass} diff --git a/sphinx/theming.py b/sphinx/theming.py index 08051be98..539184115 100644 --- a/sphinx/theming.py +++ b/sphinx/theming.py @@ -103,7 +103,8 @@ class Theme(object): if name not in self.themes: if name == 'sphinx_rtd_theme': raise ThemeError('sphinx_rtd_theme is no longer a hard dependency ' - 'since version 1.4.0. Please install it manually.') + 'since version 1.4.0. Please install it manually.' + '(pip install sphinx_rtd_theme)') else: raise ThemeError('no theme named %r found ' '(missing theme.conf?)' % name) diff --git a/sphinx/transforms.py b/sphinx/transforms.py index 2e3fb7f0b..47e22237e 100644 --- a/sphinx/transforms.py +++ b/sphinx/transforms.py @@ -114,7 +114,7 @@ class AutoNumbering(Transform): domain = self.document.settings.env.domains['std'] for node in self.document.traverse(nodes.Element): - if domain.is_enumerable_node(node) and domain.get_numfig_title(node): + if domain.is_enumerable_node(node) and domain.get_numfig_title(node) is not None: self.document.note_implicit_target(node) diff --git a/sphinx/util/i18n.py b/sphinx/util/i18n.py index f4a8bce4b..d0cb1f443 100644 --- a/sphinx/util/i18n.py +++ b/sphinx/util/i18n.py @@ -14,7 +14,6 @@ import os import re import warnings from os import path -from time import gmtime from datetime import datetime from collections import namedtuple @@ -188,7 +187,7 @@ def format_date(format, date=None, language=None, warn=None): # See https://wiki.debian.org/ReproducibleBuilds/TimestampsProposal source_date_epoch = os.getenv('SOURCE_DATE_EPOCH') if source_date_epoch is not None: - date = gmtime(float(source_date_epoch)) + date = datetime.utcfromtimestamp(float(source_date_epoch)) else: date = datetime.now() diff --git a/sphinx/util/texescape.py b/sphinx/util/texescape.py index 41ab9741c..0a3192f6a 100644 --- a/sphinx/util/texescape.py +++ b/sphinx/util/texescape.py @@ -47,8 +47,26 @@ tex_replacements = [ ('│', r'\textbar{}'), ('ℯ', r'e'), ('ⅈ', r'i'), - ('₁', r'1'), - ('₂', r'2'), + ('⁰', r'$^\text{0}$'), + ('¹', r'$^\text{1}$'), + ('²', r'$^\text{2}$'), + ('³', r'$^\text{3}$'), + ('⁴', r'$^\text{4}$'), + ('⁵', r'$^\text{5}$'), + ('⁶', r'$^\text{6}$'), + ('⁷', r'$^\text{7}$'), + ('⁸', r'$^\text{8}$'), + ('⁹', r'$^\text{9}$'), + ('₀', r'$_\text{0}$'), + ('₁', r'$_\text{1}$'), + ('₂', r'$_\text{2}$'), + ('₃', r'$_\text{3}$'), + ('₄', r'$_\text{4}$'), + ('₅', r'$_\text{5}$'), + ('₆', r'$_\text{6}$'), + ('₇', r'$_\text{7}$'), + ('₈', r'$_\text{8}$'), + ('₉', r'$_\text{9}$'), # map Greek alphabet ('α', r'\(\alpha\)'), ('β', r'\(\beta\)'), diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py index e60def289..7b666aa52 100644 --- a/sphinx/writers/html.py +++ b/sphinx/writers/html.py @@ -278,7 +278,7 @@ class HTMLTranslator(BaseTranslator): figtype = self.builder.env.domains['std'].get_figtype(node) if figtype: if len(node['ids']) == 0: - msg = 'Any IDs not assiend for %s node' % node.tagname + msg = 'Any IDs not assigned for %s node' % node.tagname self.builder.env.warn_node(msg, node) else: append_fignumber(figtype, node['ids'][0]) diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 7ce35b80f..a79938143 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -263,6 +263,24 @@ class Table(object): self.longtable = False +def width_to_latex_length(length_str): + """Convert `length_str` with rst length to LaTeX length. + + This function is copied from docutils' latex writer + """ + match = re.match('(\d*\.?\d*)\s*(\S*)', length_str) + if not match: + return length_str + value, unit = match.groups()[:2] + if unit in ('', 'pt'): + length_str = '%sbp' % value # convert to 'bp' + # percentage: relate to current line width + elif unit == '%': + length_str = '%.3f\\linewidth' % (float(value)/100.0) + + return length_str + + class LaTeXTranslator(nodes.NodeVisitor): sectionnames = ["part", "chapter", "section", "subsection", "subsubsection", "paragraph", "subparagraph"] @@ -281,7 +299,7 @@ class LaTeXTranslator(nodes.NodeVisitor): '\\else\\fi'), 'cmappkg': '\\usepackage{cmap}', 'fontenc': '\\usepackage[T1]{fontenc}', - 'amsmath': '\\usepackage{amsmath,amssymb}', + 'amsmath': '\\usepackage{amsmath,amssymb,amstext}', 'babel': '\\usepackage{babel}', 'fontpkg': '\\usepackage{times}', 'fncychap': '\\usepackage[Bjarne]{fncychap}', @@ -764,7 +782,8 @@ class LaTeXTranslator(nodes.NodeVisitor): elif isinstance(parent, nodes.section): short = '' if node.traverse(nodes.image): - short = '[%s]' % ' '.join(clean_astext(node).split()).translate(tex_escape_map) + short = ('[%s]' % + u' '.join(clean_astext(node).split()).translate(tex_escape_map)) try: self.body.append(r'\%s%s{' % (self.sectionnames[self.sectionlevel], short)) @@ -1406,10 +1425,13 @@ class LaTeXTranslator(nodes.NodeVisitor): isinstance(node.children[0], nodes.image) and node.children[0]['ids']): ids += self.hypertarget(node.children[0]['ids'][0], anchor=False) - if 'width' in node and node.get('align', '') in ('left', 'right'): + if node.get('align', '') in ('left', 'right'): + if 'width' in node: + length = width_to_latex_length(node['width']) + else: + length = '0pt' self.body.append('\\begin{wrapfigure}{%s}{%s}\n\\centering' % - (node['align'] == 'right' and 'r' or 'l', - node['width'])) + (node['align'] == 'right' and 'r' or 'l', length)) self.context.append(ids + '\\end{wrapfigure}\n') elif self.in_minipage: if ('align' not in node.attributes or diff --git a/tests/root/contents.txt b/tests/root/contents.txt index bee6a896e..ce0e5d3fa 100644 --- a/tests/root/contents.txt +++ b/tests/root/contents.txt @@ -62,3 +62,7 @@ Test for issue #1700 :ref:`mastertoc` +Test for indirect hyperlink targets +=================================== + +:ref:`indirect hyperref ` diff --git a/tests/root/markup.txt b/tests/root/markup.txt index 5cd7d2cbf..cc1fa2a63 100644 --- a/tests/root/markup.txt +++ b/tests/root/markup.txt @@ -107,6 +107,9 @@ Admonitions .. tip:: Tip text. +Indirect hyperlink targets + +.. _other-label: some-label_ Inline markup ------------- @@ -142,6 +145,7 @@ Adding \n to test unescaping. * :token:`try statement ` * :ref:`admonition-section` * :ref:`here ` +* :ref:`there ` * :ref:`my-figure` * :ref:`my-figure-name` * :ref:`my-table` @@ -231,6 +235,16 @@ Figures Description paragraph is wraped with legend node. +.. figure:: rimg.png + :align: right + + figure with align option + +.. figure:: rimg.png + :align: right + :figwidth: 50% + + figure with align & figwidth option Version markup -------------- diff --git a/tests/roots/test-correct-year/conf.py b/tests/roots/test-correct-year/conf.py new file mode 100644 index 000000000..9b554aabe --- /dev/null +++ b/tests/roots/test-correct-year/conf.py @@ -0,0 +1,3 @@ + +copyright = u'2006-2009, Author' + diff --git a/tests/roots/test-correct-year/contents.rst b/tests/roots/test-correct-year/contents.rst new file mode 100644 index 000000000..938dfd503 --- /dev/null +++ b/tests/roots/test-correct-year/contents.rst @@ -0,0 +1,4 @@ +================= +test-correct-year +================= + diff --git a/tests/roots/test-image-in-section/index.rst b/tests/roots/test-image-in-section/index.rst index a69db0a77..08416d671 100644 --- a/tests/roots/test-image-in-section/index.rst +++ b/tests/roots/test-image-in-section/index.rst @@ -16,3 +16,7 @@ another blah Other [blah] |picture| section ------------------------------ other blah + +|picture| +--------- +blah blah blah diff --git a/tests/test_build_html.py b/tests/test_build_html.py index 18a91760a..87ea8c595 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -31,16 +31,16 @@ http://www.python.org/logo.png reading included file u'.*?wrongenc.inc' seems to be wrong, try giving an \ :encoding: option\\n? %(root)s/includes.txt:4: WARNING: download file not readable: .*?nonexisting.png -(%(root)s/markup.txt:359: WARNING: invalid single index entry u'')? +(%(root)s/markup.txt:373: WARNING: invalid single index entry u'')? (%(root)s/undecodable.txt:3: WARNING: undecodable source characters, replacing \ with "\\?": b?'here: >>>(\\\\|/)xbb<<<' )?""" HTML_WARNINGS = ENV_WARNINGS + """\ %(root)s/images.txt:20: WARNING: no matching candidate for image URI u'foo.\\*' -%(root)s/markup.txt:271: WARNING: Could not lex literal_block as "c". Highlighting skipped. +%(root)s/markup.txt:285: WARNING: Could not lex literal_block as "c". Highlighting skipped. %(root)s/footnote.txt:60: WARNING: citation not found: missing -%(root)s/markup.txt:160: WARNING: unknown option: &option +%(root)s/markup.txt:164: WARNING: unknown option: &option """ if PY3: @@ -151,6 +151,8 @@ HTML_XPATH = { "[@class='reference internal']/code/span[@class='pre']", '^with$'), (".//a[@href='#grammar-token-try_stmt']" "[@class='reference internal']/code/span", '^statement$'), + (".//a[@href='#some-label'][@class='reference internal']/span", '^here$'), + (".//a[@href='#some-label'][@class='reference internal']/span", '^there$'), (".//a[@href='subdir/includes.html']" "[@class='reference internal']/span", 'Including in subdir'), (".//a[@href='objects.html#cmdoption-python-c']" @@ -274,6 +276,9 @@ HTML_XPATH = { 'http://sphinx-doc.org/'), (".//a[@class='reference external'][@href='http://sphinx-doc.org/latest/']", 'Latest reference'), + # Indirect hyperlink targets across files + (".//a[@href='markup.html#some-label'][@class='reference internal']/span", + '^indirect hyperref$'), ], 'bom.html': [ (".//title", " File with UTF-8 BOM"), diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py index 3da4f697d..448826b21 100644 --- a/tests/test_build_latex.py +++ b/tests/test_build_latex.py @@ -24,10 +24,10 @@ from test_build_html import ENV_WARNINGS LATEX_WARNINGS = ENV_WARNINGS + """\ -%(root)s/markup.txt:160: WARNING: unknown option: &option +%(root)s/markup.txt:164: WARNING: unknown option: &option %(root)s/footnote.txt:60: WARNING: citation not found: missing %(root)s/images.txt:20: WARNING: no matching candidate for image URI u'foo.\\*' -%(root)s/markup.txt:271: WARNING: Could not lex literal_block as "c". Highlighting skipped. +%(root)s/markup.txt:285: WARNING: Could not lex literal_block as "c". Highlighting skipped. """ if PY3: @@ -106,6 +106,20 @@ def test_latex(app, status, warning): run_latex(app.outdir) +@with_app(buildername='latex') +def test_writer(app, status, warning): + app.builder.build_all() + result = (app.outdir / 'SphinxTests.tex').text(encoding='utf8') + + assert ('\\begin{wrapfigure}{r}{0pt}\n\\centering\n' + '\\includegraphics{{rimg}.png}\n\\caption{figure with align option}' + '\\label{markup:id7}\\end{wrapfigure}' in result) + + assert ('\\begin{wrapfigure}{r}{0.500\\linewidth}\n\\centering\n' + '\\includegraphics{{rimg}.png}\n\\caption{figure with align \\& figwidth option}' + '\\label{markup:id8}\\end{wrapfigure}' in result) + + @with_app(buildername='latex', freshenv=True, # use freshenv to check warnings confoverrides={'latex_documents': [ ('contents', 'SphinxTests.tex', 'Sphinx Tests Documentation', diff --git a/tests/test_build_texinfo.py b/tests/test_build_texinfo.py index 1cf2232c4..7f7570fd2 100644 --- a/tests/test_build_texinfo.py +++ b/tests/test_build_texinfo.py @@ -23,7 +23,7 @@ from test_build_html import ENV_WARNINGS TEXINFO_WARNINGS = ENV_WARNINGS + """\ -%(root)s/markup.txt:160: WARNING: unknown option: &option +%(root)s/markup.txt:164: WARNING: unknown option: &option %(root)s/footnote.txt:60: WARNING: citation not found: missing %(root)s/images.txt:20: WARNING: no matching candidate for image URI u'foo.\\*' %(root)s/images.txt:29: WARNING: no matching candidate for image URI u'svgimg.\\*' diff --git a/tests/test_correct_year.py b/tests/test_correct_year.py new file mode 100644 index 000000000..7a156cbf2 --- /dev/null +++ b/tests/test_correct_year.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +""" + test_correct_year + ~~~~~~~~~~~~~~~~~ + + Test copyright year adjustment + + :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" +import os + +from util import TestApp + + +def test_correct_year(): + try: + # save current value of SOURCE_DATE_EPOCH + sde = os.environ.pop('SOURCE_DATE_EPOCH',None) + + # test with SOURCE_DATE_EPOCH unset: no modification + app = TestApp(buildername='html',testroot='correct-year') + app.builder.build_all() + content = (app.outdir / 'contents.html').text() + app.cleanup() + assert '2006-2009' in content + + # test with SOURCE_DATE_EPOCH set: copyright year should be + # updated + os.environ['SOURCE_DATE_EPOCH'] = "1293840000" + app = TestApp(buildername='html',testroot='correct-year') + app.builder.build_all() + content = (app.outdir / 'contents.html').text() + app.cleanup() + assert '2006-2011' in content + + os.environ['SOURCE_DATE_EPOCH'] = "1293839999" + app = TestApp(buildername='html',testroot='correct-year') + app.builder.build_all() + content = (app.outdir / 'contents.html').text() + app.cleanup() + assert '2006-2010' in content + + finally: + # Restores SOURCE_DATE_EPOCH + if sde == None: + os.environ.pop('SOURCE_DATE_EPOCH',None) + else: + os.environ['SOURCE_DATE_EPOCH'] = sde diff --git a/tests/test_environment.py b/tests/test_environment.py index 5b5c166d3..b369d4609 100644 --- a/tests/test_environment.py +++ b/tests/test_environment.py @@ -24,7 +24,7 @@ def setup_module(): global app, env app = TestApp(srcdir='root-envtest') env = app.env - env.set_warnfunc(lambda *args: warnings.append(args)) + env.set_warnfunc(lambda *args, **kwargs: warnings.append(args)) def teardown_module(): diff --git a/tests/test_ext_intersphinx.py b/tests/test_ext_intersphinx.py index 8bf3f92a7..decc985fe 100644 --- a/tests/test_ext_intersphinx.py +++ b/tests/test_ext_intersphinx.py @@ -199,6 +199,16 @@ class TestStripBasicAuth(unittest.TestCase): self.assertEqual(None, actual_username) self.assertEqual(None, actual_password) + def test_having_port(self): + """basic auth creds correctly stripped from URL containing creds even if URL + contains port""" + url = 'https://user:12345@domain.com:8080/project/objects.inv' + expected = 'https://domain.com:8080/project/objects.inv' + actual_url, actual_username, actual_password = _strip_basic_auth(url) + self.assertEqual(expected, actual_url) + self.assertEqual('user', actual_username) + self.assertEqual('12345', actual_password) + @mock.patch('six.moves.urllib.request.HTTPBasicAuthHandler') @mock.patch('six.moves.urllib.request.HTTPPasswordMgrWithDefaultRealm')