From a9256bfa707f5f0816ff554dcc44f1808b707d22 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 6 Apr 2017 09:41:49 +0900 Subject: [PATCH 01/91] Remove Sphinx.egg-info on `make clean` --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 101cfca2c..6df3ef0ad 100644 --- a/Makefile +++ b/Makefile @@ -56,6 +56,7 @@ clean-backupfiles: clean-generated: find . -name '.DS_Store' -exec rm -f {} + + rm -rf Sphinx.egg-info/ rm -rf doc/_build/ rm -f sphinx/pycode/*.pickle rm -f utils/*3.py* From b6e4cceb8f34483d46a6cbf4410cad91f50531bf Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 8 Apr 2017 13:41:06 +0900 Subject: [PATCH 02/91] Fix #3614: Sphinx crashes with requests-2.5.0 --- CHANGES | 2 ++ sphinx/util/requests.py | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 6ef2c3b2b..e192bf5b7 100644 --- a/CHANGES +++ b/CHANGES @@ -13,6 +13,8 @@ Features added Bugs fixed ---------- +* #3614: Sphinx crashes with requests-2.5.0 + Testing -------- diff --git a/sphinx/util/requests.py b/sphinx/util/requests.py index 48d9ae93a..9fe4cfdcd 100644 --- a/sphinx/util/requests.py +++ b/sphinx/util/requests.py @@ -36,6 +36,16 @@ except ImportError: # for requests < 2.4.0 InsecureRequestWarning = None +try: + from requests.packages.urllib3.exceptions import InsecurePlatformWarning +except ImportError: + try: + # for Debian-jessie + from urllib3.exceptions import InsecurePlatformWarning + except ImportError: + # for requests < 2.4.0 + InsecurePlatformWarning = None + # try to load requests[security] (but only if SSL is available) try: import ssl @@ -48,8 +58,8 @@ else: pkg_resources.VersionConflict): if not getattr(ssl, 'HAS_SNI', False): # don't complain on each url processed about the SSL issue - requests.packages.urllib3.disable_warnings( - requests.packages.urllib3.exceptions.InsecurePlatformWarning) + if InsecurePlatformWarning: + requests.packages.urllib3.disable_warnings(InsecurePlatformWarning) warnings.warn( 'Some links may return broken results due to being unable to ' 'check the Server Name Indication (SNI) in the returned SSL cert ' From b8fe49b0a43f8415e957a85bc005767c036eedf1 Mon Sep 17 00:00:00 2001 From: shimizukawa Date: Mon, 10 Apr 2017 10:33:53 +0900 Subject: [PATCH 03/91] add appveyor for testing on Windows --- .appveyor.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .appveyor.yml diff --git a/.appveyor.yml b/.appveyor.yml new file mode 100644 index 000000000..0e40891d7 --- /dev/null +++ b/.appveyor.yml @@ -0,0 +1,11 @@ +environment: + matrix: + - PYTHON: 36-x64 + +install: + - pip install -U pip setuptools + - pip install -r test-reqs.txt + +test_script: + - tox -e py36 -- -vvv + From 317c86e9b926135f2cc1dfdee56a57ce06850d74 Mon Sep 17 00:00:00 2001 From: shimizukawa Date: Mon, 10 Apr 2017 10:34:53 +0900 Subject: [PATCH 04/91] Revert "add appveyor for testing on Windows" This reverts commit b8fe49b0a43f8415e957a85bc005767c036eedf1. --- .appveyor.yml | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 .appveyor.yml diff --git a/.appveyor.yml b/.appveyor.yml deleted file mode 100644 index 0e40891d7..000000000 --- a/.appveyor.yml +++ /dev/null @@ -1,11 +0,0 @@ -environment: - matrix: - - PYTHON: 36-x64 - -install: - - pip install -U pip setuptools - - pip install -r test-reqs.txt - -test_script: - - tox -e py36 -- -vvv - From 7897678777982e942aa6c22b823aced7ffd07095 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Tue, 11 Apr 2017 23:44:17 +0900 Subject: [PATCH 05/91] Fix #3618: autodoc crashes with tupled arguments --- CHANGES | 1 + sphinx/ext/autodoc.py | 17 +++++++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index e192bf5b7..4035a50ce 100644 --- a/CHANGES +++ b/CHANGES @@ -14,6 +14,7 @@ Bugs fixed ---------- * #3614: Sphinx crashes with requests-2.5.0 +* #3618: autodoc crashes with tupled arguments Testing -------- diff --git a/sphinx/ext/autodoc.py b/sphinx/ext/autodoc.py index 9e513276f..9203e673b 100644 --- a/sphinx/ext/autodoc.py +++ b/sphinx/ext/autodoc.py @@ -380,10 +380,19 @@ def formatargspec(function, args, varargs=None, varkw=None, defaults=None, for i, arg in enumerate(args): arg_fd = StringIO() - arg_fd.write(format_arg_with_annotation(arg)) - if defaults and i >= defaults_start: - arg_fd.write(' = ' if arg in annotations else '=') - arg_fd.write(object_description(defaults[i - defaults_start])) + if isinstance(arg, list): + # support tupled arguments list (only for py2): def foo((x, y)) + arg_fd.write('(') + arg_fd.write(format_arg_with_annotation(arg[0])) + for param in arg[1:]: + arg_fd.write(', ') + arg_fd.write(format_arg_with_annotation(param)) + arg_fd.write(')') + else: + arg_fd.write(format_arg_with_annotation(arg)) + if defaults and i >= defaults_start: + arg_fd.write(' = ' if arg in annotations else '=') + arg_fd.write(object_description(defaults[i - defaults_start])) formatted.append(arg_fd.getvalue()) if varargs: From ac1020b540c7f5d04ebcc428b48fea5053fe734a Mon Sep 17 00:00:00 2001 From: jfbu Date: Mon, 24 Apr 2017 10:19:49 +0200 Subject: [PATCH 06/91] Remove empty 1.5.6 section in CHANGES file --- CHANGES | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/CHANGES b/CHANGES index 979c39ff3..bb682a792 100644 --- a/CHANGES +++ b/CHANGES @@ -193,24 +193,6 @@ Deprecated * #3628: ``sphinx_themes`` entry_point is deprecated. Please use ``sphinx.html_themes`` instead. -Release 1.5.6 (in development) -============================== - -Incompatible changes --------------------- - -Deprecated ----------- - -Features added --------------- - -Bugs fixed ----------- - -Testing --------- - Release 1.5.5 (released Apr 03, 2017) ===================================== From 2807175f10158da274d7a3714d0dc612bbcff403 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Tue, 25 Apr 2017 00:36:42 +0900 Subject: [PATCH 07/91] Fix #3661: sphinx-build crashes on parallel build --- CHANGES | 2 ++ sphinx/environment/__init__.py | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index bb682a792..aecd3ce5b 100644 --- a/CHANGES +++ b/CHANGES @@ -13,6 +13,8 @@ Features added Bugs fixed ---------- +* #3661: sphinx-build crashes on parallel build + Testing -------- diff --git a/sphinx/environment/__init__.py b/sphinx/environment/__init__.py index 066f57209..fd89a07e4 100644 --- a/sphinx/environment/__init__.py +++ b/sphinx/environment/__init__.py @@ -20,7 +20,7 @@ import warnings from os import path from collections import defaultdict -from six import StringIO, itervalues, class_types, next +from six import BytesIO, itervalues, class_types, next from six.moves import cPickle as pickle from docutils.io import NullOutput @@ -121,7 +121,7 @@ class BuildEnvironment(object): @classmethod def loads(cls, string, app=None): # type: (unicode, Sphinx) -> BuildEnvironment - io = StringIO(string) + io = BytesIO(string) return cls.load(io, app) @classmethod @@ -156,7 +156,7 @@ class BuildEnvironment(object): @classmethod def dumps(cls, env): # type: (BuildEnvironment) -> unicode - io = StringIO() + io = BytesIO() cls.dump(env, io) return io.getvalue() From 10afb48b8e6e6db61dec9665ae3bd7035b571853 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Tue, 25 Apr 2017 00:39:00 +0900 Subject: [PATCH 08/91] Revert "Remove empty 1.5.6 section in CHANGES file" This reverts commit ac1020b540c7f5d04ebcc428b48fea5053fe734a. --- CHANGES | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/CHANGES b/CHANGES index aecd3ce5b..77f75a6c5 100644 --- a/CHANGES +++ b/CHANGES @@ -195,6 +195,24 @@ Deprecated * #3628: ``sphinx_themes`` entry_point is deprecated. Please use ``sphinx.html_themes`` instead. +Release 1.5.6 (in development) +============================== + +Incompatible changes +-------------------- + +Deprecated +---------- + +Features added +-------------- + +Bugs fixed +---------- + +Testing +-------- + Release 1.5.5 (released Apr 03, 2017) ===================================== From 7c2f5f16eb1898e8b154d1a9bcfa13782b3c2d24 Mon Sep 17 00:00:00 2001 From: jfbu Date: Tue, 25 Apr 2017 10:14:53 +0200 Subject: [PATCH 09/91] Fix #3664 relative to ``\labelsep`` setting in LaTeX fulllineitems This also removes re-definition of description environment which is not needed anymore as ``\labelsep`` is not modified. --- CHANGES | 1 + sphinx/texinputs/sphinx.sty | 21 +++++++-------------- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/CHANGES b/CHANGES index 4035a50ce..b38d91c4d 100644 --- a/CHANGES +++ b/CHANGES @@ -15,6 +15,7 @@ Bugs fixed * #3614: Sphinx crashes with requests-2.5.0 * #3618: autodoc crashes with tupled arguments +* #3664: No space after the bullet in items of a latex list produced by Sphinx Testing -------- diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty index 571f26c72..9c2a0d72f 100644 --- a/sphinx/texinputs/sphinx.sty +++ b/sphinx/texinputs/sphinx.sty @@ -6,7 +6,7 @@ % \NeedsTeXFormat{LaTeX2e}[1995/12/01] -\ProvidesPackage{sphinx}[2017/03/26 v1.5.4 LaTeX package (Sphinx markup)] +\ProvidesPackage{sphinx}[2017/04/25 v1.5.6 LaTeX package (Sphinx markup)] % this is the \ltx@ifundefined of ltxcmds.sty, which is loaded by % kvoptions (and later by hyperref), but the first release of @@ -1164,26 +1164,19 @@ % {fulllineitems} is the main environment for object descriptions. % \newcommand{\py@itemnewline}[1]{% + \kern\labelsep \@tempdima\linewidth - \advance\@tempdima \leftmargin\makebox[\@tempdima][l]{#1}% + \advance\@tempdima \labelwidth\makebox[\@tempdima][l]{#1}% + \kern-\labelsep } -\newenvironment{fulllineitems}{ - \begin{list}{}{\labelwidth \leftmargin \labelsep \z@ +\newenvironment{fulllineitems}{% + \begin{list}{}{\labelwidth \leftmargin \rightmargin \z@ \topsep -\parskip \partopsep \parskip \itemsep -\parsep - \let\makelabel=\py@itemnewline} + \let\makelabel=\py@itemnewline}% }{\end{list}} -% Redefine description environment so that it is usable inside fulllineitems. -% -% FIXME: use sphinxdescription, do not redefine description environment! -\renewcommand{\description}{% - \list{}{\labelwidth\z@ - \itemindent-\leftmargin - \labelsep5pt\relax - \let\makelabel=\descriptionlabel}} - % Signatures, possibly multi-line % \newlength{\py@argswidth} From 710ddb388045184f921666b2ad7aef16d131de3f Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Tue, 25 Apr 2017 20:05:18 +0900 Subject: [PATCH 10/91] Fix mypy violations --- sphinx/util/requests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/util/requests.py b/sphinx/util/requests.py index 09b7a43b1..6834e6368 100644 --- a/sphinx/util/requests.py +++ b/sphinx/util/requests.py @@ -41,7 +41,7 @@ try: except ImportError: try: # for Debian-jessie - from urllib3.exceptions import InsecurePlatformWarning + from urllib3.exceptions import InsecurePlatformWarning # type: ignore except ImportError: # for requests < 2.4.0 InsecurePlatformWarning = None From b3f596a1330137cf34bc9163cbeebba52ab511e5 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Tue, 25 Apr 2017 01:16:19 +0900 Subject: [PATCH 11/91] Fix #3662: ``builder.css_files`` is deprecated --- CHANGES | 3 +++ sphinx/builders/html.py | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 0d72fa989..2e4e6f676 100644 --- a/CHANGES +++ b/CHANGES @@ -7,6 +7,9 @@ Incompatible changes Deprecated ---------- +* #3662: ``builder.css_files`` is deprecated. Please use ``add_stylesheet()`` + API instead. + Features added -------------- diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index 7369ea23d..cea14965a 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -13,6 +13,7 @@ import os import re import sys import codecs +import warnings import posixpath from os import path from hashlib import md5 @@ -38,6 +39,7 @@ from sphinx.util.docutils import is_html5_writer_available, __version_info__ from sphinx.util.fileutil import copy_asset from sphinx.util.matching import patmatch, Matcher, DOTFILES from sphinx.config import string_classes +from sphinx.deprecation import RemovedInSphinx20Warning from sphinx.locale import _, l_ from sphinx.search import js_index from sphinx.theming import HTMLThemeFactory @@ -87,6 +89,35 @@ def get_stable_hash(obj): return md5(text_type(obj).encode('utf8')).hexdigest() +class CSSContainer(list): + """The container of stylesheets. + + To support the extensions which access the container directly, this wraps + the entry with Stylesheet class. + """ + def append(self, obj): + warnings.warn('builder.css_files is deprecated. ' + 'Please use app.add_stylesheet() instead.', + RemovedInSphinx20Warning) + if isinstance(obj, Stylesheet): + super(CSSContainer, self).append(obj) + else: + super(CSSContainer, self).append(Stylesheet(obj, None, 'stylesheet')) + + def extend(self, other): + for item in other: + self.append(item) + + def __iadd__(self, other): + for item in other: + self.append(item) + + def __add__(self, other): + ret = CSSContainer(self) + ret += other + return ret + + class Stylesheet(text_type): """The metadata of stylesheet. @@ -136,7 +167,7 @@ class StandaloneHTMLBuilder(Builder): script_files = ['_static/jquery.js', '_static/underscore.js', '_static/doctools.js'] # type: List[unicode] # Ditto for this one (Sphinx.add_stylesheet). - css_files = [] # type: List[Dict[unicode, unicode]] + css_files = CSSContainer() # type: List[Dict[unicode, unicode]] imgpath = None # type: unicode domain_indices = [] # type: List[Tuple[unicode, Type[Index], List[Tuple[unicode, List[List[Union[unicode, int]]]]], bool]] # NOQA From edc7f30b9cc92f4d721eeef62ffa826f23a09a9f Mon Sep 17 00:00:00 2001 From: Dmitry Shachnev Date: Tue, 14 Mar 2017 23:43:04 +0300 Subject: [PATCH 12/91] Remove the custom smartypants code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead rely on docutils’ ‘smart_quotes’ option which is available since docutils 0.10. This adds support for internationalization: our code supported only English quotes, while docutils code supports 27 different languages. Closes #498, #580, #3345, #3472. --- sphinx/builders/_epub_base.py | 14 +- sphinx/builders/html.py | 15 +- sphinx/environment/__init__.py | 5 + sphinx/util/smartypants.py | 312 ------------------------------ sphinx/writers/html.py | 94 --------- sphinx/writers/html5.py | 94 --------- sphinx/writers/latex.py | 5 - tests/test_api_translator.py | 9 - tests/test_build_epub.py | 8 +- tests/test_environment_toctree.py | 6 +- tests/test_markup.py | 11 +- tests/test_metadata.py | 4 +- 12 files changed, 38 insertions(+), 539 deletions(-) delete mode 100644 sphinx/util/smartypants.py diff --git a/sphinx/builders/_epub_base.py b/sphinx/builders/_epub_base.py index f7ebaa0e8..a63ecbc39 100644 --- a/sphinx/builders/_epub_base.py +++ b/sphinx/builders/_epub_base.py @@ -25,6 +25,7 @@ except ImportError: Image = None from docutils import nodes +from docutils.utils import smartquotes from sphinx import addnodes from sphinx.builders.html import StandaloneHTMLBuilder @@ -32,7 +33,6 @@ from sphinx.util import logging from sphinx.util import status_iterator from sphinx.util.osutil import ensuredir, copyfile from sphinx.util.fileutil import copy_asset_file -from sphinx.util.smartypants import sphinx_smarty_pants as ssp if False: # For type annotation @@ -94,6 +94,18 @@ Guide = namedtuple('Guide', ['type', 'title', 'uri']) NavPoint = namedtuple('NavPoint', ['navpoint', 'playorder', 'text', 'refuri', 'children']) +def sphinx_smarty_pants(t, language='en'): + # type: (unicode, str) -> unicode + t = t.replace('"', '"') + t = smartquotes.educateDashesOldSchool(t) + t = smartquotes.educateQuotes(t, language) + t = t.replace('"', '"') + return t + + +ssp = sphinx_smarty_pants + + # The epub publisher class EpubBuilder(StandaloneHTMLBuilder): diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index 7369ea23d..a0b3d8e87 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -45,8 +45,7 @@ from sphinx.builders import Builder from sphinx.application import ENV_PICKLE_FILENAME from sphinx.highlighting import PygmentsBridge from sphinx.util.console import bold, darkgreen # type: ignore -from sphinx.writers.html import HTMLWriter, HTMLTranslator, \ - SmartyPantsHTMLTranslator +from sphinx.writers.html import HTMLWriter, HTMLTranslator from sphinx.environment.adapters.asset import ImageAdapter from sphinx.environment.adapters.toctree import TocTree from sphinx.environment.adapters.indexentries import IndexEntries @@ -59,7 +58,7 @@ if False: # Experimental HTML5 Writer if is_html5_writer_available(): - from sphinx.writers.html5 import HTML5Translator, SmartyPantsHTML5Translator + from sphinx.writers.html5 import HTML5Translator html5_ready = True else: html5_ready = False @@ -220,15 +219,9 @@ class StandaloneHTMLBuilder(Builder): @property def default_translator_class(self): if self.config.html_experimental_html5_writer and html5_ready: - if self.config.html_use_smartypants: - return SmartyPantsHTML5Translator - else: - return HTML5Translator + return HTML5Translator else: - if self.config.html_use_smartypants: - return SmartyPantsHTMLTranslator - else: - return HTMLTranslator + return HTMLTranslator def get_outdated_docs(self): # type: () -> Iterator[unicode] diff --git a/sphinx/environment/__init__.py b/sphinx/environment/__init__.py index fd89a07e4..b0a1a2ce5 100644 --- a/sphinx/environment/__init__.py +++ b/sphinx/environment/__init__.py @@ -26,6 +26,7 @@ from six.moves import cPickle as pickle from docutils.io import NullOutput from docutils.core import Publisher from docutils.utils import Reporter, get_source_line +from docutils.utils.smartquotes import smartchars from docutils.parsers.rst import roles from docutils.parsers.rst.languages import en as english from docutils.frontend import OptionParser @@ -671,6 +672,10 @@ class BuildEnvironment(object): self.settings['trim_footnote_reference_space'] = \ self.config.trim_footnote_reference_space self.settings['gettext_compact'] = self.config.gettext_compact + language = (self.config.language or 'en').replace('_', '-') + self.settings['language_code'] = language + if language in smartchars.quotes: + self.settings['smart_quotes'] = self.config.html_use_smartypants docutilsconf = path.join(self.srcdir, 'docutils.conf') # read docutils.conf from source dir, not from current dir diff --git a/sphinx/util/smartypants.py b/sphinx/util/smartypants.py deleted file mode 100644 index 0146ba6e9..000000000 --- a/sphinx/util/smartypants.py +++ /dev/null @@ -1,312 +0,0 @@ -r""" -This is based on SmartyPants.py by `Chad Miller`_ , -version 1.5_1.6. - -Copyright and License -===================== - -SmartyPants_ license:: - - Copyright (c) 2003 John Gruber - (http://daringfireball.net/) - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name "SmartyPants" nor the names of its contributors - may be used to endorse or promote products derived from this - software without specific prior written permission. - - This software is provided by the copyright holders and contributors "as - is" and any express or implied warranties, including, but not limited - to, the implied warranties of merchantability and fitness for a - particular purpose are disclaimed. In no event shall the copyright - owner or contributors be liable for any direct, indirect, incidental, - special, exemplary, or consequential damages (including, but not - limited to, procurement of substitute goods or services; loss of use, - data, or profits; or business interruption) however caused and on any - theory of liability, whether in contract, strict liability, or tort - (including negligence or otherwise) arising in any way out of the use - of this software, even if advised of the possibility of such damage. - - -smartypants.py license:: - - smartypants.py is a derivative work of SmartyPants. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - This software is provided by the copyright holders and contributors "as - is" and any express or implied warranties, including, but not limited - to, the implied warranties of merchantability and fitness for a - particular purpose are disclaimed. In no event shall the copyright - owner or contributors be liable for any direct, indirect, incidental, - special, exemplary, or consequential damages (including, but not - limited to, procurement of substitute goods or services; loss of use, - data, or profits; or business interruption) however caused and on any - theory of liability, whether in contract, strict liability, or tort - (including negligence or otherwise) arising in any way out of the use - of this software, even if advised of the possibility of such damage. - -.. _Chad Miller: http://web.chad.org/ -""" - -import re - -if False: - # For type annotation - from typing import Tuple # NOQA - - -def sphinx_smarty_pants(t): - # type: (unicode) -> unicode - t = t.replace('"', '"') - t = educate_dashes_oldschool(t) - t = educate_quotes(t) # type: ignore - t = t.replace('"', '"') - return t - - -# Constants for quote education. -punct_class = r"""[!"#\$\%'()*+,-.\/:;<=>?\@\[\\\]\^_`{|}~]""" -end_of_word_class = r"""[\s.,;:!?)]""" -close_class = r"""[^\ \t\r\n\[\{\(\-]""" -dec_dashes = r"""–|—""" - -# Special case if the very first character is a quote -# followed by punctuation at a non-word-break. Close the quotes by brute force: -single_quote_start_re = re.compile(r"""^'(?=%s\\B)""" % (punct_class,)) -double_quote_start_re = re.compile(r"""^"(?=%s\\B)""" % (punct_class,)) - -# Special case for double sets of quotes, e.g.: -#

He said, "'Quoted' words in a larger quote."

-double_quote_sets_re = re.compile(r""""'(?=\w)""") -single_quote_sets_re = re.compile(r"""'"(?=\w)""") - -# Special case for decade abbreviations (the '80s): -decade_abbr_re = re.compile(r"""\b'(?=\d{2}s)""") - -# Get most opening double quotes: -opening_double_quotes_regex = re.compile(r""" - ( - \s | # a whitespace char, or -   | # a non-breaking space entity, or - -- | # dashes, or - &[mn]dash; | # named dash entities - %s | # or decimal entities - &\#x201[34]; # or hex - ) - " # the quote - (?=\w) # followed by a word character - """ % (dec_dashes,), re.VERBOSE) - -# Double closing quotes: -closing_double_quotes_regex = re.compile(r""" - #(%s)? # character that indicates the quote should be closing - " - (?=%s) - """ % (close_class, end_of_word_class), re.VERBOSE) - -closing_double_quotes_regex_2 = re.compile(r""" - (%s) # character that indicates the quote should be closing - " - """ % (close_class,), re.VERBOSE) - -# Get most opening single quotes: -opening_single_quotes_regex = re.compile(r""" - ( - \s | # a whitespace char, or -   | # a non-breaking space entity, or - -- | # dashes, or - &[mn]dash; | # named dash entities - %s | # or decimal entities - &\#x201[34]; # or hex - ) - ' # the quote - (?=\w) # followed by a word character - """ % (dec_dashes,), re.VERBOSE) - -closing_single_quotes_regex = re.compile(r""" - (%s) - ' - (?!\s | s\b | \d) - """ % (close_class,), re.VERBOSE) - -closing_single_quotes_regex_2 = re.compile(r""" - (%s) - ' - (\s | s\b) - """ % (close_class,), re.VERBOSE) - - -def educate_quotes(s): - # type: (str) -> str - """ - Parameter: String. - - Returns: The string, with "educated" curly quote HTML entities. - - Example input: "Isn't this fun?" - Example output: “Isn’t this fun?” - """ - - # Special case if the very first character is a quote - # followed by punctuation at a non-word-break. Close the quotes - # by brute force: - s = single_quote_start_re.sub("’", s) - s = double_quote_start_re.sub("”", s) - - # Special case for double sets of quotes, e.g.: - #

He said, "'Quoted' words in a larger quote."

- s = double_quote_sets_re.sub("“‘", s) - s = single_quote_sets_re.sub("‘“", s) - - # Special case for decade abbreviations (the '80s): - s = decade_abbr_re.sub("’", s) - - s = opening_single_quotes_regex.sub(r"\1‘", s) - s = closing_single_quotes_regex.sub(r"\1’", s) - s = closing_single_quotes_regex_2.sub(r"\1’\2", s) - - # Any remaining single quotes should be opening ones: - s = s.replace("'", "‘") - - s = opening_double_quotes_regex.sub(r"\1“", s) - s = closing_double_quotes_regex.sub(r"”", s) - s = closing_double_quotes_regex_2.sub(r"\1”", s) - - # Any remaining quotes should be opening ones. - return s.replace('"', "“") - - -def educate_quotes_latex(s, dquotes=("``", "''")): - # type: (str, Tuple[str, str]) -> unicode - """ - Parameter: String. - - Returns: The string, with double quotes corrected to LaTeX quotes. - - Example input: "Isn't this fun?" - Example output: ``Isn't this fun?''; - """ - - # Special case if the very first character is a quote - # followed by punctuation at a non-word-break. Close the quotes - # by brute force: - s = single_quote_start_re.sub("\x04", s) - s = double_quote_start_re.sub("\x02", s) - - # Special case for double sets of quotes, e.g.: - #

He said, "'Quoted' words in a larger quote."

- s = double_quote_sets_re.sub("\x01\x03", s) - s = single_quote_sets_re.sub("\x03\x01", s) - - # Special case for decade abbreviations (the '80s): - s = decade_abbr_re.sub("\x04", s) - - s = opening_single_quotes_regex.sub("\\1\x03", s) - s = closing_single_quotes_regex.sub("\\1\x04", s) - s = closing_single_quotes_regex_2.sub("\\1\x04\\2", s) - - # Any remaining single quotes should be opening ones: - s = s.replace("'", "\x03") - - s = opening_double_quotes_regex.sub("\\1\x01", s) - s = closing_double_quotes_regex.sub("\x02", s) - s = closing_double_quotes_regex_2.sub("\\1\x02", s) - - # Any remaining quotes should be opening ones. - s = s.replace('"', "\x01") - - # Finally, replace all helpers with quotes. - return s.replace("\x01", dquotes[0]).replace("\x02", dquotes[1]).\ - replace("\x03", "`").replace("\x04", "'") - - -def educate_backticks(s): - # type: (unicode) -> unicode - """ - Parameter: String. - Returns: The string, with ``backticks'' -style double quotes - translated into HTML curly quote entities. - Example input: ``Isn't this fun?'' - Example output: “Isn't this fun?” - """ - return s.replace("``", "“").replace("''", "”") - - -def educate_single_backticks(s): - # type: (unicode) -> unicode - """ - Parameter: String. - Returns: The string, with `backticks' -style single quotes - translated into HTML curly quote entities. - - Example input: `Isn't this fun?' - Example output: ‘Isn’t this fun?’ - """ - return s.replace('`', "‘").replace("'", "’") - - -def educate_dashes_oldschool(s): - # type: (unicode) -> unicode - """ - Parameter: String. - - Returns: The string, with each instance of "--" translated to - an en-dash HTML entity, and each "---" translated to - an em-dash HTML entity. - """ - return s.replace('---', "—").replace('--', "–") - - -def educate_dashes_oldschool_inverted(s): - # type: (unicode) -> unicode - """ - Parameter: String. - - Returns: The string, with each instance of "--" translated to - an em-dash HTML entity, and each "---" translated to - an en-dash HTML entity. Two reasons why: First, unlike the - en- and em-dash syntax supported by - educate_dashes_oldschool(), it's compatible with existing - entries written before SmartyPants 1.1, back when "--" was - only used for em-dashes. Second, em-dashes are more - common than en-dashes, and so it sort of makes sense that - the shortcut should be shorter to type. (Thanks to Aaron - Swartz for the idea.) - """ - return s.replace('---', "–").replace('--', "—") - - -def educate_ellipses(s): - # type: (unicode) -> unicode - """ - Parameter: String. - Returns: The string, with each instance of "..." translated to - an ellipsis HTML entity. - - Example input: Huh...? - Example output: Huh…? - """ - return s.replace('...', "…").replace('. . .', "…") diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py index 63aba3cd6..572b2825e 100644 --- a/sphinx/writers/html.py +++ b/sphinx/writers/html.py @@ -22,7 +22,6 @@ from sphinx import addnodes from sphinx.locale import admonitionlabels, _ from sphinx.util import logging from sphinx.util.images import get_image_size -from sphinx.util.smartypants import sphinx_smarty_pants if False: # For type annotation @@ -74,7 +73,6 @@ class HTMLTranslator(BaseTranslator): # type: (StandaloneHTMLBuilder, Any, Any) -> None BaseTranslator.__init__(self, *args, **kwds) self.highlighter = builder.highlighter - self.no_smarty = 0 self.builder = builder self.highlightlang = self.highlightlang_base = \ builder.config.highlight_language @@ -786,7 +784,6 @@ class HTMLTranslator(BaseTranslator): # type: (nodes.Node) -> None self.depart_admonition(node) - # these are only handled specially in the SmartyPantsHTMLTranslator def visit_literal_emphasis(self, node): # type: (nodes.Node) -> None return self.visit_emphasis(node) @@ -875,94 +872,3 @@ class HTMLTranslator(BaseTranslator): def unknown_visit(self, node): # type: (nodes.Node) -> None raise NotImplementedError('Unknown node: ' + node.__class__.__name__) - - -class SmartyPantsHTMLTranslator(HTMLTranslator): - """ - Handle ordinary text via smartypants, converting quotes and dashes - to the correct entities. - """ - - def __init__(self, *args, **kwds): - # type: (Any, Any) -> None - self.no_smarty = 0 - HTMLTranslator.__init__(self, *args, **kwds) - - def visit_literal(self, node): - # type: (nodes.Node) -> None - self.no_smarty += 1 - try: - # this raises SkipNode - HTMLTranslator.visit_literal(self, node) - finally: - self.no_smarty -= 1 - - def visit_literal_block(self, node): - # type: (nodes.Node) -> None - self.no_smarty += 1 - try: - HTMLTranslator.visit_literal_block(self, node) - except nodes.SkipNode: - # HTMLTranslator raises SkipNode for simple literal blocks, - # but not for parsed literal blocks - self.no_smarty -= 1 - raise - - def depart_literal_block(self, node): - # type: (nodes.Node) -> None - HTMLTranslator.depart_literal_block(self, node) - self.no_smarty -= 1 - - def visit_literal_emphasis(self, node): - # type: (nodes.Node) -> None - self.no_smarty += 1 - self.visit_emphasis(node) - - def depart_literal_emphasis(self, node): - # type: (nodes.Node) -> None - self.depart_emphasis(node) - self.no_smarty -= 1 - - def visit_literal_strong(self, node): - # type: (nodes.Node) -> None - self.no_smarty += 1 - self.visit_strong(node) - - def depart_literal_strong(self, node): - # type: (nodes.Node) -> None - self.depart_strong(node) - self.no_smarty -= 1 - - def visit_desc_signature(self, node): - # type: (nodes.Node) -> None - self.no_smarty += 1 - HTMLTranslator.visit_desc_signature(self, node) - - def depart_desc_signature(self, node): - # type: (nodes.Node) -> None - self.no_smarty -= 1 - HTMLTranslator.depart_desc_signature(self, node) - - def visit_productionlist(self, node): - # type: (nodes.Node) -> None - self.no_smarty += 1 - try: - HTMLTranslator.visit_productionlist(self, node) - finally: - self.no_smarty -= 1 - - def visit_option(self, node): - # type: (nodes.Node) -> None - self.no_smarty += 1 - HTMLTranslator.visit_option(self, node) - - def depart_option(self, node): - # type: (nodes.Node) -> None - self.no_smarty -= 1 - HTMLTranslator.depart_option(self, node) - - def bulk_text_processor(self, text): - # type: (unicode) -> unicode - if self.no_smarty <= 0: - return sphinx_smarty_pants(text) - return text diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py index 9deb35a88..8e3843c09 100644 --- a/sphinx/writers/html5.py +++ b/sphinx/writers/html5.py @@ -21,7 +21,6 @@ from sphinx import addnodes from sphinx.locale import admonitionlabels, _ from sphinx.util import logging from sphinx.util.images import get_image_size -from sphinx.util.smartypants import sphinx_smarty_pants if False: # For type annotation @@ -44,7 +43,6 @@ class HTML5Translator(BaseTranslator): # type: (StandaloneHTMLBuilder, Any, Any) -> None BaseTranslator.__init__(self, *args, **kwds) self.highlighter = builder.highlighter - self.no_smarty = 0 self.builder = builder self.highlightlang = self.highlightlang_base = \ builder.config.highlight_language @@ -729,7 +727,6 @@ class HTML5Translator(BaseTranslator): # type: (nodes.Node) -> None self.depart_admonition(node) - # these are only handled specially in the SmartyPantsHTML5Translator def visit_literal_emphasis(self, node): # type: (nodes.Node) -> None return self.visit_emphasis(node) @@ -830,94 +827,3 @@ class HTML5Translator(BaseTranslator): def unknown_visit(self, node): # type: (nodes.Node) -> None raise NotImplementedError('Unknown node: ' + node.__class__.__name__) - - -class SmartyPantsHTML5Translator(HTML5Translator): - """ - Handle ordinary text via smartypants, converting quotes and dashes - to the correct entities. - """ - - def __init__(self, *args, **kwds): - # type: (Any, Any) -> None - self.no_smarty = 0 - HTML5Translator.__init__(self, *args, **kwds) - - def visit_literal(self, node): - # type: (nodes.Node) -> None - self.no_smarty += 1 - try: - # this raises SkipNode - HTML5Translator.visit_literal(self, node) - finally: - self.no_smarty -= 1 - - def visit_literal_block(self, node): - # type: (nodes.Node) -> None - self.no_smarty += 1 - try: - HTML5Translator.visit_literal_block(self, node) - except nodes.SkipNode: - # HTML5Translator raises SkipNode for simple literal blocks, - # but not for parsed literal blocks - self.no_smarty -= 1 - raise - - def depart_literal_block(self, node): - # type: (nodes.Node) -> None - HTML5Translator.depart_literal_block(self, node) - self.no_smarty -= 1 - - def visit_literal_emphasis(self, node): - # type: (nodes.Node) -> None - self.no_smarty += 1 - self.visit_emphasis(node) - - def depart_literal_emphasis(self, node): - # type: (nodes.Node) -> None - self.depart_emphasis(node) - self.no_smarty -= 1 - - def visit_literal_strong(self, node): - # type: (nodes.Node) -> None - self.no_smarty += 1 - self.visit_strong(node) - - def depart_literal_strong(self, node): - # type: (nodes.Node) -> None - self.depart_strong(node) - self.no_smarty -= 1 - - def visit_desc_signature(self, node): - # type: (nodes.Node) -> None - self.no_smarty += 1 - HTML5Translator.visit_desc_signature(self, node) - - def depart_desc_signature(self, node): - # type: (nodes.Node) -> None - self.no_smarty -= 1 - HTML5Translator.depart_desc_signature(self, node) - - def visit_productionlist(self, node): - # type: (nodes.Node) -> None - self.no_smarty += 1 - try: - HTML5Translator.visit_productionlist(self, node) - finally: - self.no_smarty -= 1 - - def visit_option(self, node): - # type: (nodes.Node) -> None - self.no_smarty += 1 - HTML5Translator.visit_option(self, node) - - def depart_option(self, node): - # type: (nodes.Node) -> None - self.no_smarty -= 1 - HTML5Translator.depart_option(self, node) - - def bulk_text_processor(self, text): - # type: (unicode) -> unicode - if self.no_smarty <= 0: - return sphinx_smarty_pants(text) - return text diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index fc2cbb272..c1e33374d 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -30,7 +30,6 @@ from sphinx.util.i18n import format_date from sphinx.util.nodes import clean_astext, traverse_parent from sphinx.util.template import LaTeXRenderer from sphinx.util.texescape import tex_escape_map, tex_replace_map -from sphinx.util.smartypants import educate_quotes_latex if False: # For type annotation @@ -2533,10 +2532,6 @@ class LaTeXTranslator(nodes.NodeVisitor): def visit_Text(self, node): # type: (nodes.Node) -> None text = self.encode(node.astext()) - if not self.no_contractions and not self.in_parsed_literal: - text = educate_quotes_latex(text, # type: ignore - dquotes=("\\sphinxquotedblleft{}", - "\\sphinxquotedblright{}")) self.body.append(text) def depart_Text(self, node): diff --git a/tests/test_api_translator.py b/tests/test_api_translator.py index 511fe1416..dadea50e5 100644 --- a/tests/test_api_translator.py +++ b/tests/test_api_translator.py @@ -28,15 +28,6 @@ def test_html_translator(app, status, warning): # no set_translator() translator_class = app.builder.get_translator_class() assert translator_class - assert translator_class.__name__ == 'SmartyPantsHTMLTranslator' - - -@pytest.mark.sphinx('html', confoverrides={ - 'html_use_smartypants': False}) -def test_html_with_smartypants(app, status, warning): - # no set_translator(), html_use_smartypants=False - translator_class = app.builder.get_translator_class() - assert translator_class assert translator_class.__name__ == 'HTMLTranslator' diff --git a/tests/test_build_epub.py b/tests/test_build_epub.py index cf0e2857a..e5d86b0ed 100644 --- a/tests/test_build_epub.py +++ b/tests/test_build_epub.py @@ -162,7 +162,7 @@ def test_nested_toc(app): app.build() # toc.ncx - toc = EPUBElementTree.fromstring((app.outdir / 'toc.ncx').text()) + toc = EPUBElementTree.fromstring((app.outdir / 'toc.ncx').bytes()) assert toc.find("./ncx:docTitle/ncx:text").text == 'Python documentation' # toc.ncx / navPoint @@ -175,7 +175,7 @@ def test_nested_toc(app): navpoints = toc.findall("./ncx:navMap/ncx:navPoint") assert len(navpoints) == 4 assert navinfo(navpoints[0]) == ('navPoint1', '1', 'index.xhtml', - "Welcome to Sphinx Tests's documentation!") + u"Welcome to Sphinx Tests’s documentation!") assert navpoints[0].findall("./ncx:navPoint") == [] # toc.ncx / nested navPoints @@ -192,11 +192,11 @@ def test_nested_toc(app): anchor = elem.find("./xhtml:a") return (anchor.get('href'), anchor.text) - nav = EPUBElementTree.fromstring((app.outdir / 'nav.xhtml').text()) + nav = EPUBElementTree.fromstring((app.outdir / 'nav.xhtml').bytes()) toc = nav.findall("./xhtml:body/xhtml:nav/xhtml:ol/xhtml:li") assert len(toc) == 4 assert navinfo(toc[0]) == ('index.xhtml', - "Welcome to Sphinx Tests's documentation!") + u"Welcome to Sphinx Tests’s documentation!") assert toc[0].findall("./xhtml:ol") == [] # nav.xhtml / nested toc diff --git a/tests/test_environment_toctree.py b/tests/test_environment_toctree.py index 0f1dffa43..a2d54fb79 100644 --- a/tests/test_environment_toctree.py +++ b/tests/test_environment_toctree.py @@ -36,7 +36,7 @@ def test_process_doc(app): list_item)]) assert_node(toctree[0][0], - [compact_paragraph, reference, "Welcome to Sphinx Tests's documentation!"]) + [compact_paragraph, reference, u"Welcome to Sphinx Tests’s documentation!"]) assert_node(toctree[0][0][0], reference, anchorname='') assert_node(toctree[0][1][0], addnodes.toctree, caption="Table of Contents", glob=False, hidden=False, @@ -150,7 +150,7 @@ def test_get_toc_for(app): addnodes.toctree)])], [list_item, compact_paragraph])]) # [2][0] assert_node(toctree[0][0], - [compact_paragraph, reference, "Welcome to Sphinx Tests's documentation!"]) + [compact_paragraph, reference, u"Welcome to Sphinx Tests’s documentation!"]) assert_node(toctree[0][1][2], ([compact_paragraph, reference, "subsection"], [bullet_list, list_item, compact_paragraph, reference, "subsubsection"])) @@ -177,7 +177,7 @@ def test_get_toc_for_only(app): addnodes.toctree)])], [list_item, compact_paragraph])]) # [2][0] assert_node(toctree[0][0], - [compact_paragraph, reference, "Welcome to Sphinx Tests's documentation!"]) + [compact_paragraph, reference, u"Welcome to Sphinx Tests’s documentation!"]) assert_node(toctree[0][1][1], ([compact_paragraph, reference, "Section for HTML"], [bullet_list, addnodes.toctree])) diff --git a/tests/test_markup.py b/tests/test_markup.py index ecc4b6a11..18c9d0bbd 100644 --- a/tests/test_markup.py +++ b/tests/test_markup.py @@ -14,11 +14,12 @@ import pickle from docutils import frontend, utils, nodes from docutils.parsers.rst import Parser as RstParser +from docutils.transforms.universal import SmartQuotes from sphinx import addnodes from sphinx.util import texescape from sphinx.util.docutils import sphinx_domains -from sphinx.writers.html import HTMLWriter, SmartyPantsHTMLTranslator +from sphinx.writers.html import HTMLWriter, HTMLTranslator from sphinx.writers.latex import LaTeXWriter, LaTeXTranslator import pytest @@ -31,6 +32,7 @@ def settings(app): optparser = frontend.OptionParser( components=(RstParser, HTMLWriter, LaTeXWriter)) settings = optparser.get_default_values() + settings.smart_quotes = True settings.env = app.builder.env settings.env.temp_data['docname'] = 'dummy' domain_context = sphinx_domains(settings.env) @@ -46,6 +48,7 @@ def parse(settings): document['file'] = 'dummy' parser = RstParser() parser.parse(rst, document) + SmartQuotes(document, startnode=None).apply() for msg in document.traverse(nodes.system_message): if msg['level'] == 1: msg.replace_self([]) @@ -62,7 +65,7 @@ class ForgivingTranslator: pass -class ForgivingHTMLTranslator(SmartyPantsHTMLTranslator, ForgivingTranslator): +class ForgivingHTMLTranslator(HTMLTranslator, ForgivingTranslator): pass @@ -178,8 +181,8 @@ def get_verifier(verify, verify_re): # verify smarty-pants quotes 'verify', '"John"', - '

“John”

', - r'\sphinxquotedblleft{}John\sphinxquotedblright{}', + u'

“John”

', + u"“John”", ), ( # ... but not in literal text diff --git a/tests/test_metadata.py b/tests/test_metadata.py index 77efff5ed..58f573b0a 100644 --- a/tests/test_metadata.py +++ b/tests/test_metadata.py @@ -34,14 +34,14 @@ def test_docinfo(app, status, warning): 'field name': u'This is a generic bibliographic field.', 'field name 2': (u'Generic bibliographic fields may contain multiple ' u'body elements.\n\nLike this.'), - 'status': u'This is a "work in progress"', + 'status': u'This is a “work in progress”', 'version': u'1', 'copyright': (u'This document has been placed in the public domain. ' u'You\nmay do with it as you wish. You may copy, modify,' u'\nredistribute, reattribute, sell, buy, rent, lease,\n' u'destroy, or improve it, quote it at length, excerpt,\n' u'incorporate, collate, fold, staple, or mutilate it, or ' - u'do\nanything else to it that your or anyone else\'s ' + u'do\nanything else to it that your or anyone else’s ' u'heart\ndesires.'), 'contact': u'goodger@python.org', 'date': u'2006-05-21', From c857f52f4b933ce50c8280f32b400092a9af2ac3 Mon Sep 17 00:00:00 2001 From: Dmitry Shachnev Date: Fri, 24 Mar 2017 21:29:51 +0300 Subject: [PATCH 13/91] =?UTF-8?q?Monkey=20patch=20docutils=E2=80=99=20educ?= =?UTF-8?q?ateQuotes=20function=20to=20fix=20apostrophes=20handling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sphinx/util/__init__.py | 1 + sphinx/util/smartypants.py | 143 +++++++++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+) create mode 100644 sphinx/util/smartypants.py diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py index d86882259..295848e40 100644 --- a/sphinx/util/__init__.py +++ b/sphinx/util/__init__.py @@ -34,6 +34,7 @@ from sphinx.util import logging from sphinx.util.console import strip_colors, colorize, bold, term_width_line # type: ignore from sphinx.util.fileutil import copy_asset_file from sphinx.util.osutil import fs_encoding +from sphinx.util import smartypants # noqa # import other utilities; partly for backwards compatibility, so don't # prune unused ones indiscriminately diff --git a/sphinx/util/smartypants.py b/sphinx/util/smartypants.py new file mode 100644 index 000000000..3932e9386 --- /dev/null +++ b/sphinx/util/smartypants.py @@ -0,0 +1,143 @@ +# -*- coding: utf-8 -*- +""" + sphinx.util.smartypants + ~~~~~~~~~~~~~~~~~~~~~~~ + + This code is copied from docutils’ docutils/utils/smartquotes.py + version 1.7.1 (from 2017-03-19). It should be removed in the future. + + :copyright: © 2010 Günter Milde, + original `SmartyPants`_: © 2003 John Gruber + smartypants.py: © 2004, 2007 Chad Miller + :license: Released under the terms of the `2-Clause BSD license`_, in short: + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notices and this notice are preserved. + This file is offered as-is, without any warranty. + + .. _SmartyPants: http://daringfireball.net/projects/smartypants/ + .. _2-Clause BSD license: http://www.spdx.org/licenses/BSD-2-Clause + + See the LICENSE file and the original docutils code for details. +""" +from __future__ import absolute_import, unicode_literals + +import re +from docutils.utils import smartquotes +from sphinx.util.docutils import __version_info__ as docutils_version + + +def educateQuotes(text, language='en'): + # type: (unicode, unicode) -> unicode + """ + Parameter: - text string (unicode or bytes). + - language (`BCP 47` language tag.) + Returns: The `text`, with "educated" curly quote characters. + + Example input: "Isn't this fun?" + Example output: “Isn’t this fun?“; + """ + + smart = smartquotes.smartchars(language) + smart.apostrophe = u'’' + + # oldtext = text + punct_class = r"""[!"#\$\%'()*+,-.\/:;<=>?\@\[\\\]\^_`{|}~]""" + + # Special case if the very first character is a quote + # followed by punctuation at a non-word-break. + # Close the quotes by brute force: + text = re.sub(r"""^'(?=%s\\B)""" % (punct_class,), smart.csquote, text) + text = re.sub(r"""^"(?=%s\\B)""" % (punct_class,), smart.cpquote, text) + + # Special case for double sets of quotes, e.g.: + #

He said, "'Quoted' words in a larger quote."

+ text = re.sub(r""""'(?=\w)""", smart.opquote + smart.osquote, text) + text = re.sub(r"""'"(?=\w)""", smart.osquote + smart.opquote, text) + + # Special case for decade abbreviations (the '80s): + text = re.sub(r"""\b'(?=\d{2}s)""", smart.csquote, text) + + close_class = r"""[^\ \t\r\n\[\{\(\-]""" + dec_dashes = r"""–|—""" + + # Get most opening single quotes: + opening_single_quotes_regex = re.compile(r""" + ( + \s | # a whitespace char, or +   | # a non-breaking space entity, or + -- | # dashes, or + &[mn]dash; | # named dash entities + %s | # or decimal entities + &\#x201[34]; # or hex + ) + ' # the quote + (?=\w) # followed by a word character + """ % (dec_dashes,), re.VERBOSE | re.UNICODE) + text = opening_single_quotes_regex.sub(r'\1' + smart.osquote, text) + + # In many locales, single closing quotes are different from apostrophe: + if smart.csquote != smart.apostrophe: + apostrophe_regex = re.compile(r"(?<=(\w|\d))'(?=\w)", re.UNICODE) + text = apostrophe_regex.sub(smart.apostrophe, text) + + closing_single_quotes_regex = re.compile(r""" + (%s) + ' + (?!\s | # whitespace + s\b | + \d # digits ('80s) + ) + """ % (close_class,), re.VERBOSE | re.UNICODE) + text = closing_single_quotes_regex.sub(r'\1' + smart.csquote, text) + + closing_single_quotes_regex = re.compile(r""" + (%s) + ' + (\s | s\b) + """ % (close_class,), re.VERBOSE | re.UNICODE) + text = closing_single_quotes_regex.sub(r'\1%s\2' % smart.csquote, text) + + # Any remaining single quotes should be opening ones: + text = re.sub(r"""'""", smart.osquote, text) + + # Get most opening double quotes: + opening_double_quotes_regex = re.compile(r""" + ( + \s | # a whitespace char, or +   | # a non-breaking space entity, or + -- | # dashes, or + &[mn]dash; | # named dash entities + %s | # or decimal entities + &\#x201[34]; # or hex + ) + " # the quote + (?=\w) # followed by a word character + """ % (dec_dashes,), re.VERBOSE) + text = opening_double_quotes_regex.sub(r'\1' + smart.opquote, text) + + # Double closing quotes: + closing_double_quotes_regex = re.compile(r""" + #(%s)? # character that indicates the quote should be closing + " + (?=\s) + """ % (close_class,), re.VERBOSE) + text = closing_double_quotes_regex.sub(smart.cpquote, text) + + closing_double_quotes_regex = re.compile(r""" + (%s) # character that indicates the quote should be closing + " + """ % (close_class,), re.VERBOSE) + text = closing_double_quotes_regex.sub(r'\1' + smart.cpquote, text) + + # Any remaining quotes should be opening ones. + text = re.sub(r'"', smart.opquote, text) + + return text + + +if docutils_version < (0, 13, 2): + # Monkey patch the old docutils versions to fix the issue mentioned + # at https://sourceforge.net/p/docutils/bugs/313/ + smartquotes.educateQuotes = educateQuotes From 1f9e25194339835ab3c68f02ba42dca842bf2803 Mon Sep 17 00:00:00 2001 From: Dmitry Shachnev Date: Fri, 24 Mar 2017 21:51:21 +0300 Subject: [PATCH 14/91] Add tests for English, Russian and French smart quotes --- tests/root/markup.txt | 20 ++++++++++++++++++++ tests/test_build_html.py | 7 +++++++ 2 files changed, 27 insertions(+) diff --git a/tests/root/markup.txt b/tests/root/markup.txt index 714dabe8d..b514b3587 100644 --- a/tests/root/markup.txt +++ b/tests/root/markup.txt @@ -426,6 +426,26 @@ More domains: .. default-role:: +Smart quotes +------------ + +* Smart "quotes" in English 'text'. +* Smart --- long and -- short dashes. +* Ellipsis... +* No smartypants in literal blocks: ``foo--"bar"...``. + +.. only:: html + + .. LaTeX does not like Cyrillic letters in this test, so it is HTML only. + + .. rst-class:: language-ru + + Этот "абзац" должен использовать 'русские' кавычки. + + .. rst-class:: language-fr + + Il dit : "C'est 'super' !" + .. rubric:: Footnotes .. [#] Like footnotes. diff --git a/tests/test_build_html.py b/tests/test_build_html.py index 9265caed4..e8dc2b831 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -297,6 +297,13 @@ def test_static_output(app): # tests for ``any`` role (".//a[@href='#with']/span", 'headings'), (".//a[@href='objects.html#func_without_body']/code/span", 'objects'), + # tests for smartypants + (".//li", u'Smart “quotes” in English ‘text’.'), + (".//li", u'Smart — long and – short dashes.'), + (".//li", u'Ellipsis…'), + (".//li//code//span[@class='pre']", 'foo--"bar"...'), + (".//p", u'Этот «абзац» должен использовать „русские“ кавычки.'), + (".//p", u'Il dit : « C’est ‹ super › ! »'), ], 'objects.html': [ (".//dt[@id='mod.Cls.meth1']", ''), From 4f4014c7c0d5543cb9a3fdebc6b4cd569a026541 Mon Sep 17 00:00:00 2001 From: Dmitry Shachnev Date: Thu, 16 Mar 2017 19:41:23 +0300 Subject: [PATCH 15/91] Remove sphinxquotedblleft and sphinxquotedblright, no longer needed --- sphinx/texinputs/sphinx.sty | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty index a4ceb4940..cb443536c 100644 --- a/sphinx/texinputs/sphinx.sty +++ b/sphinx/texinputs/sphinx.sty @@ -1388,10 +1388,6 @@ \protected\def\sphinxstyleabbreviation {\textsc} \protected\def\sphinxstyleliteralintitle {\sphinxcode} -% LaTeX writer uses macros to hide double quotes from \sphinxcode's \@noligs -\protected\def\sphinxquotedblleft{``} -\protected\def\sphinxquotedblright{''} - % Tell TeX about pathological hyphenation cases: \hyphenation{Base-HTTP-Re-quest-Hand-ler} \endinput From 9236beb9ee41899897d8dbfa373ee21d7d4b252f Mon Sep 17 00:00:00 2001 From: Dmitry Shachnev Date: Mon, 17 Apr 2017 14:15:28 +0300 Subject: [PATCH 16/91] Deprecate the html_use_smartypants option --- doc/config.rst | 6 ++++++ sphinx/builders/html.py | 2 +- sphinx/environment/__init__.py | 10 ++++++++-- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/doc/config.rst b/doc/config.rst index 5e641cbfb..39feaae78 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -768,6 +768,12 @@ that use Sphinx's HTMLWriter class. will be used to convert quotes and dashes to typographically correct entities. Default: ``True``. + .. deprecated:: 1.6 + Use the `smart_quotes option`_ in the Docutils configuration file + (``docutils.conf``) instead. + + .. _`smart_quotes option`: http://docutils.sourceforge.net/docs/user/config.html#smart-quotes + .. confval:: html_add_permalinks Sphinx will add "permalinks" for each heading and description environment as diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index a0b3d8e87..16145beb3 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -1312,7 +1312,7 @@ def setup(app): app.add_config_value('html_static_path', [], 'html') app.add_config_value('html_extra_path', [], 'html') app.add_config_value('html_last_updated_fmt', None, 'html', string_classes) - app.add_config_value('html_use_smartypants', True, 'html') + app.add_config_value('html_use_smartypants', None, 'html') app.add_config_value('html_sidebars', {}, 'html') app.add_config_value('html_additional_pages', {}, 'html') app.add_config_value('html_domain_indices', True, 'html', [list]) diff --git a/sphinx/environment/__init__.py b/sphinx/environment/__init__.py index b0a1a2ce5..4c078a9a3 100644 --- a/sphinx/environment/__init__.py +++ b/sphinx/environment/__init__.py @@ -674,8 +674,14 @@ class BuildEnvironment(object): self.settings['gettext_compact'] = self.config.gettext_compact language = (self.config.language or 'en').replace('_', '-') self.settings['language_code'] = language - if language in smartchars.quotes: - self.settings['smart_quotes'] = self.config.html_use_smartypants + if self.config.html_use_smartypants is not None: + warnings.warn("html_use_smartypants option is deprecated. Use the " + "smart_quotes option in docutils.conf instead.", + RemovedInSphinx17Warning) + if language in smartchars.quotes: + self.settings['smart_quotes'] = self.config.html_use_smartypants + elif language in smartchars.quotes: # We enable smartypants by default + self.settings['smart_quotes'] = True docutilsconf = path.join(self.srcdir, 'docutils.conf') # read docutils.conf from source dir, not from current dir From f99f5b23ee630a33cf2c149a3928a533296e3129 Mon Sep 17 00:00:00 2001 From: Dmitry Shachnev Date: Mon, 17 Apr 2017 14:25:28 +0300 Subject: [PATCH 17/91] Backport fixes for French quotes from docutils --- sphinx/util/smartypants.py | 6 ++++++ tests/test_build_html.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/sphinx/util/smartypants.py b/sphinx/util/smartypants.py index 3932e9386..e42bfa82f 100644 --- a/sphinx/util/smartypants.py +++ b/sphinx/util/smartypants.py @@ -141,3 +141,9 @@ if docutils_version < (0, 13, 2): # Monkey patch the old docutils versions to fix the issue mentioned # at https://sourceforge.net/p/docutils/bugs/313/ smartquotes.educateQuotes = educateQuotes + + # Fix the issue with French quotes mentioned at + # https://sourceforge.net/p/docutils/mailman/message/35760696/ + quotes = smartquotes.smartchars.quotes + quotes['fr'] = (u'«\u00a0', u'\u00a0»', u'“', u'”') + quotes['fr-ch'] = u'«»‹›' diff --git a/tests/test_build_html.py b/tests/test_build_html.py index e8dc2b831..0758874a7 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -303,7 +303,7 @@ def test_static_output(app): (".//li", u'Ellipsis…'), (".//li//code//span[@class='pre']", 'foo--"bar"...'), (".//p", u'Этот «абзац» должен использовать „русские“ кавычки.'), - (".//p", u'Il dit : « C’est ‹ super › ! »'), + (".//p", u'Il dit : « C’est “super” ! »'), ], 'objects.html': [ (".//dt[@id='mod.Cls.meth1']", ''), From a9cbb42e7fd42dfbfd88df5d262fae343418528e Mon Sep 17 00:00:00 2001 From: Dmitry Shachnev Date: Mon, 17 Apr 2017 14:44:52 +0300 Subject: [PATCH 18/91] Remove bulk_text_processors methods, they are no longer needed --- sphinx/writers/html.py | 6 ------ sphinx/writers/html5.py | 6 ------ 2 files changed, 12 deletions(-) diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py index 572b2825e..6dd8bafe5 100644 --- a/sphinx/writers/html.py +++ b/sphinx/writers/html.py @@ -683,10 +683,6 @@ class HTMLTranslator(BaseTranslator): BaseTranslator.visit_option_group(self, node) self.context[-2] = self.context[-2].replace(' ', ' ') - def bulk_text_processor(self, text): - # type: (unicode) -> unicode - return text - # overwritten def visit_Text(self, node): # type: (nodes.Node) -> None @@ -708,8 +704,6 @@ class HTMLTranslator(BaseTranslator): else: if self.in_mailto and self.settings.cloak_email_addresses: encoded = self.cloak_email(encoded) - else: - encoded = self.bulk_text_processor(encoded) self.body.append(encoded) def visit_note(self, node): diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py index 8e3843c09..51c4e8ecb 100644 --- a/sphinx/writers/html5.py +++ b/sphinx/writers/html5.py @@ -626,10 +626,6 @@ class HTML5Translator(BaseTranslator): # type: (nodes.Node) -> None self.body.append('') - def bulk_text_processor(self, text): - # type: (unicode) -> unicode - return text - # overwritten def visit_Text(self, node): # type: (nodes.Node) -> None @@ -651,8 +647,6 @@ class HTML5Translator(BaseTranslator): else: if self.in_mailto and self.settings.cloak_email_addresses: encoded = self.cloak_email(encoded) - else: - encoded = self.bulk_text_processor(encoded) self.body.append(encoded) def visit_note(self, node): From 4721767d08a3e4700e72e5df39f7064f0d6ceba0 Mon Sep 17 00:00:00 2001 From: Dmitry Shachnev Date: Mon, 24 Apr 2017 15:12:35 +0300 Subject: [PATCH 19/91] Also monkey-patch the educate_tokens from Docutils To fix https://sourceforge.net/p/docutils/bugs/317/. --- sphinx/util/smartypants.py | 131 +++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) diff --git a/sphinx/util/smartypants.py b/sphinx/util/smartypants.py index e42bfa82f..2c4fe7d92 100644 --- a/sphinx/util/smartypants.py +++ b/sphinx/util/smartypants.py @@ -27,6 +27,9 @@ import re from docutils.utils import smartquotes from sphinx.util.docutils import __version_info__ as docutils_version +if False: # For type annotation + from typing import Iterable, Iterator, Tuple # NOQA + def educateQuotes(text, language='en'): # type: (unicode, unicode) -> unicode @@ -137,11 +140,139 @@ def educateQuotes(text, language='en'): return text +def educate_tokens(text_tokens, attr='1', language='en'): + # type: (Iterable[Tuple[str, unicode]], unicode, unicode) -> Iterator + """Return iterator that "educates" the items of `text_tokens`. + """ + + # Parse attributes: + # 0 : do nothing + # 1 : set all + # 2 : set all, using old school en- and em- dash shortcuts + # 3 : set all, using inverted old school en and em- dash shortcuts + # + # q : quotes + # b : backtick quotes (``double'' only) + # B : backtick quotes (``double'' and `single') + # d : dashes + # D : old school dashes + # i : inverted old school dashes + # e : ellipses + # w : convert " entities to " for Dreamweaver users + + convert_quot = False # translate " entities into normal quotes? + do_dashes = 0 + do_backticks = 0 + do_quotes = False + do_ellipses = False + do_stupefy = False + + if attr == "0": # Do nothing. + pass + elif attr == "1": # Do everything, turn all options on. + do_quotes = True + do_backticks = 1 + do_dashes = 1 + do_ellipses = True + elif attr == "2": + # Do everything, turn all options on, use old school dash shorthand. + do_quotes = True + do_backticks = 1 + do_dashes = 2 + do_ellipses = True + elif attr == "3": + # Do everything, use inverted old school dash shorthand. + do_quotes = True + do_backticks = 1 + do_dashes = 3 + do_ellipses = True + elif attr == "-1": # Special "stupefy" mode. + do_stupefy = True + else: + if "q" in attr: + do_quotes = True + if "b" in attr: + do_backticks = 1 + if "B" in attr: + do_backticks = 2 + if "d" in attr: + do_dashes = 1 + if "D" in attr: + do_dashes = 2 + if "i" in attr: + do_dashes = 3 + if "e" in attr: + do_ellipses = True + if "w" in attr: + convert_quot = True + + prev_token_last_char = " " + # Last character of the previous text token. Used as + # context to curl leading quote characters correctly. + + for (ttype, text) in text_tokens: + + # skip HTML and/or XML tags as well as emtpy text tokens + # without updating the last character + if ttype == 'tag' or not text: + yield text + continue + + # skip literal text (math, literal, raw, ...) + if ttype == 'literal': + prev_token_last_char = text[-1:] + yield text + continue + + last_char = text[-1:] # Remember last char before processing. + + text = smartquotes.processEscapes(text) + + if convert_quot: + text = re.sub('"', '"', text) + + if do_dashes == 1: + text = smartquotes.educateDashes(text) + elif do_dashes == 2: + text = smartquotes.educateDashesOldSchool(text) + elif do_dashes == 3: + text = smartquotes.educateDashesOldSchoolInverted(text) + + if do_ellipses: + text = smartquotes.educateEllipses(text) + + # Note: backticks need to be processed before quotes. + if do_backticks: + text = smartquotes.educateBackticks(text, language) + + if do_backticks == 2: + text = smartquotes.educateSingleBackticks(text, language) + + if do_quotes: + # Replace plain quotes to prevent converstion to + # 2-character sequence in French. + context = prev_token_last_char.replace('"', ';').replace("'", ';') + text = educateQuotes(context + text, language)[1:] + + if do_stupefy: + text = smartquotes.stupefyEntities(text, language) + + # Remember last char as context for the next token + prev_token_last_char = last_char + + text = smartquotes.processEscapes(text, restore=True) + + yield text + + if docutils_version < (0, 13, 2): # Monkey patch the old docutils versions to fix the issue mentioned # at https://sourceforge.net/p/docutils/bugs/313/ smartquotes.educateQuotes = educateQuotes + # And the one mentioned at https://sourceforge.net/p/docutils/bugs/317/ + smartquotes.educate_tokens = educate_tokens + # Fix the issue with French quotes mentioned at # https://sourceforge.net/p/docutils/mailman/message/35760696/ quotes = smartquotes.smartchars.quotes From 50609f200006a24eefa44a80a5ce703831c2beb8 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Tue, 25 Apr 2017 23:28:13 +0900 Subject: [PATCH 20/91] Update CHANGES for PR #3666 --- CHANGES | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES b/CHANGES index 0d72fa989..12d67a00a 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,8 @@ Release 1.6 beta2 (in development) Incompatible changes -------------------- +* #3345: Replace the custom smartypants code with docutils' smart_quotes + Deprecated ---------- From f8b58e41cd1bb44d576f6b9da9f07b89f3d0a823 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 27 Apr 2017 00:17:07 +0900 Subject: [PATCH 21/91] Fix #3669: gettext builder fails with "ValueError: substring not found" --- CHANGES | 1 + sphinx/builders/gettext.py | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/CHANGES b/CHANGES index 12d67a00a..d6de0d3ad 100644 --- a/CHANGES +++ b/CHANGES @@ -16,6 +16,7 @@ Bugs fixed ---------- * #3661: sphinx-build crashes on parallel build +* #3669: gettext builder fails with "ValueError: substring not found" Testing -------- diff --git a/sphinx/builders/gettext.py b/sphinx/builders/gettext.py index 280f2be99..4da19bd2c 100644 --- a/sphinx/builders/gettext.py +++ b/sphinx/builders/gettext.py @@ -194,14 +194,18 @@ ltz = LocalTimeZone() def should_write(filepath, new_content): if not path.exists(filepath): return True - with open(filepath, 'r', encoding='utf-8') as oldpot: # type: ignore - old_content = oldpot.read() - old_header_index = old_content.index('"POT-Creation-Date:') - new_header_index = new_content.index('"POT-Creation-Date:') - old_body_index = old_content.index('"PO-Revision-Date:') - new_body_index = new_content.index('"PO-Revision-Date:') - return ((old_content[:old_header_index] != new_content[:new_header_index]) or - (new_content[new_body_index:] != old_content[old_body_index:])) + try: + with open(filepath, 'r', encoding='utf-8') as oldpot: # type: ignore + old_content = oldpot.read() + old_header_index = old_content.index('"POT-Creation-Date:') + new_header_index = new_content.index('"POT-Creation-Date:') + old_body_index = old_content.index('"PO-Revision-Date:') + new_body_index = new_content.index('"PO-Revision-Date:') + return ((old_content[:old_header_index] != new_content[:new_header_index]) or + (new_content[new_body_index:] != old_content[old_body_index:])) + except ValueError: + pass + return True From 02ff0863c671c2468b9c841b5a362eb7435bb4b2 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 27 Apr 2017 00:33:40 +0900 Subject: [PATCH 22/91] Move deprecation warning --- sphinx/builders/html.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index cea14965a..a356c5f23 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -96,19 +96,22 @@ class CSSContainer(list): the entry with Stylesheet class. """ def append(self, obj): - warnings.warn('builder.css_files is deprecated. ' - 'Please use app.add_stylesheet() instead.', - RemovedInSphinx20Warning) if isinstance(obj, Stylesheet): super(CSSContainer, self).append(obj) else: super(CSSContainer, self).append(Stylesheet(obj, None, 'stylesheet')) def extend(self, other): + warnings.warn('builder.css_files is deprecated. ' + 'Please use app.add_stylesheet() instead.', + RemovedInSphinx20Warning) for item in other: self.append(item) def __iadd__(self, other): + warnings.warn('builder.css_files is deprecated. ' + 'Please use app.add_stylesheet() instead.', + RemovedInSphinx20Warning) for item in other: self.append(item) From 45a61843ece6049012ca117aecf9aebf81e8152b Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 27 Apr 2017 21:24:12 +0900 Subject: [PATCH 23/91] Make ImportWarning silent --- tests/run.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/run.py b/tests/run.py index 71a41c7c1..673ad1339 100755 --- a/tests/run.py +++ b/tests/run.py @@ -24,6 +24,7 @@ sys.path.insert(0, os.path.abspath(os.path.join(testroot, os.path.pardir))) # filter warnings of test dependencies warnings.filterwarnings('ignore', category=DeprecationWarning, module='site') # virtualenv warnings.filterwarnings('ignore', category=ImportWarning, module='backports') +warnings.filterwarnings('ignore', category=ImportWarning, module='pkgutil') warnings.filterwarnings('ignore', category=ImportWarning, module='pytest_cov') warnings.filterwarnings('ignore', category=PendingDeprecationWarning, module=r'_pytest\..*') From 8462d4bcdab78f1bd3f064e14c31f67519e023e7 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 27 Apr 2017 21:24:12 +0900 Subject: [PATCH 24/91] doc: Update --- doc/theming.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/theming.rst b/doc/theming.rst index 2a7063925..711d0ae72 100644 --- a/doc/theming.rst +++ b/doc/theming.rst @@ -47,7 +47,9 @@ file :file:`blue.zip`, you can put it right in the directory containing html_theme_path = ["."] The third form is a python package. If a theme you want to use is distributed -as a python package, you can use it after installing:: +as a python package, you can use it after installing + +.. code-block:: bash # installing theme package $ pip install sphinxjp.themes.dotted From 5c5272c4aba57b56290039d5b765e5ff07dea6b3 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 27 Apr 2017 21:44:46 +0900 Subject: [PATCH 25/91] Fix #3657: EPUB builder crashes if document startswith genindex exists --- CHANGES | 1 + sphinx/builders/epub.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index b38d91c4d..344e65fff 100644 --- a/CHANGES +++ b/CHANGES @@ -16,6 +16,7 @@ Bugs fixed * #3614: Sphinx crashes with requests-2.5.0 * #3618: autodoc crashes with tupled arguments * #3664: No space after the bullet in items of a latex list produced by Sphinx +* #3657: EPUB builder crashes if document startswith genindex exists Testing -------- diff --git a/sphinx/builders/epub.py b/sphinx/builders/epub.py index 152401447..ea90642bc 100644 --- a/sphinx/builders/epub.py +++ b/sphinx/builders/epub.py @@ -500,7 +500,7 @@ class EpubBuilder(StandaloneHTMLBuilder): This method is overwritten for genindex pages in order to fix href link attributes. """ - if pagename.startswith('genindex'): + if pagename.startswith('genindex') and 'genindexentries' in addctx: if not self.use_index: return self.fix_genindex(addctx['genindexentries']) From b4eadba3a1a6c787fbcfcef041d10383e1f51bcc Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 27 Apr 2017 21:49:19 +0900 Subject: [PATCH 26/91] Remove deprecated feature: sphinx.util.nodes.process_only_nodes() --- sphinx/util/nodes.py | 26 -------------------------- tests/test_directive_only.py | 3 +-- 2 files changed, 1 insertion(+), 28 deletions(-) diff --git a/sphinx/util/nodes.py b/sphinx/util/nodes.py index 5c444fb56..14bd09791 100644 --- a/sphinx/util/nodes.py +++ b/sphinx/util/nodes.py @@ -11,14 +11,12 @@ from __future__ import absolute_import import re -import warnings from six import text_type from docutils import nodes from sphinx import addnodes -from sphinx.deprecation import RemovedInSphinx17Warning from sphinx.locale import pairindextypes from sphinx.util import logging @@ -354,30 +352,6 @@ def set_role_source_info(inliner, lineno, node): node.source, node.line = inliner.reporter.get_source_and_line(lineno) -def process_only_nodes(doctree, tags): - # type: (nodes.Node, Tags) -> None - # A comment on the comment() nodes being inserted: replacing by [] would - # result in a "Losing ids" exception if there is a target node before - # the only node, so we make sure docutils can transfer the id to - # something, even if it's just a comment and will lose the id anyway... - warnings.warn('process_only_nodes() is deprecated. ' - 'Use sphinx.environment.apply_post_transforms() instead.', - RemovedInSphinx17Warning) - - for node in doctree.traverse(addnodes.only): - try: - ret = tags.eval_condition(node['expr']) - except Exception as err: - logger.warning('exception while evaluating only directive expression: %s', err, - location=node) - node.replace_self(node.children or nodes.comment()) - else: - if ret: - node.replace_self(node.children or nodes.comment()) - else: - node.replace_self(nodes.comment()) - - # monkey-patch Element.copy to copy the rawsource and line def _new_copy(self): diff --git a/tests/test_directive_only.py b/tests/test_directive_only.py index 35d15b391..d8017f469 100644 --- a/tests/test_directive_only.py +++ b/tests/test_directive_only.py @@ -12,7 +12,6 @@ import re from docutils import nodes -from sphinx.util.nodes import process_only_nodes import pytest @@ -46,7 +45,7 @@ def test_sectioning(app, status, warning): app.builder.build(['only']) doctree = app.env.get_doctree('only') - process_only_nodes(doctree, app.builder.tags) + app.env.apply_post_transforms(doctree, 'only') parts = [getsects(n) for n in [_n for _n in doctree.children if isinstance(_n, nodes.section)]] From 3cd87176b6fccf009642c7f0d3f6293e87da654e Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 27 Apr 2017 21:49:19 +0900 Subject: [PATCH 27/91] Remove deprecated feature: sphinx.util.nodes.compat module --- sphinx/util/compat.py | 48 ------------------------------------------- 1 file changed, 48 deletions(-) delete mode 100644 sphinx/util/compat.py diff --git a/sphinx/util/compat.py b/sphinx/util/compat.py deleted file mode 100644 index 30a89bbfb..000000000 --- a/sphinx/util/compat.py +++ /dev/null @@ -1,48 +0,0 @@ -# -*- coding: utf-8 -*- -""" - sphinx.util.compat - ~~~~~~~~~~~~~~~~~~ - - Stuff for docutils compatibility. - - :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. - :license: BSD, see LICENSE for details. -""" -from __future__ import absolute_import - -import sys -import warnings - -from docutils.parsers.rst import Directive # noqa -from docutils import __version__ as _du_version - -from sphinx.deprecation import RemovedInSphinx17Warning - -docutils_version = tuple(int(x) for x in _du_version.split('.')[:2]) - -if False: - # For type annotation - from typing import Any, Dict # NOQA - - -class _DeprecationWrapper(object): - def __init__(self, mod, deprecated): - # type: (Any, Dict) -> None - self._mod = mod - self._deprecated = deprecated - - def __getattr__(self, attr): - # type: (str) -> Any - if attr in self._deprecated: - warnings.warn("sphinx.util.compat.%s is deprecated and will be " - "removed in Sphinx 1.7, please use the standard " - "library version instead." % attr, - RemovedInSphinx17Warning, stacklevel=2) - return self._deprecated[attr] - return getattr(self._mod, attr) - - -sys.modules[__name__] = _DeprecationWrapper(sys.modules[__name__], dict( # type: ignore - docutils_version = docutils_version, - Directive = Directive, -)) From f2c93b3175c087cbb7305be3a9ed8f8b32cabc57 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 27 Apr 2017 21:49:19 +0900 Subject: [PATCH 28/91] Remove deprecated feature: epub2 builder --- doc/builders.rst | 20 -------- sphinx/application.py | 1 - sphinx/builders/epub2.py | 100 --------------------------------------- sphinx/builders/epub3.py | 27 +++++++++-- tests/test_build.py | 2 +- 5 files changed, 25 insertions(+), 125 deletions(-) delete mode 100644 sphinx/builders/epub2.py diff --git a/doc/builders.rst b/doc/builders.rst index 618eb005c..bf63e7235 100644 --- a/doc/builders.rst +++ b/doc/builders.rst @@ -125,26 +125,6 @@ The builder's "name" must be given to the **-b** command-line option of .. autoattribute:: supported_image_types -.. module:: sphinx.builders.epub2 -.. class:: Epub2Builder - - This builder produces the same output as the standalone HTML builder, but - also generates an *epub* file for ebook readers. See :ref:`epub-faq` for - details about it. For definition of the epub format, have a look at - ``_ or ``_. - The builder creates *EPUB 2* files. - - .. autoattribute:: name - - .. autoattribute:: format - - .. autoattribute:: supported_image_types - - .. deprecated:: 1.5 - - Since Sphinx-1.5, the epub3 builder is used for the default builder of epub. - Now EpubBuilder is renamed to epub2. - .. module:: sphinx.builders.epub3 .. class:: Epub3Builder diff --git a/sphinx/application.py b/sphinx/application.py index 87ed66637..93032578f 100644 --- a/sphinx/application.py +++ b/sphinx/application.py @@ -60,7 +60,6 @@ if False: builtin_extensions = ( 'sphinx.builders.applehelp', 'sphinx.builders.changes', - 'sphinx.builders.epub2', 'sphinx.builders.epub3', 'sphinx.builders.devhelp', 'sphinx.builders.dummy', diff --git a/sphinx/builders/epub2.py b/sphinx/builders/epub2.py deleted file mode 100644 index a6dcc8568..000000000 --- a/sphinx/builders/epub2.py +++ /dev/null @@ -1,100 +0,0 @@ -# -*- coding: utf-8 -*- -""" - sphinx.builders.epub2 - ~~~~~~~~~~~~~~~~~~~~~ - - Build epub2 files. - Originally derived from qthelp.py. - - :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS. - :license: BSD, see LICENSE for details. -""" - -import warnings -from os import path - -from sphinx import package_dir -from sphinx.builders import _epub_base -from sphinx.util.osutil import make_filename -from sphinx.deprecation import RemovedInSphinx17Warning - -if False: - # For type annotation - from typing import Any, Dict # NOQA - from sphinx.application import Sphinx # NOQA - - -DOCTYPE = '''''' - - -# The epub publisher - -class Epub2Builder(_epub_base.EpubBuilder): - """ - Builder that outputs epub files. - - It creates the metainfo files container.opf, toc.ncx, mimetype, and - META-INF/container.xml. Afterwards, all necessary files are zipped to an - epub file. - """ - name = 'epub2' - - template_dir = path.join(package_dir, 'templates', 'epub2') - doctype = DOCTYPE - - # Finish by building the epub file - def handle_finish(self): - # type: () -> None - """Create the metainfo files and finally the epub.""" - self.get_toc() - self.build_mimetype(self.outdir, 'mimetype') - self.build_container(self.outdir, 'META-INF/container.xml') - self.build_content(self.outdir, 'content.opf') - self.build_toc(self.outdir, 'toc.ncx') - self.build_epub(self.outdir, self.config.epub_basename + '.epub') - - -def emit_deprecation_warning(app): - # type: (Sphinx) -> None - if app.builder.__class__ is Epub2Builder: - warnings.warn('epub2 builder is deprecated. Please use epub3 builder instead.', - RemovedInSphinx17Warning) - - -def setup(app): - # type: (Sphinx) -> Dict[unicode, Any] - app.setup_extension('sphinx.builders.html') - app.add_builder(Epub2Builder) - app.connect('builder-inited', emit_deprecation_warning) - - # config values - app.add_config_value('epub_basename', lambda self: make_filename(self.project), None) - app.add_config_value('epub_theme', 'epub', 'html') - app.add_config_value('epub_theme_options', {}, 'html') - app.add_config_value('epub_title', lambda self: self.html_title, 'html') - app.add_config_value('epub_author', 'unknown', 'html') - app.add_config_value('epub_language', lambda self: self.language or 'en', 'html') - app.add_config_value('epub_publisher', 'unknown', 'html') - app.add_config_value('epub_copyright', lambda self: self.copyright, 'html') - app.add_config_value('epub_identifier', 'unknown', 'html') - app.add_config_value('epub_scheme', 'unknown', 'html') - app.add_config_value('epub_uid', 'unknown', 'env') - app.add_config_value('epub_cover', (), 'env') - app.add_config_value('epub_guide', (), 'env') - app.add_config_value('epub_pre_files', [], 'env') - app.add_config_value('epub_post_files', [], 'env') - app.add_config_value('epub_exclude_files', [], 'env') - app.add_config_value('epub_tocdepth', 3, 'env') - app.add_config_value('epub_tocdup', True, 'env') - app.add_config_value('epub_tocscope', 'default', 'env') - app.add_config_value('epub_fix_images', False, 'env') - app.add_config_value('epub_max_image_width', 0, 'env') - app.add_config_value('epub_show_urls', 'inline', 'html') - app.add_config_value('epub_use_index', lambda self: self.html_use_index, 'html') - - return { - 'version': 'builtin', - 'parallel_read_safe': True, - 'parallel_write_safe': True, - } diff --git a/sphinx/builders/epub3.py b/sphinx/builders/epub3.py index 19baad344..574447544 100644 --- a/sphinx/builders/epub3.py +++ b/sphinx/builders/epub3.py @@ -19,6 +19,7 @@ from sphinx.config import string_classes, ENUM from sphinx.builders import _epub_base from sphinx.util import logging from sphinx.util.fileutil import copy_asset_file +from sphinx.util.osutil import make_filename if False: # For type annotation @@ -225,12 +226,32 @@ class Epub3Builder(_epub_base.EpubBuilder): def setup(app): # type: (Sphinx) -> Dict[unicode, Any] - - app.setup_extension('sphinx.builders.epub2') - app.add_builder(Epub3Builder) # config values + app.add_config_value('epub_basename', lambda self: make_filename(self.project), None) + app.add_config_value('epub_theme', 'epub', 'html') + app.add_config_value('epub_theme_options', {}, 'html') + app.add_config_value('epub_title', lambda self: self.html_title, 'html') + app.add_config_value('epub_author', 'unknown', 'html') + app.add_config_value('epub_language', lambda self: self.language or 'en', 'html') + app.add_config_value('epub_publisher', 'unknown', 'html') + app.add_config_value('epub_copyright', lambda self: self.copyright, 'html') + app.add_config_value('epub_identifier', 'unknown', 'html') + app.add_config_value('epub_scheme', 'unknown', 'html') + app.add_config_value('epub_uid', 'unknown', 'env') + app.add_config_value('epub_cover', (), 'env') + app.add_config_value('epub_guide', (), 'env') + app.add_config_value('epub_pre_files', [], 'env') + app.add_config_value('epub_post_files', [], 'env') + app.add_config_value('epub_exclude_files', [], 'env') + app.add_config_value('epub_tocdepth', 3, 'env') + app.add_config_value('epub_tocdup', True, 'env') + app.add_config_value('epub_tocscope', 'default', 'env') + app.add_config_value('epub_fix_images', False, 'env') + app.add_config_value('epub_max_image_width', 0, 'env') + app.add_config_value('epub_show_urls', 'inline', 'html') + app.add_config_value('epub_use_index', lambda self: self.html_use_index, 'html') app.add_config_value('epub_description', 'unknown', 'epub3', string_classes) app.add_config_value('epub_contributor', 'unknown', 'epub3', string_classes) app.add_config_value('epub_writing_mode', 'horizontal', 'epub3', diff --git a/tests/test_build.py b/tests/test_build.py index 221ea6462..02c29d393 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -58,7 +58,7 @@ def nonascii_srcdir(request): [ # note: no 'html' - if it's ok with dirhtml it's ok with html 'dirhtml', 'singlehtml', 'latex', 'texinfo', 'pickle', 'json', 'text', - 'htmlhelp', 'qthelp', 'epub2', 'epub', 'applehelp', 'changes', 'xml', + 'htmlhelp', 'qthelp', 'epub', 'applehelp', 'changes', 'xml', 'pseudoxml', 'man', 'linkcheck', ], ) From 361a34257becbc0ae87c6ae10609427dab7ab11f Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 27 Apr 2017 21:49:19 +0900 Subject: [PATCH 29/91] Remove deprecated feature: latex_keep_old_macro_names --- doc/_static/conf.py.txt | 6 ------ doc/config.rst | 20 -------------------- doc/latex.rst | 5 +---- sphinx/builders/latex.py | 18 ------------------ sphinx/texinputs/sphinx.sty | 32 -------------------------------- sphinx/writers/latex.py | 4 +--- 6 files changed, 2 insertions(+), 83 deletions(-) diff --git a/doc/_static/conf.py.txt b/doc/_static/conf.py.txt index be0c846db..f1a4ecdbc 100644 --- a/doc/_static/conf.py.txt +++ b/doc/_static/conf.py.txt @@ -280,12 +280,6 @@ latex_documents = [ # # latex_appendices = [] -# If false, will not define \strong, \code, \titleref, \crossref ... but only -# \sphinxstrong, ..., \sphinxtitleref, ... to help avoid clash with user added -# packages. -# -# latex_keep_old_macro_names = True - # If false, no module index is generated. # # latex_domain_indices = True diff --git a/doc/config.rst b/doc/config.rst index 8c77ee2c6..7d303b0ee 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -1615,26 +1615,6 @@ These options influence LaTeX output. See further :doc:`latex`. value selected the ``'inline'`` display. For backwards compatibility, ``True`` is still accepted. -.. confval:: latex_keep_old_macro_names - - If ``True`` the ``\strong``, ``\code``, ``\bfcode``, ``\email``, - ``\tablecontinued``, ``\titleref``, ``\menuselection``, ``\accelerator``, - ``\crossref``, ``\termref``, and ``\optional`` text styling macros are - pre-defined by Sphinx and may be user-customized by some - ``\renewcommand``'s inserted either via ``'preamble'`` key or :dudir:`raw - ` directive. If ``False``, only ``\sphinxstrong``, - etc... macros are defined (and may be redefined by user). - - The default is ``False`` as it prevents macro name conflicts caused by - latex packages. For example (``lualatex`` or ``xelatex``) ``fontspec v2.6`` - has its own ``\strong`` macro. - - .. versionadded:: 1.4.5 - .. versionchanged:: 1.6 - Default was changed from ``True`` to ``False``. - .. deprecated:: 1.6 - This setting will be removed at Sphinx 1.7. - .. confval:: latex_use_latex_multicolumn If ``False`` (default), the LaTeX writer uses for merged cells in grid diff --git a/doc/latex.rst b/doc/latex.rst index e2c518058..b8795fa19 100644 --- a/doc/latex.rst +++ b/doc/latex.rst @@ -414,10 +414,7 @@ Let us now list some macros from the package file - text styling commands (they have one argument): ``\sphinx`` with ```` being one of ``strong``, ``bfcode``, ``email``, ``tablecontinued``, ``titleref``, ``menuselection``, ``accelerator``, ``crossref``, ``termref``, - ``optional``. The non-prefixed macros will still be defined if option - :confval:`latex_keep_old_macro_names` has been set to ``True`` (default is - ``False``), in which case the prefixed macros expand to the - non-prefixed ones. + ``optional``. .. versionadded:: 1.4.5 Use of ``\sphinx`` prefixed macro names to limit possibilities of conflict diff --git a/sphinx/builders/latex.py b/sphinx/builders/latex.py index 5b4150d1d..4e906ea60 100644 --- a/sphinx/builders/latex.py +++ b/sphinx/builders/latex.py @@ -10,7 +10,6 @@ """ import os -import warnings from os import path from docutils import nodes @@ -19,7 +18,6 @@ from docutils.utils import new_document from docutils.frontend import OptionParser from sphinx import package_dir, addnodes, highlighting -from sphinx.deprecation import RemovedInSphinx17Warning from sphinx.config import string_classes, ENUM from sphinx.errors import SphinxError from sphinx.locale import _ @@ -257,21 +255,6 @@ def validate_config_values(app): app.config.latex_toplevel_sectioning) app.config.latex_toplevel_sectioning = None # type: ignore - if 'footer' in app.config.latex_elements: - if 'postamble' in app.config.latex_elements: - logger.warning("latex_elements['footer'] conflicts with " - "latex_elements['postamble'], ignored.") - else: - warnings.warn("latex_elements['footer'] is deprecated. " - "Use latex_elements['preamble'] instead.", - RemovedInSphinx17Warning) - app.config.latex_elements['postamble'] = app.config.latex_elements['footer'] - - if app.config.latex_keep_old_macro_names: - warnings.warn("latex_keep_old_macro_names is deprecated. " - "LaTeX markup since Sphinx 1.4.5 uses only prefixed macro names.", - RemovedInSphinx17Warning) - def default_latex_engine(config): # type: (Config) -> unicode @@ -305,7 +288,6 @@ def setup(app): None) app.add_config_value('latex_logo', None, None, string_classes) app.add_config_value('latex_appendices', [], None) - app.add_config_value('latex_keep_old_macro_names', False, None) app.add_config_value('latex_use_latex_multicolumn', False, None) app.add_config_value('latex_toplevel_sectioning', None, None, [str]) app.add_config_value('latex_domain_indices', True, None, [list]) diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty index 0689a9480..f50384a62 100644 --- a/sphinx/texinputs/sphinx.sty +++ b/sphinx/texinputs/sphinx.sty @@ -185,7 +185,6 @@ \DeclareStringOption[.5\dimexpr\inv@mag in\relax]{marginpar} \fi -\DeclareBoolOption{dontkeepoldnames} % \ifspx@opt@dontkeepoldnames = \iffalse \DeclareStringOption[0]{maxlistdepth}% \newcommand*\spx@opt@maxlistdepth{0} % dimensions, we declare the \dimen registers here. @@ -1347,37 +1346,6 @@ \long\protected\def\sphinxoptional#1{% {\textnormal{\Large[}}{#1}\hspace{0.5mm}{\textnormal{\Large]}}} -\ifspx@opt@dontkeepoldnames\else - \let\spx@alreadydefinedlist\@empty - \typeout{** (sphinx) defining (legacy) text style macros without \string\sphinx\space prefix} - \typeout{** if clashes with packages, do not set latex_keep_old_macro_names=True - in conf.py} - \@for\@tempa:=code,strong,bfcode,email,tablecontinued,titleref,% - menuselection,accelerator,crossref,termref,optional\do - {% first, check if command with no prefix already exists - \ltx@ifundefined{\@tempa}{% - % give it the meaning defined so far with \sphinx prefix - \expandafter\let\csname\@tempa\expandafter\endcsname - \csname sphinx\@tempa\endcsname - % redefine the \sphinx prefixed macro to expand to non-prefixed one - \expandafter\def\csname sphinx\@tempa\expandafter\endcsname - \expandafter{\csname\@tempa\endcsname}% - }{\edef\spx@alreadydefinedlist{\spx@alreadydefinedlist{\@tempa}}}% - }% - \ifx\spx@alreadydefinedlist\@empty\else - \expandafter\@tfor\expandafter\@tempa\expandafter:\expandafter=\spx@alreadydefinedlist\do - {% emit warning now - \PackageWarning{sphinx}{not redefining already existing \@backslashchar\@tempa\space!^^J% - Anyhow, Sphinx mark-up uses only \string\sphinx\@tempa.}% - % and also at end of log for better visibility - \expandafter\sphinxdeprecationwarning\expandafter{\csname\@tempa\endcsname}{1.6}{1.7} - {\sphinxdeprecatedmacro\space already existed at Sphinx loading time! Not redefined!^^J - Sphinx mark-up uses only \string\sphinx\expandafter\@gobble\sphinxdeprecatedmacro.}% - }% - \fi - \sphinxdeprecationwarning{latex_keep_old_macro_names=True}{1.6}{1.7}{}% -\fi - % additional customizable styling % FIXME: convert this to package options ? \protected\def\sphinxstyleindexentry {\texttt} diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index fc2cbb272..4900dbe29 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -58,7 +58,7 @@ DEFAULT_SETTINGS = { 'classoptions': '', 'extraclassoptions': '', 'maxlistdepth': '', - 'sphinxpkgoptions': 'dontkeepoldnames', + 'sphinxpkgoptions': '', 'sphinxsetup': '', 'passoptionstopackages': '', 'geometry': '\\usepackage{geometry}', @@ -548,8 +548,6 @@ class LaTeXTranslator(nodes.NodeVisitor): self.elements.update({ 'releasename': _('Release'), }) - if builder.config.latex_keep_old_macro_names: - self.elements['sphinxpkgoptions'] = '' if document.settings.docclass == 'howto': docclass = builder.config.latex_docclass.get('howto', 'article') else: From c74eb9448b3a335510daa362be25d7ac5bd2277c Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 27 Apr 2017 21:49:19 +0900 Subject: [PATCH 30/91] Remove deprecated feature: deprecated APIs --- sphinx/application.py | 67 ++-------------------------------- sphinx/builders/__init__.py | 3 -- sphinx/environment/__init__.py | 20 +--------- 3 files changed, 5 insertions(+), 85 deletions(-) diff --git a/sphinx/application.py b/sphinx/application.py index 93032578f..63834b119 100644 --- a/sphinx/application.py +++ b/sphinx/application.py @@ -29,7 +29,7 @@ import sphinx from sphinx import package_dir, locale from sphinx.config import Config from sphinx.errors import ConfigError, ExtensionError, VersionRequirementError -from sphinx.deprecation import RemovedInSphinx17Warning, RemovedInSphinx20Warning +from sphinx.deprecation import RemovedInSphinx20Warning from sphinx.environment import BuildEnvironment from sphinx.events import EventManager from sphinx.extension import verify_required_extensions @@ -39,10 +39,9 @@ from sphinx.registry import SphinxComponentRegistry from sphinx.util import pycompat # noqa: F401 from sphinx.util import import_object from sphinx.util import logging -from sphinx.util import status_iterator, old_status_iterator, display_chunk from sphinx.util.tags import Tags from sphinx.util.osutil import ENOENT -from sphinx.util.console import bold, darkgreen # type: ignore +from sphinx.util.console import bold # type: ignore from sphinx.util.docutils import is_html5_writer_available, directive_helper from sphinx.util.i18n import find_catalog_source_files @@ -314,13 +313,6 @@ class Sphinx(object): for node, settings in iteritems(self.enumerable_nodes): self.env.get_domain('std').enumerable_nodes[node] = settings # type: ignore - @property - def buildername(self): - # type: () -> unicode - warnings.warn('app.buildername is deprecated. Please use app.builder.name instead', - RemovedInSphinx17Warning) - return self.builder.name - # ---- main "build" method ------------------------------------------------- def build(self, force_all=False, filenames=None): @@ -356,31 +348,15 @@ class Sphinx(object): self.builder.cleanup() # ---- logging handling ---------------------------------------------------- - def warn(self, message, location=None, prefix=None, - type=None, subtype=None, colorfunc=None): - # type: (unicode, unicode, unicode, unicode, unicode, Callable) -> None + def warn(self, message, location=None, type=None, subtype=None): + # type: (unicode, unicode, unicode, unicode) -> None """Emit a warning. If *location* is given, it should either be a tuple of (docname, lineno) or a string describing the location of the warning as well as possible. - *prefix* usually should not be changed. - *type* and *subtype* are used to suppress warnings with :confval:`suppress_warnings`. - - .. note:: - - For warnings emitted during parsing, you should use - :meth:`.BuildEnvironment.warn` since that will collect all - warnings during parsing for later output. """ - if prefix: - warnings.warn('prefix option of warn() is now deprecated.', - RemovedInSphinx17Warning) - if colorfunc: - warnings.warn('colorfunc option of warn() is now deprecated.', - RemovedInSphinx17Warning) - warnings.warn('app.warning() is now deprecated. Use sphinx.util.logging instead.', RemovedInSphinx20Warning) logger.warning(message, type=type, subtype=subtype, location=location) @@ -417,34 +393,6 @@ class Sphinx(object): RemovedInSphinx20Warning) logger.debug(message, *args, **kwargs) - def _display_chunk(chunk): - # type: (Any) -> unicode - warnings.warn('app._display_chunk() is now deprecated. ' - 'Use sphinx.util.display_chunk() instead.', - RemovedInSphinx17Warning) - return display_chunk(chunk) - - def old_status_iterator(self, iterable, summary, colorfunc=darkgreen, - stringify_func=display_chunk): - # type: (Iterable, unicode, Callable, Callable[[Any], unicode]) -> Iterator - warnings.warn('app.old_status_iterator() is now deprecated. ' - 'Use sphinx.util.status_iterator() instead.', - RemovedInSphinx17Warning) - for item in old_status_iterator(iterable, summary, - color="darkgreen", stringify_func=stringify_func): - yield item - - # new version with progress info - def status_iterator(self, iterable, summary, colorfunc=darkgreen, length=0, - stringify_func=_display_chunk): - # type: (Iterable, unicode, Callable, int, Callable[[Any], unicode]) -> Iterable - warnings.warn('app.status_iterator() is now deprecated. ' - 'Use sphinx.util.status_iterator() instead.', - RemovedInSphinx17Warning) - for item in status_iterator(iterable, summary, length=length, verbosity=self.verbosity, - color="darkgreen", stringify_func=stringify_func): - yield item - # ---- general extensibility interface ------------------------------------- def setup_extension(self, extname): @@ -566,13 +514,6 @@ class Sphinx(object): self.enumerable_nodes[node] = (figtype, title_getter) self.add_node(node, **kwds) - def _directive_helper(self, obj, has_content=None, argument_spec=None, **option_spec): - # type: (Any, bool, Tuple[int, int, bool], Any) -> Any - warnings.warn('_directive_helper() is now deprecated. ' - 'Please use sphinx.util.docutils.directive_helper() instead.', - RemovedInSphinx17Warning) - return directive_helper(obj, has_content, argument_spec, **option_spec) - def add_directive(self, name, obj, content=None, arguments=None, **options): # type: (unicode, Any, bool, Tuple[int, int, bool], Any) -> None logger.debug('[app] adding directive: %r', diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py index fdf524bf6..a8a507eaa 100644 --- a/sphinx/builders/__init__.py +++ b/sphinx/builders/__init__.py @@ -91,9 +91,6 @@ class Builder(object): self.tags.add(self.name) self.tags.add("format_%s" % self.format) self.tags.add("builder_%s" % self.name) - # compatibility aliases - self.status_iterator = app.status_iterator - self.old_status_iterator = app.old_status_iterator # images that need to be copied over (source -> dest) self.images = {} # type: Dict[unicode, unicode] diff --git a/sphinx/environment/__init__.py b/sphinx/environment/__init__.py index 066f57209..1a56d0299 100644 --- a/sphinx/environment/__init__.py +++ b/sphinx/environment/__init__.py @@ -46,7 +46,7 @@ from sphinx.errors import SphinxError, ExtensionError from sphinx.locale import _ from sphinx.transforms import SphinxTransformer from sphinx.versioning import add_uids, merge_doctrees -from sphinx.deprecation import RemovedInSphinx17Warning, RemovedInSphinx20Warning +from sphinx.deprecation import RemovedInSphinx20Warning from sphinx.environment.adapters.indexentries import IndexEntries from sphinx.environment.adapters.toctree import TocTree @@ -767,24 +767,6 @@ class BuildEnvironment(object): """Returns the docname of the document currently being parsed.""" return self.temp_data['docname'] - @property - def currmodule(self): - # type: () -> None - """Backwards compatible alias. Will be removed.""" - warnings.warn('env.currmodule is deprecated. ' - 'Use env.ref_context["py:module"] instead.', - RemovedInSphinx17Warning) - return self.ref_context.get('py:module') - - @property - def currclass(self): - # type: () -> None - """Backwards compatible alias. Will be removed.""" - warnings.warn('env.currclass is deprecated. ' - 'Use env.ref_context["py:class"] instead.', - RemovedInSphinx17Warning) - return self.ref_context.get('py:class') - def new_serialno(self, category=''): # type: (unicode) -> int """Return a serial number, e.g. for index entry targets. From b15cf7f89fced7d270e19b9bfd175ec6bda4b04b Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 27 Apr 2017 21:49:19 +0900 Subject: [PATCH 31/91] Remove deprecated feature: drop RemovedInSphinx17Warning --- sphinx/deprecation.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sphinx/deprecation.py b/sphinx/deprecation.py index 608b23c1b..bb63df330 100644 --- a/sphinx/deprecation.py +++ b/sphinx/deprecation.py @@ -10,11 +10,11 @@ """ -class RemovedInSphinx17Warning(DeprecationWarning): +class RemovedInSphinx18Warning(DeprecationWarning): pass -class RemovedInSphinx18Warning(PendingDeprecationWarning): +class RemovedInSphinx19Warning(PendingDeprecationWarning): pass @@ -22,4 +22,4 @@ class RemovedInSphinx20Warning(PendingDeprecationWarning): pass -RemovedInNextVersionWarning = RemovedInSphinx17Warning +RemovedInNextVersionWarning = RemovedInSphinx18Warning From 8d5d70ba62c4138c52bba3c640a00b964ffa82c1 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 27 Apr 2017 21:49:19 +0900 Subject: [PATCH 32/91] latex: Use ENUM for validate config value --- sphinx/builders/latex.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/sphinx/builders/latex.py b/sphinx/builders/latex.py index 4e906ea60..c03803c5a 100644 --- a/sphinx/builders/latex.py +++ b/sphinx/builders/latex.py @@ -248,14 +248,6 @@ class LaTeXBuilder(Builder): path.join(self.srcdir, src), err) -def validate_config_values(app): - # type: (Sphinx) -> None - if app.config.latex_toplevel_sectioning not in (None, 'part', 'chapter', 'section'): - logger.warning('invalid latex_toplevel_sectioning, ignored: %s', - app.config.latex_toplevel_sectioning) - app.config.latex_toplevel_sectioning = None # type: ignore - - def default_latex_engine(config): # type: (Config) -> unicode """ Better default latex_engine settings for specific languages. """ @@ -278,7 +270,6 @@ def default_latex_docclass(config): def setup(app): # type: (Sphinx) -> Dict[unicode, Any] app.add_builder(LaTeXBuilder) - app.connect('builder-inited', validate_config_values) app.add_config_value('latex_engine', default_latex_engine, None, ENUM('pdflatex', 'xelatex', 'lualatex', 'platex')) @@ -289,7 +280,8 @@ def setup(app): app.add_config_value('latex_logo', None, None, string_classes) app.add_config_value('latex_appendices', [], None) app.add_config_value('latex_use_latex_multicolumn', False, None) - app.add_config_value('latex_toplevel_sectioning', None, None, [str]) + app.add_config_value('latex_toplevel_sectioning', None, None, + ENUM('part', 'chapter', 'section')) app.add_config_value('latex_domain_indices', True, None, [list]) app.add_config_value('latex_show_urls', 'no', None) app.add_config_value('latex_show_pagerefs', False, None) From 8fda21099d33696b84318c51e22a807cda9f32da Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 27 Apr 2017 23:38:42 +0900 Subject: [PATCH 33/91] Remove deprecated testing utilities --- tests/test_build_html5.py | 11 +-- tests/test_build_latex.py | 8 +- tests/test_build_texinfo.py | 4 +- tests/test_build_text.py | 5 +- tests/test_docutilsconf.py | 4 +- tests/test_ext_math.py | 9 +- tests/test_quickstart.py | 4 +- tests/util.py | 160 ------------------------------------ 8 files changed, 18 insertions(+), 187 deletions(-) diff --git a/tests/test_build_html5.py b/tests/test_build_html5.py index b491b6306..e43decaf9 100644 --- a/tests/test_build_html5.py +++ b/tests/test_build_html5.py @@ -14,19 +14,13 @@ :license: BSD, see LICENSE for details. """ -import os -import re -from itertools import cycle, chain import xml.etree.cElementTree as ElementTree -from six import PY3 import pytest from html5lib import getTreeBuilder, HTMLParser -from sphinx import __display_version__ from sphinx.util.docutils import is_html5_writer_available -from util import remove_unicode_literals, strip_escseq, skip_unless from test_build_html import flat_dict, tail_check, check_xpath TREE_BUILDER = getTreeBuilder('etree', implementation=ElementTree) @@ -35,7 +29,8 @@ HTML_PARSER = HTMLParser(TREE_BUILDER, namespaceHTMLElements=False) etree_cache = {} -@skip_unless(is_html5_writer_available()) + +@pytest.mark.skipif(not is_html5_writer_available(), reason='HTML5 writer is not available') @pytest.fixture(scope='module') def cached_etree_parse(): def parse(fname): @@ -50,7 +45,7 @@ def cached_etree_parse(): etree_cache.clear() -@skip_unless(is_html5_writer_available()) +@pytest.mark.skipif(not is_html5_writer_available(), reason='HTML5 writer is not available') @pytest.mark.parametrize("fname,expect", flat_dict({ 'images.html': [ (".//img[@src='_images/img.png']", ''), diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py index ad882758c..3cda73546 100644 --- a/tests/test_build_latex.py +++ b/tests/test_build_latex.py @@ -24,7 +24,7 @@ from sphinx.util.osutil import cd, ensuredir from sphinx.util import docutils from sphinx.writers.latex import LaTeXTranslator -from util import SkipTest, remove_unicode_literals, strip_escseq, skip_if +from util import remove_unicode_literals, strip_escseq from test_build_html import ENV_WARNINGS @@ -77,7 +77,7 @@ def compile_latex_document(app): 'SphinxTests.tex'], stdout=PIPE, stderr=PIPE) except OSError: # most likely the latex executable was not found - raise SkipTest + raise pytest.skip.Exception else: stdout, stderr = p.communicate() if p.returncode != 0: @@ -90,7 +90,7 @@ def compile_latex_document(app): def skip_if_requested(testfunc): if 'SKIP_LATEX_BUILD' in os.environ: msg = 'Skip LaTeX builds because SKIP_LATEX_BUILD is set' - return skip_if(True, msg)(testfunc) + return pytest.mark.skipif(True, reason=msg)(testfunc) else: return testfunc @@ -98,7 +98,7 @@ def skip_if_requested(testfunc): def skip_if_stylefiles_notfound(testfunc): if kpsetest(*STYLEFILES) is False: msg = 'not running latex, the required styles do not seem to be installed' - return skip_if(True, msg)(testfunc) + return pytest.mark.skipif(True, reason=msg)(testfunc) else: return testfunc diff --git a/tests/test_build_texinfo.py b/tests/test_build_texinfo.py index 0177bc392..68b0e2067 100644 --- a/tests/test_build_texinfo.py +++ b/tests/test_build_texinfo.py @@ -19,7 +19,7 @@ import pytest from sphinx.writers.texinfo import TexinfoTranslator -from util import SkipTest, remove_unicode_literals, strip_escseq +from util import remove_unicode_literals, strip_escseq from test_build_html import ENV_WARNINGS @@ -58,7 +58,7 @@ def test_texinfo(app, status, warning): p = Popen(['makeinfo', '--no-split', 'SphinxTests.texi'], stdout=PIPE, stderr=PIPE) except OSError: - raise SkipTest # most likely makeinfo was not found + raise pytest.skip.Exception # most likely makeinfo was not found else: stdout, stderr = p.communicate() retcode = p.returncode diff --git a/tests/test_build_text.py b/tests/test_build_text.py index 6ce3b5276..81e354ecd 100644 --- a/tests/test_build_text.py +++ b/tests/test_build_text.py @@ -8,12 +8,11 @@ :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. """ +import pytest from docutils.utils import column_width from sphinx.writers.text import MAXWIDTH -from util import with_app - def with_text_app(*args, **kw): default_kw = { @@ -21,7 +20,7 @@ def with_text_app(*args, **kw): 'testroot': 'build-text', } default_kw.update(kw) - return with_app(*args, **default_kw) + return pytest.mark.sphinx(*args, **default_kw) @with_text_app() diff --git a/tests/test_docutilsconf.py b/tests/test_docutilsconf.py index 3c43b6e7c..fe848de2e 100644 --- a/tests/test_docutilsconf.py +++ b/tests/test_docutilsconf.py @@ -12,7 +12,7 @@ import re import pytest -from util import path, SkipTest +from util import path def regex_count(expr, result): @@ -78,7 +78,7 @@ def test_docutils_source_link_with_nonascii_file(app, status, warning): (srcdir / (mb_name + '.txt')).write_text('') except UnicodeEncodeError: from path import FILESYSTEMENCODING - raise SkipTest( + raise pytest.skip.Exception( 'nonascii filename not supported on this filesystem encoding: ' '%s', FILESYSTEMENCODING) diff --git a/tests/test_ext_math.py b/tests/test_ext_math.py index 296bba94f..a6de6b788 100644 --- a/tests/test_ext_math.py +++ b/tests/test_ext_math.py @@ -12,7 +12,6 @@ import re import pytest -from util import SkipTest @pytest.mark.sphinx( @@ -40,9 +39,9 @@ def test_jsmath(app, status, warning): def test_imgmath_png(app, status, warning): app.builder.build_all() if "LaTeX command 'latex' cannot be run" in warning.getvalue(): - raise SkipTest('LaTeX command "latex" is not available') + raise pytest.skip.Exception('LaTeX command "latex" is not available') if "dvipng command 'dvipng' cannot be run" in warning.getvalue(): - raise SkipTest('dvipng command "dvipng" is not available') + raise pytest.skip.Exception('dvipng command "dvipng" is not available') content = (app.outdir / 'index.html').text() html = (r'
\s*

\s*\s*

\s* Date: Fri, 28 Apr 2017 01:19:01 +0900 Subject: [PATCH 34/91] Fix the unreleased version number is shown (refs: #3678, #3027) --- doc/_templates/indexsidebar.html | 2 +- doc/conf.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/_templates/indexsidebar.html b/doc/_templates/indexsidebar.html index bfa0139cb..6359921a5 100644 --- a/doc/_templates/indexsidebar.html +++ b/doc/_templates/indexsidebar.html @@ -3,7 +3,7 @@ {%trans%}project{%endtrans%}

Download

-{% if version.endswith('a0') %} +{% if version.endswith('+') %}

{%trans%}This documentation is for version {{ version }}, which is not released yet.{%endtrans%}

{%trans%}You can use it from the diff --git a/doc/conf.py b/doc/conf.py index 95d14ec46..6817af12a 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -16,7 +16,7 @@ exclude_patterns = ['_build'] project = 'Sphinx' copyright = '2007-2017, Georg Brandl and the Sphinx team' -version = sphinx.__released__ +version = sphinx.__display_version__ release = version show_authors = True From ac5e76f5989eeb74b05d9332b6a3922952a161c7 Mon Sep 17 00:00:00 2001 From: shimizukawa Date: Sat, 29 Apr 2017 00:54:58 +0900 Subject: [PATCH 35/91] extract messages --- sphinx/locale/sphinx.pot | 920 ++++++++++++++++++++++++++++----------- utils/release-checklist | 7 +- 2 files changed, 666 insertions(+), 261 deletions(-) diff --git a/sphinx/locale/sphinx.pot b/sphinx/locale/sphinx.pot index f63434cc2..13e387dcd 100644 --- a/sphinx/locale/sphinx.pot +++ b/sphinx/locale/sphinx.pot @@ -1,14 +1,14 @@ # Translations template for Sphinx. -# Copyright (C) 2016 ORGANIZATION +# Copyright (C) 2017 ORGANIZATION # This file is distributed under the same license as the Sphinx project. -# FIRST AUTHOR , 2016. +# FIRST AUTHOR , 2017. # #, fuzzy msgid "" msgstr "" -"Project-Id-Version: Sphinx 1.5b1\n" +"Project-Id-Version: Sphinx 1.6\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2016-11-06 22:40+0900\n" +"POT-Creation-Date: 2017-04-28 15:54+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,600 +17,987 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.3.4\n" -#: sphinx/config.py:109 +#: sphinx/application.py:181 +#, python-format +msgid "" +"This project needs at least Sphinx v%s and therefore cannot be built with" +" this version." +msgstr "" + +#: sphinx/application.py:208 +msgid "" +"'setup' that is specified in the conf.py has not been callable. Please " +"provide a callable `setup` function in order to behave as a sphinx " +"extension conf.py itself." +msgstr "" + +#: sphinx/application.py:222 +#, python-format +msgid "primary_domain %r not found, ignored." +msgstr "" + +#: sphinx/application.py:259 sphinx/application.py:287 +msgid "done" +msgstr "" + +#: sphinx/application.py:280 +msgid "loading pickled environment... " +msgstr "" + +#: sphinx/application.py:290 +msgid "not yet created" +msgstr "" + +#: sphinx/application.py:292 +#, python-format +msgid "failed: %s" +msgstr "" + +#: sphinx/application.py:302 +msgid "No builder selected, using default: html" +msgstr "" + +#: sphinx/application.py:341 +msgid "succeeded" +msgstr "" + +#: sphinx/application.py:341 +msgid "finished with problems" +msgstr "" + +#: sphinx/application.py:343 +#, python-format +msgid "build %s, %s warning%s." +msgstr "" + +#: sphinx/application.py:347 +#, python-format +msgid "build %s." +msgstr "" + +#: sphinx/application.py:506 +#, python-format +msgid "Config value %r already present" +msgstr "" + +#: sphinx/application.py:518 +#, python-format +msgid "A Translator for the %s builder is changed." +msgstr "" + +#: sphinx/application.py:526 +#, python-format +msgid "" +"while setting up extension %s: node class %r is already registered, its " +"visitors will be overridden" +msgstr "" + +#: sphinx/application.py:535 +#, python-format +msgid "Value for key %r must be a (visit, depart) function tuple" +msgstr "" + +#: sphinx/application.py:582 +#, python-format +msgid "" +"while setting up extension %s: directive %r is already registered, it " +"will be overridden" +msgstr "" + +#: sphinx/application.py:593 sphinx/application.py:605 +#, python-format +msgid "" +"while setting up extension %s: role %r is already registered, it will be " +"overridden" +msgstr "" + +#: sphinx/config.py:127 #, python-format msgid "Section %s" msgstr "" -#: sphinx/config.py:110 +#: sphinx/config.py:128 #, python-format msgid "Fig. %s" msgstr "" -#: sphinx/config.py:111 +#: sphinx/config.py:129 #, python-format msgid "Table %s" msgstr "" -#: sphinx/config.py:112 +#: sphinx/config.py:130 #, python-format msgid "Listing %s" msgstr "" -#: sphinx/roles.py:187 +#: sphinx/config.py:236 +#, python-format +msgid "" +"cannot override dictionary config setting %r, ignoring (use %r to set " +"individual elements)" +msgstr "" + +#: sphinx/config.py:245 +#, python-format +msgid "invalid number %r for config value %r, ignoring" +msgstr "" + +#: sphinx/config.py:250 +#, python-format +msgid "cannot override config setting %r with unsupported type, ignoring" +msgstr "" + +#: sphinx/config.py:280 +#, python-format +msgid "unknown config value %r in override, ignoring" +msgstr "" + +#: sphinx/config.py:299 +#, python-format +msgid "No such config value: %s" +msgstr "" + +#: sphinx/events.py:56 +#, python-format +msgid "Event %r already present" +msgstr "" + +#: sphinx/events.py:62 +#, python-format +msgid "Unknown event name: %s" +msgstr "" + +#: sphinx/extension.py:53 +#, python-format +msgid "" +"needs_extensions config value specifies a version requirement for " +"extension %s, but it is not loaded" +msgstr "" + +#: sphinx/extension.py:59 +#, python-format +msgid "" +"This project needs the extension %s at least in version %s and therefore " +"cannot be built with the loaded version (%s)." +msgstr "" + +#: sphinx/registry.py:56 +#, python-format +msgid "Builder class %s has no \"name\" attribute" +msgstr "" + +#: sphinx/registry.py:58 +#, python-format +msgid "Builder %r already exists (in module %s)" +msgstr "" + +#: sphinx/registry.py:72 +#, python-format +msgid "Builder name %s not registered or available through entry point" +msgstr "" + +#: sphinx/registry.py:80 +#, python-format +msgid "Builder name %s not registered" +msgstr "" + +#: sphinx/registry.py:87 +#, python-format +msgid "domain %s already registered" +msgstr "" + +#: sphinx/registry.py:102 sphinx/registry.py:112 sphinx/registry.py:119 +#: sphinx/registry.py:125 +#, python-format +msgid "domain %s not yet registered" +msgstr "" + +#: sphinx/registry.py:104 +#, python-format +msgid "new domain not a subclass of registered %s domain" +msgstr "" + +#: sphinx/registry.py:160 +#, python-format +msgid "source_parser for %r is already registered" +msgstr "" + +#: sphinx/registry.py:187 +#, python-format +msgid "" +"the extension %r was already merged with Sphinx since version %s; this " +"extension is ignored." +msgstr "" + +#: sphinx/registry.py:198 +msgid "Original exception:\n" +msgstr "" + +#: sphinx/registry.py:199 +#, python-format +msgid "Could not import extension %s" +msgstr "" + +#: sphinx/registry.py:202 +#, python-format +msgid "" +"extension %r has no setup() function; is it really a Sphinx extension " +"module?" +msgstr "" + +#: sphinx/registry.py:211 +#, python-format +msgid "" +"The %s extension used by this project needs at least Sphinx v%s; it " +"therefore cannot be built with this version." +msgstr "" + +#: sphinx/registry.py:221 +#, python-format +msgid "" +"extension %r returned an unsupported object from its setup() function; it" +" should return None or a metadata dictionary" +msgstr "" + +#: sphinx/roles.py:200 #, python-format msgid "Python Enhancement Proposals; PEP %s" msgstr "" -#: sphinx/builders/changes.py:75 +#: sphinx/theming.py:83 +#, python-format +msgid "theme %r doesn't have \"theme\" setting" +msgstr "" + +#: sphinx/theming.py:85 +#, python-format +msgid "theme %r doesn't have \"inherit\" setting" +msgstr "" + +#: sphinx/theming.py:91 +#, python-format +msgid "no theme named %r found, inherited by %r" +msgstr "" + +#: sphinx/theming.py:116 +#, python-format +msgid "setting %s.%s occurs in none of the searched theme configs" +msgstr "" + +#: sphinx/theming.py:237 +#, python-format +msgid "Theme extension %r does not response correctly." +msgstr "" + +#: sphinx/theming.py:264 +#, python-format +msgid "file %r on theme path is not a valid zipfile or contains no theme" +msgstr "" + +#: sphinx/theming.py:280 +msgid "" +"sphinx_rtd_theme is no longer a hard dependency since version 1.4.0. " +"Please install it manually.(pip install sphinx_rtd_theme)" +msgstr "" + +#: sphinx/theming.py:284 +#, python-format +msgid "no theme named %r found (missing theme.conf?)" +msgstr "" + +#: sphinx/builders/changes.py:86 msgid "Builtins" msgstr "" -#: sphinx/builders/changes.py:77 +#: sphinx/builders/changes.py:88 msgid "Module level" msgstr "" -#: sphinx/builders/html.py:294 sphinx/transforms/__init__.py:46 -#: sphinx/writers/latex.py:393 sphinx/writers/manpage.py:100 -#: sphinx/writers/texinfo.py:221 +#: sphinx/builders/html.py:357 sphinx/transforms/__init__.py:119 +#: sphinx/writers/latex.py:561 sphinx/writers/manpage.py:110 +#: sphinx/writers/texinfo.py:241 #, python-format msgid "%b %d, %Y" msgstr "" -#: sphinx/builders/html.py:315 sphinx/themes/basic/defindex.html:30 +#: sphinx/builders/html.py:377 sphinx/themes/basic/defindex.html:30 msgid "General Index" msgstr "" -#: sphinx/builders/html.py:315 +#: sphinx/builders/html.py:377 msgid "index" msgstr "" -#: sphinx/builders/html.py:377 +#: sphinx/builders/html.py:441 msgid "next" msgstr "" -#: sphinx/builders/html.py:386 +#: sphinx/builders/html.py:450 msgid "previous" msgstr "" -#: sphinx/builders/html.py:1222 +#: sphinx/builders/html.py:1313 #, python-format msgid "%s %s documentation" msgstr "" -#: sphinx/builders/latex.py:177 sphinx/builders/texinfo.py:199 +#: sphinx/builders/latex.py:199 sphinx/builders/texinfo.py:217 msgid " (in " msgstr "" -#: sphinx/directives/code.py:140 sphinx/directives/code.py:370 +#: sphinx/directives/code.py:66 +msgid "Over dedent has detected" +msgstr "" + +#: sphinx/directives/code.py:86 #, python-format msgid "Invalid caption: %s" msgstr "" -#: sphinx/directives/other.py:149 +#: sphinx/directives/code.py:201 +#, python-format +msgid "Cannot use both \"%s\" and \"%s\" options" +msgstr "" + +#: sphinx/directives/code.py:218 +#, python-format +msgid "Include file %r not found or reading it failed" +msgstr "" + +#: sphinx/directives/code.py:220 +#, python-format +msgid "" +"Encoding %r used for reading included file %r seems to be wrong, try " +"giving an :encoding: option" +msgstr "" + +#: sphinx/directives/code.py:257 +#, python-format +msgid "Object named %r not found in include file %r" +msgstr "" + +#: sphinx/directives/code.py:283 +msgid "Cannot use \"lineno-match\" with a disjoint set of \"lines\"" +msgstr "" + +#: sphinx/directives/code.py:288 +#, python-format +msgid "Line spec %r: no lines pulled from include file %r" +msgstr "" + +#: sphinx/directives/other.py:158 msgid "Section author: " msgstr "" -#: sphinx/directives/other.py:151 +#: sphinx/directives/other.py:160 msgid "Module author: " msgstr "" -#: sphinx/directives/other.py:153 +#: sphinx/directives/other.py:162 msgid "Code author: " msgstr "" -#: sphinx/directives/other.py:155 +#: sphinx/directives/other.py:164 msgid "Author: " msgstr "" -#: sphinx/domains/__init__.py:277 +#: sphinx/domains/__init__.py:310 #, python-format msgid "%s %s" msgstr "" -#: sphinx/domains/c.py:58 sphinx/domains/cpp.py:4051 -#: sphinx/domains/python.py:149 +#: sphinx/domains/c.py:65 sphinx/domains/cpp.py:4436 +#: sphinx/domains/python.py:177 msgid "Parameters" msgstr "" -#: sphinx/domains/c.py:61 sphinx/domains/cpp.py:4060 -#: sphinx/domains/javascript.py:128 sphinx/domains/python.py:161 +#: sphinx/domains/c.py:68 sphinx/domains/cpp.py:4445 +#: sphinx/domains/javascript.py:210 sphinx/domains/python.py:189 msgid "Returns" msgstr "" -#: sphinx/domains/c.py:63 sphinx/domains/javascript.py:130 -#: sphinx/domains/python.py:163 +#: sphinx/domains/c.py:70 sphinx/domains/javascript.py:212 +#: sphinx/domains/python.py:191 msgid "Return type" msgstr "" -#: sphinx/domains/c.py:177 +#: sphinx/domains/c.py:188 #, python-format msgid "%s (C function)" msgstr "" -#: sphinx/domains/c.py:179 +#: sphinx/domains/c.py:190 #, python-format msgid "%s (C member)" msgstr "" -#: sphinx/domains/c.py:181 +#: sphinx/domains/c.py:192 #, python-format msgid "%s (C macro)" msgstr "" -#: sphinx/domains/c.py:183 +#: sphinx/domains/c.py:194 #, python-format msgid "%s (C type)" msgstr "" -#: sphinx/domains/c.py:185 +#: sphinx/domains/c.py:196 #, python-format msgid "%s (C variable)" msgstr "" -#: sphinx/domains/c.py:242 sphinx/domains/cpp.py:4418 -#: sphinx/domains/javascript.py:164 sphinx/domains/python.py:614 +#: sphinx/domains/c.py:257 sphinx/domains/cpp.py:4820 +#: sphinx/domains/javascript.py:299 sphinx/domains/python.py:696 msgid "function" msgstr "" -#: sphinx/domains/c.py:243 sphinx/domains/cpp.py:4419 +#: sphinx/domains/c.py:258 sphinx/domains/cpp.py:4821 msgid "member" msgstr "" -#: sphinx/domains/c.py:244 +#: sphinx/domains/c.py:259 msgid "macro" msgstr "" -#: sphinx/domains/c.py:245 sphinx/domains/cpp.py:4420 +#: sphinx/domains/c.py:260 sphinx/domains/cpp.py:4822 msgid "type" msgstr "" -#: sphinx/domains/c.py:246 +#: sphinx/domains/c.py:261 msgid "variable" msgstr "" -#: sphinx/domains/cpp.py:4054 +#: sphinx/domains/cpp.py:4439 msgid "Template Parameters" msgstr "" -#: sphinx/domains/cpp.py:4057 sphinx/domains/javascript.py:125 +#: sphinx/domains/cpp.py:4442 sphinx/domains/javascript.py:207 msgid "Throws" msgstr "" -#: sphinx/domains/cpp.py:4205 +#: sphinx/domains/cpp.py:4607 #, python-format msgid "%s (C++ type)" msgstr "" -#: sphinx/domains/cpp.py:4216 +#: sphinx/domains/cpp.py:4617 #, python-format msgid "%s (C++ concept)" msgstr "" -#: sphinx/domains/cpp.py:4227 +#: sphinx/domains/cpp.py:4627 #, python-format msgid "%s (C++ member)" msgstr "" -#: sphinx/domains/cpp.py:4238 +#: sphinx/domains/cpp.py:4637 #, python-format msgid "%s (C++ function)" msgstr "" -#: sphinx/domains/cpp.py:4249 +#: sphinx/domains/cpp.py:4647 #, python-format msgid "%s (C++ class)" msgstr "" -#: sphinx/domains/cpp.py:4260 +#: sphinx/domains/cpp.py:4657 #, python-format msgid "%s (C++ enum)" msgstr "" -#: sphinx/domains/cpp.py:4281 +#: sphinx/domains/cpp.py:4677 #, python-format msgid "%s (C++ enumerator)" msgstr "" -#: sphinx/domains/cpp.py:4417 sphinx/domains/javascript.py:165 -#: sphinx/domains/python.py:616 +#: sphinx/domains/cpp.py:4819 sphinx/domains/javascript.py:301 +#: sphinx/domains/python.py:698 msgid "class" msgstr "" -#: sphinx/domains/cpp.py:4421 +#: sphinx/domains/cpp.py:4823 msgid "concept" msgstr "" -#: sphinx/domains/cpp.py:4422 +#: sphinx/domains/cpp.py:4824 msgid "enum" msgstr "" -#: sphinx/domains/cpp.py:4423 +#: sphinx/domains/cpp.py:4825 msgid "enumerator" msgstr "" -#: sphinx/domains/javascript.py:106 sphinx/domains/python.py:307 +#: sphinx/domains/javascript.py:131 sphinx/domains/python.py:386 #, python-format msgid "%s() (built-in function)" msgstr "" -#: sphinx/domains/javascript.py:107 sphinx/domains/python.py:371 +#: sphinx/domains/javascript.py:132 sphinx/domains/python.py:451 #, python-format msgid "%s() (%s method)" msgstr "" -#: sphinx/domains/javascript.py:109 +#: sphinx/domains/javascript.py:134 #, python-format msgid "%s() (class)" msgstr "" -#: sphinx/domains/javascript.py:111 +#: sphinx/domains/javascript.py:136 #, python-format msgid "%s (global variable or constant)" msgstr "" -#: sphinx/domains/javascript.py:113 sphinx/domains/python.py:409 +#: sphinx/domains/javascript.py:138 sphinx/domains/python.py:489 #, python-format msgid "%s (%s attribute)" msgstr "" -#: sphinx/domains/javascript.py:122 +#: sphinx/domains/javascript.py:204 msgid "Arguments" msgstr "" -#: sphinx/domains/javascript.py:166 sphinx/domains/python.py:615 -msgid "data" -msgstr "" - -#: sphinx/domains/javascript.py:167 sphinx/domains/python.py:621 -msgid "attribute" -msgstr "" - -#: sphinx/domains/python.py:154 -msgid "Variables" -msgstr "" - -#: sphinx/domains/python.py:158 -msgid "Raises" -msgstr "" - -#: sphinx/domains/python.py:308 sphinx/domains/python.py:365 -#: sphinx/domains/python.py:377 sphinx/domains/python.py:390 -#, python-format -msgid "%s() (in module %s)" -msgstr "" - -#: sphinx/domains/python.py:311 -#, python-format -msgid "%s (built-in variable)" -msgstr "" - -#: sphinx/domains/python.py:312 sphinx/domains/python.py:403 -#, python-format -msgid "%s (in module %s)" -msgstr "" - -#: sphinx/domains/python.py:328 -#, python-format -msgid "%s (built-in class)" -msgstr "" - -#: sphinx/domains/python.py:329 -#, python-format -msgid "%s (class in %s)" -msgstr "" - -#: sphinx/domains/python.py:369 -#, python-format -msgid "%s() (%s.%s method)" -msgstr "" - -#: sphinx/domains/python.py:381 -#, python-format -msgid "%s() (%s.%s static method)" -msgstr "" - -#: sphinx/domains/python.py:384 -#, python-format -msgid "%s() (%s static method)" -msgstr "" - -#: sphinx/domains/python.py:394 -#, python-format -msgid "%s() (%s.%s class method)" -msgstr "" - -#: sphinx/domains/python.py:397 -#, python-format -msgid "%s() (%s class method)" -msgstr "" - -#: sphinx/domains/python.py:407 -#, python-format -msgid "%s (%s.%s attribute)" -msgstr "" - -#: sphinx/domains/python.py:488 +#: sphinx/domains/javascript.py:266 sphinx/domains/python.py:566 #, python-format msgid "%s (module)" msgstr "" -#: sphinx/domains/python.py:545 -msgid "Python Module Index" -msgstr "" - -#: sphinx/domains/python.py:546 -msgid "modules" -msgstr "" - -#: sphinx/domains/python.py:592 -msgid "Deprecated" -msgstr "" - -#: sphinx/domains/python.py:617 sphinx/locale/__init__.py:183 -msgid "exception" -msgstr "" - -#: sphinx/domains/python.py:618 +#: sphinx/domains/javascript.py:300 sphinx/domains/python.py:700 msgid "method" msgstr "" -#: sphinx/domains/python.py:619 -msgid "class method" +#: sphinx/domains/javascript.py:302 sphinx/domains/python.py:697 +msgid "data" msgstr "" -#: sphinx/domains/python.py:620 -msgid "static method" +#: sphinx/domains/javascript.py:303 sphinx/domains/python.py:703 +msgid "attribute" msgstr "" -#: sphinx/domains/python.py:622 sphinx/locale/__init__.py:179 +#: sphinx/domains/javascript.py:304 sphinx/domains/python.py:704 +#: sphinx/locale/__init__.py:218 msgid "module" msgstr "" -#: sphinx/domains/python.py:787 +#: sphinx/domains/python.py:182 +msgid "Variables" +msgstr "" + +#: sphinx/domains/python.py:186 +msgid "Raises" +msgstr "" + +#: sphinx/domains/python.py:387 sphinx/domains/python.py:445 +#: sphinx/domains/python.py:457 sphinx/domains/python.py:470 +#, python-format +msgid "%s() (in module %s)" +msgstr "" + +#: sphinx/domains/python.py:390 +#, python-format +msgid "%s (built-in variable)" +msgstr "" + +#: sphinx/domains/python.py:391 sphinx/domains/python.py:483 +#, python-format +msgid "%s (in module %s)" +msgstr "" + +#: sphinx/domains/python.py:411 +#, python-format +msgid "%s (built-in class)" +msgstr "" + +#: sphinx/domains/python.py:412 +#, python-format +msgid "%s (class in %s)" +msgstr "" + +#: sphinx/domains/python.py:449 +#, python-format +msgid "%s() (%s.%s method)" +msgstr "" + +#: sphinx/domains/python.py:461 +#, python-format +msgid "%s() (%s.%s static method)" +msgstr "" + +#: sphinx/domains/python.py:464 +#, python-format +msgid "%s() (%s static method)" +msgstr "" + +#: sphinx/domains/python.py:474 +#, python-format +msgid "%s() (%s.%s class method)" +msgstr "" + +#: sphinx/domains/python.py:477 +#, python-format +msgid "%s() (%s class method)" +msgstr "" + +#: sphinx/domains/python.py:487 +#, python-format +msgid "%s (%s.%s attribute)" +msgstr "" + +#: sphinx/domains/python.py:625 +msgid "Python Module Index" +msgstr "" + +#: sphinx/domains/python.py:626 +msgid "modules" +msgstr "" + +#: sphinx/domains/python.py:674 +msgid "Deprecated" +msgstr "" + +#: sphinx/domains/python.py:699 sphinx/locale/__init__.py:222 +msgid "exception" +msgstr "" + +#: sphinx/domains/python.py:701 +msgid "class method" +msgstr "" + +#: sphinx/domains/python.py:702 +msgid "static method" +msgstr "" + +#: sphinx/domains/python.py:874 msgid " (deprecated)" msgstr "" -#: sphinx/domains/rst.py:55 +#: sphinx/domains/rst.py:65 #, python-format msgid "%s (directive)" msgstr "" -#: sphinx/domains/rst.py:57 +#: sphinx/domains/rst.py:67 #, python-format msgid "%s (role)" msgstr "" -#: sphinx/domains/rst.py:106 +#: sphinx/domains/rst.py:119 msgid "directive" msgstr "" -#: sphinx/domains/rst.py:107 +#: sphinx/domains/rst.py:120 msgid "role" msgstr "" -#: sphinx/domains/std.py:72 sphinx/domains/std.py:88 +#: sphinx/domains/std.py:84 sphinx/domains/std.py:101 #, python-format msgid "environment variable; %s" msgstr "" -#: sphinx/domains/std.py:186 +#: sphinx/domains/std.py:200 #, python-format msgid "%scommand line option; %s" msgstr "" -#: sphinx/domains/std.py:434 +#: sphinx/domains/std.py:455 msgid "glossary term" msgstr "" -#: sphinx/domains/std.py:435 +#: sphinx/domains/std.py:456 msgid "grammar token" msgstr "" -#: sphinx/domains/std.py:436 +#: sphinx/domains/std.py:457 msgid "reference label" msgstr "" -#: sphinx/domains/std.py:438 +#: sphinx/domains/std.py:459 msgid "environment variable" msgstr "" -#: sphinx/domains/std.py:439 +#: sphinx/domains/std.py:460 msgid "program option" msgstr "" -#: sphinx/domains/std.py:473 sphinx/themes/basic/genindex-single.html:30 +#: sphinx/domains/std.py:461 +msgid "document" +msgstr "" + +#: sphinx/domains/std.py:497 sphinx/themes/basic/genindex-single.html:30 #: sphinx/themes/basic/genindex-single.html:55 #: sphinx/themes/basic/genindex-split.html:11 #: sphinx/themes/basic/genindex-split.html:14 #: sphinx/themes/basic/genindex.html:30 sphinx/themes/basic/genindex.html:33 -#: sphinx/themes/basic/genindex.html:66 sphinx/themes/basic/layout.html:135 -#: sphinx/writers/latex.py:381 sphinx/writers/texinfo.py:480 +#: sphinx/themes/basic/genindex.html:66 sphinx/themes/basic/layout.html:149 +#: sphinx/writers/latex.py:545 sphinx/writers/texinfo.py:517 msgid "Index" msgstr "" -#: sphinx/domains/std.py:474 +#: sphinx/domains/std.py:498 msgid "Module Index" msgstr "" -#: sphinx/domains/std.py:475 sphinx/themes/basic/defindex.html:25 +#: sphinx/domains/std.py:499 sphinx/themes/basic/defindex.html:25 msgid "Search Page" msgstr "" -#: sphinx/environment/managers/indexentries.py:104 +#: sphinx/environment/__init__.py:568 +#, python-format +msgid "" +"the %s extension does not declare if it is safe for parallel reading, " +"assuming it isn't - please ask the extension author to check and make it " +"explicit" +msgstr "" + +#: sphinx/environment/adapters/indexentries.py:85 #, python-format msgid "see %s" msgstr "" -#: sphinx/environment/managers/indexentries.py:108 +#: sphinx/environment/adapters/indexentries.py:89 #, python-format msgid "see also %s" msgstr "" -#: sphinx/environment/managers/indexentries.py:168 +#: sphinx/environment/adapters/indexentries.py:159 msgid "Symbols" msgstr "" -#: sphinx/ext/autodoc.py:1297 +#: sphinx/ext/autodoc.py:1451 #, python-format msgid "Bases: %s" msgstr "" -#: sphinx/ext/autodoc.py:1350 +#: sphinx/ext/autodoc.py:1506 #, python-format msgid "alias of :class:`%s`" msgstr "" -#: sphinx/ext/graphviz.py:331 sphinx/ext/graphviz.py:340 +#: sphinx/ext/doctest.py:134 +#, python-format +msgid "missing '+' or '-' in '%s' option." +msgstr "" + +#: sphinx/ext/doctest.py:139 +#, python-format +msgid "'%s' is not a valid option." +msgstr "" + +#: sphinx/ext/doctest.py:155 +#, python-format +msgid "'%s' is not a valid pyversion option" +msgstr "" + +#: sphinx/ext/graphviz.py:96 +msgid "Graphviz directive cannot have both content and a filename argument" +msgstr "" + +#: sphinx/ext/graphviz.py:107 +#, python-format +msgid "External Graphviz file %r not found or reading it failed" +msgstr "" + +#: sphinx/ext/graphviz.py:113 +msgid "Ignoring \"graphviz\" directive without content." +msgstr "" + +#: sphinx/ext/graphviz.py:204 +#, python-format +msgid "" +"dot command %r cannot be run (needed for graphviz output), check the " +"graphviz_dot setting" +msgstr "" + +#: sphinx/ext/graphviz.py:222 +#, python-format +msgid "" +"dot exited with error:\n" +"[stderr]\n" +"%s\n" +"[stdout]\n" +"%s" +msgstr "" + +#: sphinx/ext/graphviz.py:225 +#, python-format +msgid "" +"dot did not produce an output file:\n" +"[stderr]\n" +"%s\n" +"[stdout]\n" +"%s" +msgstr "" + +#: sphinx/ext/graphviz.py:236 +#, python-format +msgid "graphviz_output_format must be one of 'png', 'svg', but is %r" +msgstr "" + +#: sphinx/ext/graphviz.py:336 sphinx/ext/graphviz.py:345 #, python-format msgid "[graph: %s]" msgstr "" -#: sphinx/ext/graphviz.py:333 sphinx/ext/graphviz.py:342 +#: sphinx/ext/graphviz.py:338 sphinx/ext/graphviz.py:347 msgid "[graph]" msgstr "" -#: sphinx/ext/imgmath.py:258 sphinx/ext/jsmath.py:39 sphinx/ext/mathjax.py:40 +#: sphinx/ext/imgconverter.py:46 sphinx/ext/imgconverter.py:63 +#, python-format +msgid "convert command %r cannot be run.check the image_converter setting" +msgstr "" + +#: sphinx/ext/imgconverter.py:76 +#, python-format +msgid "" +"convert exited with error:\n" +"[stderr]\n" +"%s\n" +"[stdout]\n" +"%s" +msgstr "" + +#: sphinx/ext/imgmath.py:274 sphinx/ext/jsmath.py:39 sphinx/ext/mathjax.py:40 msgid "Permalink to this equation" msgstr "" -#: sphinx/ext/intersphinx.py:337 +#: sphinx/ext/intersphinx.py:319 #, python-format msgid "(in %s v%s)" msgstr "" -#: sphinx/ext/linkcode.py:69 sphinx/ext/viewcode.py:103 +#: sphinx/ext/linkcode.py:75 sphinx/ext/viewcode.py:112 msgid "[source]" msgstr "" -#: sphinx/ext/mathbase.py:92 +#: sphinx/ext/mathbase.py:106 #, python-format msgid "duplicate label of equation %s, other instance in %s" msgstr "" -#: sphinx/ext/todo.py:56 +#: sphinx/ext/todo.py:66 msgid "Todo" msgstr "" -#: sphinx/ext/todo.py:134 +#: sphinx/ext/todo.py:148 msgid "<>" msgstr "" -#: sphinx/ext/todo.py:137 +#: sphinx/ext/todo.py:151 #, python-format msgid "(The <> is located in %s, line %d.)" msgstr "" -#: sphinx/ext/todo.py:146 +#: sphinx/ext/todo.py:160 msgid "original entry" msgstr "" -#: sphinx/ext/viewcode.py:166 +#: sphinx/ext/viewcode.py:179 msgid "[docs]" msgstr "" -#: sphinx/ext/viewcode.py:180 +#: sphinx/ext/viewcode.py:193 msgid "Module code" msgstr "" -#: sphinx/ext/viewcode.py:186 +#: sphinx/ext/viewcode.py:199 #, python-format msgid "

Source code for %s

" msgstr "" -#: sphinx/ext/viewcode.py:212 +#: sphinx/ext/viewcode.py:225 msgid "Overview: module code" msgstr "" -#: sphinx/ext/viewcode.py:213 +#: sphinx/ext/viewcode.py:226 msgid "

All modules for which code is available

" msgstr "" -#: sphinx/ext/napoleon/__init__.py:313 +#: sphinx/ext/napoleon/__init__.py:320 msgid "Keyword Arguments" msgstr "" -#: sphinx/locale/__init__.py:159 +#: sphinx/locale/__init__.py:198 msgid "Attention" msgstr "" -#: sphinx/locale/__init__.py:160 +#: sphinx/locale/__init__.py:199 msgid "Caution" msgstr "" -#: sphinx/locale/__init__.py:161 +#: sphinx/locale/__init__.py:200 msgid "Danger" msgstr "" -#: sphinx/locale/__init__.py:162 +#: sphinx/locale/__init__.py:201 msgid "Error" msgstr "" -#: sphinx/locale/__init__.py:163 +#: sphinx/locale/__init__.py:202 msgid "Hint" msgstr "" -#: sphinx/locale/__init__.py:164 +#: sphinx/locale/__init__.py:203 msgid "Important" msgstr "" -#: sphinx/locale/__init__.py:165 +#: sphinx/locale/__init__.py:204 msgid "Note" msgstr "" -#: sphinx/locale/__init__.py:166 +#: sphinx/locale/__init__.py:205 msgid "See also" msgstr "" -#: sphinx/locale/__init__.py:167 +#: sphinx/locale/__init__.py:206 msgid "Tip" msgstr "" -#: sphinx/locale/__init__.py:168 +#: sphinx/locale/__init__.py:207 msgid "Warning" msgstr "" -#: sphinx/locale/__init__.py:172 +#: sphinx/locale/__init__.py:211 #, python-format msgid "New in version %s" msgstr "" -#: sphinx/locale/__init__.py:173 +#: sphinx/locale/__init__.py:212 #, python-format msgid "Changed in version %s" msgstr "" -#: sphinx/locale/__init__.py:174 +#: sphinx/locale/__init__.py:213 #, python-format msgid "Deprecated since version %s" msgstr "" -#: sphinx/locale/__init__.py:180 +#: sphinx/locale/__init__.py:219 msgid "keyword" msgstr "" -#: sphinx/locale/__init__.py:181 +#: sphinx/locale/__init__.py:220 msgid "operator" msgstr "" -#: sphinx/locale/__init__.py:182 +#: sphinx/locale/__init__.py:221 msgid "object" msgstr "" -#: sphinx/locale/__init__.py:184 +#: sphinx/locale/__init__.py:223 msgid "statement" msgstr "" -#: sphinx/locale/__init__.py:185 +#: sphinx/locale/__init__.py:224 msgid "built-in function" msgstr "" #: sphinx/themes/agogo/layout.html:46 sphinx/themes/basic/globaltoc.html:10 -#: sphinx/themes/basic/localtoc.html:11 sphinx/themes/scrolls/layout.html:35 +#: sphinx/themes/basic/localtoc.html:11 sphinx/themes/scrolls/layout.html:38 msgid "Table Of Contents" msgstr "" -#: sphinx/themes/agogo/layout.html:51 sphinx/themes/basic/layout.html:138 +#: sphinx/themes/agogo/layout.html:51 sphinx/themes/basic/layout.html:152 #: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:23 #: sphinx/themes/basic/searchresults.html:10 msgid "Search" @@ -688,39 +1075,39 @@ msgstr "" msgid "can be huge" msgstr "" -#: sphinx/themes/basic/layout.html:29 +#: sphinx/themes/basic/layout.html:31 msgid "Navigation" msgstr "" -#: sphinx/themes/basic/layout.html:123 +#: sphinx/themes/basic/layout.html:137 #, python-format msgid "Search within %(docstitle)s" msgstr "" -#: sphinx/themes/basic/layout.html:132 +#: sphinx/themes/basic/layout.html:146 msgid "About these documents" msgstr "" -#: sphinx/themes/basic/layout.html:141 +#: sphinx/themes/basic/layout.html:155 msgid "Copyright" msgstr "" -#: sphinx/themes/basic/layout.html:186 +#: sphinx/themes/basic/layout.html:200 #, python-format msgid "© Copyright %(copyright)s." msgstr "" -#: sphinx/themes/basic/layout.html:188 +#: sphinx/themes/basic/layout.html:202 #, python-format msgid "© Copyright %(copyright)s." msgstr "" -#: sphinx/themes/basic/layout.html:192 +#: sphinx/themes/basic/layout.html:206 #, python-format msgid "Last updated on %(last_updated)s." msgstr "" -#: sphinx/themes/basic/layout.html:195 +#: sphinx/themes/basic/layout.html:209 #, python-format msgid "" "Created using Sphinx " @@ -767,12 +1154,12 @@ msgid "search" msgstr "" #: sphinx/themes/basic/search.html:43 sphinx/themes/basic/searchresults.html:21 -#: sphinx/themes/basic/static/searchtools.js_t:287 +#: sphinx/themes/basic/static/searchtools.js_t:336 msgid "Search Results" msgstr "" #: sphinx/themes/basic/search.html:45 sphinx/themes/basic/searchresults.html:23 -#: sphinx/themes/basic/static/searchtools.js_t:289 +#: sphinx/themes/basic/static/searchtools.js_t:338 msgid "" "Your search did not match any documents. Please make sure that all words " "are spelled correctly and that you've selected enough categories." @@ -814,43 +1201,45 @@ msgstr "" msgid "Other changes" msgstr "" -#: sphinx/themes/basic/static/doctools.js_t:169 sphinx/writers/html.py:708 -#: sphinx/writers/html.py:713 +#: sphinx/themes/basic/static/doctools.js_t:171 sphinx/writers/html.py:405 +#: sphinx/writers/html.py:410 sphinx/writers/html5.py:352 +#: sphinx/writers/html5.py:357 msgid "Permalink to this headline" msgstr "" -#: sphinx/themes/basic/static/doctools.js_t:175 sphinx/writers/html.py:108 -#: sphinx/writers/html.py:117 +#: sphinx/themes/basic/static/doctools.js_t:177 sphinx/writers/html.py:126 +#: sphinx/writers/html.py:137 sphinx/writers/html5.py:96 +#: sphinx/writers/html5.py:107 msgid "Permalink to this definition" msgstr "" -#: sphinx/themes/basic/static/doctools.js_t:208 +#: sphinx/themes/basic/static/doctools.js_t:210 msgid "Hide Search Matches" msgstr "" -#: sphinx/themes/basic/static/searchtools.js_t:121 +#: sphinx/themes/basic/static/searchtools.js_t:142 msgid "Searching" msgstr "" -#: sphinx/themes/basic/static/searchtools.js_t:126 +#: sphinx/themes/basic/static/searchtools.js_t:147 msgid "Preparing search..." msgstr "" -#: sphinx/themes/basic/static/searchtools.js_t:291 +#: sphinx/themes/basic/static/searchtools.js_t:340 #, python-format msgid "Search finished, found %s page(s) matching the search query." msgstr "" -#: sphinx/themes/basic/static/searchtools.js_t:344 +#: sphinx/themes/basic/static/searchtools.js_t:398 msgid ", in " msgstr "" -#: sphinx/themes/classic/static/sidebar.js_t:83 +#: sphinx/themes/classic/static/sidebar.js_t:92 msgid "Expand sidebar" msgstr "" -#: sphinx/themes/classic/static/sidebar.js_t:96 -#: sphinx/themes/classic/static/sidebar.js_t:124 +#: sphinx/themes/classic/static/sidebar.js_t:105 +#: sphinx/themes/classic/static/sidebar.js_t:135 msgid "Collapse sidebar" msgstr "" @@ -858,54 +1247,65 @@ msgstr "" msgid "Contents" msgstr "" -#: sphinx/writers/html.py:389 -msgid "Permalink to this code" +#: sphinx/transforms/post_transforms/__init__.py:105 +#, python-format +msgid "more than one target found for 'any' cross-reference %r: could be %s" msgstr "" -#: sphinx/writers/html.py:393 -msgid "Permalink to this image" +#: sphinx/transforms/post_transforms/__init__.py:135 +#, python-format +msgid "%s:%s reference target not found: %%(target)s" msgstr "" -#: sphinx/writers/html.py:395 -msgid "Permalink to this toctree" +#: sphinx/transforms/post_transforms/__init__.py:138 +#, python-format +msgid "%r reference target not found: %%(target)s" msgstr "" -#: sphinx/writers/html.py:717 +#: sphinx/util/docutils.py:171 +msgid "when adding directive classes, no additional arguments may be given" +msgstr "" + +#: sphinx/writers/html.py:414 sphinx/writers/html5.py:361 msgid "Permalink to this table" msgstr "" -#: sphinx/writers/latex.py:380 +#: sphinx/writers/html.py:466 sphinx/writers/html5.py:413 +msgid "Permalink to this code" +msgstr "" + +#: sphinx/writers/html.py:470 sphinx/writers/html5.py:417 +msgid "Permalink to this image" +msgstr "" + +#: sphinx/writers/html.py:472 sphinx/writers/html5.py:419 +msgid "Permalink to this toctree" +msgstr "" + +#: sphinx/writers/latex.py:549 msgid "Release" msgstr "" -#: sphinx/writers/latex.py:483 +#: sphinx/writers/latex.py:654 msgid "page" msgstr "" -#: sphinx/writers/latex.py:528 +#: sphinx/writers/latex.py:704 #, python-format msgid "Unknown configure key: latex_elements[%r] is ignored." msgstr "" -#: sphinx/writers/latex.py:1003 sphinx/writers/manpage.py:238 -#: sphinx/writers/texinfo.py:619 +#: sphinx/writers/latex.py:1252 sphinx/writers/manpage.py:275 +#: sphinx/writers/texinfo.py:670 msgid "Footnotes" msgstr "" -#: sphinx/writers/latex.py:1112 -msgid "continued from previous page" -msgstr "" - -#: sphinx/writers/latex.py:1118 -msgid "Continued on next page" -msgstr "" - -#: sphinx/writers/manpage.py:287 sphinx/writers/text.py:591 +#: sphinx/writers/manpage.py:331 sphinx/writers/text.py:699 #, python-format msgid "[image: %s]" msgstr "" -#: sphinx/writers/manpage.py:288 sphinx/writers/text.py:592 +#: sphinx/writers/manpage.py:332 sphinx/writers/text.py:700 msgid "[image]" msgstr "" diff --git a/utils/release-checklist b/utils/release-checklist index e8f716acd..a79147724 100644 --- a/utils/release-checklist +++ b/utils/release-checklist @@ -4,7 +4,12 @@ Release checklist * open https://travis-ci.org/sphinx-doc/sphinx/branches and check stable branch is green * Check `git status` * Run `make style-check` -* if final major release ... +* Update sphinx/locale/sphinx.pot if first major release (beta1) + + * Run `pytho nsetup.py extract_messages` + * Run `(cd sphinx/locale; tx push -s)` + +* Update sphinx/locale// files if final major release ... * Run `(cd sphinx/locale; tx pull -a -f)` * Run `python setup.py compile_catalog` From c7d90ab18c68bde73d38b1dac42b63ee90ccfcd4 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 29 Apr 2017 15:15:22 +0900 Subject: [PATCH 36/91] Update doc --- doc/latex.rst | 9 --------- 1 file changed, 9 deletions(-) diff --git a/doc/latex.rst b/doc/latex.rst index b8795fa19..14f21f6e2 100644 --- a/doc/latex.rst +++ b/doc/latex.rst @@ -416,15 +416,6 @@ Let us now list some macros from the package file ``titleref``, ``menuselection``, ``accelerator``, ``crossref``, ``termref``, ``optional``. - .. versionadded:: 1.4.5 - Use of ``\sphinx`` prefixed macro names to limit possibilities of conflict - with LaTeX packages. - .. versionchanged:: 1.6 - The default value of :confval:`latex_keep_old_macro_names` changes to - ``False``, and even if set to ``True``, if a non-prefixed macro - already exists at ``sphinx.sty`` loading time, only the ``\sphinx`` - prefixed one will be defined. The setting will be removed at 1.7. - - more text styling commands: ``\sphinxstyle`` with ```` one of ``indexentry``, ``indexextra``, ``indexpageref``, ``topictitle``, ``sidebartitle``, ``othertitle``, ``sidebarsubtitle``, ``thead``, From cddb163e3b109c129bca0e8b7083418b02e9d09f Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 29 Apr 2017 15:16:44 +0900 Subject: [PATCH 37/91] sphinx.sty: Remove old comment --- sphinx/texinputs/sphinx.sty | 1 - 1 file changed, 1 deletion(-) diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty index f50384a62..2d11d81d1 100644 --- a/sphinx/texinputs/sphinx.sty +++ b/sphinx/texinputs/sphinx.sty @@ -1310,7 +1310,6 @@ %% TEXT STYLING % % Some custom font markup commands. -% *** the macros without \sphinx prefix are still defined farther down *** \protected\def\sphinxstrong#1{{\textbf{#1}}} % to obtain straight quotes we execute \@noligs as patched by upquote, and % \scantokens is needed in cases where it would be too late for the macro to From 370bdddc7c9cc20383d6211a27ef6868773bd82e Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 29 Apr 2017 15:22:34 +0900 Subject: [PATCH 38/91] make websupport-dependency optional --- CHANGES | 1 + setup.py | 4 +++- sphinx/websupport/__init__.py | 19 ++++++++++++------- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/CHANGES b/CHANGES index 0ed869b50..c333a95f3 100644 --- a/CHANGES +++ b/CHANGES @@ -20,6 +20,7 @@ Bugs fixed * #3661: sphinx-build crashes on parallel build * #3669: gettext builder fails with "ValueError: substring not found" +* #3660: Sphinx always depends on sphinxcontrib-websupport and its dependencies Testing -------- diff --git a/setup.py b/setup.py index 1fd2d34d0..4354b0baa 100644 --- a/setup.py +++ b/setup.py @@ -51,7 +51,6 @@ requires = [ 'alabaster>=0.7,<0.8', 'imagesize', 'requests>=2.0.0', - 'sphinxcontrib-websupport', 'typing', 'setuptools', ] @@ -60,6 +59,9 @@ extras_require = { ':sys_platform=="win32"': [ 'colorama>=0.3.5', ], + 'websupport': [ + 'sphinxcontrib-websupport', + ], 'test': [ 'pytest', 'mock', # it would be better for 'test:python_version in 2.7' diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index f06d98433..f71034708 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -12,11 +12,16 @@ import warnings from sphinx.deprecation import RemovedInSphinx20Warning -from sphinxcontrib.websupport import WebSupport # NOQA -from sphinxcontrib.websupport import errors # NOQA -from sphinxcontrib.websupport.search import BaseSearch, SEARCH_ADAPTERS # NOQA -from sphinxcontrib.websupport.storage import StorageBackend # NOQA -warnings.warn('sphinx.websupport module is now provided as sphinxcontrib.webuspport. ' - 'sphinx.websupport will be removed in Sphinx-2.0. Please use it instaed', - RemovedInSphinx20Warning) +try: + from sphinxcontrib.websupport import WebSupport # NOQA + from sphinxcontrib.websupport import errors # NOQA + from sphinxcontrib.websupport.search import BaseSearch, SEARCH_ADAPTERS # NOQA + from sphinxcontrib.websupport.storage import StorageBackend # NOQA + + warnings.warn('sphinx.websupport module is now provided as sphinxcontrib-webuspport. ' + 'sphinx.websupport will be removed in Sphinx-2.0. Please use it instaed', + RemovedInSphinx20Warning) +except ImportError: + warnings.warn('Since Sphinx-1.6, sphinx.websupport module is now separated to ' + 'sphinxcontrib-webuspport package. Please add it into your dependency list.') From d8f57ea86525107e9f9a7e050fd12a5e95a63ec8 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 29 Apr 2017 23:26:28 +0900 Subject: [PATCH 39/91] Update CHANGES --- CHANGES | 6 ------ 1 file changed, 6 deletions(-) diff --git a/CHANGES b/CHANGES index c333a95f3..5350385bb 100644 --- a/CHANGES +++ b/CHANGES @@ -12,9 +12,6 @@ Deprecated * #3662: ``builder.css_files`` is deprecated. Please use ``add_stylesheet()`` API instead. -Features added --------------- - Bugs fixed ---------- @@ -22,9 +19,6 @@ Bugs fixed * #3669: gettext builder fails with "ValueError: substring not found" * #3660: Sphinx always depends on sphinxcontrib-websupport and its dependencies -Testing --------- - Release 1.6 beta1 (released Apr 24, 2017) ========================================= From 0960c7d8571e090827064f2229e404c5320dcb96 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 29 Apr 2017 23:29:43 +0900 Subject: [PATCH 40/91] Bump to 1.6b2 --- CHANGES | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 5350385bb..d9ac606ad 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,5 @@ -Release 1.6 beta2 (in development) -================================== +Release 1.6 beta2 (released Apr 29, 2017) +========================================= Incompatible changes -------------------- From 0f25c18ce90dd1019db63fe29a6f4878aff702c9 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 29 Apr 2017 23:46:19 +0900 Subject: [PATCH 41/91] Bump to 1.6b2 --- sphinx/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/__init__.py b/sphinx/__init__.py index a071097da..62562c067 100644 --- a/sphinx/__init__.py +++ b/sphinx/__init__.py @@ -34,8 +34,8 @@ if 'PYTHONWARNINGS' not in os.environ: warnings.filterwarnings('ignore', "'U' mode is deprecated", DeprecationWarning, module='docutils.io') -__version__ = '1.6+' -__released__ = '1.6' # used when Sphinx builds its own docs +__version__ = '1.6b2' +__released__ = '1.6b2' # used when Sphinx builds its own docs # version info for better programmatic use # possible values for 3rd element: 'alpha', 'beta', 'rc', 'final' From 004dfae2db177eda4570bfdf4a04ae014ef6ada5 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 29 Apr 2017 23:50:17 +0900 Subject: [PATCH 42/91] Bump version --- CHANGES | 18 ++++++++++++++++++ sphinx/__init__.py | 6 +++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index d9ac606ad..dea12e681 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,21 @@ +Release 1.6 beta3 (in development) +================================== + +Incompatible changes +-------------------- + +Deprecated +---------- + +Features added +-------------- + +Bugs fixed +---------- + +Testing +-------- + Release 1.6 beta2 (released Apr 29, 2017) ========================================= diff --git a/sphinx/__init__.py b/sphinx/__init__.py index 62562c067..cd8ad2c0d 100644 --- a/sphinx/__init__.py +++ b/sphinx/__init__.py @@ -34,13 +34,13 @@ if 'PYTHONWARNINGS' not in os.environ: warnings.filterwarnings('ignore', "'U' mode is deprecated", DeprecationWarning, module='docutils.io') -__version__ = '1.6b2' -__released__ = '1.6b2' # used when Sphinx builds its own docs +__version__ = '1.6+' +__released__ = '1.6' # used when Sphinx builds its own docs # version info for better programmatic use # possible values for 3rd element: 'alpha', 'beta', 'rc', 'final' # 'final' has 0 as the last element -version_info = (1, 6, 0, 'beta', 2) +version_info = (1, 6, 0, 'beta', 3) package_dir = path.abspath(path.dirname(__file__)) From 4de4fd060a866e31adc5135a421418ba4e825fc4 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 30 Apr 2017 00:40:31 +0900 Subject: [PATCH 43/91] bump_version.py: Fix generate non-beta version number on beta release --- utils/bump_version.py | 40 ++++++++++++++++++++++++++-------------- utils/release-checklist | 2 +- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/utils/bump_version.py b/utils/bump_version.py index 95db65ea7..da193a3de 100755 --- a/utils/bump_version.py +++ b/utils/bump_version.py @@ -5,6 +5,7 @@ from __future__ import print_function import os import re import sys +import argparse from datetime import datetime from contextlib import contextmanager @@ -14,17 +15,22 @@ package_dir = os.path.abspath(os.path.join(script_dir, '..')) RELEASE_TYPE = {'a': 'alpha', 'b': 'beta'} -def stringify_version(version_info): +def stringify_version(version_info, in_develop=True): if version_info[2] == 0: - return '.'.join(str(v) for v in version_info[:2]) + version = '.'.join(str(v) for v in version_info[:2]) else: - return '.'.join(str(v) for v in version_info[:3]) + version = '.'.join(str(v) for v in version_info[:3]) + + if not in_develop and version_info[3] != 'final': + version += version_info[3][0] + str(version_info[4]) + + return version -def bump_version(path, version_info): - version = stringify_version(version_info) +def bump_version(path, version_info, in_develop=True): + version = stringify_version(version_info, in_develop) release = version - if version_info[3] != 'final': + if in_develop: version += '+' with open(path, 'r+') as f: @@ -143,19 +149,25 @@ class Changes(object): f.write(body) -def main(): - if len(sys.argv) != 2: - print("bump_version.py [version]") - return -1 +def parse_options(argv): + parser = argparse.ArgumentParser() + parser.add_argument('version', help='A version number (cf. 1.6b0)') + parser.add_argument('--in-develop', action='store_true') + options = parser.parse_args(argv) + options.version = parse_version(options.version) + return options - version_info = parse_version(sys.argv[-1]) + +def main(): + options = parse_options(sys.argv[1:]) with processing("Rewriting sphinx/__init__.py"): - bump_version(os.path.join(package_dir, 'sphinx/__init__.py'), version_info) + bump_version(os.path.join(package_dir, 'sphinx/__init__.py'), + options.version, options.in_develop) with processing('Rewriting CHANGES'): changes = Changes(os.path.join(package_dir, 'CHANGES')) - if changes.version_info == version_info: + if changes.version_info == options.version: if changes.in_development: changes.finalize_release_date() else: @@ -163,7 +175,7 @@ def main(): else: if changes.in_development: print('WARNING: last version is not released yet: %s' % changes.version) - changes.add_release(version_info) + changes.add_release(options.version) if __name__ == '__main__': diff --git a/utils/release-checklist b/utils/release-checklist index a79147724..8dd728027 100644 --- a/utils/release-checklist +++ b/utils/release-checklist @@ -26,7 +26,7 @@ Release checklist * `git push origin stable --tags` * open https://readthedocs.org/dashboard/sphinx/versions/ and enable the released version * Add new version/milestone to tracker categories -* `python utils/bump_version.py a.b.cb0` (ex. 1.5.3b0) +* `python utils/bump_version.py --in-develop a.b.cb0` (ex. 1.5.3b0) * Check diff by `git diff` * `git commit -am 'Bump version'` * `git push origin stable` From b9b982d5f0885995f56db967d407a6163f280050 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 30 Apr 2017 01:04:30 +0900 Subject: [PATCH 44/91] Fix dependency errors --- sphinx/builders/websupport.py | 8 +++++--- sphinx/util/websupport.py | 6 +++++- tox.ini | 1 + 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/sphinx/builders/websupport.py b/sphinx/builders/websupport.py index a1b6cdb2d..2e416b287 100644 --- a/sphinx/builders/websupport.py +++ b/sphinx/builders/websupport.py @@ -9,8 +9,6 @@ :license: BSD, see LICENSE for details. """ -from sphinxcontrib.websupport.builder import WebSupportBuilder - if False: # For type annotation from typing import Any, Dict # NOQA @@ -19,7 +17,11 @@ if False: def setup(app): # type: (Sphinx) -> Dict[unicode, Any] - app.add_builder(WebSupportBuilder) + try: + from sphinxcontrib.websupport.builder import WebSupportBuilder + app.add_builder(WebSupportBuilder) + except ImportError: + pass return { 'version': 'builtin', diff --git a/sphinx/util/websupport.py b/sphinx/util/websupport.py index 20a35cf69..4d91cb77c 100644 --- a/sphinx/util/websupport.py +++ b/sphinx/util/websupport.py @@ -7,4 +7,8 @@ :license: BSD, see LICENSE for details. """ -from sphinxcontrib.websupport.utils import is_commentable # NOQA +try: + from sphinxcontrib.websupport.utils import is_commentable # NOQA +except ImportError: + def is_commentable(node): + raise RuntimeError diff --git a/tox.ini b/tox.ini index 530e5b941..d8db6442f 100644 --- a/tox.ini +++ b/tox.ini @@ -9,6 +9,7 @@ deps= mock enum34 typing + sphinxcontrib-websupport setenv = SPHINX_TEST_TEMPDIR = {envdir}/testbuild PYTHONDONTWRITEBYTECODE = true From fdebd2b1b2a73978031de29bb0d6fc1a61afd3f9 Mon Sep 17 00:00:00 2001 From: shimizukawa Date: Mon, 1 May 2017 17:17:14 +0900 Subject: [PATCH 45/91] refs #3588: No compact (p tag) html output in the i18n document build even when :confval:`html_compact_lists` is True. --- CHANGES | 3 +++ sphinx/transforms/i18n.py | 14 ++++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index dea12e681..944275f8d 100644 --- a/CHANGES +++ b/CHANGES @@ -13,6 +13,9 @@ Features added Bugs fixed ---------- +* #3588: No compact (p tag) html output in the i18n document build even when + :confval:`html_compact_lists` is True. + Testing -------- diff --git a/sphinx/transforms/i18n.py b/sphinx/transforms/i18n.py index 7776af237..2618c7246 100644 --- a/sphinx/transforms/i18n.py +++ b/sphinx/transforms/i18n.py @@ -215,12 +215,12 @@ class Locale(SphinxTransform): for child in patch.children: child.parent = node node.children = patch.children - node['translated'] = True + node['translated'] = True # to avoid double translation # phase2: translation for node, msg in extract_messages(self.document): - if node.get('translated', False): - continue + if node.get('translated', False): # to avoid double translation + continue # skip if the node is already translated by phase1 msgstr = catalog.gettext(msg) # XXX add marker to untranslated parts @@ -395,7 +395,7 @@ class Locale(SphinxTransform): if isinstance(node, IMAGE_TYPE_NODES): node.update_all_atts(patch) - node['translated'] = True + node['translated'] = True # to avoid double translation if 'index' in self.config.gettext_additional_targets: # Extract and translate messages for index entries. @@ -415,6 +415,12 @@ class Locale(SphinxTransform): node['raw_entries'] = entries node['entries'] = new_entries + # remove translated attribute that is used for avoiding double translation. + def has_translatable(node): + return isinstance(node, nodes.Element) and 'translated' in node + for node in self.document.traverse(has_translatable): + node.delattr('translated') + class RemoveTranslatableInline(SphinxTransform): """ From 23b1d846f1a33fae8404300af4c1146c1d25a885 Mon Sep 17 00:00:00 2001 From: shimizukawa Date: Mon, 1 May 2017 17:17:14 +0900 Subject: [PATCH 46/91] refs #3588: No compact (p tag) html output in the i18n document build even when :confval:`html_compact_lists` is True. --- CHANGES | 2 ++ sphinx/transforms/i18n.py | 14 ++++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index 344e65fff..3e9f938bb 100644 --- a/CHANGES +++ b/CHANGES @@ -17,6 +17,8 @@ Bugs fixed * #3618: autodoc crashes with tupled arguments * #3664: No space after the bullet in items of a latex list produced by Sphinx * #3657: EPUB builder crashes if document startswith genindex exists +* #3588: No compact (p tag) html output in the i18n document build even when + :confval:`html_compact_lists` is True. Testing -------- diff --git a/sphinx/transforms/i18n.py b/sphinx/transforms/i18n.py index 1e9188d17..9d761e4c6 100644 --- a/sphinx/transforms/i18n.py +++ b/sphinx/transforms/i18n.py @@ -204,12 +204,12 @@ class Locale(Transform): for child in patch.children: child.parent = node node.children = patch.children - node['translated'] = True + node['translated'] = True # to avoid double translation # phase2: translation for node, msg in extract_messages(self.document): - if node.get('translated', False): - continue + if node.get('translated', False): # to avoid double translation + continue # skip if the node is already translated by phase1 msgstr = catalog.gettext(msg) # XXX add marker to untranslated parts @@ -379,7 +379,7 @@ class Locale(Transform): if isinstance(node, IMAGE_TYPE_NODES): node.update_all_atts(patch) - node['translated'] = True + node['translated'] = True # to avoid double translation if 'index' in env.config.gettext_additional_targets: # Extract and translate messages for index entries. @@ -399,6 +399,12 @@ class Locale(Transform): node['raw_entries'] = entries node['entries'] = new_entries + # remove translated attribute that is used for avoiding double translation. + def has_translatable(node): + return isinstance(node, nodes.Element) and 'translated' in node + for node in self.document.traverse(has_translatable): + node.delattr('translated') + class RemoveTranslatableInline(Transform): """ From efb544898b2a8e095fec8d1be44d79919ed9c453 Mon Sep 17 00:00:00 2001 From: jfbu Date: Mon, 1 May 2017 17:22:54 +0200 Subject: [PATCH 47/91] Better handling of titlesec 2.10.1's bug (ref #3241) --- sphinx/texinputs/sphinx.sty | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty index 9c2a0d72f..15f9910ff 100644 --- a/sphinx/texinputs/sphinx.sty +++ b/sphinx/texinputs/sphinx.sty @@ -6,7 +6,7 @@ % \NeedsTeXFormat{LaTeX2e}[1995/12/01] -\ProvidesPackage{sphinx}[2017/04/25 v1.5.6 LaTeX package (Sphinx markup)] +\ProvidesPackage{sphinx}[2017/05/01 v1.5.6 LaTeX package (Sphinx markup)] % this is the \ltx@ifundefined of ltxcmds.sty, which is loaded by % kvoptions (and later by hyperref), but the first release of @@ -50,10 +50,29 @@ \RequirePackage{textcomp} \RequirePackage{titlesec} \@ifpackagelater{titlesec}{2016/03/15}% - {\@ifpackagelater{titlesec}{2016/03/21}{}% - {\AtEndDocument{\PackageWarningNoLine{sphinx}{^^J% -******** ERROR !! PLEASE UPDATE titlesec.sty !!********^^J% -******** THIS VERSION SWALLOWS SECTION NUMBERS.********}}}}{} + {\@ifpackagelater{titlesec}{2016/03/21}% + {}% + {\newif\ifsphinx@ttlpatch@ok + \IfFileExists{etoolbox.sty}{% + \RequirePackage{etoolbox}% + \patchcmd{\ttlh@hang}{\parindent\z@}{\parindent\z@\leavevmode}% + {\sphinx@ttlpatch@oktrue}{}% + \ifsphinx@ttlpatch@ok + \patchcmd{\ttlh@hang}{\noindent}{}{}{\sphinx@ttlpatch@okfalse}% + \fi + }{}% + \ifsphinx@ttlpatch@ok + \typeout{^^J Package Sphinx Info: ^^J + **** titlesec 2.10.1 successfully patched for bugfix ****^^J}% + \else + \AtEndDocument{\PackageWarningNoLine{sphinx}{^^J% +******** titlesec 2.10.1 has a bug, (section numbers disappear) ......|^^J% +******** and Sphinx could not patch it, perhaps because your local ...|^^J% +******** copy is already fixed without a changed release date. .......|^^J% +******** If not, you must update titlesec! ...........................|}}% + \fi + }% + }{} \RequirePackage{tabulary} \RequirePackage{makeidx} % For framing code-blocks and warning type notices, and shadowing topics From 609a08660e8978cccea7ffc061afbe493da542cd Mon Sep 17 00:00:00 2001 From: jfbu Date: Mon, 1 May 2017 18:12:59 +0200 Subject: [PATCH 48/91] Remove deprecated latex features at 1.7 notice environment, loading of packages eqparbox and multirow --- sphinx/templates/latex/latex.tex_t | 10 ---------- sphinx/texinputs/sphinx.sty | 8 -------- 2 files changed, 18 deletions(-) diff --git a/sphinx/templates/latex/latex.tex_t b/sphinx/templates/latex/latex.tex_t index 55f60437e..6c57939aa 100644 --- a/sphinx/templates/latex/latex.tex_t +++ b/sphinx/templates/latex/latex.tex_t @@ -25,16 +25,6 @@ \usepackage<%= sphinxpkgoptions %>{sphinx} <%= sphinxsetup %> <%= geometry %> -\usepackage{multirow} -\let\originalmutirow\multirow\protected\def\multirow{% - \sphinxdeprecationwarning{\multirow}{1.6}{1.7} - {Sphinx does not use package multirow. Its loading will be removed at 1.7.}% - \originalmultirow}% -\usepackage{eqparbox} -\let\originaleqparbox\eqparbox\protected\def\eqparbox{% - \sphinxdeprecationwarning{\eqparbox}{1.6}{1.7} - {Sphinx does not use package eqparbox. Its loading will be removed at 1.7.}% - \originaleqparbox}% <%= usepackages %> <%= hyperref %> <%= contentsname %> diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty index 6317485cf..b069b712f 100644 --- a/sphinx/texinputs/sphinx.sty +++ b/sphinx/texinputs/sphinx.sty @@ -1159,14 +1159,6 @@ \begin{sphinx#1}{#2}} % in end part, need to go around a LaTeX's "feature" {\edef\spx@temp{\noexpand\end{sphinx\spx@noticetype}}\spx@temp} -% use of ``notice'' is for backwards compatibility and will be removed in -% Sphinx 1.7. -\newenvironment{notice} - {\sphinxdeprecationwarning {notice}{1.6}{1.7}{% - This document was probably built with a Sphinx extension using ``notice''^^J - environment. At Sphinx 1.7, ``notice'' environment will be removed. Please^^J - report to extension author to use ``sphinxadmonition'' instead.^^J% - ****}\begin{sphinxadmonition}}{\end{sphinxadmonition}} %% PYTHON DOCS MACROS AND ENVIRONMENTS From 5cd0f235a891de80b637c20e0d90fd916cc68a86 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Tue, 2 May 2017 19:51:04 +0900 Subject: [PATCH 49/91] C++, handle decltype(auto) --- CHANGES | 2 ++ sphinx/domains/cpp.py | 26 ++++++++++++++++++++++++-- tests/test_domain_cpp.py | 5 ++--- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/CHANGES b/CHANGES index c949983ec..0bf3e4765 100644 --- a/CHANGES +++ b/CHANGES @@ -10,6 +10,8 @@ Deprecated Features added -------------- +- C++, handle ``decltype(auto)``. + Bugs fixed ---------- diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index b1e83a041..308d20b24 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -47,7 +47,7 @@ logger = logging.getLogger(__name__) It is not the actual old code, but a replication of the behaviour. - v2: 1.3 <= version < now Standardised mangling scheme from - http://mentorembedded.github.io/cxx-abi/abi.html#mangling + http://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling though not completely implemented. All versions are generated and attached to elements. The newest is used for the index. All of the versions should work as permalinks. @@ -1440,6 +1440,21 @@ class ASTTrailingTypeSpecName(ASTBase): self.nestedName.describe_signature(signode, mode, env, symbol=symbol) +class ASTTrailingTypeSpecDecltypeAuto(ASTBase): + def __unicode__(self): + return u'decltype(auto)' + + def get_id_v1(self): + raise NoOldIdError() + + def get_id_v2(self): + return 'Dc' + + def describe_signature(self, signode, mode, env, symbol): + # type: (addnodes.desc_signature, unicode, BuildEnvironment, Symbol) -> None + signode.append(nodes.Text(text_type(self))) + + class ASTFunctionParameter(ASTBase): def __init__(self, arg, ellipsis=False): # type: (Any, bool) -> None @@ -3642,7 +3657,14 @@ class DefinitionParser(object): # decltype self.skip_ws() if self.skip_word_and_ws('decltype'): - self.fail('"decltype(.)" in trailing_type_spec not implemented') + if not self.skip_string_and_ws('('): + self.fail("Expected '(' after 'decltype'.") + if self.skip_word_and_ws('auto'): + if not self.skip_string(')'): + self.fail("Expected ')' after 'decltype(auto'.") + return ASTTrailingTypeSpecDecltypeAuto() + self.fail('"decltype()" in trailing_type_spec not implemented') + #return ASTTrailingTypeSpecDecltype() # prefixed prefix = None diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py index 49d8e3206..446c264fd 100644 --- a/tests/test_domain_cpp.py +++ b/tests/test_domain_cpp.py @@ -85,10 +85,9 @@ def check(name, input, idv1output=None, idv2output=None, output=None): def test_fundamental_types(): # see http://en.cppreference.com/w/cpp/language/types for t, id_v2 in cppDomain._id_fundamental_v2.items(): - if t == "decltype(auto)": - continue - def makeIdV1(): + if t == 'decltype(auto)': + return None id = t.replace(" ", "-").replace("long", "l").replace("int", "i") id = id.replace("bool", "b").replace("char", "c") id = id.replace("wc_t", "wchar_t").replace("c16_t", "char16_t") From 4cc35418ec12d5786855e9755cb8ced453ea9176 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Haso=C5=88?= Date: Tue, 2 May 2017 15:23:00 +0200 Subject: [PATCH 50/91] Fix typo in sphinxcontrib-websupport --- sphinx/websupport/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index f71034708..4fa18db4f 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -19,9 +19,9 @@ try: from sphinxcontrib.websupport.search import BaseSearch, SEARCH_ADAPTERS # NOQA from sphinxcontrib.websupport.storage import StorageBackend # NOQA - warnings.warn('sphinx.websupport module is now provided as sphinxcontrib-webuspport. ' + warnings.warn('sphinx.websupport module is now provided as sphinxcontrib-websupport. ' 'sphinx.websupport will be removed in Sphinx-2.0. Please use it instaed', RemovedInSphinx20Warning) except ImportError: warnings.warn('Since Sphinx-1.6, sphinx.websupport module is now separated to ' - 'sphinxcontrib-webuspport package. Please add it into your dependency list.') + 'sphinxcontrib-websupport package. Please add it into your dependency list.') From b616f1dcef385cb1e5b4bcbbaf2cc09d16971437 Mon Sep 17 00:00:00 2001 From: jfbu Date: Tue, 2 May 2017 15:47:59 +0200 Subject: [PATCH 51/91] Add Thanks note to @mitya57 (ref PR #3666) and docutils@milde --- CHANGES | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index bb30f1251..799d26f1a 100644 --- a/CHANGES +++ b/CHANGES @@ -25,7 +25,8 @@ Release 1.6 beta2 (released Apr 29, 2017) Incompatible changes -------------------- -* #3345: Replace the custom smartypants code with docutils' smart_quotes +* #3345: Replace the custom smartypants code with Docutils' smart_quotes. + Thanks to Dmitry Shachnev, and to Günter Milde at Docutils. Deprecated ---------- From 00883914ae47cbd0a7faac998d9d36007d6eec9b Mon Sep 17 00:00:00 2001 From: jfbu Date: Tue, 2 May 2017 16:09:38 +0200 Subject: [PATCH 52/91] Fix typo --- sphinx/websupport/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index 4fa18db4f..35550f0ec 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -20,7 +20,8 @@ try: from sphinxcontrib.websupport.storage import StorageBackend # NOQA warnings.warn('sphinx.websupport module is now provided as sphinxcontrib-websupport. ' - 'sphinx.websupport will be removed in Sphinx-2.0. Please use it instaed', + 'sphinx.websupport will be removed in Sphinx-2.0. ' + 'Please use the package instead.', RemovedInSphinx20Warning) except ImportError: warnings.warn('Since Sphinx-1.6, sphinx.websupport module is now separated to ' From b9e9e1ed856f3bf40a2c94125ca2bba6c515387f Mon Sep 17 00:00:00 2001 From: jfbu Date: Tue, 2 May 2017 16:10:24 +0200 Subject: [PATCH 53/91] Fix typo (again) --- sphinx/websupport/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index 35550f0ec..528343f8c 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -20,7 +20,7 @@ try: from sphinxcontrib.websupport.storage import StorageBackend # NOQA warnings.warn('sphinx.websupport module is now provided as sphinxcontrib-websupport. ' - 'sphinx.websupport will be removed in Sphinx-2.0. ' + 'sphinx.websupport will be removed at Sphinx-2.0. ' 'Please use the package instead.', RemovedInSphinx20Warning) except ImportError: From 87b30cc3cbb23eb8d1f95c9a16536f1b7937d54f Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 3 May 2017 01:07:20 +0900 Subject: [PATCH 54/91] Fix #3685: AttributeError when using 3rd party domains --- CHANGES | 1 + sphinx/util/docfields.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 3e9f938bb..b48e17778 100644 --- a/CHANGES +++ b/CHANGES @@ -19,6 +19,7 @@ Bugs fixed * #3657: EPUB builder crashes if document startswith genindex exists * #3588: No compact (p tag) html output in the i18n document build even when :confval:`html_compact_lists` is True. +* #3685: AttributeError when using 3rd party domains Testing -------- diff --git a/sphinx/util/docfields.py b/sphinx/util/docfields.py index 79b19d00e..45bdb0600 100644 --- a/sphinx/util/docfields.py +++ b/sphinx/util/docfields.py @@ -310,7 +310,8 @@ class DocFieldTransformer(object): else: fieldtype, content = entry fieldtypes = types.get(fieldtype.name, {}) + env = self.directive.state.document.settings.env new_list += fieldtype.make_field(fieldtypes, self.directive.domain, - content, env=self.directive.env) + content, env=env) node.replace_self(new_list) From 097cd87fd8ceda2fed377c5207909e351315b24e Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 3 May 2017 01:18:13 +0900 Subject: [PATCH 55/91] Update CHANGES for PR #2454 (refs: 3691) --- CHANGES | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES b/CHANGES index b48e17778..9b96777ba 100644 --- a/CHANGES +++ b/CHANGES @@ -259,6 +259,7 @@ Incompatible changes * Emit warnings that will be deprecated in Sphinx 1.6 by default. Users can change the behavior by setting the environment variable PYTHONWARNINGS. Please refer :ref:`when-deprecation-warnings-are-displayed`. +* #2454: new JavaScript variable ``SOURCELINK_SUFFIX`` is added Deprecated ---------- From 4ffe79c54872eea943663dc5bfe9d74139adfb80 Mon Sep 17 00:00:00 2001 From: jfbu Date: Tue, 2 May 2017 18:35:33 +0200 Subject: [PATCH 56/91] Fix duplicate entry in CHANGES (in 1.5a1 Incompatible changes) --- CHANGES | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index 9b96777ba..7c0c7e564 100644 --- a/CHANGES +++ b/CHANGES @@ -218,10 +218,8 @@ Incompatible changes * QtHelpBuilder doens't generate search page (ref: #2352) * QtHelpBuilder uses ``nonav`` theme instead of default one to improve readability. -* latex: To provide good default settings to Japanese docs, Sphinx uses ``jsbook`` - as a docclass by default if the ``language`` is ``ja``. -* latex: To provide good default settings to Japanese docs, Sphinx uses - ``jreport`` and ``jsbooks`` as a docclass by default if the ``language`` is +* latex: To provide good default settings to Japanese documents, Sphinx uses + ``jreport`` and ``jsbook`` as docclass if :confval:`language` is ``ja``. * ``sphinx-quickstart`` now allows a project version is empty * Fix :download: role on epub/qthelp builder. They ignore the role because they don't support it. From 7bbc8c1f886568bbbde85cb798c8baf7a9c3be70 Mon Sep 17 00:00:00 2001 From: jfbu Date: Wed, 3 May 2017 00:06:21 +0200 Subject: [PATCH 57/91] By default, ``make latexpdf`` will try to ignore LaTeX errors For example, if a graphics file is missing. The error is still reported but some functional PDF may still have been produced. Some projects have temporary errors with LaTeX (missing Unicode characters, graphics file using wrong formats, ...) affecting some small percentage of the documentation, and it may be important to make at least some PDF available, postponing complete resolution of LaTeX problems to a later date. --- doc/builders.rst | 11 +++++++++++ sphinx/texinputs/Makefile_t | 6 +++--- sphinx/texinputs/latexmkjarc | 2 +- sphinx/texinputs/latexmkrc | 8 ++++---- 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/doc/builders.rst b/doc/builders.rst index 618eb005c..f4b8fa4ca 100644 --- a/doc/builders.rst +++ b/doc/builders.rst @@ -202,6 +202,17 @@ The builder's "name" must be given to the **-b** command-line option of .. versionchanged:: 1.6 Use of ``latexmk`` on GNU/Linux or Mac OS X. + Since 1.6, ``make latexpdf`` (or ``make -C "/latex"`` after a + run of ``sphinx-build``) uses ``latexmk`` (on GNU/Linux and Mac OS X) + and it invokes it with options ``-f --interaction=nonstopmode``. This + tries to force compilation to PDF even if some types of LaTeX errors + arise. It can be overridden by appending ``LATEXOPTS=""`` to + the command, for example + ``LATEXOPTS="--halt-on-error --interaction=nonstopmode"`` will halt on + first LaTeX error, but still report the copious console output from + LaTeX while e.g. ``LATEXOPTS="-silent --halt-on-error"`` would reduce + console output to a minimum. + .. autoattribute:: name .. autoattribute:: format diff --git a/sphinx/texinputs/Makefile_t b/sphinx/texinputs/Makefile_t index de6bcc085..09f045bf5 100644 --- a/sphinx/texinputs/Makefile_t +++ b/sphinx/texinputs/Makefile_t @@ -15,7 +15,7 @@ ALLIMGS = $(wildcard *.png *.gif *.jpg *.jpeg) # Prefix for archive names ARCHIVEPRREFIX = # Additional LaTeX options -LATEXOPTS = +LATEXOPTS = -f --interaction=nonstopmode # format: pdf or dvi FMT = pdf @@ -58,14 +58,14 @@ PDFLATEX = $(LATEX) {%- endif %} $(PDFLATEX) $(LATEXOPTS) '$<' +all: $(ALLPDF) + all-dvi: $(ALLDVI) all-ps: $(ALLPS) all-pdf: $(ALLPDF) -all: $(ALLPDF) - zip: all-$(FMT) mkdir $(ARCHIVEPREFIX)docs-$(FMT) cp $(ALLPDF) $(ARCHIVEPREFIX)docs-$(FMT) diff --git a/sphinx/texinputs/latexmkjarc b/sphinx/texinputs/latexmkjarc index 4a6864e44..d91c4b0a3 100644 --- a/sphinx/texinputs/latexmkjarc +++ b/sphinx/texinputs/latexmkjarc @@ -1,4 +1,4 @@ -$latex = 'platex --halt-on-error --interaction=nonstopmode -kanji=utf8 %O %S'; +$latex = 'platex -kanji=utf8 %O %S'; $dvipdf = 'dvipdfmx %O -o %D %S'; $makeindex = 'rm -f %D; mendex -U -f -d %B.dic -s python.ist %S || echo "mendex exited with error code $? (ignoring)" && : >> %D'; add_cus_dep( "glo", "gls", 0, "makeglo" ); diff --git a/sphinx/texinputs/latexmkrc b/sphinx/texinputs/latexmkrc index ddb165783..262d7df03 100644 --- a/sphinx/texinputs/latexmkrc +++ b/sphinx/texinputs/latexmkrc @@ -1,7 +1,7 @@ -$latex = 'latex --halt-on-error --interaction=nonstopmode %O %S'; -$pdflatex = 'pdflatex --halt-on-error --interaction=nonstopmode %O %S'; -$lualatex = 'lualatex --halt-on-error --interaction=nonstopmode %O %S'; -$xelatex = 'xelatex --no-pdf --halt-on-error --interaction=nonstopmode %O %S'; +$latex = 'latex %O %S'; +$pdflatex = 'pdflatex %O %S'; +$lualatex = 'lualatex %O %S'; +$xelatex = 'xelatex --no-pdf %O %S'; $makeindex = 'makeindex -s python.ist %O -o %D %S'; add_cus_dep( "glo", "gls", 0, "makeglo" ); sub makeglo { From af76149a2b7deed8e91fb5c08a38be3b32060cdb Mon Sep 17 00:00:00 2001 From: jfbu Date: Wed, 3 May 2017 07:52:32 +0200 Subject: [PATCH 58/91] Fix style-check whitespace error in doc --- doc/builders.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/builders.rst b/doc/builders.rst index f4b8fa4ca..dc0af14ff 100644 --- a/doc/builders.rst +++ b/doc/builders.rst @@ -212,7 +212,7 @@ The builder's "name" must be given to the **-b** command-line option of first LaTeX error, but still report the copious console output from LaTeX while e.g. ``LATEXOPTS="-silent --halt-on-error"`` would reduce console output to a minimum. - + .. autoattribute:: name .. autoattribute:: format From b27fbc4aa5454723b83d234d1f29a0efcd8c8810 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 3 May 2017 17:27:32 +0900 Subject: [PATCH 59/91] Remove html_use_smartypants (deprecated) --- doc/_static/conf.py.txt | 5 ----- doc/config.rst | 12 ------------ sphinx/builders/html.py | 1 - sphinx/environment/__init__.py | 8 +------- 4 files changed, 1 insertion(+), 25 deletions(-) diff --git a/doc/_static/conf.py.txt b/doc/_static/conf.py.txt index f1a4ecdbc..50c7bb782 100644 --- a/doc/_static/conf.py.txt +++ b/doc/_static/conf.py.txt @@ -167,11 +167,6 @@ html_static_path = ['_static'] # # html_last_updated_fmt = None -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -# -# html_use_smartypants = True - # Custom sidebar templates, maps document names to template names. # # html_sidebars = {} diff --git a/doc/config.rst b/doc/config.rst index 229b0348f..e04d46b71 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -765,18 +765,6 @@ that use Sphinx's HTMLWriter class. The empty string is equivalent to ``'%b %d, %Y'`` (or a locale-dependent equivalent). -.. confval:: html_use_smartypants - - If true, `SmartyPants `_ - will be used to convert quotes and dashes to typographically correct - entities. Default: ``True``. - - .. deprecated:: 1.6 - Use the `smart_quotes option`_ in the Docutils configuration file - (``docutils.conf``) instead. - - .. _`smart_quotes option`: http://docutils.sourceforge.net/docs/user/config.html#smart-quotes - .. confval:: html_add_permalinks Sphinx will add "permalinks" for each heading and description environment as diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index 08ec76618..4c9a5c492 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -1346,7 +1346,6 @@ def setup(app): app.add_config_value('html_static_path', [], 'html') app.add_config_value('html_extra_path', [], 'html') app.add_config_value('html_last_updated_fmt', None, 'html', string_classes) - app.add_config_value('html_use_smartypants', None, 'html') app.add_config_value('html_sidebars', {}, 'html') app.add_config_value('html_additional_pages', {}, 'html') app.add_config_value('html_domain_indices', True, 'html', [list]) diff --git a/sphinx/environment/__init__.py b/sphinx/environment/__init__.py index ed2740f02..3fa90459c 100644 --- a/sphinx/environment/__init__.py +++ b/sphinx/environment/__init__.py @@ -674,13 +674,7 @@ class BuildEnvironment(object): self.settings['gettext_compact'] = self.config.gettext_compact language = (self.config.language or 'en').replace('_', '-') self.settings['language_code'] = language - if self.config.html_use_smartypants is not None: - warnings.warn("html_use_smartypants option is deprecated. Use the " - "smart_quotes option in docutils.conf instead.", - RemovedInSphinx17Warning) - if language in smartchars.quotes: - self.settings['smart_quotes'] = self.config.html_use_smartypants - elif language in smartchars.quotes: # We enable smartypants by default + if language in smartchars.quotes: # We enable smartypants by default self.settings['smart_quotes'] = True docutilsconf = path.join(self.srcdir, 'docutils.conf') From 349881a52fec2cd585bbf299e582840efd1cfd5f Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 3 May 2017 17:28:04 +0900 Subject: [PATCH 60/91] Fix flake8 violation --- sphinx/domains/cpp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 308d20b24..4dfe15f02 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -3664,7 +3664,7 @@ class DefinitionParser(object): self.fail("Expected ')' after 'decltype(auto'.") return ASTTrailingTypeSpecDecltypeAuto() self.fail('"decltype()" in trailing_type_spec not implemented') - #return ASTTrailingTypeSpecDecltype() + # return ASTTrailingTypeSpecDecltype() # prefixed prefix = None From 51ad88f1487a442f4ac244e2941f6b25d1fa9c06 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 3 May 2017 17:35:45 +0900 Subject: [PATCH 61/91] Update CHANGES --- CHANGES | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/CHANGES b/CHANGES index 0bf3e4765..94207fb8d 100644 --- a/CHANGES +++ b/CHANGES @@ -12,6 +12,33 @@ Features added - C++, handle ``decltype(auto)``. +Features removed +---------------- + +* Configuration variables + + - html_use_smartypants + - latex_keep_old_macro_names + - latex_elements['footer'] + +* utility methods of ``sphinx.application.Sphinx`` class + + - buildername (property) + - _display_chunk() + - old_status_iterator() + - status_iterator() + - _directive_helper() + +* utility methods of ``sphinx.environment.BuildEnvironment`` class + + - currmodule (property) + - currclass (property) + +* epub2 builder +* prefix and colorfunc parameter for warn() +* ``sphinx.util.compat`` module +* ``sphinx.util.nodes.process_only_nodes()`` + Bugs fixed ---------- From ca543eee23dcc5593cea074fe069a8fbf8844eac Mon Sep 17 00:00:00 2001 From: jfbu Date: Wed, 3 May 2017 10:45:53 +0200 Subject: [PATCH 62/91] Separate latexmk and pdflatex options for better control --- doc/builders.rst | 32 +++++++++++++++++++++++--------- sphinx/texinputs/Makefile_t | 12 +++++++----- sphinx/texinputs/latexmkjarc | 2 +- sphinx/texinputs/latexmkrc | 8 ++++---- 4 files changed, 35 insertions(+), 19 deletions(-) diff --git a/doc/builders.rst b/doc/builders.rst index dc0af14ff..123244b28 100644 --- a/doc/builders.rst +++ b/doc/builders.rst @@ -203,15 +203,29 @@ The builder's "name" must be given to the **-b** command-line option of Use of ``latexmk`` on GNU/Linux or Mac OS X. Since 1.6, ``make latexpdf`` (or ``make -C "/latex"`` after a - run of ``sphinx-build``) uses ``latexmk`` (on GNU/Linux and Mac OS X) - and it invokes it with options ``-f --interaction=nonstopmode``. This - tries to force compilation to PDF even if some types of LaTeX errors - arise. It can be overridden by appending ``LATEXOPTS=""`` to - the command, for example - ``LATEXOPTS="--halt-on-error --interaction=nonstopmode"`` will halt on - first LaTeX error, but still report the copious console output from - LaTeX while e.g. ``LATEXOPTS="-silent --halt-on-error"`` would reduce - console output to a minimum. + ``sphinx-build`` run) uses ``latexmk`` (on GNU/Linux and Mac OS X). + It invokes it with option ``-f`` which attempts to complete targets + even in case of LaTeX processing errors. This can be overridden via + ``LATEXMKOPTS`` variable, for example: + + .. code-block:: console + + make latexpdf LATEXMKOPTS="" + + The ``pdflatex`` calls themselves obey the ``LATEXOPTS`` variable whose + default is ``--interaction=nonstopmode`` (same as ``-interaction + nonstopmode``.) In order to stop the + compilation on first error one can use ``--halt-on-error``. + + Example: + + .. code-block:: console + + make latexpdf LATEXMKOPTS="-silent" LATEXOPTS="--halt-on-error" + + In case the first ``pdflatex`` run aborts with an error, this will stop + further ``latexmk`` processing (no ``-f`` option). The console output + will be kept to a bare minimum during target processing (``-silent``). .. autoattribute:: name diff --git a/sphinx/texinputs/Makefile_t b/sphinx/texinputs/Makefile_t index 09f045bf5..72874ec47 100644 --- a/sphinx/texinputs/Makefile_t +++ b/sphinx/texinputs/Makefile_t @@ -14,8 +14,10 @@ ALLIMGS = $(wildcard *.png *.gif *.jpg *.jpeg) # Prefix for archive names ARCHIVEPRREFIX = -# Additional LaTeX options -LATEXOPTS = -f --interaction=nonstopmode +# Additional LaTeX options (used via latexmkrc/latexmkjarc file) +LATEXOPTS = --interaction=nonstopmode +# Additional latexmk options +LATEXMKOPTS = -f # format: pdf or dvi FMT = pdf @@ -40,11 +42,11 @@ PDFLATEX = $(LATEX) {% if latex_engine == 'platex' -%} %.dvi: %.tex $(ALLIMGS) FORCE_MAKE for f in *.pdf; do extractbb "$$f"; done - $(LATEX) $(LATEXOPTS) '$<' + $(LATEX) $(LATEXMKOPTS) '$<' {% elif latex_engine != 'xelatex' -%} %.dvi: %.tex FORCE_MAKE - $(LATEX) $(LATEXOPTS) '$<' + $(LATEX) $(LATEXMKOPTS) '$<' {% endif -%} %.ps: %.dvi @@ -56,7 +58,7 @@ PDFLATEX = $(LATEX) {%- else -%} %.pdf: %.tex FORCE_MAKE {%- endif %} - $(PDFLATEX) $(LATEXOPTS) '$<' + $(PDFLATEX) $(LATEXMKOPTS) '$<' all: $(ALLPDF) diff --git a/sphinx/texinputs/latexmkjarc b/sphinx/texinputs/latexmkjarc index d91c4b0a3..2124d4673 100644 --- a/sphinx/texinputs/latexmkjarc +++ b/sphinx/texinputs/latexmkjarc @@ -1,4 +1,4 @@ -$latex = 'platex -kanji=utf8 %O %S'; +$latex = 'platex $LATEXOPTS -kanji=utf8 %O %S'; $dvipdf = 'dvipdfmx %O -o %D %S'; $makeindex = 'rm -f %D; mendex -U -f -d %B.dic -s python.ist %S || echo "mendex exited with error code $? (ignoring)" && : >> %D'; add_cus_dep( "glo", "gls", 0, "makeglo" ); diff --git a/sphinx/texinputs/latexmkrc b/sphinx/texinputs/latexmkrc index 262d7df03..e0bfe9fd0 100644 --- a/sphinx/texinputs/latexmkrc +++ b/sphinx/texinputs/latexmkrc @@ -1,7 +1,7 @@ -$latex = 'latex %O %S'; -$pdflatex = 'pdflatex %O %S'; -$lualatex = 'lualatex %O %S'; -$xelatex = 'xelatex --no-pdf %O %S'; +$latex = 'latex $LATEXOPTS %O %S'; +$pdflatex = 'pdflatex $LATEXOPTS %O %S'; +$lualatex = 'lualatex $LATEXOPTS %O %S'; +$xelatex = 'xelatex --no-pdf $LATEXOPTS %O %S'; $makeindex = 'makeindex -s python.ist %O -o %D %S'; add_cus_dep( "glo", "gls", 0, "makeglo" ); sub makeglo { From a398388501946ca357057f4f5ca33119516fc7a4 Mon Sep 17 00:00:00 2001 From: jfbu Date: Wed, 3 May 2017 11:27:24 +0200 Subject: [PATCH 63/91] Update CHANGES for PR #3695 --- CHANGES | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES b/CHANGES index 799d26f1a..48477560e 100644 --- a/CHANGES +++ b/CHANGES @@ -15,6 +15,10 @@ Bugs fixed * #3588: No compact (p tag) html output in the i18n document build even when :confval:`html_compact_lists` is True. +* The ``make latexpdf`` (which uses ``latexmk``) aborted earlier in case of + LaTeX errors than was the case with 1.5 series. Now, ``LATEXMKOPTS`` + variable is provided whose default value ``-f`` mimics former behaviour. + (refs #3695) Testing -------- From 81a1835cf1d9409a234f835b6c6fe805f80ea6a5 Mon Sep 17 00:00:00 2001 From: jfbu Date: Mon, 1 May 2017 18:12:59 +0200 Subject: [PATCH 64/91] Remove deprecated latex features at 1.7 notice environment, loading of packages eqparbox and multirow --- sphinx/templates/latex/latex.tex_t | 10 ---------- sphinx/texinputs/sphinx.sty | 8 -------- 2 files changed, 18 deletions(-) diff --git a/sphinx/templates/latex/latex.tex_t b/sphinx/templates/latex/latex.tex_t index 55f60437e..6c57939aa 100644 --- a/sphinx/templates/latex/latex.tex_t +++ b/sphinx/templates/latex/latex.tex_t @@ -25,16 +25,6 @@ \usepackage<%= sphinxpkgoptions %>{sphinx} <%= sphinxsetup %> <%= geometry %> -\usepackage{multirow} -\let\originalmutirow\multirow\protected\def\multirow{% - \sphinxdeprecationwarning{\multirow}{1.6}{1.7} - {Sphinx does not use package multirow. Its loading will be removed at 1.7.}% - \originalmultirow}% -\usepackage{eqparbox} -\let\originaleqparbox\eqparbox\protected\def\eqparbox{% - \sphinxdeprecationwarning{\eqparbox}{1.6}{1.7} - {Sphinx does not use package eqparbox. Its loading will be removed at 1.7.}% - \originaleqparbox}% <%= usepackages %> <%= hyperref %> <%= contentsname %> diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty index 6317485cf..b069b712f 100644 --- a/sphinx/texinputs/sphinx.sty +++ b/sphinx/texinputs/sphinx.sty @@ -1159,14 +1159,6 @@ \begin{sphinx#1}{#2}} % in end part, need to go around a LaTeX's "feature" {\edef\spx@temp{\noexpand\end{sphinx\spx@noticetype}}\spx@temp} -% use of ``notice'' is for backwards compatibility and will be removed in -% Sphinx 1.7. -\newenvironment{notice} - {\sphinxdeprecationwarning {notice}{1.6}{1.7}{% - This document was probably built with a Sphinx extension using ``notice''^^J - environment. At Sphinx 1.7, ``notice'' environment will be removed. Please^^J - report to extension author to use ``sphinxadmonition'' instead.^^J% - ****}\begin{sphinxadmonition}}{\end{sphinxadmonition}} %% PYTHON DOCS MACROS AND ENVIRONMENTS From 6c293ced2b54b1c455b077cf1f391c5b1f236951 Mon Sep 17 00:00:00 2001 From: jfbu Date: Wed, 3 May 2017 11:39:26 +0200 Subject: [PATCH 65/91] Update CHANGES after PR #3687 --- CHANGES | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES b/CHANGES index 48477560e..8fdcf36da 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,9 @@ Release 1.6 beta3 (in development) Incompatible changes -------------------- +* LaTeX package ``eqparbox`` is not used and not loaded by Sphinx anymore +* LaTeX package ``multirow`` is not used and not loaded by Sphinx anymore + Deprecated ---------- From 3485f003498d349b6311a289fec0406fb022ef97 Mon Sep 17 00:00:00 2001 From: jfbu Date: Wed, 3 May 2017 11:48:21 +0200 Subject: [PATCH 66/91] Define (again) deprecated `notice` environment for 1.6 series It was removed by mistake at 81a1835 --- sphinx/texinputs/sphinx.sty | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty index b069b712f..6317485cf 100644 --- a/sphinx/texinputs/sphinx.sty +++ b/sphinx/texinputs/sphinx.sty @@ -1159,6 +1159,14 @@ \begin{sphinx#1}{#2}} % in end part, need to go around a LaTeX's "feature" {\edef\spx@temp{\noexpand\end{sphinx\spx@noticetype}}\spx@temp} +% use of ``notice'' is for backwards compatibility and will be removed in +% Sphinx 1.7. +\newenvironment{notice} + {\sphinxdeprecationwarning {notice}{1.6}{1.7}{% + This document was probably built with a Sphinx extension using ``notice''^^J + environment. At Sphinx 1.7, ``notice'' environment will be removed. Please^^J + report to extension author to use ``sphinxadmonition'' instead.^^J% + ****}\begin{sphinxadmonition}}{\end{sphinxadmonition}} %% PYTHON DOCS MACROS AND ENVIRONMENTS From 71a1ffb30376c9a2f21f5027a7ef2ee73423bcc2 Mon Sep 17 00:00:00 2001 From: jfbu Date: Wed, 3 May 2017 11:51:38 +0200 Subject: [PATCH 67/91] Remove ``notice`` environment which was deprecated at 1.6 --- CHANGES | 1 + sphinx/texinputs/sphinx.sty | 8 -------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/CHANGES b/CHANGES index b6a81c486..2dcefca01 100644 --- a/CHANGES +++ b/CHANGES @@ -38,6 +38,7 @@ Features removed * prefix and colorfunc parameter for warn() * ``sphinx.util.compat`` module * ``sphinx.util.nodes.process_only_nodes()`` +* LaTeX environment ``notice``, use ``sphinxadmonition`` instead Bugs fixed ---------- diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty index 3d6a678ac..28f6d8431 100644 --- a/sphinx/texinputs/sphinx.sty +++ b/sphinx/texinputs/sphinx.sty @@ -1158,14 +1158,6 @@ \begin{sphinx#1}{#2}} % in end part, need to go around a LaTeX's "feature" {\edef\spx@temp{\noexpand\end{sphinx\spx@noticetype}}\spx@temp} -% use of ``notice'' is for backwards compatibility and will be removed in -% Sphinx 1.7. -\newenvironment{notice} - {\sphinxdeprecationwarning {notice}{1.6}{1.7}{% - This document was probably built with a Sphinx extension using ``notice''^^J - environment. At Sphinx 1.7, ``notice'' environment will be removed. Please^^J - report to extension author to use ``sphinxadmonition'' instead.^^J% - ****}\begin{sphinxadmonition}}{\end{sphinxadmonition}} %% PYTHON DOCS MACROS AND ENVIRONMENTS From 17631e0b9a8e81179e49fa4c7e7017efcde9ec3d Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 3 May 2017 19:02:16 +0900 Subject: [PATCH 68/91] Fix #3698: Moving :doc: to std domain broke backwards compatibility --- CHANGES | 1 + sphinx/transforms/post_transforms/__init__.py | 34 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/CHANGES b/CHANGES index 48477560e..3a222607b 100644 --- a/CHANGES +++ b/CHANGES @@ -19,6 +19,7 @@ Bugs fixed LaTeX errors than was the case with 1.5 series. Now, ``LATEXMKOPTS`` variable is provided whose default value ``-f`` mimics former behaviour. (refs #3695) +* #3698: Moving :doc: to std domain broke backwards compatibility Testing -------- diff --git a/sphinx/transforms/post_transforms/__init__.py b/sphinx/transforms/post_transforms/__init__.py index 8b2a5b22c..8e2580a2d 100644 --- a/sphinx/transforms/post_transforms/__init__.py +++ b/sphinx/transforms/post_transforms/__init__.py @@ -9,9 +9,13 @@ :license: BSD, see LICENSE for details. """ +import warnings + from docutils import nodes +from docutils.utils import get_source_line from sphinx import addnodes +from sphinx.deprecation import RemovedInSphinx20Warning from sphinx.environment import NoUri from sphinx.locale import _ from sphinx.transforms import SphinxTransform @@ -27,6 +31,35 @@ if False: logger = logging.getLogger(__name__) +class DocReferenceMigrator(SphinxTransform): + """Migrate :doc: reference to std domain.""" + + default_priority = 5 # before ReferencesResolver + + def apply(self): + # type: () -> None + for node in self.document.traverse(addnodes.pending_xref): + if node.get('reftype') == 'doc' and node.get('refdomain') is None: + source, line = get_source_line(node) + if source and line: + location = "%s:%s" % (source, line) + elif source: + location = "%s:" % source + elif line: + location = ":%s" % line + else: + location = None + + message = ('Invalid pendig_xref node detected. ' + ':doc: reference should have refdomain=std attribute.') + if location: + warnings.warn("%s: %s" % (location, message), + RemovedInSphinx20Warning) + else: + warnings.warn(message, RemovedInSphinx20Warning) + node['refdomain'] = 'std' + + class ReferencesResolver(SphinxTransform): """ Resolves cross-references on doctrees. @@ -165,6 +198,7 @@ class OnlyNodeTransform(SphinxTransform): def setup(app): # type: (Sphinx) -> Dict[unicode, Any] + app.add_post_transform(DocReferenceMigrator) app.add_post_transform(ReferencesResolver) app.add_post_transform(OnlyNodeTransform) From 1bfa25e6feb67d2e77df941698c0e61f3afa0bf4 Mon Sep 17 00:00:00 2001 From: jfbu Date: Wed, 3 May 2017 12:46:51 +0200 Subject: [PATCH 69/91] Update CHANGES better after PR #3695 --- CHANGES | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index 8fdcf36da..d2308ed4c 100644 --- a/CHANGES +++ b/CHANGES @@ -13,15 +13,18 @@ Deprecated Features added -------------- +* New ``LATEXMKOPTS`` variable to pass options to ``latexmk`` when executing + ``make latexpdf``. Default is ``-f``. (refs #3695) + Bugs fixed ---------- * #3588: No compact (p tag) html output in the i18n document build even when :confval:`html_compact_lists` is True. -* The ``make latexpdf`` (which uses ``latexmk``) aborted earlier in case of - LaTeX errors than was the case with 1.5 series. Now, ``LATEXMKOPTS`` - variable is provided whose default value ``-f`` mimics former behaviour. - (refs #3695) + +* The ``make latexpdf`` from 1.6b1 (for GNU/Linux and Mac OS, using + ``latexmk``) aborted earlier in case of LaTeX errors than was the case with + 1.5 series, due to hard-coded usage of ``--halt-on-error`` option. (refs #3695) Testing -------- From 02c4854ba04e1267968c87b84cccaf6ba6fbf355 Mon Sep 17 00:00:00 2001 From: jfbu Date: Wed, 3 May 2017 12:52:24 +0200 Subject: [PATCH 70/91] Update CHANGES (again!) for PR #3695 --- CHANGES | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index d2308ed4c..f8183a1b3 100644 --- a/CHANGES +++ b/CHANGES @@ -13,8 +13,9 @@ Deprecated Features added -------------- -* New ``LATEXMKOPTS`` variable to pass options to ``latexmk`` when executing - ``make latexpdf``. Default is ``-f``. (refs #3695) +* ``LATEXMKOPTS`` variable for the Makefile in ``$BUILDDIR/latex`` to pass + options to ``latexmk`` when executing ``make latexpdf``. Default is ``-f``. + (refs #3695) Bugs fixed ---------- From 1048a239c68a93f0ea245f86e3676c0c1424cc62 Mon Sep 17 00:00:00 2001 From: jfbu Date: Wed, 3 May 2017 15:06:04 +0200 Subject: [PATCH 71/91] Remove extra white line in CHANGES will I ever get this entry right ? --- CHANGES | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGES b/CHANGES index f8183a1b3..8408d0849 100644 --- a/CHANGES +++ b/CHANGES @@ -22,7 +22,6 @@ Bugs fixed * #3588: No compact (p tag) html output in the i18n document build even when :confval:`html_compact_lists` is True. - * The ``make latexpdf`` from 1.6b1 (for GNU/Linux and Mac OS, using ``latexmk``) aborted earlier in case of LaTeX errors than was the case with 1.5 series, due to hard-coded usage of ``--halt-on-error`` option. (refs #3695) From 0135948485f1a57ff0b6fd81f017a7cf310acac8 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 3 May 2017 22:58:30 +0900 Subject: [PATCH 72/91] Fix #3683: sphinx.websupport module is not provided by default --- CHANGES | 1 + setup.py | 4 +++- test-reqs.txt | 1 - tox.ini | 1 - 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index 8408d0849..872ff5756 100644 --- a/CHANGES +++ b/CHANGES @@ -25,6 +25,7 @@ Bugs fixed * The ``make latexpdf`` from 1.6b1 (for GNU/Linux and Mac OS, using ``latexmk``) aborted earlier in case of LaTeX errors than was the case with 1.5 series, due to hard-coded usage of ``--halt-on-error`` option. (refs #3695) +* #3683: sphinx.websupport module is not provided by default Testing -------- diff --git a/setup.py b/setup.py index 4354b0baa..95bb3778f 100644 --- a/setup.py +++ b/setup.py @@ -53,6 +53,7 @@ requires = [ 'requests>=2.0.0', 'typing', 'setuptools', + 'sphinxcontrib-websupport', ] extras_require = { # Environment Marker works for wheel 0.24 or later @@ -60,7 +61,8 @@ extras_require = { 'colorama>=0.3.5', ], 'websupport': [ - 'sphinxcontrib-websupport', + 'sqlalchemy>=0.9', + 'whoosh>=2.0', ], 'test': [ 'pytest', diff --git a/test-reqs.txt b/test-reqs.txt index 874948dd5..a08cc8300 100644 --- a/test-reqs.txt +++ b/test-reqs.txt @@ -10,7 +10,6 @@ snowballstemmer>=1.1 babel alabaster sphinx_rtd_theme -sphinxcontrib-websupport imagesize requests html5lib diff --git a/tox.ini b/tox.ini index d8db6442f..530e5b941 100644 --- a/tox.ini +++ b/tox.ini @@ -9,7 +9,6 @@ deps= mock enum34 typing - sphinxcontrib-websupport setenv = SPHINX_TEST_TEMPDIR = {envdir}/testbuild PYTHONDONTWRITEBYTECODE = true From b8799b2aee4c83ecb93a20b2bb4308a71ad72305 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 3 May 2017 23:16:12 +0900 Subject: [PATCH 73/91] Fix #3683: Failed to build document if builder.css_file.insert() is called --- CHANGES | 1 + sphinx/builders/html.py | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/CHANGES b/CHANGES index 872ff5756..9831addde 100644 --- a/CHANGES +++ b/CHANGES @@ -26,6 +26,7 @@ Bugs fixed ``latexmk``) aborted earlier in case of LaTeX errors than was the case with 1.5 series, due to hard-coded usage of ``--halt-on-error`` option. (refs #3695) * #3683: sphinx.websupport module is not provided by default +* #3683: Failed to build document if builder.css_file.insert() is called Testing -------- diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index 08ec76618..178e5424a 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -100,6 +100,12 @@ class CSSContainer(list): else: super(CSSContainer, self).append(Stylesheet(obj, None, 'stylesheet')) + def insert(self, index, other): + warnings.warn('builder.css_files is deprecated. ' + 'Please use app.add_stylesheet() instead.', + RemovedInSphinx20Warning) + self.insert(index, other) + def extend(self, other): warnings.warn('builder.css_files is deprecated. ' 'Please use app.add_stylesheet() instead.', From 0e5a610b2c7e576cde8669bf0a17c8350259ecb5 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 3 May 2017 23:39:49 +0900 Subject: [PATCH 74/91] Fix CSSContainer should call super method --- sphinx/builders/html.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index 178e5424a..d147f0dd1 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -104,7 +104,7 @@ class CSSContainer(list): warnings.warn('builder.css_files is deprecated. ' 'Please use app.add_stylesheet() instead.', RemovedInSphinx20Warning) - self.insert(index, other) + super(CSSContainer, self).insert(index, other) def extend(self, other): warnings.warn('builder.css_files is deprecated. ' From fa4817eb2e6491c208a7cc0c8ad56120f50e4f45 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 3 May 2017 23:40:10 +0900 Subject: [PATCH 75/91] Add sphinxcontrib-websupport to test-reqs.txt --- test-reqs.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/test-reqs.txt b/test-reqs.txt index a08cc8300..874948dd5 100644 --- a/test-reqs.txt +++ b/test-reqs.txt @@ -10,6 +10,7 @@ snowballstemmer>=1.1 babel alabaster sphinx_rtd_theme +sphinxcontrib-websupport imagesize requests html5lib From 5f4a88e0a4fd4e5f8d4418b71fd3bc5a72eda2d0 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 3 May 2017 23:48:23 +0900 Subject: [PATCH 76/91] Fix CSSContainer.insert() should convert strings to Stylesheet objects --- sphinx/builders/html.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index d147f0dd1..c98f5fee8 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -100,11 +100,14 @@ class CSSContainer(list): else: super(CSSContainer, self).append(Stylesheet(obj, None, 'stylesheet')) - def insert(self, index, other): + def insert(self, index, obj): warnings.warn('builder.css_files is deprecated. ' 'Please use app.add_stylesheet() instead.', RemovedInSphinx20Warning) - super(CSSContainer, self).insert(index, other) + if isinstance(obj, Stylesheet): + super(CSSContainer, self).insert(index, obj) + else: + super(CSSContainer, self).insert(index, Stylesheet(obj, None, 'stylesheet')) def extend(self, other): warnings.warn('builder.css_files is deprecated. ' From 85a94b79cad231818793b5704e35cbe51c0169cd Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Tue, 21 Mar 2017 20:10:41 +0900 Subject: [PATCH 77/91] C++, internal changes to id generation --- sphinx/domains/cpp.py | 855 ++++++++++++++++----------------------- tests/test_domain_cpp.py | 459 ++++++++++----------- 2 files changed, 582 insertions(+), 732 deletions(-) diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 4dfe15f02..9245c851a 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -318,6 +318,9 @@ _keywords = [ 'while', 'xor', 'xor_eq' ] +_max_id = 2 +_id_prefix = [None, '', '_CPPv2'] + # ------------------------------------------------------------------------------ # Id v1 constants # ------------------------------------------------------------------------------ @@ -393,10 +396,9 @@ _id_operator_v1 = { } # type: Dict[unicode, unicode] # ------------------------------------------------------------------------------ -# Id v2 constants +# Id v > 1 constants # ------------------------------------------------------------------------------ -_id_prefix_v2 = '_CPPv2' _id_fundamental_v2 = { # not all of these are actually parsed as fundamental types, TODO: do that 'void': 'v', @@ -546,14 +548,9 @@ class ASTBase(UnicodeMixin): """Clone a definition expression node.""" return deepcopy(self) - def get_id_v1(self): - # type: () -> unicode - """Return the v1 id for the node.""" - raise NotImplementedError(repr(self)) - - def get_id_v2(self): - # type: () -> unicode - """Return the v2 id for the node.""" + def get_id(self, version): + # type: (int) -> unicode + """Return the id for the node.""" raise NotImplementedError(repr(self)) def get_name(self): @@ -678,15 +675,13 @@ class ASTIdentifier(ASTBase): assert identifier is not None self.identifier = identifier - def get_id_v1(self): - # type: () -> unicode - if self.identifier == 'size_t': - return 's' - else: - return self.identifier - - def get_id_v2(self): - # type: () -> unicode + def get_id(self, version): + # type: (int) -> unicode + if version == 1: + if self.identifier == 'size_t': + return 's' + else: + return self.identifier if self.identifier == "std": return 'St' elif self.identifier[0] == "~": @@ -736,8 +731,9 @@ class ASTTemplateKeyParamPackIdDefault(ASTBase): # type: () -> unicode return self.identifier - def get_id_v2(self): - # type: () -> unicode + def get_id(self, version): + # type: (int) -> unicode + assert version >= 2 # this is not part of the normal name mangling in C++ res = [] if self.parameterPack: @@ -794,14 +790,15 @@ class ASTTemplateParamType(ASTBase): # type: () -> unicode return self.data.get_identifier() - def get_id_v2(self, objectType=None, symbol=None): + def get_id(self, version, objectType=None, symbol=None): # type: (unicode, Symbol) -> unicode # this is not part of the normal name mangling in C++ + assert version >= 2 if symbol: # the anchor will be our parent - return symbol.parent.declaration.get_id_v2(prefixed=None) + return symbol.parent.declaration.get_id(version, prefixed=False) else: - return self.data.get_id_v2() + return self.data.get_id(version) def __unicode__(self): # type: () -> unicode @@ -830,14 +827,15 @@ class ASTTemplateParamTemplateType(ASTBase): # type: () -> unicode return self.data.get_identifier() - def get_id_v2(self, objectType=None, symbol=None): - # type: (unicode, Symbol) -> unicode + def get_id(self, version, objectType=None, symbol=None): + # type: (int, unicode, Symbol) -> unicode + assert version >= 2 # this is not part of the normal name mangling in C++ if symbol: # the anchor will be our parent - return symbol.parent.declaration.get_id_v2(prefixed=None) + return symbol.parent.declaration.get_id(version, prefixed=None) else: - return self.nestedParams.get_id_v2() + self.data.get_id_v2() + return self.nestedParams.get_id(version) + self.data.get_id(version) def __unicode__(self): # type: () -> unicode @@ -873,14 +871,15 @@ class ASTTemplateParamNonType(ASTBase): else: return None - def get_id_v2(self, objectType=None, symbol=None): - # type: (unicode, Symbol) -> unicode + def get_id(self, version, objectType=None, symbol=None): + # type: (int, unicode, Symbol) -> unicode + assert version >= 2 # this is not part of the normal name mangling in C++ if symbol: # the anchor will be our parent - return symbol.parent.declaration.get_id_v2(prefixed=None) + return symbol.parent.declaration.get_id(version, prefixed=None) else: - return '_' + self.param.get_id_v2() + return '_' + self.param.get_id(version) def __unicode__(self): # type: () -> unicode @@ -898,12 +897,13 @@ class ASTTemplateParams(ASTBase): self.params = params self.isNested = False # whether it's a template template param - def get_id_v2(self): - # type: () -> unicode + def get_id(self, version): + # type: (int) -> unicode + assert version >= 2 res = [] res.append("I") for param in self.params: - res.append(param.get_id_v2()) + res.append(param.get_id(version)) res.append("E") return ''.join(res) @@ -951,22 +951,24 @@ class ASTTemplateIntroductionParameter(ASTBase): # type: () -> unicode return self.identifier - def get_id_v2(self, objectType=None, symbol=None): - # type: (unicode, Symbol) -> unicode + def get_id(self, version, objectType=None, symbol=None): + # type: (int, unicode, Symbol) -> unicode + assert version >= 2 # this is not part of the normal name mangling in C++ if symbol: # the anchor will be our parent - return symbol.parent.declaration.get_id_v2(prefixed=None) + return symbol.parent.declaration.get_id(version, prefixed=None) else: if self.parameterPack: return 'Dp' else: return '0' # we need to put something - def get_id_v2_as_arg(self): - # type: () -> unicode + def get_id_as_arg(self, version): + # type: (int) -> unicode + assert version >= 2 # used for the implicit requires clause - res = self.identifier.get_id_v2() + res = self.identifier.get_id(version) if self.parameterPack: return u'sp' + res else: @@ -994,22 +996,21 @@ class ASTTemplateIntroduction(ASTBase): self.concept = concept self.params = params - # id_v1 does not exist - - def get_id_v2(self): - # type: () -> unicode + def get_id(self, version): + # type: (int) -> unicode + assert version >= 2 # first do the same as a normal template parameter list res = [] res.append("I") for param in self.params: - res.append(param.get_id_v2()) + res.append(param.get_id(version)) res.append("E") # let's use X expr E, which is otherwise for constant template args res.append("X") - res.append(self.concept.get_id_v2()) + res.append(self.concept.get_id(version)) res.append("I") for param in self.params: - res.append(param.get_id_v2_as_arg()) + res.append(param.get_id_as_arg(version)) res.append("E") res.append("E") return ''.join(res) @@ -1047,14 +1048,13 @@ class ASTTemplateDeclarationPrefix(ASTBase): assert len(templates) > 0 self.templates = templates - # id_v1 does not exist - - def get_id_v2(self): - # type: () -> unicode + def get_id(self, version): + # type: (int) -> unicode + assert version >= 2 # this is not part of a normal name mangling system res = [] for t in self.templates: - res.append(t.get_id_v2()) + res.append(t.get_id(version)) return u''.join(res) def __unicode__(self): @@ -1080,19 +1080,16 @@ class ASTOperatorBuildIn(ASTBase): # type: () -> bool return True - def get_id_v1(self): - # type: () -> unicode - if self.op not in _id_operator_v1: + def get_id(self, version): + # type: (int) -> unicode + if version == 1: + ids = _id_operator_v1 + else: + ids = _id_operator_v2 + if self.op not in ids: raise Exception('Internal error: Build-in operator "%s" can not ' 'be mapped to an id.' % self.op) - return _id_operator_v1[self.op] - - def get_id_v2(self): - # type: () -> unicode - if self.op not in _id_operator_v2: - raise Exception('Internal error: Build-in operator "%s" can not ' - 'be mapped to an id.' % self.op) - return _id_operator_v2[self.op] + return ids[self.op] def __unicode__(self): # type: () -> unicode @@ -1120,13 +1117,12 @@ class ASTOperatorType(ASTBase): # type: () -> bool return True - def get_id_v1(self): - # type: () -> unicode - return u'castto-%s-operator' % self.type.get_id_v1() - - def get_id_v2(self): - # type: () -> unicode - return u'cv' + self.type.get_id_v2() + def get_id(self, version): + # type: (int) -> unicode + if version == 1: + return u'castto-%s-operator' % self.type.get_id(version) + else: + return u'cv' + self.type.get_id(version) def __unicode__(self): # type: () -> unicode @@ -1155,13 +1151,12 @@ class ASTOperatorLiteral(ASTBase): # type: () -> bool return True - def get_id_v1(self): - # type: () -> unicode - raise NoOldIdError() - - def get_id_v2(self): - # type: () -> unicode - return u'li' + self.identifier.get_id_v2() + def get_id(self, version): + # type: (int) -> unicode + if version == 1: + raise NoOldIdError() + else: + return u'li' + self.identifier.get_id(version) def __unicode__(self): # type: () -> unicode @@ -1186,15 +1181,15 @@ class ASTTemplateArgConstant(ASTBase): # type: () -> unicode return text_type(self.value) - def get_id_v1(self): - # type: () -> unicode - return text_type(self).replace(u' ', u'-') - - def get_id_v2(self): - # type: () -> unicode - # TODO: doing this properly needs parsing of expressions, let's just - # juse it verbatim for now - return u'X' + text_type(self) + u'E' + def get_id(self, version): + # type: (int) -> unicode + if version == 1: + return text_type(self).replace(u' ', u'-') + if version == 2: + # TODO: doing this properly needs parsing of expressions, let's just + # use it verbatim for now + return u'X' + text_type(self) + u'E' + assert False def describe_signature(self, signode, mode, env, symbol): # type: (addnodes.desc_signature, unicode, BuildEnvironment, Symbol) -> None @@ -1209,20 +1204,19 @@ class ASTTemplateArgs(ASTBase): assert len(args) > 0 self.args = args - def get_id_v1(self): - # type: () -> unicode - res = [] # type: List[unicode] - res.append(':') - res.append(u'.'.join(a.get_id_v1() for a in self.args)) - res.append(':') - return u''.join(res) + def get_id(self, version): + # type: (int) -> unicode + if version == 1: + res = [] # type: List[unicode] + res.append(':') + res.append(u'.'.join(a.get_id(version) for a in self.args)) + res.append(':') + return u''.join(res) - def get_id_v2(self): - # type: () -> unicode res = [] res.append('I') for a in self.args: - res.append(a.get_id_v2()) + res.append(a.get_id(version)) res.append('E') return u''.join(res) @@ -1254,18 +1248,11 @@ class ASTNestedNameElement(ASTBase): # type: () -> bool return False - def get_id_v1(self): - # type: () -> unicode - res = self.identifier.get_id_v1() + def get_id(self, version): + # type: (int) -> unicode + res = self.identifier.get_id(version) if self.templateArgs: - res += self.templateArgs.get_id_v1() - return res - - def get_id_v2(self): - # type: () -> unicode - res = self.identifier.get_id_v2() - if self.templateArgs: - res += self.templateArgs.get_id_v2() + res += self.templateArgs.get_id(version) return res def __unicode__(self): @@ -1304,22 +1291,20 @@ class ASTNestedName(ASTBase): count += 1 return count - def get_id_v1(self): - # type: () -> unicode - tt = text_type(self) - if tt in _id_shorthands_v1: - return _id_shorthands_v1[tt] - else: - return u'::'.join(n.get_id_v1() for n in self.names) - - def get_id_v2(self, modifiers=""): - # type: (unicode) -> unicode + def get_id(self, version, modifiers=''): + # type: (int, unicode) -> unicode + if version == 1: + tt = text_type(self) + if tt in _id_shorthands_v1: + return _id_shorthands_v1[tt] + else: + return u'::'.join(n.get_id(version) for n in self.names) res = [] # type: List[unicode] if len(self.names) > 1 or len(modifiers) > 0: res.append('N') res.append(modifiers) for n in self.names: - res.append(n.get_id_v2()) + res.append(n.get_id(version)) if len(self.names) > 1 or len(modifiers) > 0: res.append('E') return u''.join(res) @@ -1380,18 +1365,17 @@ class ASTTrailingTypeSpecFundamental(ASTBase): # type: () -> unicode return self.name - def get_id_v1(self): - # type: () -> unicode - res = [] - for a in self.name.split(' '): - if a in _id_fundamental_v1: - res.append(_id_fundamental_v1[a]) - else: - res.append(a) - return u'-'.join(res) + def get_id(self, version): + # type: (int) -> unicode + if version == 1: + res = [] + for a in self.name.split(' '): + if a in _id_fundamental_v1: + res.append(_id_fundamental_v1[a]) + else: + res.append(a) + return u'-'.join(res) - def get_id_v2(self): - # type: () -> unicode if self.name not in _id_fundamental_v2: raise Exception( 'Semi-internal error: Fundamental type "%s" can not be mapped ' @@ -1415,13 +1399,9 @@ class ASTTrailingTypeSpecName(ASTBase): # type: () -> Any return self.nestedName - def get_id_v1(self): - # type: () -> unicode - return self.nestedName.get_id_v1() - - def get_id_v2(self): - # type: () -> unicode - return self.nestedName.get_id_v2() + def get_id(self, version): + # type: (int) -> unicode + return self.nestedName.get_id(version) def __unicode__(self): # type: () -> unicode @@ -1444,10 +1424,9 @@ class ASTTrailingTypeSpecDecltypeAuto(ASTBase): def __unicode__(self): return u'decltype(auto)' - def get_id_v1(self): - raise NoOldIdError() - - def get_id_v2(self): + def get_id(self, version): + if version == 1: + raise NoOldIdError() return 'Dc' def describe_signature(self, signode, mode, env, symbol): @@ -1461,19 +1440,12 @@ class ASTFunctionParameter(ASTBase): self.arg = arg self.ellipsis = ellipsis - def get_id_v1(self): - # type: () -> unicode + def get_id(self, version): + # type: (int) -> unicode if self.ellipsis: return 'z' else: - return self.arg.get_id_v1() - - def get_id_v2(self): - # type: () -> unicode - if self.ellipsis: - return 'z' - else: - return self.arg.get_id_v2() + return self.arg.get_id(version) def __unicode__(self): # type: () -> unicode @@ -1504,49 +1476,33 @@ class ASTParametersQualifiers(ASTBase): self.final = final self.initializer = initializer - # Id v1 ------------------------------------------------------------------ - - def get_modifiers_id_v1(self): - # type: () -> unicode + def get_modifiers_id(self, version): + # type: (int) -> unicode res = [] if self.volatile: res.append('V') if self.const: - res.append('C') + if version == 1: + res.append('C') + else: + res.append('K') if self.refQual == '&&': res.append('O') elif self.refQual == '&': res.append('R') return u''.join(res) - def get_param_id_v1(self): - # type: () -> unicode - if len(self.args) == 0: - return '' - else: - return u'__' + u'.'.join(a.get_id_v1() for a in self.args) - - # Id v2 ------------------------------------------------------------------ - - def get_modifiers_id_v2(self): - # type: () -> unicode - res = [] - if self.volatile: - res.append('V') - if self.const: - res.append('K') - if self.refQual == '&&': - res.append('O') - elif self.refQual == '&': - res.append('R') - return u''.join(res) - - def get_param_id_v2(self): - # type: () -> unicode + def get_param_id(self, version): + # type: (int) -> unicode + if version == 1: + if len(self.args) == 0: + return '' + else: + return u'__' + u'.'.join(a.get_id(version) for a in self.args) if len(self.args) == 0: return 'v' else: - return u''.join(a.get_id_v2() for a in self.args) + return u''.join(a.get_id(version) for a in self.args) def __unicode__(self): # type: () -> unicode @@ -1713,24 +1669,22 @@ class ASTDeclSpecs(ASTBase): # type: () -> unicode return self.trailingTypeSpec.name - def get_id_v1(self): - # type: () -> unicode - res = [] - res.append(self.trailingTypeSpec.get_id_v1()) - if self.allSpecs.volatile: - res.append('V') - if self.allSpecs.const: - res.append('C') - return u''.join(res) - - def get_id_v2(self): - # type: () -> unicode + def get_id(self, version): + # type: (int) -> unicode + if version == 1: + res = [] + res.append(self.trailingTypeSpec.get_id(version)) + if self.allSpecs.volatile: + res.append('V') + if self.allSpecs.const: + res.append('C') + return u''.join(res) res = [] if self.leftSpecs.volatile or self.rightSpecs.volatile: res.append('V') if self.leftSpecs.const or self.rightSpecs.volatile: res.append('K') - res.append(self.trailingTypeSpec.get_id_v2()) + res.append(self.trailingTypeSpec.get_id(version)) return u''.join(res) def __unicode__(self): @@ -1788,14 +1742,14 @@ class ASTArray(ASTBase): # type: () -> unicode return u''.join(['[', text_type(self.size), ']']) - def get_id_v1(self): - # type: () -> unicode - return u'A' - - def get_id_v2(self): - # type: () -> unicode - # TODO: this should maybe be done differently - return u'A' + text_type(self.size) + u'_' + def get_id(self, version): + # type: (int) -> unicode + if version == 1: + return u'A' + if version == 2: + # TODO: this should maybe be done differently + return u'A' + text_type(self.size) + u'_' + assert False def describe_signature(self, signode, mode, env): _verify_description_mode(mode) @@ -1835,38 +1789,25 @@ class ASTDeclaratorPtr(ASTBase): res.append(text_type(self.next)) return u''.join(res) - # Id v1 ------------------------------------------------------------------ + def get_modifiers_id(self, version): + # type: (int) -> unicode + return self.next.get_modifiers_id(version) - def get_modifiers_id_v1(self): - # type: () -> unicode - return self.next.get_modifiers_id_v1() + def get_param_id(self, version): + # type: (int) -> unicode + return self.next.get_param_id(version) - def get_param_id_v1(self): - # type: () -> unicode - return self.next.get_param_id_v1() + def get_ptr_suffix_id(self, version): + # type: (int) -> unicode + if version == 1: + res = 'P' + if self.volatile: + res += 'V' + if self.const: + res += 'C' + return res + self.next.get_ptr_suffix_id(version) - def get_ptr_suffix_id_v1(self): - # type: () -> unicode - res = 'P' - if self.volatile: - res += 'V' - if self.const: - res += 'C' - return res + self.next.get_ptr_suffix_id_v1() - - # Id v2 ------------------------------------------------------------------ - - def get_modifiers_id_v2(self): - # type: () -> unicode - return self.next.get_modifiers_id_v2() - - def get_param_id_v2(self): - # type: () -> unicode - return self.next.get_param_id_v2() - - def get_ptr_suffix_id_v2(self): - # type: () -> unicode - res = [self.next.get_ptr_suffix_id_v2()] # type: List[unicode] + res = [self.next.get_ptr_suffix_id(version)] # type: List[unicode] res.append('P') if self.volatile: res.append('V') @@ -1874,8 +1815,8 @@ class ASTDeclaratorPtr(ASTBase): res.append('C') return u''.join(res) - def get_type_id_v2(self, returnTypeId): - # type: (unicode) -> unicode + def get_type_id(self, version, returnTypeId): + # type: (int, unicode) -> unicode # ReturnType *next, so we are part of the return type of 'next res = ['P'] # type: List[unicode] if self.volatile: @@ -1883,9 +1824,7 @@ class ASTDeclaratorPtr(ASTBase): if self.const: res.append('C') res.append(returnTypeId) - return self.next.get_type_id_v2(returnTypeId=u''.join(res)) - - # ------------------------------------------------------------------------ + return self.next.get_type_id(version, returnTypeId=u''.join(res)) def is_function_type(self): # type: () -> bool @@ -1929,40 +1868,26 @@ class ASTDeclaratorRef(ASTBase): # type: () -> unicode return '&' + text_type(self.next) - # Id v1 ------------------------------------------------------------------ + def get_modifiers_id(self, version): + # type: (int) -> unicode + return self.next.get_modifiers_id(version) - def get_modifiers_id_v1(self): - # type: () -> unicode - return self.next.get_modifiers_id_v1() + def get_param_id(self, version): # only the parameters (if any) + # type: (int) -> unicode + return self.next.get_param_id(version) - def get_param_id_v1(self): # only the parameters (if any) - # type: () -> unicode - return self.next.get_param_id_v1() + def get_ptr_suffix_id(self, version): + # type: (int) -> unicode + if version == 1: + return u'R' + self.next.get_ptr_suffix_id(version) + else: + return self.next.get_ptr_suffix_id(version) + u'R' - def get_ptr_suffix_id_v1(self): - # type: () -> unicode - return u'R' + self.next.get_ptr_suffix_id_v1() - - # Id v2 ------------------------------------------------------------------ - - def get_modifiers_id_v2(self): - # type: () -> unicode - return self.next.get_modifiers_id_v2() - - def get_param_id_v2(self): # only the parameters (if any) - # type: () -> unicode - return self.next.get_param_id_v2() - - def get_ptr_suffix_id_v2(self): - # type: () -> unicode - return self.next.get_ptr_suffix_id_v2() + u'R' - - def get_type_id_v2(self, returnTypeId): - # type: (unicode) -> unicode + def get_type_id(self, version, returnTypeId): + # type: (int, unicode) -> unicode + assert version >= 2 # ReturnType &next, so we are part of the return type of 'next - return self.next.get_type_id_v2(returnTypeId=u'R' + returnTypeId) - - # ------------------------------------------------------------------------ + return self.next.get_type_id(version, returnTypeId=u'R' + returnTypeId) def is_function_type(self): # type: () -> bool @@ -1997,39 +1922,26 @@ class ASTDeclaratorParamPack(ASTBase): res = ' ' + res return '...' + res - # Id v1 ------------------------------------------------------------------ + def get_modifiers_id(self, version): + # type: (int) -> unicode + return self.next.get_modifiers_id(version) - def get_modifiers_id_v1(self): - # type: () -> unicode - return self.next.get_modifiers_id_v1() + def get_param_id(self, version): # only the parameters (if any) + # type: (int) -> unicode + return self.next.get_param_id(version) - def get_param_id_v1(self): # only the parameters (if any) - # type: () -> unicode - return self.next.get_param_id_v1() + def get_ptr_suffix_id(self, version): + # type: (int) -> unicode + if version == 1: + return 'Dp' + self.next.get_ptr_suffix_id(version) + else: + return self.next.get_ptr_suffix_id(version) + u'Dp' - def get_ptr_suffix_id_v1(self): - # type: () -> unicode - return 'Dp' + self.next.get_ptr_suffix_id_v2() - - # Id v2 ------------------------------------------------------------------ - - def get_modifiers_id_v2(self): - # type: () -> unicode - return self.next.get_modifiers_id_v2() - - def get_param_id_v2(self): # only the parameters (if any) - return self.next.get_param_id_v2() - - def get_ptr_suffix_id_v2(self): - # type: () -> unicode - return self.next.get_ptr_suffix_id_v2() + u'Dp' - - def get_type_id_v2(self, returnTypeId): - # type: (unicode) -> unicode + def get_type_id(self, version, returnTypeId): + # type: (int, unicode) -> unicode + assert version >= 2 # ReturnType... next, so we are part of the return type of 'next - return self.next.get_type_id_v2(returnTypeId=u'Dp' + returnTypeId) - - # ------------------------------------------------------------------------ + return self.next.get_type_id(version, returnTypeId=u'Dp' + returnTypeId) def is_function_type(self): # type: () -> bool @@ -2077,37 +1989,31 @@ class ASTDeclaratorMemPtr(ASTBase): res.append(text_type(self.next)) return ''.join(res) - # Id v1 ------------------------------------------------------------------ + def get_modifiers_id(self, version): + # type: (int) -> unicode + if version == 1: + raise NoOldIdError() + else: + return self.next.get_modifiers_id(version) - def get_modifiers_id_v1(self): - # type: () -> unicode - raise NoOldIdError() + def get_param_id(self, version): # only the parameters (if any) + # type: (int) -> unicode + if version == 1: + raise NoOldIdError() + else: + return self.next.get_param_id(version) - def get_param_id_v1(self): # only the parameters (if any) - # type: () -> unicode - raise NoOldIdError() + def get_ptr_suffix_id(self, version): + # type: (int) -> unicode + if version == 1: + raise NoOldIdError() + else: + raise NotImplementedError() + return self.next.get_ptr_suffix_id(version) + u'Dp' - def get_ptr_suffix_id_v1(self): - # type: () -> unicode - raise NoOldIdError() - - # Id v2 ------------------------------------------------------------------ - - def get_modifiers_id_v2(self): - # type: () -> unicode - return self.next.get_modifiers_id_v2() - - def get_param_id_v2(self): # only the parameters (if any) - # type: () -> unicode - return self.next.get_param_id_v2() - - def get_ptr_suffix_id_v2(self): - # type: () -> unicode - raise NotImplementedError() - return self.next.get_ptr_suffix_id_v2() + u'Dp' - - def get_type_id_v2(self, returnTypeId): - # type: (unicode) -> unicode + def get_type_id(self, version, returnTypeId): + # type: (int, unicode) -> unicode + assert version >= 2 # ReturnType name::* next, so we are part of the return type of next nextReturnTypeId = '' # type: unicode if self.volatile: @@ -2115,11 +2021,9 @@ class ASTDeclaratorMemPtr(ASTBase): if self.const: nextReturnTypeId += 'K' nextReturnTypeId += 'M' - nextReturnTypeId += self.className.get_id_v2() + nextReturnTypeId += self.className.get_id(version) nextReturnTypeId += returnTypeId - return self.next.get_type_id_v2(nextReturnTypeId) - - # ------------------------------------------------------------------------ + return self.next.get_type_id(version, nextReturnTypeId) def is_function_type(self): # type: () -> bool @@ -2171,44 +2075,30 @@ class ASTDeclaratorParen(ASTBase): res.append(text_type(self.next)) return ''.join(res) - # Id v1 ------------------------------------------------------------------ + def get_modifiers_id(self, version): + # type: (int) -> unicode + return self.inner.get_modifiers_id(version) - def get_modifiers_id_v1(self): - # type: () -> unicode - return self.inner.get_modifiers_id_v1() + def get_param_id(self, version): # only the parameters (if any) + # type: (int) -> unicode + return self.inner.get_param_id(version) - def get_param_id_v1(self): # only the parameters (if any) - # type: () -> unicode - return self.inner.get_param_id_v1() + def get_ptr_suffix_id(self, version): + # type: (int) -> unicode + if version == 1: + raise NoOldIdError() # TODO: was this implemented before? + return self.next.get_ptr_suffix_id(version) + \ + self.inner.get_ptr_suffix_id(version) + else: + return self.inner.get_ptr_suffix_id(version) + \ + self.next.get_ptr_suffix_id(version) - def get_ptr_suffix_id_v1(self): - # type: () -> unicode - raise NoOldIdError() # TODO: was this implemented before? - return self.next.get_ptr_suffix_id_v2() + \ - self.inner.get_ptr_suffix_id_v2() - - # Id v2 ------------------------------------------------------------------ - - def get_modifiers_id_v2(self): - # type: () -> unicode - return self.inner.get_modifiers_id_v2() - - def get_param_id_v2(self): # only the parameters (if any) - # type: () -> unicode - return self.inner.get_param_id_v2() - - def get_ptr_suffix_id_v2(self): - # type: () -> unicode - return self.inner.get_ptr_suffix_id_v2() + \ - self.next.get_ptr_suffix_id_v2() - - def get_type_id_v2(self, returnTypeId): - # type: (unicode) -> unicode + def get_type_id(self, version, returnTypeId): + # type: (int, unicode) -> unicode + assert version >= 2 # ReturnType (inner)next, so 'inner' returns everything outside - nextId = self.next.get_type_id_v2(returnTypeId) - return self.inner.get_type_id_v2(returnTypeId=nextId) - - # ------------------------------------------------------------------------ + nextId = self.next.get_type_id(version, returnTypeId) + return self.inner.get_type_id(version, returnTypeId=nextId) def is_function_type(self): # type: () -> bool @@ -2235,58 +2125,36 @@ class ASTDeclaratorNameParamQual(ASTBase): # type: () -> unicode return self.declId - # Id v1 ------------------------------------------------------------------ - - def get_modifiers_id_v1(self): # only the modifiers for a function, e.g., - # type: () -> unicode + def get_modifiers_id(self, version): # only the modifiers for a function, e.g., + # type: (int) -> unicode # cv-qualifiers if self.paramQual: - return self.paramQual.get_modifiers_id_v1() + return self.paramQual.get_modifiers_id(version) raise Exception( "This should only be called on a function: %s" % text_type(self)) - def get_param_id_v1(self): # only the parameters (if any) - # type: () -> unicode + def get_param_id(self, version): # only the parameters (if any) + # type: (int) -> unicode if self.paramQual: - return self.paramQual.get_param_id_v1() + return self.paramQual.get_param_id(version) else: return '' - def get_ptr_suffix_id_v1(self): # only the array specifiers - # type: () -> unicode - return u''.join(a.get_id_v1() for a in self.arrayOps) + def get_ptr_suffix_id(self, version): # only the array specifiers + # type: (int) -> unicode + return u''.join(a.get_id(version) for a in self.arrayOps) - # Id v2 ------------------------------------------------------------------ - - def get_modifiers_id_v2(self): # only the modifiers for a function, e.g., - # type: () -> unicode - # cv-qualifiers - if self.paramQual: - return self.paramQual.get_modifiers_id_v2() - raise Exception( - "This should only be called on a function: %s" % text_type(self)) - - def get_param_id_v2(self): # only the parameters (if any) - # type: () -> unicode - if self.paramQual: - return self.paramQual.get_param_id_v2() - else: - return '' - - def get_ptr_suffix_id_v2(self): # only the array specifiers - # type: () -> unicode - return u''.join(a.get_id_v2() for a in self.arrayOps) - - def get_type_id_v2(self, returnTypeId): - # type: (unicode) -> unicode + def get_type_id(self, version, returnTypeId): + # type: (int, unicode) -> unicode + assert version >= 2 res = [] # TOOD: can we actually have both array ops and paramQual? - res.append(self.get_ptr_suffix_id_v2()) + res.append(self.get_ptr_suffix_id(version)) if self.paramQual: - res.append(self.get_modifiers_id_v2()) + res.append(self.get_modifiers_id(version)) res.append('F') res.append(returnTypeId) - res.append(self.get_param_id_v2()) + res.append(self.get_param_id(version)) res.append('E') else: res.append(returnTypeId) @@ -2353,49 +2221,48 @@ class ASTType(ASTBase): name = self.decl.name return name - def get_id_v1(self, objectType=None, symbol=None): - # type: (unicode, Symbol) -> unicode + def get_id(self, version, objectType=None, symbol=None): + # type: (int, unicode, Symbol) -> unicode + if version == 1: + res = [] + if objectType: # needs the name + if objectType == 'function': # also modifiers + res.append(symbol.get_full_nested_name().get_id(version)) + res.append(self.decl.get_param_id(version)) + res.append(self.decl.get_modifiers_id(version)) + if (self.declSpecs.leftSpecs.constexpr or + (self.declSpecs.rightSpecs and + self.declSpecs.rightSpecs.constexpr)): + res.append('CE') + elif objectType == 'type': # just the name + res.append(symbol.get_full_nested_name().get_id(version)) + else: + print(objectType) + assert False + else: # only type encoding + if self.decl.is_function_type(): + raise NoOldIdError() + res.append(self.declSpecs.get_id(version)) + res.append(self.decl.get_ptr_suffix_id(version)) + res.append(self.decl.get_param_id(version)) + return u''.join(res) + # other versions res = [] if objectType: # needs the name if objectType == 'function': # also modifiers - res.append(symbol.get_full_nested_name().get_id_v1()) - res.append(self.decl.get_param_id_v1()) - res.append(self.decl.get_modifiers_id_v1()) - if (self.declSpecs.leftSpecs.constexpr or - (self.declSpecs.rightSpecs and - self.declSpecs.rightSpecs.constexpr)): - res.append('CE') + modifiers = self.decl.get_modifiers_id(version) + res.append(symbol.get_full_nested_name().get_id(version, modifiers)) + res.append(self.decl.get_param_id(version)) elif objectType == 'type': # just the name - res.append(symbol.get_full_nested_name().get_id_v1()) - else: - print(objectType) - assert False - else: # only type encoding - if self.decl.is_function_type(): - raise NoOldIdError() - res.append(self.declSpecs.get_id_v1()) - res.append(self.decl.get_ptr_suffix_id_v1()) - res.append(self.decl.get_param_id_v1()) - return u''.join(res) - - def get_id_v2(self, objectType=None, symbol=None): - # type: (unicode, Symbol) -> unicode - res = [] - if objectType: # needs the name - if objectType == 'function': # also modifiers - modifiers = self.decl.get_modifiers_id_v2() - res.append(symbol.get_full_nested_name().get_id_v2(modifiers)) - res.append(self.decl.get_param_id_v2()) - elif objectType == 'type': # just the name - res.append(symbol.get_full_nested_name().get_id_v2()) + res.append(symbol.get_full_nested_name().get_id(version)) else: print(objectType) assert False else: # only type encoding # the 'returnType' of a non-function type is simply just the last # type, i.e., for 'int*' it is 'int' - returnTypeId = self.declSpecs.get_id_v2() - typeId = self.decl.get_type_id_v2(returnTypeId) + returnTypeId = self.declSpecs.get_id(version) + typeId = self.decl.get_type_id(version, returnTypeId) res.append(typeId) return u''.join(res) @@ -2437,20 +2304,14 @@ class ASTTypeWithInit(ASTBase): # type: () -> unicode return self.type.name - def get_id_v1(self, objectType=None, symbol=None): - # type: (unicode, Symbol) -> unicode - if objectType == 'member': - return symbol.get_full_nested_name().get_id_v1() + u'__' \ - + self.type.get_id_v1() - else: - return self.type.get_id_v1(objectType) - - def get_id_v2(self, objectType=None, symbol=None): - # type: (unicode, Symbol) -> unicode - if objectType == 'member': - return symbol.get_full_nested_name().get_id_v2() - else: - return self.type.get_id_v2() + def get_id(self, version, objectType=None, symbol=None): + # type: (int, unicode, Symbol) -> unicode + if objectType != 'member': + return self.type.get_id(version, objectType) + if version == 1: + return symbol.get_full_nested_name().get_id(version) + u'__' \ + + self.type.get_id(version) + return symbol.get_full_nested_name().get_id(version) def __unicode__(self): # type: () -> unicode @@ -2474,13 +2335,11 @@ class ASTTypeUsing(ASTBase): self.name = name self.type = type - def get_id_v1(self, objectType=None, symbol=None): - # type: (unicode, Symbol) -> unicode - raise NoOldIdError() - - def get_id_v2(self, objectType=None, symbol=None): - # type: (unicode, Symbol) -> unicode - return symbol.get_full_nested_name().get_id_v2() + def get_id(self, version, objectType=None, symbol=None): + # type: (int, unicode, Symbol) -> unicode + if version == 1: + raise NoOldIdError() + return symbol.get_full_nested_name().get_id(version) def __unicode__(self): # type: () -> unicode @@ -2516,13 +2375,11 @@ class ASTConcept(ASTBase): # type: () -> unicode return self.nestedName - def get_id_v1(self, objectType=None, symbol=None): - # type: (unicode, Symbol) -> unicode - raise NoOldIdError() - - def get_id_v2(self, objectType, symbol): # type: ignore - # type: (unicode, Symbol) -> unicode - return symbol.get_full_nested_name().get_id_v2() + def get_id(self, version, objectType=None, symbol=None): + # type: (int, unicode, Symbol) -> unicode + if version == 1: + raise NoOldIdError() + return symbol.get_full_nested_name().get_id(version) def __unicode__(self): # type: () -> unicode @@ -2586,13 +2443,9 @@ class ASTClass(ASTBase): self.final = final self.bases = bases - def get_id_v1(self, objectType, symbol): # type: ignore - # type: (unicode, Symbol) -> unicode - return symbol.get_full_nested_name().get_id_v1() - - def get_id_v2(self, objectType, symbol): # type: ignore - # type: (unicode, Symbol) -> unicode - return symbol.get_full_nested_name().get_id_v2() + def get_id(self, version, objectType, symbol): + # type: (int, unicode, Symbol) -> unicode + return symbol.get_full_nested_name().get_id(version) def __unicode__(self): # type: () -> unicode @@ -2632,13 +2485,11 @@ class ASTEnum(ASTBase): self.scoped = scoped self.underlyingType = underlyingType - def get_id_v1(self, objectType, symbol): # type: ignore - # type: (unicode, Symbol) -> unicode - raise NoOldIdError() - - def get_id_v2(self, objectType, symbol): # type: ignore - # type: (unicode, Symbol) -> unicode - return symbol.get_full_nested_name().get_id_v2() + def get_id(self, version, objectType, symbol): + # type: (int, unicode, Symbol) -> unicode + if version == 1: + raise NoOldIdError() + return symbol.get_full_nested_name().get_id(version) def __unicode__(self): # type: () -> unicode @@ -2669,13 +2520,11 @@ class ASTEnumerator(ASTBase): self.name = name self.init = init - def get_id_v1(self, objectType, symbol): # type: ignore - # type: (unicode, Symbol) -> unicode - raise NoOldIdError() - - def get_id_v2(self, objectType, symbol): # type: ignore - # type: (unicode, Symbol) -> unicode - return symbol.get_full_nested_name().get_id_v2() + def get_id(self, version, objectType, symbol): + # type: (int, unicode, Symbol) -> unicode + if version == 1: + raise NoOldIdError() + return symbol.get_full_nested_name().get_id(version) def __unicode__(self): # type: () -> unicode @@ -2720,30 +2569,29 @@ class ASTDeclaration(ASTBase): # type: () -> unicode return self.declaration.name - def get_id_v1(self): - # type: () -> unicode - if self.templatePrefix: - raise NoOldIdError() + def get_id(self, version, prefixed=True): + # type: (int) -> unicode + if version == 1: + if self.templatePrefix: + raise NoOldIdError() + if self.objectType == 'enumerator' and self.enumeratorScopedSymbol: + return self.enumeratorScopedSymbol.declaration.get_id(version) + return self.declaration.get_id(version, self.objectType, self.symbol) + # version >= 2 if self.objectType == 'enumerator' and self.enumeratorScopedSymbol: - return self.enumeratorScopedSymbol.declaration.get_id_v1() - return self.declaration.get_id_v1(self.objectType, self.symbol) - - def get_id_v2(self, prefixed=True): - # type: (bool) -> unicode - if self.objectType == 'enumerator' and self.enumeratorScopedSymbol: - return self.enumeratorScopedSymbol.declaration.get_id_v2(prefixed) + return self.enumeratorScopedSymbol.declaration.get_id(version, prefixed) if prefixed: - res = [_id_prefix_v2] + res = [_id_prefix[version]] else: res = [] if self.templatePrefix: - res.append(self.templatePrefix.get_id_v2()) - res.append(self.declaration.get_id_v2(self.objectType, self.symbol)) + res.append(self.templatePrefix.get_id(version)) + res.append(self.declaration.get_id(version, self.objectType, self.symbol)) return u''.join(res) def get_newest_id(self): # type: () -> unicode - return self.get_id_v2() + return self.get_id(_max_id, True) def __unicode__(self): # type: () -> unicode @@ -4520,14 +4368,15 @@ class CPPObject(ObjectDescription): def add_target_and_index(self, ast, sig, signode): # type: (Any, unicode, addnodes.desc_signature) -> None # general note: name must be lstrip(':')'ed, to remove "::" - try: - id_v1 = ast.get_id_v1() - except NoOldIdError: - id_v1 = None - id_v2 = ast.get_id_v2() - # store them in reverse order, so the newest is first - ids = [id_v2, id_v1] - + ids = [] + for i in range(1, _max_id + 1): + try: + id = ast.get_id(version=i) + ids.append(id) + except NoOldIdError: + assert i < _max_id + # let's keep the newest first + ids = list(reversed(ids)) newestId = ids[0] assert newestId # shouldn't be None if not re.compile(r'^[a-zA-Z0-9_]*$').match(newestId): diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py index 446c264fd..e7f12f09c 100644 --- a/tests/test_domain_cpp.py +++ b/tests/test_domain_cpp.py @@ -16,11 +16,9 @@ import pytest from sphinx import addnodes from sphinx.domains.cpp import DefinitionParser, DefinitionError, NoOldIdError -from sphinx.domains.cpp import Symbol +from sphinx.domains.cpp import Symbol, _max_id, _id_prefix import sphinx.domains.cpp as cppDomain -ids = [] - def parse(name, string): class Config(object): @@ -39,7 +37,7 @@ def parse(name, string): return ast -def check(name, input, idv1output=None, idv2output=None, output=None): +def check(name, input, idDict, output=None): # first a simple check of the AST if output is None: output = input @@ -58,28 +56,35 @@ def check(name, input, idv1output=None, idv2output=None, output=None): parentNode += signode ast.describe_signature(signode, 'lastIsName', symbol, options={}) - if idv2output: - idv2output = "_CPPv2" + idv2output - try: - idv1 = ast.get_id_v1() - assert idv1 is not None - except NoOldIdError: - idv1 = None - try: - idv2 = ast.get_id_v2() - assert idv2 is not None - except NoOldIdError: - idv2 = None - if idv1 != idv1output or idv2 != idv2output: + idExpected = [None] + for i in range(1, _max_id + 1): + if i in idDict: + idExpected.append(idDict[i]) + else: + idExpected.append(idExpected[i - 1]) + idActual = [None] + for i in range(1, _max_id + 1): + try: + id = ast.get_id(version=i) + assert id is not None + idActual.append(id[len(_id_prefix[i]):]) + except NoOldIdError: + idActual.append(None) + + res = [True] + for i in range(1, _max_id + 1): + res.append(idExpected[i] == idActual[i]) + + if not all(res): print("input: %s" % text_type(input).rjust(20)) - print(" %s %s" % ("Id v1".rjust(20), "Id v2".rjust(20))) - print("result: %s %s" % (str(idv1).rjust(20), str(idv2).rjust(20))) - print("expected: %s %s" % (str(idv1output).rjust(20), - str(idv2output).rjust(20))) + for i in range(1, _max_id + 1): + if res[i]: + continue + print("Error in id version %d." % i) + print("result: %s" % str(idActual[i])) + print("expected: %s" % str(idExpected[i])) print(rootSymbol.dump(0)) raise DefinitionError("") - ids.append(ast.get_id_v2()) - # print ".. %s:: %s" % (name, input) def test_fundamental_types(): @@ -99,55 +104,55 @@ def test_fundamental_types(): if t == "std::nullptr_t": id = "NSt9nullptr_tE" return "1f%s" % id - check("function", "void f(%s arg)" % t, makeIdV1(), makeIdV2()) + check("function", "void f(%s arg)" % t, {1: makeIdV1(), 2:makeIdV2()}) def test_type_definitions(): - check("type", "public bool b", "b", "1b", "bool b") - check("type", "bool A::b", "A::b", "N1A1bE") - check("type", "bool *b", "b", "1b") - check("type", "bool *const b", "b", "1b") - check("type", "bool *volatile const b", "b", "1b") - check("type", "bool *volatile const b", "b", "1b") - check("type", "bool *volatile const *b", "b", "1b") - check("type", "bool &b", "b", "1b") - check("type", "bool b[]", "b", "1b") - check("type", "std::pair coord", "coord", "5coord") - check("type", "long long int foo", "foo", "3foo") + check("type", "public bool b", {1:"b", 2:"1b"}, "bool b") + check("type", "bool A::b", {1:"A::b", 2:"N1A1bE"}) + check("type", "bool *b", {1:"b", 2:"1b"}) + check("type", "bool *const b", {1:"b", 2:"1b"}) + check("type", "bool *volatile const b", {1:"b", 2:"1b"}) + check("type", "bool *volatile const b", {1:"b", 2:"1b"}) + check("type", "bool *volatile const *b", {1:"b", 2:"1b"}) + check("type", "bool &b", {1:"b", 2:"1b"}) + check("type", "bool b[]", {1:"b", 2:"1b"}) + check("type", "std::pair coord", {1:"coord", 2:"5coord"}) + check("type", "long long int foo", {1:"foo", 2:"3foo"}) check("type", 'std::vector> module::blah', - "module::blah", "N6module4blahE") - check("type", "std::function F", "F", "1F") - check("type", "std::function F", "F", "1F") - check("type", "std::function F", "F", "1F") - check("type", "std::function F", "F", "1F") + {1:"module::blah", 2:"N6module4blahE"}) + check("type", "std::function F", {1:"F", 2:"1F"}) + check("type", "std::function F", {1:"F", 2:"1F"}) + check("type", "std::function F", {1:"F", 2:"1F"}) + check("type", "std::function F", {1:"F", 2:"1F"}) check("type", "MyContainer::const_iterator", - "MyContainer::const_iterator", "N11MyContainer14const_iteratorE") + {1:"MyContainer::const_iterator", 2:"N11MyContainer14const_iteratorE"}) check("type", "public MyContainer::const_iterator", - "MyContainer::const_iterator", "N11MyContainer14const_iteratorE", + {1:"MyContainer::const_iterator", 2:"N11MyContainer14const_iteratorE"}, output="MyContainer::const_iterator") # test decl specs on right - check("type", "bool const b", "b", "1b") + check("type", "bool const b", {1:"b", 2:"1b"}) # test name in global scope - check("type", "bool ::B::b", "B::b", "N1B1bE") + check("type", "bool ::B::b", {1:"B::b", 2:"N1B1bE"}) - check('type', 'A = B', None, '1A') + check('type', 'A = B', {2:'1A'}) # from breathe#267 (named function parameters for function pointers check('type', 'void (*gpio_callback_t)(struct device *port, uint32_t pin)', - 'gpio_callback_t', '15gpio_callback_t') - check('type', 'void (*f)(std::function g)', 'f', '1f') + {1:'gpio_callback_t', 2:'15gpio_callback_t'}) + check('type', 'void (*f)(std::function g)', {1:'f', 2:'1f'}) def test_concept_definitions(): check('concept', 'template A::B::Concept', - None, 'I0EN1A1B7ConceptE') + {2:'I0EN1A1B7ConceptE'}) check('concept', 'template Foo', - None, 'I00DpE3Foo') + {2:'I00DpE3Foo'}) check('concept', 'template A::B::Concept()', - None, 'I0EN1A1B7ConceptE') + {2:'I0EN1A1B7ConceptE'}) check('concept', 'template Foo()', - None, 'I00DpE3Foo') + {2:'I00DpE3Foo'}) with pytest.raises(DefinitionError): parse('concept', 'Foo') with pytest.raises(DefinitionError): @@ -156,256 +161,254 @@ def test_concept_definitions(): def test_member_definitions(): check('member', ' const std::string & name = 42', - "name__ssCR", "4name", output='const std::string &name = 42') - check('member', ' const std::string & name', "name__ssCR", "4name", + {1:"name__ssCR", 2:"4name"}, output='const std::string &name = 42') + check('member', ' const std::string & name', {1:"name__ssCR", 2:"4name"}, output='const std::string &name') check('member', ' const std::string & name [ n ]', - "name__ssCRA", "4name", output='const std::string &name[n]') + {1:"name__ssCRA", 2:"4name"}, output='const std::string &name[n]') check('member', 'const std::vector< unsigned int, long> &name', - "name__std::vector:unsigned-i.l:CR", - "4name", output='const std::vector &name') - check('member', 'module::myclass foo[n]', "foo__module::myclassA", "3foo") - check('member', 'int *const p', 'p__iPC', '1p') - check('member', 'extern int myInt', 'myInt__i', '5myInt') - check('member', 'thread_local int myInt', 'myInt__i', '5myInt') - check('member', 'extern thread_local int myInt', 'myInt__i', '5myInt') - check('member', 'thread_local extern int myInt', 'myInt__i', '5myInt', + {1:"name__std::vector:unsigned-i.l:CR", 2:"4name"}, + output='const std::vector &name') + check('member', 'module::myclass foo[n]', {1:"foo__module::myclassA", 2:"3foo"}) + check('member', 'int *const p', {1:'p__iPC', 2:'1p'}) + check('member', 'extern int myInt', {1:'myInt__i', 2:'5myInt'}) + check('member', 'thread_local int myInt', {1:'myInt__i', 2:'5myInt'}) + check('member', 'extern thread_local int myInt', {1:'myInt__i', 2:'5myInt'}) + check('member', 'thread_local extern int myInt', {1:'myInt__i', 2:'5myInt'}, 'extern thread_local int myInt') def test_function_definitions(): - check('function', 'operator bool() const', "castto-b-operatorC", "NKcvbEv") + check('function', 'operator bool() const', {1:"castto-b-operatorC", 2:"NKcvbEv"}) check('function', 'A::operator bool() const', - "A::castto-b-operatorC", "NK1AcvbEv") + {1:"A::castto-b-operatorC", 2:"NK1AcvbEv"}) check('function', 'A::operator bool() volatile const &', - "A::castto-b-operatorVCR", "NVKR1AcvbEv") + {1:"A::castto-b-operatorVCR", 2:"NVKR1AcvbEv"}) check('function', 'A::operator bool() volatile const &&', - "A::castto-b-operatorVCO", "NVKO1AcvbEv") + {1:"A::castto-b-operatorVCO", 2:"NVKO1AcvbEv"}) check('function', 'bool namespaced::theclass::method(arg1, arg2)', - "namespaced::theclass::method__arg1.arg2", - "N10namespaced8theclass6methodE4arg14arg2") + {1:"namespaced::theclass::method__arg1.arg2", + 2:"N10namespaced8theclass6methodE4arg14arg2"}) x = 'std::vector> &module::test(register int ' \ 'foo, bar, std::string baz = "foobar, blah, bleh") const = 0' - check('function', x, "module::test__i.bar.ssC", - "NK6module4testEi3barNSt6stringE") + check('function', x, {1:"module::test__i.bar.ssC", + 2:"NK6module4testEi3barNSt6stringE"}) check('function', 'void f(std::pair)', - "f__std::pair:A.B:", "1fNSt4pairI1A1BEE") + {1:"f__std::pair:A.B:", 2:"1fNSt4pairI1A1BEE"}) check('function', 'explicit module::myclass::foo::foo()', - "module::myclass::foo::foo", "N6module7myclass3foo3fooEv") + {1:"module::myclass::foo::foo", 2:"N6module7myclass3foo3fooEv"}) check('function', 'module::myclass::foo::~foo()', - "module::myclass::foo::~foo", "N6module7myclass3fooD0Ev") + {1:"module::myclass::foo::~foo", 2:"N6module7myclass3fooD0Ev"}) check('function', 'int printf(const char *fmt, ...)', - "printf__cCP.z", "6printfPKcz") + {1:"printf__cCP.z", 2:"6printfPKcz"}) check('function', 'int foo(const unsigned int j)', - "foo__unsigned-iC", "3fooKj") + {1:"foo__unsigned-iC", 2:"3fooKj"}) check('function', 'int foo(const int *const ptr)', - "foo__iCPC", "3fooPCKi") + {1:"foo__iCPC", 2:"3fooPCKi"}) check('function', 'module::myclass::operator std::vector()', - "module::myclass::castto-std::vector:ss:-operator", - "N6module7myclasscvNSt6vectorINSt6stringEEEEv") + {1:"module::myclass::castto-std::vector:ss:-operator", + 2:"N6module7myclasscvNSt6vectorINSt6stringEEEEv"}) check('function', 'void operator()(const boost::array &v) const', - "call-operator__boost::array:VertexID.2:CRC", - "NKclERKN5boost5arrayI8VertexIDX2EEE") + {1:"call-operator__boost::array:VertexID.2:CRC", + 2:"NKclERKN5boost5arrayI8VertexIDX2EEE"}) check('function', 'void operator()(const boost::array &v) const', - 'call-operator__boost::array:VertexID.2."foo,--bar":CRC', - 'NKclERKN5boost5arrayI8VertexIDX2EX"foo, bar"EEE') + {1:'call-operator__boost::array:VertexID.2."foo,--bar":CRC', + 2:'NKclERKN5boost5arrayI8VertexIDX2EX"foo, bar"EEE'}) check('function', 'MyClass::MyClass(MyClass::MyClass&&)', - "MyClass::MyClass__MyClass::MyClassRR", - "N7MyClass7MyClassERRN7MyClass7MyClassE") - check('function', 'constexpr int get_value()', "get_valueCE", "9get_valuev") + {1:"MyClass::MyClass__MyClass::MyClassRR", + 2:"N7MyClass7MyClassERRN7MyClass7MyClassE"}) + check('function', 'constexpr int get_value()', {1:"get_valueCE", 2:"9get_valuev"}) check('function', 'static constexpr int get_value()', - "get_valueCE", "9get_valuev") + {1:"get_valueCE", 2:"9get_valuev"}) check('function', 'int get_value() const noexcept', - "get_valueC", "NK9get_valueEv") + {1:"get_valueC", 2:"NK9get_valueEv"}) check('function', 'int get_value() const noexcept = delete', - "get_valueC", "NK9get_valueEv") + {1:"get_valueC", 2:"NK9get_valueEv"}) check('function', 'int get_value() volatile const', - "get_valueVC", "NVK9get_valueEv") + {1:"get_valueVC", 2:"NVK9get_valueEv"}) check('function', 'MyClass::MyClass(MyClass::MyClass&&) = default', - "MyClass::MyClass__MyClass::MyClassRR", - "N7MyClass7MyClassERRN7MyClass7MyClassE") + {1:"MyClass::MyClass__MyClass::MyClassRR", + 2:"N7MyClass7MyClassERRN7MyClass7MyClassE"}) check('function', 'virtual MyClass::a_virtual_function() const override', - "MyClass::a_virtual_functionC", "NK7MyClass18a_virtual_functionEv") - check('function', 'A B() override', "B", "1Bv") - check('function', 'A B() final', "B", "1Bv") - check('function', 'A B() final override', "B", "1Bv") - check('function', 'A B() override final', "B", "1Bv", + {1:"MyClass::a_virtual_functionC", 2:"NK7MyClass18a_virtual_functionEv"}) + check('function', 'A B() override', {1:"B", 2:"1Bv"}) + check('function', 'A B() final', {1:"B", 2:"1Bv"}) + check('function', 'A B() final override', {1:"B", 2:"1Bv"}) + check('function', 'A B() override final', {1:"B", 2:"1Bv"}, output='A B() final override') check('function', 'MyClass::a_member_function() volatile', - "MyClass::a_member_functionV", "NV7MyClass17a_member_functionEv") + {1:"MyClass::a_member_functionV", 2:"NV7MyClass17a_member_functionEv"}) check('function', 'MyClass::a_member_function() volatile const', - "MyClass::a_member_functionVC", "NVK7MyClass17a_member_functionEv") + {1:"MyClass::a_member_functionVC", 2:"NVK7MyClass17a_member_functionEv"}) check('function', 'MyClass::a_member_function() &&', - "MyClass::a_member_functionO", "NO7MyClass17a_member_functionEv") + {1:"MyClass::a_member_functionO", 2:"NO7MyClass17a_member_functionEv"}) check('function', 'MyClass::a_member_function() &', - "MyClass::a_member_functionR", "NR7MyClass17a_member_functionEv") + {1:"MyClass::a_member_functionR", 2:"NR7MyClass17a_member_functionEv"}) check('function', 'MyClass::a_member_function() const &', - "MyClass::a_member_functionCR", "NKR7MyClass17a_member_functionEv") + {1:"MyClass::a_member_functionCR", 2:"NKR7MyClass17a_member_functionEv"}) check('function', 'int main(int argc, char *argv[])', - "main__i.cPA", "4mainiA_Pc") + {1:"main__i.cPA", 2:"4mainiA_Pc"}) check('function', 'MyClass &MyClass::operator++()', - "MyClass::inc-operator", "N7MyClassppEv") + {1:"MyClass::inc-operator", 2:"N7MyClassppEv"}) check('function', 'MyClass::pointer MyClass::operator->()', - "MyClass::pointer-operator", "N7MyClassptEv") + {1:"MyClass::pointer-operator", 2:"N7MyClassptEv"}) x = 'std::vector> &module::test(register int ' \ 'foo, bar[n], std::string baz = "foobar, blah, bleh") const = 0' - check('function', x, "module::test__i.barA.ssC", - "NK6module4testEiAn_3barNSt6stringE") + check('function', x, {1:"module::test__i.barA.ssC", + 2:"NK6module4testEiAn_3barNSt6stringE"}) check('function', 'int foo(Foo f = Foo(double(), std::make_pair(int(2), double(3.4))))', - "foo__Foo", "3foo3Foo") - check('function', 'int foo(A a = x(a))', "foo__A", "3foo1A") + {1:"foo__Foo", 2:"3foo3Foo"}) + check('function', 'int foo(A a = x(a))', {1:"foo__A", 2:"3foo1A"}) with pytest.raises(DefinitionError): parse('function', 'int foo(B b=x(a)') with pytest.raises(DefinitionError): parse('function', 'int foo)C c=x(a))') with pytest.raises(DefinitionError): parse('function', 'int foo(D d=x(a') - check('function', 'int foo(const A&... a)', "foo__ACRDp", "3fooDpRK1A") - check('function', 'virtual void f()', "f", "1fv") + check('function', 'int foo(const A&... a)', {1:"foo__ACRDp", 2:"3fooDpRK1A"}) + check('function', 'virtual void f()', {1:"f", 2:"1fv"}) # test for ::nestedName, from issue 1738 check("function", "result(int val, ::std::error_category const &cat)", - "result__i.std::error_categoryCR", "6resultiRNSt14error_categoryE") - check("function", "int *f()", "f", "1fv") + {1:"result__i.std::error_categoryCR", 2:"6resultiRNSt14error_categoryE"}) + check("function", "int *f()", {1:"f", 2:"1fv"}) # tests derived from issue #1753 (skip to keep sanity) - check("function", "f(int (&array)[10])", None, "1fRA10_i") - check("function", "void f(int (&array)[10])", None, "1fRA10_i") - check("function", "void f(float *q(double))", None, "1fFPfdE") - check("function", "void f(float *(*q)(double))", None, "1fPFPfdE") - check("function", "void f(float (*q)(double))", None, "1fPFfdE") - check("function", "int (*f(double d))(float)", "f__double", "1fd") - check("function", "int (*f(bool b))[5]", "f__b", "1fb") + check("function", "f(int (&array)[10])", {2:"1fRA10_i"}) + check("function", "void f(int (&array)[10])", {2:"1fRA10_i"}) + check("function", "void f(float *q(double))", {2:"1fFPfdE"}) + check("function", "void f(float *(*q)(double))", {2:"1fPFPfdE"}) + check("function", "void f(float (*q)(double))", {2:"1fPFfdE"}) + check("function", "int (*f(double d))(float)", {1:"f__double", 2:"1fd"}) + check("function", "int (*f(bool b))[5]", {1:"f__b", 2:"1fb"}) check("function", "int (*A::f(double d) const)(float)", - "A::f__doubleC", "NK1A1fEd") + {1:"A::f__doubleC", 2:"NK1A1fEd"}) check("function", "void f(std::shared_ptr ptr)", - None, "1fNSt10shared_ptrIFidEEE") - check("function", "void f(int *const p)", "f__iPC", "1fPCi") - check("function", "void f(int *volatile const p)", "f__iPVC", "1fPVCi") + {2:"1fNSt10shared_ptrIFidEEE"}) + check("function", "void f(int *const p)", {1:"f__iPC", 2:"1fPCi"}) + check("function", "void f(int *volatile const p)", {1:"f__iPVC", 2:"1fPVCi"}) - check('function', 'extern int f()', 'f', '1fv') + check('function', 'extern int f()', {1:'f', 2:'1fv'}) # TODO: make tests for functions in a template, e.g., Test # such that the id generation for function type types is correct. check('function', 'friend std::ostream &f(std::ostream&, int)', - 'f__osR.i', '1fRNSt7ostreamEi') + {1:'f__osR.i', 2:'1fRNSt7ostreamEi'}) # from breathe#223 - check('function', 'void f(struct E e)', 'f__E', '1f1E') - check('function', 'void f(class E e)', 'f__E', '1f1E') - check('function', 'void f(typename E e)', 'f__E', '1f1E') - check('function', 'void f(enum E e)', 'f__E', '1f1E') - check('function', 'void f(union E e)', 'f__E', '1f1E') + check('function', 'void f(struct E e)', {1:'f__E', 2:'1f1E'}) + check('function', 'void f(class E e)', {1:'f__E', 2:'1f1E'}) + check('function', 'void f(typename E e)', {1:'f__E', 2:'1f1E'}) + check('function', 'void f(enum E e)', {1:'f__E', 2:'1f1E'}) + check('function', 'void f(union E e)', {1:'f__E', 2:'1f1E'}) # pointer to member (function) - check('function', 'void f(int C::*)', None, '1fM1Ci') - check('function', 'void f(int C::* p)', None, '1fM1Ci') - check('function', 'void f(int ::C::* p)', None, '1fM1Ci') - check('function', 'void f(int C::* const)', None, '1fKM1Ci') - check('function', 'void f(int C::* const&)', None, '1fRKM1Ci') - check('function', 'void f(int C::* volatile)', None, '1fVM1Ci') - check('function', 'void f(int C::* const volatile)', None, '1fVKM1Ci', + check('function', 'void f(int C::*)', {2:'1fM1Ci'}) + check('function', 'void f(int C::* p)', {2:'1fM1Ci'}) + check('function', 'void f(int ::C::* p)', {2:'1fM1Ci'}) + check('function', 'void f(int C::* const)', {2:'1fKM1Ci'}) + check('function', 'void f(int C::* const&)', {2:'1fRKM1Ci'}) + check('function', 'void f(int C::* volatile)', {2:'1fVM1Ci'}) + check('function', 'void f(int C::* const volatile)', {2:'1fVKM1Ci'}, output='void f(int C::* volatile const)') - check('function', 'void f(int C::* volatile const)', None, '1fVKM1Ci') - check('function', 'void f(int (C::*)(float, double))', None, '1fM1CFifdE') - check('function', 'void f(int (C::* p)(float, double))', None, '1fM1CFifdE') - check('function', 'void f(int (::C::* p)(float, double))', None, '1fM1CFifdE') - check('function', 'void f(void (C::*)() const &)', None, '1fM1CKRFvvE') - check('function', 'int C::* f(int, double)', None, '1fid') - check('function', 'void f(int C::* *)', None, '1fPM1Ci') + check('function', 'void f(int C::* volatile const)', {2:'1fVKM1Ci'}) + check('function', 'void f(int (C::*)(float, double))', {2:'1fM1CFifdE'}) + check('function', 'void f(int (C::* p)(float, double))', {2:'1fM1CFifdE'}) + check('function', 'void f(int (::C::* p)(float, double))', {2:'1fM1CFifdE'}) + check('function', 'void f(void (C::*)() const &)', {2:'1fM1CKRFvvE'}) + check('function', 'int C::* f(int, double)', {2:'1fid'}) + check('function', 'void f(int C::* *)', {2:'1fPM1Ci'}) def test_operators(): check('function', 'void operator new [ ] ()', - "new-array-operator", "nav", output='void operator new[]()') + {1:"new-array-operator", 2:"nav"}, output='void operator new[]()') check('function', 'void operator delete ()', - "delete-operator", "dlv", output='void operator delete()') + {1:"delete-operator", 2:"dlv"}, output='void operator delete()') check('function', 'operator bool() const', - "castto-b-operatorC", "NKcvbEv", output='operator bool() const') + {1:"castto-b-operatorC", 2:"NKcvbEv"}, output='operator bool() const') check('function', 'void operator * ()', - "mul-operator", "mlv", output='void operator*()') + {1:"mul-operator", 2:"mlv"}, output='void operator*()') check('function', 'void operator - ()', - "sub-operator", "miv", output='void operator-()') + {1:"sub-operator", 2:"miv"}, output='void operator-()') check('function', 'void operator + ()', - "add-operator", "plv", output='void operator+()') + {1:"add-operator", 2:"plv"}, output='void operator+()') check('function', 'void operator = ()', - "assign-operator", "aSv", output='void operator=()') + {1:"assign-operator", 2:"aSv"}, output='void operator=()') check('function', 'void operator / ()', - "div-operator", "dvv", output='void operator/()') + {1:"div-operator", 2:"dvv"}, output='void operator/()') check('function', 'void operator % ()', - "mod-operator", "rmv", output='void operator%()') + {1:"mod-operator", 2:"rmv"}, output='void operator%()') check('function', 'void operator ! ()', - "not-operator", "ntv", output='void operator!()') + {1:"not-operator", 2:"ntv"}, output='void operator!()') check('function', 'void operator "" _udl()', - None, 'li4_udlv', output='void operator""_udl()') + {2:'li4_udlv'}, output='void operator""_udl()') def test_class_definitions(): - check('class', 'public A', "A", "1A", output='A') - check('class', 'private A', "A", "1A") - check('class', 'A final', 'A', '1A') + check('class', 'public A', {1:"A", 2:"1A"}, output='A') + check('class', 'private A', {1:"A", 2:"1A"}) + check('class', 'A final', {1:'A', 2:'1A'}) # test bases - check('class', 'A', "A", "1A") - check('class', 'A::B::C', "A::B::C", "N1A1B1CE") - check('class', 'A : B', "A", "1A") - check('class', 'A : private B', "A", "1A", output='A : B') - check('class', 'A : public B', "A", "1A") - check('class', 'A : B, C', "A", "1A") - check('class', 'A : B, protected C, D', "A", "1A") - check('class', 'A : virtual private B', 'A', '1A', output='A : virtual B') - check('class', 'A : B, virtual C', 'A', '1A') - check('class', 'A : public virtual B', 'A', '1A') - check('class', 'A : B, C...', 'A', '1A') - check('class', 'A : B..., C', 'A', '1A') + check('class', 'A', {1:"A", 2:"1A"}) + check('class', 'A::B::C', {1:"A::B::C", 2:"N1A1B1CE"}) + check('class', 'A : B', {1:"A", 2:"1A"}) + check('class', 'A : private B', {1:"A", 2:"1A"}, output='A : B') + check('class', 'A : public B', {1:"A", 2:"1A"}) + check('class', 'A : B, C', {1:"A", 2:"1A"}) + check('class', 'A : B, protected C, D', {1:"A", 2:"1A"}) + check('class', 'A : virtual private B', {1:'A', 2:'1A'}, output='A : virtual B') + check('class', 'A : B, virtual C', {1:'A', 2:'1A'}) + check('class', 'A : public virtual B', {1:'A', 2:'1A'}) + check('class', 'A : B, C...', {1:'A', 2:'1A'}) + check('class', 'A : B..., C', {1:'A', 2:'1A'}) def test_enum_definitions(): - check('enum', 'A', None, "1A") - check('enum', 'A : std::underlying_type::type', None, "1A") - check('enum', 'A : unsigned int', None, "1A") - check('enum', 'public A', None, "1A", output='A') - check('enum', 'private A', None, "1A") + check('enum', 'A', {2:"1A"}) + check('enum', 'A : std::underlying_type::type', {2:"1A"}) + check('enum', 'A : unsigned int', {2:"1A"}) + check('enum', 'public A', {2:"1A"}, output='A') + check('enum', 'private A', {2:"1A"}) - check('enumerator', 'A', None, "1A") - check('enumerator', 'A = std::numeric_limits::max()', - None, "1A") + check('enumerator', 'A', {2:"1A"}) + check('enumerator', 'A = std::numeric_limits::max()', {2:"1A"}) def test_templates(): - check('class', "A", None, "IE1AI1TE", output="template<> A") + check('class', "A", {2:"IE1AI1TE"}, output="template<> A") # first just check which objects support templating - check('class', "template<> A", None, "IE1A") - check('function', "template<> void A()", None, "IE1Av") - check('member', "template<> A a", None, "IE1a") - check('type', "template<> a = A", None, "IE1a") + check('class', "template<> A", {2:"IE1A"}) + check('function', "template<> void A()", {2:"IE1Av"}) + check('member', "template<> A a", {2:"IE1a"}) + check('type', "template<> a = A", {2:"IE1a"}) with pytest.raises(DefinitionError): parse('enum', "template<> A") with pytest.raises(DefinitionError): parse('enumerator', "template<> A") # then all the real tests - check('class', "template A", None, "I00E1A") - check('type', "template<> a", None, "IE1a") + check('class', "template A", {2:"I00E1A"}) + check('type', "template<> a", {2:"IE1a"}) - check('class', "template A", None, "I0E1A") - check('class', "template A", None, "I0E1A") - check('class', "template A", None, "IDpE1A") - check('class', "template A", None, "IDpE1A") - check('class', "template A", None, "I0E1A") - check('class', "template A", None, "I0E1A") + check('class', "template A", {2:"I0E1A"}) + check('class', "template A", {2:"I0E1A"}) + check('class', "template A", {2:"IDpE1A"}) + check('class', "template A", {2:"IDpE1A"}) + check('class', "template A", {2:"I0E1A"}) + check('class', "template A", {2:"I0E1A"}) - check('class', "template typename T> A", - None, "II0E0E1A") - check('class', "template A", None, "I_iE1A") - check('class', "template A", None, "I_iE1A") - check('class', "template A", None, "I_DpiE1A") - check('class', "template A", None, "I_iE1A") - check('class', "template A", None, "I_iE1A") + check('class', "template typename T> A", {2:"II0E0E1A"}) + check('class', "template A", {2:"I_iE1A"}) + check('class', "template A", {2:"I_iE1A"}) + check('class', "template A", {2:"I_DpiE1A"}) + check('class', "template A", {2:"I_iE1A"}) + check('class', "template A", {2:"I_iE1A"}) # from #2058 check('function', @@ -413,8 +416,8 @@ def test_templates(): "inline std::basic_ostream &operator<<(" "std::basic_ostream &os, " "const c_string_view_base &str)", - None, "I00ElsRNSt13basic_ostreamI4Char6TraitsEE" - "RK18c_string_view_baseIK4Char6TraitsE") + {2:"I00ElsRNSt13basic_ostreamI4Char6TraitsEE" + "RK18c_string_view_baseIK4Char6TraitsE"}) # template introductions with pytest.raises(DefinitionError): @@ -422,63 +425,61 @@ def test_templates(): with pytest.raises(DefinitionError): parse('enumerator', 'abc::ns::foo{id_0, id_1, id_2} A') check('class', 'abc::ns::foo{id_0, id_1, id_2} xyz::bar', - None, 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barE') + {2:'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barE'}) check('class', 'abc::ns::foo{id_0, id_1, ...id_2} xyz::bar', - None, 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barE') + {2:'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barE'}) check('class', 'abc::ns::foo{id_0, id_1, id_2} xyz::bar', - None, 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barI4id_04id_14id_2EE') + {2:'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barI4id_04id_14id_2EE'}) check('class', 'abc::ns::foo{id_0, id_1, ...id_2} xyz::bar', - None, 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barI4id_04id_1Dp4id_2EE') + {2:'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barI4id_04id_1Dp4id_2EE'}) - check('class', 'template<> Concept{U} A::B', - None, 'IEI0EX7ConceptI1UEEN1AIiE1BE') + check('class', 'template<> Concept{U} A::B', {2:'IEI0EX7ConceptI1UEEN1AIiE1BE'}) check('type', 'abc::ns::foo{id_0, id_1, id_2} xyz::bar = ghi::qux', - None, 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barE') + {2:'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barE'}) check('type', 'abc::ns::foo{id_0, id_1, ...id_2} xyz::bar = ghi::qux', - None, 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barE') + {2:'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barE'}) check('function', 'abc::ns::foo{id_0, id_1, id_2} void xyz::bar()', - None, 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barEv') + {2:'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barEv'}) check('function', 'abc::ns::foo{id_0, id_1, ...id_2} void xyz::bar()', - None, 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barEv') + {2:'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barEv'}) check('member', 'abc::ns::foo{id_0, id_1, id_2} ghi::qux xyz::bar', - None, 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barE') + {2:'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barE'}) check('member', 'abc::ns::foo{id_0, id_1, ...id_2} ghi::qux xyz::bar', - None, 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barE') - check('concept', 'Iterator{T, U} Another', - None, 'I00EX8IteratorI1T1UEE7Another') + {2:'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barE'}) + check('concept', 'Iterator{T, U} Another', {2:'I00EX8IteratorI1T1UEE7Another'}) check('concept', 'template Numerics = (... && Numeric)', - None, 'IDpE8Numerics') + {2:'IDpE8Numerics'}) def test_template_args(): # from breathe#218 check('function', "template " "void allow(F *f, typename func::type tt)", - None, "I0E5allowP1FN4funcI1F1BXG!=1EE4typeE") + {2:"I0E5allowP1FN4funcI1F1BXG!=1EE4typeE"}) # from #3542 check('type', "template " "enable_if_not_array_t = std::enable_if_t::value, int>", - None, "I0E21enable_if_not_array_t") + {2:"I0E21enable_if_not_array_t"}) def test_attributes(): # style: C++ - check('member', '[[]] int f', 'f__i', '1f') - check('member', '[ [ ] ] int f', 'f__i', '1f', + check('member', '[[]] int f', {1:'f__i', 2:'1f'}) + check('member', '[ [ ] ] int f', {1:'f__i', 2:'1f'}, # this will fail when the proper grammar is implemented output='[[ ]] int f') - check('member', '[[a]] int f', 'f__i', '1f') + check('member', '[[a]] int f', {1:'f__i', 2:'1f'}) # style: GNU - check('member', '__attribute__(()) int f', 'f__i', '1f') - check('member', '__attribute__((a)) int f', 'f__i', '1f') - check('member', '__attribute__((a, b)) int f', 'f__i', '1f') + check('member', '__attribute__(()) int f', {1:'f__i', 2:'1f'}) + check('member', '__attribute__((a)) int f', {1:'f__i', 2:'1f'}) + check('member', '__attribute__((a, b)) int f', {1:'f__i', 2:'1f'}) # style: user-defined id - check('member', 'id_attr int f', 'f__i', '1f') + check('member', 'id_attr int f', {1:'f__i', 2:'1f'}) # style: user-defined paren - check('member', 'paren_attr() int f', 'f__i', '1f') - check('member', 'paren_attr(a) int f', 'f__i', '1f') - check('member', 'paren_attr("") int f', 'f__i', '1f') - check('member', 'paren_attr(()[{}][]{}) int f', 'f__i', '1f') + check('member', 'paren_attr() int f', {1:'f__i', 2:'1f'}) + check('member', 'paren_attr(a) int f', {1:'f__i', 2:'1f'}) + check('member', 'paren_attr("") int f', {1:'f__i', 2:'1f'}) + check('member', 'paren_attr(()[{}][]{}) int f', {1:'f__i', 2:'1f'}) with pytest.raises(DefinitionError): parse('member', 'paren_attr(() int f') with pytest.raises(DefinitionError): @@ -494,7 +495,7 @@ def test_attributes(): # position: decl specs check('function', 'static inline __attribute__(()) void f()', - 'f', '1fv', + {1:'f', 2:'1fv'}, output='__attribute__(()) static inline void f()') From 4bcaf137667c734a2640c66e0a538432cebb2167 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Mon, 20 Mar 2017 12:19:48 +0900 Subject: [PATCH 78/91] C++, expressions --- sphinx/domains/cpp.py | 882 +++++++++++++++++++++++++++++++++++++-- tests/test_domain_cpp.py | 98 ++++- 2 files changed, 937 insertions(+), 43 deletions(-) diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 9245c851a..e95de4be9 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -94,7 +94,7 @@ logger = logging.getLogger(__name__) "class" "..."[opt] identifier[opt] | "template" "<" template-parameter-list ">" "class" identifier[opt] "=" id-expression - # also, from C++17 we can have "typname" in template templates + # also, from C++17 we can have "typename" in template templates templateDeclPrefix -> "template" "<" template-parameter-list ">" @@ -288,6 +288,9 @@ logger = logging.getLogger(__name__) nested-name """ +# TODO: support hex, oct, etc. work +_integer_literal_re = re.compile(r'-?[1-9][0-9]*') +_float_literal_re = re.compile(r'[+-]?[0-9]*\.[0-9]+') _identifier_re = re.compile(r'(~?\b[a-zA-Z_][a-zA-Z0-9_]*)\b') _whitespace_re = re.compile(r'\s+(?u)') _string_re = re.compile(r"[LuU8]?('([^'\\]*(?:\\.[^'\\]*)*)'" @@ -301,6 +304,12 @@ _operator_re = re.compile(r'''(?x) | (<<|>>)=? | && | \|\| | [!<>=/*%+|&^~-]=? ''') +_fold_operator_re = re.compile(r'''(?x) + ->\* | \.\* | \, + | (<<|>>)=? | && | \|\| + | != + | [<>=/*%+|&^~-]=? +''') # see http://en.cppreference.com/w/cpp/keyword _keywords = [ 'alignas', 'alignof', 'and', 'and_eq', 'asm', 'auto', 'bitand', 'bitor', @@ -445,6 +454,7 @@ _id_operator_v2 = { 'delete': 'dl', 'delete[]': 'da', # the arguments will make the difference between unary and binary + # in operator definitions # '+(unary)' : 'ps', # '-(unary)' : 'ng', # '&(unary)' : 'ad', @@ -486,8 +496,36 @@ _id_operator_v2 = { '->*': 'pm', '->': 'pt', '()': 'cl', - '[]': 'ix' + '[]': 'ix', + '.*': 'ds' # this one is not overloadable, but we need it for expressions } # type: Dict[unicode, unicode] +_id_operator_unary_v2 = { + '++': 'pp_', + '--': 'mm_', + '*': 'de', + '&': 'ad', + '+': 'ps', + '-': 'ng', + '!': 'nt', + '~': 'co' +} +# these are ordered by preceedence +_expression_bin_ops = [ + ['||'], + ['&&'], + ['|'], + ['^'], + ['&'], + ['==', '!='], + ['<=', '>=', '<', '>'], + ['<<', '>>'], + ['+', '-'], + ['*', '/', '%'], + ['.*', '->*'] +] +_expression_unary_ops = ["++", "--", "*", "&", "+", "-", "!", "~"] +_expression_assignment_ops = ["=", "*=", "/=", "%=", "+=", "-=", + ">>=", "<<=", "&=", "^=", "|="] class NoOldIdError(UnicodeMixin, Exception): @@ -581,6 +619,10 @@ def _verify_description_mode(mode): raise Exception("Description mode '%s' is invalid." % mode) +################################################################################ +# Attributes +################################################################################ + class ASTCPPAttribute(ASTBase): def __init__(self, arg): # type: (unicode) -> None @@ -669,6 +711,368 @@ class ASTParenAttribute(ASTBase): signode.append(nodes.Text(txt, txt)) +################################################################################ +# Expressions and Literals +################################################################################ + +class ASTPointerLiteral(ASTBase): + def __unicode__(self): + return u'nullptr' + + def get_id_v2(self): + return 'LDnE' + + def describe_signature(self, signode, mode, env, symbol): + signode.append(nodes.Text('nullptr')) + + +class ASTBooleanLiteral(ASTBase): + def __init__(self, value): + self.value = value + + def __unicode__(self): + if self.value: + return u'true' + else: + return u'false' + + def get_id_v2(self): + if self.value: + return 'L1E' + else: + return 'L0E' + + def describe_signature(self, signode, mode, env, symbol): + signode.append(nodes.Text(text_type(self))) + + +class ASTNumberLiteral(ASTBase): + def __init__(self, data): + # type: (unicode) -> None + self.data = data + + def __unicode__(self): + return self.data + + def get_id_v2(self): + return "L%sE" % self.data + + def describe_signature(self, signode, mode, env, symbol): + txt = text_type(self) + signode.append(nodes.Text(txt, txt)) + + +class ASTStringLiteral(ASTBase): + def __init__(self, data): + # type: (unicode) -> None + self.data = data + + def __unicode__(self): + return self.data + + def get_id_v2(self): + # note: the length is not really correct with escaping + return "LA%d_KcE" % (len(self.data) - 2) + + def describe_signature(self, signode, mode, env, symbol): + txt = text_type(self) + signode.append(nodes.Text(txt, txt)) + + +class ASTParenExpr(ASTBase): + def __init__(self, expr): + self.expr = expr + + def __unicode__(self): + return '(' + text_type(self.expr) + ')' + + def get_id_v2(self): + return self.expr.get_id_v2() + + def describe_signature(self, signode, mode, env, symbol): + signode.append(nodes.Text('(', '(')) + self.expr.describe_signature(signode, mode, env, symbol) + signode.append(nodes.Text(')', ')')) + + +class ASTFoldExpr(ASTBase): + def __init__(self, leftExpr, op, rightExpr): + assert leftExpr is not None or rightExpr is not None + self.leftExpr = leftExpr + self.op = op + self.rightExpr = rightExpr + + def __unicode__(self): + res = ['('] + if self.leftExpr: + res.append(text_type(self.leftExpr)) + res.append(' ') + res.append(text_type(self.op)) + res.append(' ') + res.append('...') + if self.rightExpr: + res.append(' ') + res.append(text_type(self.op)) + res.append(' ') + res.append(text_type(self.rightExpr)) + res.append(')') + return u''.join(res) + + def get_id_v2(self): + # TODO: find the right mangling scheme + return text_type(self) + + def describe_signature(self, signode, mode, env, symbol): + signode.append(nodes.Text('(')) + if self.leftExpr: + self.leftExpr.describe_signature(signode, mode, env, symbol) + signode.append(nodes.Text(' ')) + signode.append(nodes.Text(self.op)) + signode.append(nodes.Text(' ')) + signode.append(nodes.Text('...')) + if self.rightExpr: + signode.append(nodes.Text(' ')) + signode.append(nodes.Text(self.op)) + signode.append(nodes.Text(' ')) + self.rightExpr.describe_signature(signode, mode, env, symbol) + signode.append(nodes.Text(')')) + + +class ASTBinOpExpr(ASTBase): + def __init__(self, exprs, ops): + assert len(exprs) > 0 + assert len(exprs) == len(ops) + 1 + self.exprs = exprs + self.ops = ops + + def __unicode__(self): + res = [] + res.append(text_type(self.exprs[0])) + for i in range(1, len(self.exprs)): + res.append(' ') + res.append(self.ops[i - 1]) + res.append(' ') + res.append(text_type(self.exprs[i])) + return u''.join(res) + + def get_id_v2(self): + res = [] + for i in range(len(self.ops)): + res.append(_id_operator_v2[self.ops[i]]) + res.append(self.exprs[i].get_id_v2()) + res.append(self.exprs[-1].get_id_v2()) + return u''.join(res) + + def describe_signature(self, signode, mode, env, symbol): + self.exprs[0].describe_signature(signode, mode, env, symbol) + for i in range(1, len(self.exprs)): + signode.append(nodes.Text(' ')) + signode.append(nodes.Text(self.ops[i - 1])) + signode.append(nodes.Text(' ')) + self.exprs[i].describe_signature(signode, mode, env, symbol) + + +class ASTAssignmentExpr(ASTBase): + def __init__(self, exprs, ops): + assert len(exprs) > 0 + assert len(exprs) == len(ops) + 1 + self.exprs = exprs + self.ops = ops + + def __unicode__(self): + res = [] + res.append(text_type(self.exprs[0])) + for i in range(1, len(self.exprs)): + res.append(' ') + res.append(self.ops[i - 1]) + res.append(' ') + res.append(text_type(self.exprs[i])) + return u''.join(res) + + def get_id(self, version): + res = [] + for i in range(len(self.ops)): + res.append(_id_operator_v2[self.ops[i]]) + res.append(self.exprs[i].get_id(version)) + res.append(self.exprs[-1].get_id(version)) + return u''.join(res) + + def describe_signature(self, signode, mode, env, symbol): + self.exprs[0].describe_signature(signode, mode, env, symbol) + for i in range(1, len(self.exprs)): + signode.append(nodes.Text(' ')) + signode.append(nodes.Text(self.ops[i - 1])) + signode.append(nodes.Text(' ')) + self.exprs[i].describe_signature(signode, mode, env, symbol) + + +class ASTCastExpr(ASTBase): + def __init__(self, typ, expr): + self.typ = typ + self.expr = expr + + def __unicode__(self): + res = ['('] + res.append(text_type(self.typ)) + res.append(')') + res.append(text_type(self.expr)) + return u''.join(res) + + def get_id(self, version): + return 'cv' + self.typ.get_id(version) + self.expr.get_id(version) + + def describe_signature(self, signode, mode, env, symbol): + signode.append(nodes.Text('(')) + self.typ.describe_signature(signode, mode, env, symbol) + signode.append(nodes.Text(')')) + self.expr.describe_signature(signode, mode, env, symbol) + + +class ASTUnaryOpExpr(ASTBase): + def __init__(self, op, expr): + self.op = op + self.expr = expr + + def __unicode__(self): + return text_type(self.op) + text_type(self.expr) + + def get_id_v2(self): + return _id_operator_unary_v2[self.op] + self.expr.get_id_v2() + + def describe_signature(self, signode, mode, env, symbol): + signode.append(nodes.Text(self.op)) + self.expr.describe_signature(signode, mode, env, symbol) + + +class ASTPostfixCallExpr(ASTBase): + def __init__(self, exprs): + self.exprs = exprs + + def __unicode__(self): + res = ['('] + first = True + for e in self.exprs: + if not first: + res.append(', ') + first = False + res.append(text_type(e)) + res.append(')') + return u''.join(res) + + def get_id_v2(self, idPrefix): + res = ['cl', idPrefix] + for e in self.exprs: + res.append(e.get_id_v2()) + res.append('E') + return u''.join(res) + + def describe_signature(self, signode, mode, env, symbol): + signode.append(nodes.Text('(')) + first = True + for e in self.exprs: + if not first: + signode.append(nodes.Text(', ')) + first = False + e.describe_signature(signode, mode, env, symbol) + signode.append(nodes.Text(')')) + + +class ASTPostfixArray(ASTBase): + def __init__(self, expr): + self.expr = expr + + def __unicode__(self): + return u'[' + text_type(self.expr) + ']' + + def get_id_v2(self, idPrefix): + return 'ix' + idPrefix + self.expr.get_id_v2() + + def describe_signature(self, signode, mode, env, symbol): + signode.append(nodes.Text('[')) + self.expr.describe_signature(signode, mode, env, symbol) + signode.append(nodes.Text(']')) + + +class ASTPostfixInc(ASTBase): + def __unicode__(self): + return u'++' + + def get_id(self, idPrefix, version): + return 'pp' + idPrefix + + def describe_signature(self, signode, mode, env, symbol): + signode.append(nodes.Text('++')) + + +class ASTPostfixDec(ASTBase): + def __unicode__(self): + return u'--' + + def get_id(self, idPrefix, version): + return 'mm' + idPrefix + + def describe_signature(self, signode, mode, env, symbol): + signode.append(nodes.Text('--')) + + +class ASTPostfixMember(ASTBase): + def __init__(self, name): + self.name = name + + def __unicode__(self): + return u'.' + text_type(self.name) + + def get_id(self, idPrefix, version): + return 'dt' + idPrefix + self.name.get_id(version) + + def describe_signature(self, signode, mode, env, symbol): + signode.append(nodes.Text('.')) + self.name.describe_signature(signode, 'noneIsName', env, symbol) + + +class ASTPostfixMemberOfPointer(ASTBase): + def __init__(self, name): + self.name = name + + def __unicode__(self): + return u'->' + text_type(self.name) + + def get_id(self, idPrefix, version): + return 'pt' + idPrefix + self.name.get_id(version) + + def describe_signature(self, signode, mode, env, symbol): + signode.append(nodes.Text('->')) + self.name.describe_signature(signode, 'noneIsName', env, symbol) + + +class ASTPostfixExpr(ASTBase): + def __init__(self, prefix, postFixes): + assert len(postFixes) > 0 + self.prefix = prefix + self.postFixes = postFixes + + def __unicode__(self): + res = [text_type(self.prefix)] + for p in self.postFixes: + res.append(text_type(p)) + return u''.join(res) + + def get_id_v2(self): + id = self.prefix.get_id_v2() + for p in self.postFixes: + id = p.get_id_v2(id) + return id + + def describe_signature(self, signode, mode, env, symbol): + self.prefix.describe_signature(signode, mode, env, symbol) + for p in self.postFixes: + p.describe_signature(signode, mode, env, symbol) + + +################################################################################ +# The Rest +################################################################################ + class ASTIdentifier(ASTBase): def __init__(self, identifier): # type: (unicode) -> None @@ -1186,15 +1590,13 @@ class ASTTemplateArgConstant(ASTBase): if version == 1: return text_type(self).replace(u' ', u'-') if version == 2: - # TODO: doing this properly needs parsing of expressions, let's just - # use it verbatim for now return u'X' + text_type(self) + u'E' - assert False + return u'X' + self.value.get_id_v2() + u'E' def describe_signature(self, signode, mode, env, symbol): # type: (addnodes.desc_signature, unicode, BuildEnvironment, Symbol) -> None _verify_description_mode(mode) - signode += nodes.Text(text_type(self)) + self.value.describe_signature(signode, mode, env, symbol) class ASTTemplateArgs(ASTBase): @@ -1740,20 +2142,31 @@ class ASTArray(ASTBase): def __unicode__(self): # type: () -> unicode - return u''.join(['[', text_type(self.size), ']']) + if self.size: + return u''.join(['[', text_type(self.size), ']']) + else: + return u'[]' def get_id(self, version): # type: (int) -> unicode if version == 1: return u'A' if version == 2: - # TODO: this should maybe be done differently - return u'A' + text_type(self.size) + u'_' - assert False + if self.size: + return u'A' + text_type(self.size) + u'_' + else: + return u'A_' + if self.size: + return u'A' + self.size.get_id_v2() + u'_' + else: + return u'A_' - def describe_signature(self, signode, mode, env): + def describe_signature(self, signode, mode, env, symbol): _verify_description_mode(mode) - signode += nodes.Text(text_type(self)) + signode.append(nodes.Text("[")) + if self.size: + self.size.describe_signature(signode, mode, env, symbol) + signode.append(nodes.Text("]")) class ASTDeclaratorPtr(ASTBase): @@ -2187,7 +2600,7 @@ class ASTDeclaratorNameParamQual(ASTBase): if self.declId: self.declId.describe_signature(signode, mode, env, symbol) for op in self.arrayOps: - op.describe_signature(signode, mode, env) + op.describe_signature(signode, mode, env, symbol) if self.paramQual: self.paramQual.describe_signature(signode, mode, env, symbol) @@ -2201,10 +2614,11 @@ class ASTInitializer(ASTBase): # type: () -> unicode return u''.join([' = ', text_type(self.value)]) - def describe_signature(self, signode, mode): + def describe_signature(self, signode, mode, env, symbol): # type: (addnodes.desc_signature, unicode) -> None _verify_description_mode(mode) - signode += nodes.Text(text_type(self)) + signode.append(nodes.Text(' = ')) + self.value.describe_signature(signode, 'markType', env, symbol) class ASTType(ASTBase): @@ -2324,9 +2738,9 @@ class ASTTypeWithInit(ASTBase): def describe_signature(self, signode, mode, env, symbol): # type: (addnodes.desc_signature, unicode, BuildEnvironment, Symbol) -> None _verify_description_mode(mode) - self.type.describe_signature(signode, mode, env, symbol=symbol) + self.type.describe_signature(signode, mode, env, symbol) if self.init: - self.init.describe_signature(signode, mode) + self.init.describe_signature(signode, mode, env, symbol) class ASTTypeUsing(ASTBase): @@ -2397,7 +2811,7 @@ class ASTConcept(ASTBase): if self.isFunction: signode += nodes.Text("()") if self.initializer: - self.initializer.describe_signature(signode, mode) + self.initializer.describe_signature(signode, mode, env, symbol) class ASTBaseClass(ASTBase): @@ -2537,9 +2951,9 @@ class ASTEnumerator(ASTBase): def describe_signature(self, signode, mode, env, symbol): # type: (addnodes.desc_signature, unicode, BuildEnvironment, Symbol) -> None _verify_description_mode(mode) - self.name.describe_signature(signode, mode, env, symbol=symbol) + self.name.describe_signature(signode, mode, env, symbol) if self.init: - self.init.describe_signature(signode, 'noneIsName') + self.init.describe_signature(signode, 'markType', env, symbol) class ASTDeclaration(ASTBase): @@ -2646,8 +3060,7 @@ class ASTDeclaration(ASTBase): mainDeclNode += addnodes.desc_annotation('enumerator ', 'enumerator ') else: assert False - self.declaration.describe_signature(mainDeclNode, mode, env, - symbol=self.symbol) + self.declaration.describe_signature(mainDeclNode, mode, env, self.symbol) class ASTNamespace(ASTBase): @@ -3130,6 +3543,7 @@ class DefinitionParser(object): self.end = len(self.definition) self.last_match = None # type: Match self._previous_state = (0, None) # type: Tuple[int, Match] + self.otherErrors = [] # type: List[DefinitionError] self.warnEnv = warnEnv self.config = config @@ -3137,7 +3551,10 @@ class DefinitionParser(object): def _make_multi_error(self, errors, header): # type: (List[Any], unicode) -> DefinitionError if len(errors) == 1: - return DefinitionError(header + '\n' + errors[0][0].description) + if len(header) > 0: + return DefinitionError(header + '\n' + errors[0][0].description) + else: + return DefinitionError(errors[0][0].description) result = [header, '\n'] for e in errors: if len(e[1]) > 0: @@ -3162,10 +3579,16 @@ class DefinitionParser(object): def fail(self, msg): # type: (unicode) -> None + errors = [] indicator = '-' * self.pos + '^' - raise DefinitionError( + exMain = DefinitionError( 'Invalid definition: %s [error at %d]\n %s\n %s' % (msg, self.pos, self.definition, indicator)) + errors.append((exMain, "Main error")) + for err in self.otherErrors: + errors.append((err, "Potential other error")) + self.otherErrors = [] + raise self._make_multi_error(errors, '') def warn(self, msg): # type: (unicode) -> None @@ -3251,6 +3674,25 @@ class DefinitionParser(object): if not self.eof: self.fail('Expected end of definition.') + def _parse_string(self): + if self.current_char != '"': + return None + startPos = self.pos + self.pos += 1 + escape = False + while True: + if self.eof: + self.fail("Unexpected end during inside string.") + elif self.current_char == '"' and not escape: + self.pos += 1 + break + elif self.current_char == '\\': + escape = True + else: + escape = False + self.pos += 1 + return self.definition[startPos:self.pos] + def _parse_balanced_token_seq(self, end): # type: (List[unicode]) -> unicode # TODO: add handling of string literals and similar @@ -3332,11 +3774,332 @@ class DefinitionParser(object): return None - def _parse_expression(self, end): + def _parse_literal(self): + # -> integer-literal + # | character-literal + # | floating-literal + # | string-literal + # | boolean-literal -> "false" | "true" + # | pointer-literal -> "nullptr" + # | user-defined-literal + self.skip_ws() + if self.skip_word('nullptr'): + return ASTPointerLiteral() + if self.skip_word('true'): + return ASTBooleanLiteral(True) + if self.skip_word('false'): + return ASTBooleanLiteral(False) + if self.match(_float_literal_re): + return ASTNumberLiteral(self.matched_text) + if self.match(_integer_literal_re): + return ASTNumberLiteral(self.matched_text) + string = self._parse_string() + if string is not None: + return ASTStringLiteral(string) + # TODO: char lit + # TODO: user-defined lit + return None + + def _parse_fold_or_paren_expression(self): + # "(" expression ")" + # fold-expression + # -> ( cast-expression fold-operator ... ) + # | ( ... fold-operator cast-expression ) + # | ( cast-expression fold-operator ... fold-operator cast-expression + if self.current_char != '(': + return None + self.pos += 1 + self.skip_ws() + if self.skip_string_and_ws("..."): + # ( ... fold-operator cast-expression ) + if not self.match(_fold_operator_re): + self.fail("Expected fold operator after '...' in fold expression.") + op = self.matched_text + rightExpr = self._parse_cast_expression() + if not self.skip_string(')'): + self.fail("Expected ')' in end of fold expression.") + return ASTFoldExpr(None, op, rightExpr) + # TODO: actually try to parse fold expression + # fall back to a paren expression + res = self._parse_expression(inTemplate=False) + self.skip_ws() + if not self.skip_string(')'): + self.fail("Expected ')' in end of fold expression or parenthesized expression.") + return ASTParenExpr(res) + + def _parse_primary_expression(self): + # literal + # "this" + # lambda-expression + # "(" expression ")" + # fold-expression + # id-expression -> we parse this with _parse_nested_name + self.skip_ws() + res = self._parse_literal() + if res is not None: + return res + # TODO: try 'this' and lambda expression + res = self._parse_fold_or_paren_expression() + if res is not None: + return res + return self._parse_nested_name() + + def _parse_postfix_expression(self): + # -> primary + # | postfix "[" expression "]" + # | postfix "[" braced-init-list [opt] "]" + # | postfix "(" expression-list [opt] ")" + # | postfix "." "template" [opt] id-expression + # | postfix "->" "template" [opt] id-expression + # | postfix "." pseudo-destructor-name + # | postfix "->" pseudo-destructor-name + # | postfix "++" + # | postfix "--" + # | simple-type-specifier "(" expression-list [opt] ")" + # | simple-type-specifier braced-init-list + # | typename-specifier "(" expression-list [opt] ")" + # | typename-specifier braced-init-list + # | "dynamic_cast" "<" type-id ">" "(" expression ")" + # | "static_cast" "<" type-id ">" "(" expression ")" + # | "reinterpret_cast" "<" type-id ">" "(" expression ")" + # | "const_cast" "<" type-id ">" "(" expression ")" + # | "typeid" "(" expression ")" + # | "typeid" "(" type-id ")" + + # TODO: try the productions with prefixes: + # dynamic_cast, static_cast, reinterpret_cast, const_cast, typeid + prefixType = None + pos = self.pos + try: + prefix = self._parse_primary_expression() + prefixType = 'expr' + except DefinitionError as eOuter: + self.pos = pos + try: + # we are potentially casting, so save parens for us + # TODO: hmm, would we need to try both with operatorCast and with None? + prefix = self._parse_type(False, 'operatorCast') + prefixType = 'typeOperatorCast' + except DefinitionError as eInner: + self.pos = pos + header = "Error in postfix expression, expected primary expression or type." + errors = [] + errors.append((eOuter, "If primary expression")) + errors.append((eInner, "If type")) + raise self._make_multi_error(errors, header) + # and now parse postfixes + postFixes = [] + while True: + self.skip_ws() + if prefixType == 'expr': + if self.skip_string_and_ws('['): + expr = self._parse_expression(inTemplate=False) + self.skip_ws() + if not self.skip_string(']'): + self.fail("Expected ']' in end of postfix expression.") + postFixes.append(ASTPostfixArray(expr)) + continue + if self.skip_string('.'): + if self.skip_string('*'): + # don't steal the dot + self.pos -= 2 + else: + name = self._parse_nested_name() + postFixes.append(ASTPostfixMember(name)) + continue + if self.skip_string('->'): + if self.skip_string('*'): + # don't steal the arrow + self.pos -= 3 + else: + name = self._parse_nested_name() + postFixes.append(ASTPostfixMemberOfPointer(name)) + continue + if self.skip_string('++'): + postFixes.append(ASTPostfixInc()) + continue + if self.skip_string('--'): + postFixes.append(ASTPostfixDec()) + continue + if self.skip_string_and_ws('('): + # TODO: handled braced init + exprs = [] + self.skip_ws() + if not self.skip_string(')'): + while True: + self.skip_ws() + expr = self._parse_expression(inTemplate=False) + exprs.append(expr) + self.skip_ws() + if self.skip_string(')'): + break + if not self.skip_string(','): + self.fail("Error in cast or call, expected ',' or ')'.") + postFixes.append(ASTPostfixCallExpr(exprs)) + continue + break + if len(postFixes) == 0: + return prefix + else: + return ASTPostfixExpr(prefix, postFixes) + + def _parse_unary_expression(self): + # -> postfix + # | "++" cast + # | "--" cast + # | unary-operator cast -> (* | & | + | - | ! | ~) cast + # | "sizeof" unary + # | "sizeof" "(" type-id ")" + # | "sizeof" "..." "(" identifier ")" + # | "alignof" "(" type-id ")" + # | noexcept-expression -> noexcept "(" expression ")" + # | new-expression + # | delete-expression + self.skip_ws() + for op in _expression_unary_ops: + # TODO: hmm, should we be able to backtrack here? + if self.skip_string(op): + expr = self._parse_cast_expression() + return ASTUnaryOpExpr(op, expr) + # TODO: the rest + return self._parse_postfix_expression() + + def _parse_cast_expression(self): + # -> unary | "(" type-id ")" cast + pos = self.pos + self.skip_ws() + if self.skip_string('('): + try: + typ = self._parse_type(False) + if not self.skip_string(')'): + raise DefinitionError("Expected ')' in cast expression.") + expr = self._parse_cast_expression() + return ASTCastExpr(typ, expr) + except DefinitionError as exCast: + self.pos = pos + try: + return self._parse_unary_expression() + except DefinitionError as exUnary: + errs = [] + errs.append((exCast, "If type cast expression")) + errs.append((exUnary, "If unary expression")) + raise self._make_multi_error(errs, "Error in cast expression.") + else: + return self._parse_unary_expression() + + def _parse_logical_or_expression(self, inTemplate): + # logical-or = logical-and || + # logical-and = inclusive-or && + # inclusive-or = exclusive-or | + # exclusive-or = and ^ + # and = equality & + # equality = relational ==, != + # relational = shift <, >, <=, >= + # shift = additive <<, >> + # additive = multiplicative +, - + # multiplicative = pm *, /, % + # pm = cast .*, ->* + def _parse_bin_op_expr(self, opId, inTemplate): + if opId + 1 == len(_expression_bin_ops): + def parser(inTemplate): + return self._parse_cast_expression() + else: + def parser(inTemplate): + return _parse_bin_op_expr(self, opId + 1, inTemplate=inTemplate) + exprs = [] + ops = [] + exprs.append(parser(inTemplate=inTemplate)) + while True: + self.skip_ws() + if inTemplate and self.current_char == '>': + break + pos = self.pos + oneMore = False + for op in _expression_bin_ops[opId]: + if not self.skip_string(op): + continue + if op == '&' and self.current_char == '&': + # don't split the && 'token' + self.pos -= 1 + # and btw. && has lower precedence, so we are done + break + try: + expr = parser(inTemplate=inTemplate) + exprs.append(expr) + ops.append(op) + oneMore = True + break + except DefinitionError: + self.pos = pos + if not oneMore: + break + return ASTBinOpExpr(exprs, ops) + return _parse_bin_op_expr(self, 0, inTemplate=inTemplate) + + def _parse_conditional_expression_tail(self, orExprHead): + # -> "?" expression ":" assignment-expression + return None + + def _parse_assignment_expression(self, inTemplate): + # -> conditional-expression + # | logical-or-expression assignment-operator initializer-clause + # | throw-expression + # TODO: parse throw-expression: "throw" assignment-expression [opt] + # if not a throw expression, then: + # -> conditional-expression -> + # logical-or-expression + # | logical-or-expression "?" expression ":" assignment-expression + # | logical-or-expression assignment-operator initializer-clause + exprs = [] + ops = [] + orExpr = self._parse_logical_or_expression(inTemplate=inTemplate) + exprs.append(orExpr) + # TODO: handle ternary with _parse_conditional_expression_tail + while True: + oneMore = False + self.skip_ws() + for op in _expression_assignment_ops: + if not self.skip_string(op): + continue + expr = self._parse_logical_or_expression(False) + exprs.append(expr) + ops.append(op) + oneMore = True + if not oneMore: + break + if len(ops) == 0: + return orExpr + else: + return ASTAssignmentExpr(exprs, ops) + + def _parse_constant_expression(self, inTemplate): + # -> conditional-expression + orExpr = self._parse_logical_or_expression(inTemplate=inTemplate) + # TODO: use _parse_conditional_expression_tail + return orExpr + + def _parse_expression(self, inTemplate): + # -> assignment-expression + # | expression "," assignment-expresion + # TODO: actually parse the second production + return self._parse_assignment_expression(inTemplate=inTemplate) + + def _parse_expression_fallback(self, end, parser): # type: (List[unicode]) -> unicode # Stupidly "parse" an expression. # 'end' should be a list of characters which ends the expression. - assert end + + # first try to use the provided parser + prevPos = self.pos + try: + return parser() + except DefinitionError as e: + raise + self.warn("Parsing of expression failed. Using fallback parser." + " Error was:\n%s" % e.description) + self.pos = prevPos + # and then the fallback scanning + assert end is not None self.skip_ws() startPos = self.pos if self.match(_string_re): @@ -3353,7 +4116,7 @@ class DefinitionParser(object): elif len(symbols) > 0 and self.current_char == symbols[-1]: symbols.pop() self.pos += 1 - if self.eof: + if len(end) > 0 and self.eof: self.fail("Could not find end of expression starting at %d." % startPos) value = self.definition[startPos:self.pos].strip() @@ -3417,7 +4180,9 @@ class DefinitionParser(object): prevErrors.append((e, "If type argument")) self.pos = pos try: - value = self._parse_expression(end=[',', '>']) + def parser(): + return self._parse_constant_expression(inTemplate=True) + value = self._parse_expression_fallback([',', '>'], parser) self.skip_ws() if self.skip_string('>'): parsedEnd = True @@ -3461,7 +4226,15 @@ class DefinitionParser(object): if identifier in _keywords: self.fail("Expected identifier in nested name, " "got keyword: %s" % identifier) - templateArgs = self._parse_template_argument_list() + # try greedily to get template parameters, + # but otherwise a < might be because we are in an expression + pos = self.pos + try: + templateArgs = self._parse_template_argument_list() + except DefinitionError as ex: + self.pos = pos + templateArgs = None + self.otherErrors.append(ex) identifier = ASTIdentifier(identifier) # type: ignore names.append(ASTNestedNameElement(identifier, templateArgs)) @@ -3747,9 +4520,16 @@ class DefinitionParser(object): while 1: self.skip_ws() if typed and self.skip_string('['): - value = self._parse_expression(end=[']']) - res = self.skip_string(']') - assert res + self.skip_ws() + if self.skip_string(']'): + arrayOps.append(ASTArray(None)) + continue + + def parser(): + return self._parse_expression(inTemplate=False) + value = self._parse_expression_fallback([']'], parser) + if not self.skip_string(']'): + self.fail("Expected ']' in end of array operator.") arrayOps.append(ASTArray(value)) continue else: @@ -3867,11 +4647,17 @@ class DefinitionParser(object): return None else: if outer == 'member': - value = self.read_rest().strip() # type: unicode + def parser(): + return self._parse_assignment_expression(inTemplate=False) + value = self._parse_expression_fallback([], parser) elif outer == 'templateParam': - value = self._parse_expression(end=[',', '>']) + def parser(): + return self._parse_assignment_expression(inTemplate=True) + value = self._parse_expression_fallback([',', '>'], parser) elif outer is None: # function parameter - value = self._parse_expression(end=[',', ')']) + def parser(): + return self._parse_assignment_expression(inTemplate=False) + value = self._parse_expression_fallback([',', ')'], parser) else: self.fail("Internal error, initializer for outer '%s' not " "implemented." % outer) @@ -4045,7 +4831,10 @@ class DefinitionParser(object): init = None if self.skip_string('='): self.skip_ws() - init = ASTInitializer(self.read_rest()) + + def parser(): + return self._parse_constant_expression(inTemplate=False) + init = ASTInitializer(self._parse_expression_fallback([], parser)) return ASTEnumerator(name, init) def _parse_template_parameter_list(self): @@ -4682,6 +5471,27 @@ class CPPXRefRole(XRefRole): return title, target +class CPPExprRole(object): + def __call__(self, typ, rawtext, text, lineno, inliner, options={}, content=[]): + class Warner(object): + def warn(self, msg): + inliner.reporter.warning(msg, line=lineno) + env = inliner.document.settings.env + parser = DefinitionParser(text, Warner(), env.config) + try: + ast = parser.parse_expression() + except DefinitionError as ex: + Warner().warn('Unparseable C++ expression: %r\n%s' + % (text, text_type(ex.description))) + return [nodes.literal(text)], [] + parentSymbol = env.temp_data.get('cpp:parent_symbol', None) + if parentSymbol is None: + parentSymbol = env.domaindata['cpp']['root_symbol'] + p = nodes.literal() + ast.describe_signature(p, 'markType', env, parentSymbol) + return [p], [] + + class CPPDomain(Domain): """C++ language domain.""" name = 'cpp' diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py index e7f12f09c..f8ebdac7d 100644 --- a/tests/test_domain_cpp.py +++ b/tests/test_domain_cpp.py @@ -26,11 +26,7 @@ def parse(name, string): cpp_paren_attributes = ["paren_attr"] parser = DefinitionParser(string, None, Config()) ast = parser.parse_declaration(name) - if not parser.eof: - print("Parsing stopped at", parser.pos) - print(string) - print('-' * parser.pos + '^') - raise DefinitionError("") + parser.assert_end() # The scopedness would usually have been set by CPPEnumObject if name == "enum": ast.scoped = None # simulate unscoped enum @@ -107,6 +103,92 @@ def test_fundamental_types(): check("function", "void f(%s arg)" % t, {1: makeIdV1(), 2:makeIdV2()}) +def test_expressions(): + def exprCheck(expr, id): + ids = 'IE1CIA%s_1aE' + check('class', 'template<> C' % expr, {2:ids % expr, 3:ids % id}) + # primary + exprCheck('nullptr', 'LDnE') + exprCheck('true', 'L1E') + exprCheck('false', 'L0E') + exprCheck('5', 'L5E') + exprCheck('5.0', 'L5.0E') + exprCheck('"abc\\"cba"', 'LA8_KcE') + # TODO: test the rest + exprCheck('(... + Ns)', '(... + Ns)') + exprCheck('(5)', 'L5E') + exprCheck('C', '1C') + # postfix + exprCheck('A(2)', 'cl1AL2EE') + exprCheck('A[2]', 'ix1AL2E') + exprCheck('a.b.c', 'dtdt1a1b1c') + exprCheck('a->b->c', 'ptpt1a1b1c') + exprCheck('i++', 'pp1i') + exprCheck('i--', 'mm1i') + # TODO, those with prefixes + # unary + exprCheck('++5', 'pp_L5E') + exprCheck('--5', 'mm_L5E') + exprCheck('*5', 'deL5E') + exprCheck('&5', 'adL5E') + exprCheck('+5', 'psL5E') + exprCheck('-5', 'ngL5E') + exprCheck('!5', 'ntL5E') + exprCheck('~5', 'coL5E') + # cast + exprCheck('(int)2', 'cviL2E') + # binary op + exprCheck('5 || 42', 'ooL5EL42E') + exprCheck('5 && 42', 'aaL5EL42E') + exprCheck('5 | 42', 'orL5EL42E') + exprCheck('5 ^ 42', 'eoL5EL42E') + exprCheck('5 & 42', 'anL5EL42E') + # ['==', '!='] + exprCheck('5 == 42', 'eqL5EL42E') + exprCheck('5 != 42', 'neL5EL42E') + # ['<=', '>=', '<', '>'] + exprCheck('5 <= 42', 'leL5EL42E') + exprCheck('5 >= 42', 'geL5EL42E') + exprCheck('5 < 42', 'ltL5EL42E') + exprCheck('5 > 42', 'gtL5EL42E') + # ['<<', '>>'] + exprCheck('5 << 42', 'lsL5EL42E') + exprCheck('5 >> 42', 'rsL5EL42E') + # ['+', '-'] + exprCheck('5 + 42', 'plL5EL42E') + exprCheck('5 - 42', 'miL5EL42E') + # ['*', '/', '%'] + exprCheck('5 * 42', 'mlL5EL42E') + exprCheck('5 / 42', 'dvL5EL42E') + exprCheck('5 % 42', 'rmL5EL42E') + # ['.*', '->*'] + exprCheck('5 .* 42', 'dsL5EL42E') + exprCheck('5 ->* 42', 'pmL5EL42E') + # conditional + # TODO + # assignment + exprCheck('a = 5', 'aS1aL5E') + exprCheck('a *= 5', 'mL1aL5E') + exprCheck('a /= 5', 'dV1aL5E') + exprCheck('a %= 5', 'rM1aL5E') + exprCheck('a += 5', 'pL1aL5E') + exprCheck('a -= 5', 'mI1aL5E') + exprCheck('a >>= 5', 'rS1aL5E') + exprCheck('a <<= 5', 'lS1aL5E') + exprCheck('a &= 5', 'aN1aL5E') + exprCheck('a ^= 5', 'eO1aL5E') + exprCheck('a |= 5', 'oR1aL5E') + + # Additional tests + # a < expression that starts with something that could be a template + exprCheck('A < 42', 'lt1AL42E') + check('function', 'template<> void f(A &v)', + {2:"IE1fR1AI1BX2EE", 3:"IE1fR1AI1BXL2EEE"}) + exprCheck('A<1>::value', {2:'N1AIXL1EEE5valueE'}) + check('class', "template A", {2:"I_iE1A"}) + check('enumerator', 'A = std::numeric_limits::max()', {2:"1A"}) + + def test_type_definitions(): check("type", "public bool b", {1:"b", 2:"1b"}, "bool b") check("type", "bool A::b", {1:"A::b", 2:"N1A1bE"}) @@ -451,17 +533,19 @@ def test_templates(): check('concept', 'template Numerics = (... && Numeric)', {2:'IDpE8Numerics'}) + def test_template_args(): # from breathe#218 check('function', "template " - "void allow(F *f, typename func::type tt)", - {2:"I0E5allowP1FN4funcI1F1BXG!=1EE4typeE"}) + "void allow(F *f, typename func::type tt)", + {2:"I0E5allowP1FN4funcI1F1BXG != 1EE4typeE"}) # from #3542 check('type', "template " "enable_if_not_array_t = std::enable_if_t::value, int>", {2:"I0E21enable_if_not_array_t"}) + def test_attributes(): # style: C++ check('member', '[[]] int f', {1:'f__i', 2:'1f'}) From 32788c4e81a1b1501abbbd11f6ced9742ae7d010 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Tue, 28 Mar 2017 22:29:30 +0900 Subject: [PATCH 79/91] C++, turn on version 3 mangling --- sphinx/domains/cpp.py | 52 +++++++++++++++++++++------------------- tests/test_domain_cpp.py | 18 ++++++++------ 2 files changed, 39 insertions(+), 31 deletions(-) diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index e95de4be9..ce0b589b8 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -327,8 +327,8 @@ _keywords = [ 'while', 'xor', 'xor_eq' ] -_max_id = 2 -_id_prefix = [None, '', '_CPPv2'] +_max_id = 3 +_id_prefix = [None, '', '_CPPv2', '_CPPv3'] # ------------------------------------------------------------------------------ # Id v1 constants @@ -719,7 +719,7 @@ class ASTPointerLiteral(ASTBase): def __unicode__(self): return u'nullptr' - def get_id_v2(self): + def get_id(self, version): return 'LDnE' def describe_signature(self, signode, mode, env, symbol): @@ -736,7 +736,7 @@ class ASTBooleanLiteral(ASTBase): else: return u'false' - def get_id_v2(self): + def get_id(self, version): if self.value: return 'L1E' else: @@ -754,7 +754,7 @@ class ASTNumberLiteral(ASTBase): def __unicode__(self): return self.data - def get_id_v2(self): + def get_id(self, version): return "L%sE" % self.data def describe_signature(self, signode, mode, env, symbol): @@ -770,7 +770,7 @@ class ASTStringLiteral(ASTBase): def __unicode__(self): return self.data - def get_id_v2(self): + def get_id(self, version): # note: the length is not really correct with escaping return "LA%d_KcE" % (len(self.data) - 2) @@ -786,8 +786,8 @@ class ASTParenExpr(ASTBase): def __unicode__(self): return '(' + text_type(self.expr) + ')' - def get_id_v2(self): - return self.expr.get_id_v2() + def get_id(self, version): + return self.expr.get_id(version) def describe_signature(self, signode, mode, env, symbol): signode.append(nodes.Text('(', '(')) @@ -818,9 +818,12 @@ class ASTFoldExpr(ASTBase): res.append(')') return u''.join(res) - def get_id_v2(self): + def get_id(self, version): + assert version >= 3 + if version == 3: + return text_type(self) # TODO: find the right mangling scheme - return text_type(self) + assert False def describe_signature(self, signode, mode, env, symbol): signode.append(nodes.Text('(')) @@ -855,12 +858,13 @@ class ASTBinOpExpr(ASTBase): res.append(text_type(self.exprs[i])) return u''.join(res) - def get_id_v2(self): + def get_id(self, version): + assert version >= 2 res = [] for i in range(len(self.ops)): res.append(_id_operator_v2[self.ops[i]]) - res.append(self.exprs[i].get_id_v2()) - res.append(self.exprs[-1].get_id_v2()) + res.append(self.exprs[i].get_id(version)) + res.append(self.exprs[-1].get_id(version)) return u''.join(res) def describe_signature(self, signode, mode, env, symbol): @@ -936,8 +940,8 @@ class ASTUnaryOpExpr(ASTBase): def __unicode__(self): return text_type(self.op) + text_type(self.expr) - def get_id_v2(self): - return _id_operator_unary_v2[self.op] + self.expr.get_id_v2() + def get_id(self, version): + return _id_operator_unary_v2[self.op] + self.expr.get_id(version) def describe_signature(self, signode, mode, env, symbol): signode.append(nodes.Text(self.op)) @@ -959,10 +963,10 @@ class ASTPostfixCallExpr(ASTBase): res.append(')') return u''.join(res) - def get_id_v2(self, idPrefix): + def get_id(self, idPrefix, version): res = ['cl', idPrefix] for e in self.exprs: - res.append(e.get_id_v2()) + res.append(e.get_id(version)) res.append('E') return u''.join(res) @@ -984,8 +988,8 @@ class ASTPostfixArray(ASTBase): def __unicode__(self): return u'[' + text_type(self.expr) + ']' - def get_id_v2(self, idPrefix): - return 'ix' + idPrefix + self.expr.get_id_v2() + def get_id(self, idPrefix, version): + return 'ix' + idPrefix + self.expr.get_id(version) def describe_signature(self, signode, mode, env, symbol): signode.append(nodes.Text('[')) @@ -1057,10 +1061,10 @@ class ASTPostfixExpr(ASTBase): res.append(text_type(p)) return u''.join(res) - def get_id_v2(self): - id = self.prefix.get_id_v2() + def get_id(self, version): + id = self.prefix.get_id(version) for p in self.postFixes: - id = p.get_id_v2(id) + id = p.get_id(id, version) return id def describe_signature(self, signode, mode, env, symbol): @@ -1591,7 +1595,7 @@ class ASTTemplateArgConstant(ASTBase): return text_type(self).replace(u' ', u'-') if version == 2: return u'X' + text_type(self) + u'E' - return u'X' + self.value.get_id_v2() + u'E' + return u'X' + self.value.get_id(version) + u'E' def describe_signature(self, signode, mode, env, symbol): # type: (addnodes.desc_signature, unicode, BuildEnvironment, Symbol) -> None @@ -2157,7 +2161,7 @@ class ASTArray(ASTBase): else: return u'A_' if self.size: - return u'A' + self.size.get_id_v2() + u'_' + return u'A' + self.size.get_id(version) + u'_' else: return u'A_' diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py index f8ebdac7d..7c471d61e 100644 --- a/tests/test_domain_cpp.py +++ b/tests/test_domain_cpp.py @@ -184,7 +184,7 @@ def test_expressions(): exprCheck('A < 42', 'lt1AL42E') check('function', 'template<> void f(A &v)', {2:"IE1fR1AI1BX2EE", 3:"IE1fR1AI1BXL2EEE"}) - exprCheck('A<1>::value', {2:'N1AIXL1EEE5valueE'}) + exprCheck('A<1>::value', 'N1AIXL1EEE5valueE') check('class', "template A", {2:"I_iE1A"}) check('enumerator', 'A = std::numeric_limits::max()', {2:"1A"}) @@ -293,11 +293,13 @@ def test_function_definitions(): check('function', 'void operator()(const boost::array &v) const', {1:"call-operator__boost::array:VertexID.2:CRC", - 2:"NKclERKN5boost5arrayI8VertexIDX2EEE"}) + 2:"NKclERKN5boost5arrayI8VertexIDX2EEE", + 3:"NKclERKN5boost5arrayI8VertexIDXL2EEEE"}) check('function', 'void operator()(const boost::array &v) const', {1:'call-operator__boost::array:VertexID.2."foo,--bar":CRC', - 2:'NKclERKN5boost5arrayI8VertexIDX2EX"foo, bar"EEE'}) + 2:'NKclERKN5boost5arrayI8VertexIDX2EX"foo, bar"EEE', + 3:'NKclERKN5boost5arrayI8VertexIDXL2EEXLA9_KcEEEE'}) check('function', 'MyClass::MyClass(MyClass::MyClass&&)', {1:"MyClass::MyClass__MyClass::MyClassRR", 2:"N7MyClass7MyClassERRN7MyClass7MyClassE"}) @@ -340,7 +342,8 @@ def test_function_definitions(): x = 'std::vector> &module::test(register int ' \ 'foo, bar[n], std::string baz = "foobar, blah, bleh") const = 0' check('function', x, {1:"module::test__i.barA.ssC", - 2:"NK6module4testEiAn_3barNSt6stringE"}) + 2:"NK6module4testEiAn_3barNSt6stringE", + 3:"NK6module4testEiA1n_3barNSt6stringE"}) check('function', 'int foo(Foo f = Foo(double(), std::make_pair(int(2), double(3.4))))', {1:"foo__Foo", 2:"3foo3Foo"}) @@ -358,8 +361,8 @@ def test_function_definitions(): {1:"result__i.std::error_categoryCR", 2:"6resultiRNSt14error_categoryE"}) check("function", "int *f()", {1:"f", 2:"1fv"}) # tests derived from issue #1753 (skip to keep sanity) - check("function", "f(int (&array)[10])", {2:"1fRA10_i"}) - check("function", "void f(int (&array)[10])", {2:"1fRA10_i"}) + check("function", "f(int (&array)[10])", {2:"1fRA10_i", 3:"1fRAL10E_i"}) + check("function", "void f(int (&array)[10])", {2:"1fRA10_i", 3:"1fRAL10E_i"}) check("function", "void f(float *q(double))", {2:"1fFPfdE"}) check("function", "void f(float *(*q)(double))", {2:"1fPFPfdE"}) check("function", "void f(float (*q)(double))", {2:"1fPFfdE"}) @@ -539,7 +542,8 @@ def test_template_args(): check('function', "template " "void allow(F *f, typename func::type tt)", - {2:"I0E5allowP1FN4funcI1F1BXG != 1EE4typeE"}) + {2:"I0E5allowP1FN4funcI1F1BXG != 1EE4typeE", + 3:"I0E5allowP1FN4funcI1F1BXne1GL1EEE4typeE"}) # from #3542 check('type', "template " "enable_if_not_array_t = std::enable_if_t::value, int>", From d0fc4f80f10fa768c7c32d6bb2c344ff83fe5251 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Thu, 23 Mar 2017 20:09:55 +0900 Subject: [PATCH 80/91] C++, add expr role --- sphinx/domains/cpp.py | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index ce0b589b8..89da9d2ce 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -18,6 +18,7 @@ from docutils import nodes from docutils.parsers.rst import Directive, directives from sphinx import addnodes +from sphinx.environment import NoUri from sphinx.roles import XRefRole from sphinx.locale import l_, _ from sphinx.domains import Domain, ObjType @@ -27,6 +28,7 @@ from sphinx.util.nodes import make_refnode from sphinx.util.pycompat import UnicodeMixin from sphinx.util.docfields import Field, GroupedField + if False: # For type annotation from typing import Any, Callable, Dict, Iterator, List, Match, Pattern, Tuple, Union # NOQA @@ -1108,11 +1110,10 @@ class ASTIdentifier(ASTBase): if mode == 'markType': targetText = prefix + self.identifier pnode = addnodes.pending_xref('', refdomain='cpp', - reftype='typeOrConcept', + reftype='identifier', reftarget=targetText, modname=None, classname=None) key = symbol.get_lookup_key() - assert key pnode['cpp:parent_key'] = key pnode += nodes.Text(self.identifier) signode += pnode @@ -3159,9 +3160,6 @@ class Symbol(object): def get_lookup_key(self): # type: () -> List[Tuple[ASTNestedNameElement, Any]] - if not self.parent: - # specialise for the root - return None symbols = [] s = self while s.parent: @@ -5085,6 +5083,26 @@ class DefinitionParser(object): res.objectType = 'xref' # type: ignore return res + def parse_expression(self): + pos = self.pos + try: + expr = self._parse_expression(False) + self.skip_ws() + self.assert_end() + except DefinitionError as exExpr: + self.pos = pos + try: + expr = self._parse_type(False) + self.skip_ws() + self.assert_end() + except DefinitionError as exType: + header = "Error when parsing (type) expression." + errs = [] + errs.append((exExpr, "If expression:")) + errs.append((exType, "If type:")) + raise self._make_multi_error(errs, header) + return expr + def _make_phony_error_name(): # type: () -> ASTNestedName @@ -5480,6 +5498,7 @@ class CPPExprRole(object): class Warner(object): def warn(self, msg): inliner.reporter.warning(msg, line=lineno) + env = inliner.document.settings.env parser = DefinitionParser(text, Warner(), env.config) try: @@ -5534,7 +5553,8 @@ class CPPDomain(Domain): 'type': CPPXRefRole(), 'concept': CPPXRefRole(), 'enum': CPPXRefRole(), - 'enumerator': CPPXRefRole() + 'enumerator': CPPXRefRole(), + 'expr': CPPExprRole() } initial_data = { 'root_symbol': Symbol(None, None, None, None, None, None), @@ -5627,6 +5647,9 @@ class CPPDomain(Domain): templateShorthand=True, matchSelf=True) if s is None or s.declaration is None: + txtName = text_type(name) + if txtName.startswith('std::') or txtName == 'std': + raise NoUri() return None, None if typ.startswith('cpp:'): @@ -5636,7 +5659,7 @@ class CPPDomain(Domain): declTyp = s.declaration.objectType def checkType(): - if typ == 'any': + if typ == 'any' or typ == 'identifier': return True if declTyp == 'templateParam': return True From d4d0ea68fe78e7b5d5a44439152d244d27b6a53c Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Thu, 4 May 2017 13:34:00 +0900 Subject: [PATCH 81/91] C++, update docs and changelog --- CHANGES | 2 ++ doc/domains.rst | 54 +++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index 2dcefca01..9030eb447 100644 --- a/CHANGES +++ b/CHANGES @@ -11,6 +11,8 @@ Features added -------------- - C++, handle ``decltype(auto)``. +- #2406: C++, add proper parsing of expressions, including linking of identifiers. +- C++, add a ``cpp:expr`` role for inserting inline C++ expressions or types. Features removed ---------------- diff --git a/doc/domains.rst b/doc/domains.rst index 9b2743645..ce6ef9e70 100644 --- a/doc/domains.rst +++ b/doc/domains.rst @@ -563,7 +563,7 @@ a visibility statement (``public``, ``private`` or ``protected``). .. cpp:class:: OuterScope::MyClass : public MyBase, MyOtherBase - A template class can be declared:: + A class template can be declared:: .. cpp:class:: template std::array @@ -728,6 +728,17 @@ a visibility statement (``public``, ``private`` or ``protected``). Proxy to an element of a notional sequence that can be compared, indirected, or incremented. + **Notation** + + .. cpp:var:: It r + + An lvalue. + + **Valid Expressions** + + - :cpp:expr:`*r`, when :cpp:expr:`r` is dereferenceable. + - :cpp:expr:`++r`, with return type :cpp:expr:`It&`, when :cpp:expr:`r` is incrementable. + .. cpp:concept:: template std::Container() Holder of elements, to which it can provide access via @@ -740,6 +751,17 @@ a visibility statement (``public``, ``private`` or ``protected``). Proxy to an element of a notional sequence that can be compared, indirected, or incremented. + **Notation** + + .. cpp:var:: It r + + An lvalue. + + **Valid Expressions** + + - :cpp:expr:`*r`, when :cpp:expr:`r` is dereferenceable. + - :cpp:expr:`++r`, with return type :cpp:expr:`It&`, when :cpp:expr:`r` is incrementable. + .. cpp:concept:: template std::Container() Holder of elements, to which it can provide access via @@ -807,6 +829,30 @@ compatibility. E.g., ``Iterator{A, B, C}`` will be accepted as an introduction even though it would not be valid C++. +Inline Expressions and Tpes +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. rst:role:: cpp:expr + + A role for inserting a C++ expression or type as inline text. + For example:: + + .. cpp:var:: int a = 42 + + .. cpp:function:: int f(int i) + + An expression: :cpp:expr:`a * f(a)`. + A type: :cpp:expr:`const MySortedContainer&`. + + will be rendered as follows: + + .. cpp:var:: int a = 42 + + .. cpp:function:: int f(int i) + + An expression: :cpp:expr:`a * f(a)`. + A type: :cpp:expr:`const MySortedContainer&`. + Namespacing ~~~~~~~~~~~~~~~~~ @@ -842,7 +888,7 @@ directive. .. cpp:function:: std::size_t size() const - declares ``size`` as a member function of the template class ``std::vector``. + declares ``size`` as a member function of the class template ``std::vector``. Equivalently this could have been declared using:: .. cpp:class:: template \ @@ -922,7 +968,7 @@ These roles link to the given declaration types: .. admonition:: Note on References with Templates Parameters/Arguments Sphinx's syntax to give references a custom title can interfere with - linking to template classes, if nothing follows the closing angle + linking to class templates, if nothing follows the closing angle bracket, i.e. if the link looks like this: ``:cpp:class:`MyClass```. This is interpreted as a link to ``int`` with a title of ``MyClass``. In this case, please escape the opening angle bracket with a backslash, @@ -961,7 +1007,7 @@ In general the reference must include the template paraemter declarations, e.g., Currently the lookup only succeed if the template parameter identifiers are equal strings. That is, ``template\ Wrapper::Outer`` will not work. -The inner template class can not be directly referenced, unless the current namespace +The inner class template can not be directly referenced, unless the current namespace is changed or the following shorthand is used. If a template parameter list is omitted, then the lookup will assume either a template or a non-template, but not a partial template specialisation. From b55526f4e87ecfdd5c43cbe4e0dab847ebb0c7c9 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Thu, 4 May 2017 13:43:18 +0900 Subject: [PATCH 82/91] C++, remove fallback expression parser Fixes sphinx-doc/sphinx#2406 --- sphinx/domains/cpp.py | 65 +++++-------------------------------------- 1 file changed, 7 insertions(+), 58 deletions(-) diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 89da9d2ce..863433cbc 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -4086,44 +4086,6 @@ class DefinitionParser(object): # TODO: actually parse the second production return self._parse_assignment_expression(inTemplate=inTemplate) - def _parse_expression_fallback(self, end, parser): - # type: (List[unicode]) -> unicode - # Stupidly "parse" an expression. - # 'end' should be a list of characters which ends the expression. - - # first try to use the provided parser - prevPos = self.pos - try: - return parser() - except DefinitionError as e: - raise - self.warn("Parsing of expression failed. Using fallback parser." - " Error was:\n%s" % e.description) - self.pos = prevPos - # and then the fallback scanning - assert end is not None - self.skip_ws() - startPos = self.pos - if self.match(_string_re): - value = self.matched_text - else: - # TODO: add handling of more bracket-like things, and quote handling - brackets = {'(': ')', '[': ']', '<': '>'} # type: Dict[unicode, unicode] - symbols = [] # type: List[unicode] - while not self.eof: - if (len(symbols) == 0 and self.current_char in end): - break - if self.current_char in brackets.keys(): - symbols.append(brackets[self.current_char]) - elif len(symbols) > 0 and self.current_char == symbols[-1]: - symbols.pop() - self.pos += 1 - if len(end) > 0 and self.eof: - self.fail("Could not find end of expression starting at %d." - % startPos) - value = self.definition[startPos:self.pos].strip() - return value.strip() - def _parse_operator(self): # type: () -> Any self.skip_ws() @@ -4182,9 +4144,7 @@ class DefinitionParser(object): prevErrors.append((e, "If type argument")) self.pos = pos try: - def parser(): - return self._parse_constant_expression(inTemplate=True) - value = self._parse_expression_fallback([',', '>'], parser) + value = self._parse_constant_expression(inTemplate=True) self.skip_ws() if self.skip_string('>'): parsedEnd = True @@ -4526,10 +4486,7 @@ class DefinitionParser(object): if self.skip_string(']'): arrayOps.append(ASTArray(None)) continue - - def parser(): - return self._parse_expression(inTemplate=False) - value = self._parse_expression_fallback([']'], parser) + value = self._parse_expression(inTemplate=False) if not self.skip_string(']'): self.fail("Expected ']' in end of array operator.") arrayOps.append(ASTArray(value)) @@ -4649,17 +4606,11 @@ class DefinitionParser(object): return None else: if outer == 'member': - def parser(): - return self._parse_assignment_expression(inTemplate=False) - value = self._parse_expression_fallback([], parser) + value = self._parse_assignment_expression(inTemplate=False) elif outer == 'templateParam': - def parser(): - return self._parse_assignment_expression(inTemplate=True) - value = self._parse_expression_fallback([',', '>'], parser) + value = self._parse_assignment_expression(inTemplate=True) elif outer is None: # function parameter - def parser(): - return self._parse_assignment_expression(inTemplate=False) - value = self._parse_expression_fallback([',', ')'], parser) + value = self._parse_assignment_expression(inTemplate=False) else: self.fail("Internal error, initializer for outer '%s' not " "implemented." % outer) @@ -4833,10 +4784,8 @@ class DefinitionParser(object): init = None if self.skip_string('='): self.skip_ws() - - def parser(): - return self._parse_constant_expression(inTemplate=False) - init = ASTInitializer(self._parse_expression_fallback([], parser)) + initVal = self._parse_constant_expression(inTemplate=False) + init = ASTInitializer(initVal) return ASTEnumerator(name, init) def _parse_template_parameter_list(self): From 139e09d12023a255b206c1158487d215217be920 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Thu, 4 May 2017 15:57:18 +0900 Subject: [PATCH 83/91] C++, fix type check errors --- sphinx/domains/cpp.py | 60 +++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 33 deletions(-) diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 863433cbc..70a806ae5 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -588,11 +588,6 @@ class ASTBase(UnicodeMixin): """Clone a definition expression node.""" return deepcopy(self) - def get_id(self, version): - # type: (int) -> unicode - """Return the id for the node.""" - raise NotImplementedError(repr(self)) - def get_name(self): # type: () -> unicode """Return the name. @@ -805,19 +800,19 @@ class ASTFoldExpr(ASTBase): self.rightExpr = rightExpr def __unicode__(self): - res = ['('] + res = [u'('] if self.leftExpr: res.append(text_type(self.leftExpr)) - res.append(' ') + res.append(u' ') res.append(text_type(self.op)) - res.append(' ') - res.append('...') + res.append(u' ') + res.append(u'...') if self.rightExpr: - res.append(' ') + res.append(u' ') res.append(text_type(self.op)) - res.append(' ') + res.append(u' ') res.append(text_type(self.rightExpr)) - res.append(')') + res.append(u')') return u''.join(res) def get_id(self, version): @@ -918,9 +913,9 @@ class ASTCastExpr(ASTBase): self.expr = expr def __unicode__(self): - res = ['('] + res = [u'('] res.append(text_type(self.typ)) - res.append(')') + res.append(u')') res.append(text_type(self.expr)) return u''.join(res) @@ -955,14 +950,14 @@ class ASTPostfixCallExpr(ASTBase): self.exprs = exprs def __unicode__(self): - res = ['('] + res = [u'('] first = True for e in self.exprs: if not first: - res.append(', ') + res.append(u', ') first = False res.append(text_type(e)) - res.append(')') + res.append(u')') return u''.join(res) def get_id(self, idPrefix, version): @@ -1200,7 +1195,7 @@ class ASTTemplateParamType(ASTBase): return self.data.get_identifier() def get_id(self, version, objectType=None, symbol=None): - # type: (unicode, Symbol) -> unicode + # type: (int, unicode, Symbol) -> unicode # this is not part of the normal name mangling in C++ assert version >= 2 if symbol: @@ -2142,7 +2137,6 @@ class ASTDeclSpecs(ASTBase): class ASTArray(ASTBase): def __init__(self, size): - # type: (unicode) -> None self.size = size def __unicode__(self): @@ -2218,14 +2212,15 @@ class ASTDeclaratorPtr(ASTBase): def get_ptr_suffix_id(self, version): # type: (int) -> unicode if version == 1: - res = 'P' + res = ['P'] if self.volatile: - res += 'V' + res.append('V') if self.const: - res += 'C' - return res + self.next.get_ptr_suffix_id(version) + res.append('C') + res.append(self.next.get_ptr_suffix_id(version)) + return u''.join(res) - res = [self.next.get_ptr_suffix_id(version)] # type: List[unicode] + res = [self.next.get_ptr_suffix_id(version)] res.append('P') if self.volatile: res.append('V') @@ -2612,7 +2607,6 @@ class ASTDeclaratorNameParamQual(ASTBase): class ASTInitializer(ASTBase): def __init__(self, value): - # type: (unicode) -> None self.value = value def __unicode__(self): @@ -2620,7 +2614,7 @@ class ASTInitializer(ASTBase): return u''.join([' = ', text_type(self.value)]) def describe_signature(self, signode, mode, env, symbol): - # type: (addnodes.desc_signature, unicode) -> None + # type: (addnodes.desc_signature, unicode, BuildEnvironment, Symbol) -> None _verify_description_mode(mode) signode.append(nodes.Text(' = ')) self.value.describe_signature(signode, 'markType', env, symbol) @@ -2989,7 +2983,7 @@ class ASTDeclaration(ASTBase): return self.declaration.name def get_id(self, version, prefixed=True): - # type: (int) -> unicode + # type: (int, bool) -> unicode if version == 1: if self.templatePrefix: raise NoOldIdError() @@ -3907,7 +3901,7 @@ class DefinitionParser(object): self.pos -= 2 else: name = self._parse_nested_name() - postFixes.append(ASTPostfixMember(name)) + postFixes.append(ASTPostfixMember(name)) # type: ignore continue if self.skip_string('->'): if self.skip_string('*'): @@ -3915,13 +3909,13 @@ class DefinitionParser(object): self.pos -= 3 else: name = self._parse_nested_name() - postFixes.append(ASTPostfixMemberOfPointer(name)) + postFixes.append(ASTPostfixMemberOfPointer(name)) # type: ignore continue if self.skip_string('++'): - postFixes.append(ASTPostfixInc()) + postFixes.append(ASTPostfixInc()) # type: ignore continue if self.skip_string('--'): - postFixes.append(ASTPostfixDec()) + postFixes.append(ASTPostfixDec()) # type: ignore continue if self.skip_string_and_ws('('): # TODO: handled braced init @@ -3937,7 +3931,7 @@ class DefinitionParser(object): break if not self.skip_string(','): self.fail("Error in cast or call, expected ',' or ')'.") - postFixes.append(ASTPostfixCallExpr(exprs)) + postFixes.append(ASTPostfixCallExpr(exprs)) # type: ignore continue break if len(postFixes) == 0: @@ -5493,7 +5487,7 @@ class CPPDomain(Domain): 'namespace-push': CPPNamespacePushObject, 'namespace-pop': CPPNamespacePopObject } - roles = { + roles = { # type: ignore 'any': CPPXRefRole(), 'class': CPPXRefRole(), 'func': CPPXRefRole(fix_parens=True), From 321a5e7a5e757b7138e4dfb9aa0e163afe3916ea Mon Sep 17 00:00:00 2001 From: jfbu Date: Thu, 4 May 2017 09:36:04 +0200 Subject: [PATCH 84/91] Fix #3702: abstract LaTeX styling of figure legends into sphinxlegend --- doc/latex.rst | 9 ++++++++- sphinx/texinputs/sphinx.sty | 2 ++ sphinx/writers/latex.py | 4 ++-- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/doc/latex.rst b/doc/latex.rst index ebc14765e..b027528fa 100644 --- a/doc/latex.rst +++ b/doc/latex.rst @@ -438,7 +438,14 @@ Let us now list some macros from the package file the new macros are wrappers of the formerly hard-coded ``\texttt``, ``\emph``, ... The default definitions can be found in :file:`sphinx.sty`. -- paragraph level environments: for each admonition type ````, the +- a :dudir:`figure` may have an optional legend with arbitrary body + elements: they are rendered in a ``sphinxlegend`` environment. The default + definition issues ``\small``, and ends with ``\par``. + + .. versionadded:: 1.5.6 + formerly, the ``\small`` was hardcoded in LaTeX writer and the ending + ``\par`` was lacking. +- for each admonition type ````, the used environment is named ``sphinx``. They may be ``\renewenvironment`` 'd individually, and must then be defined with one argument (it is the heading of the notice, for example ``Warning:`` for :dudir:`warning` directive, if diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty index 15f9910ff..7a142c116 100644 --- a/sphinx/texinputs/sphinx.sty +++ b/sphinx/texinputs/sphinx.sty @@ -1383,6 +1383,8 @@ \protected\def\sphinxstyleliteralstrong {\sphinxbfcode} \protected\def\sphinxstyleabbreviation {\textsc} \protected\def\sphinxstyleliteralintitle {\sphinxcode} +% figure legend comes after caption and may contain arbitrary body elements +\newenvironment{sphinxlegend}{\par\small}{\par} % LaTeX writer uses macros to hide double quotes from \sphinxcode's \@noligs \protected\def\sphinxquotedblleft{``} diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index be0771c09..44155da71 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -1599,10 +1599,10 @@ class LaTeXTranslator(nodes.NodeVisitor): self.unrestrict_footnote(node) def visit_legend(self, node): - self.body.append('{\\small ') + self.body.append('\n\\begin{sphinxlegend}') def depart_legend(self, node): - self.body.append('}') + self.body.append('\\end{sphinxlegend}\n') def visit_admonition(self, node): self.body.append('\n\\begin{sphinxadmonition}{note}') From da8007c3f481615085157a18c3fe8932366913e6 Mon Sep 17 00:00:00 2001 From: jfbu Date: Fri, 5 May 2017 13:15:41 +0200 Subject: [PATCH 85/91] Update CHANGES for PR #3703 --- CHANGES | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES b/CHANGES index 7c0c7e564..e5fc48393 100644 --- a/CHANGES +++ b/CHANGES @@ -20,6 +20,7 @@ Bugs fixed * #3588: No compact (p tag) html output in the i18n document build even when :confval:`html_compact_lists` is True. * #3685: AttributeError when using 3rd party domains +* #3702: LaTeX writer styles figure legends with a hard-coded ``\small`` Testing -------- From 698d8d440eebdbea04f769e625dcecf834778ab2 Mon Sep 17 00:00:00 2001 From: jfbu Date: Fri, 5 May 2017 18:18:51 +0200 Subject: [PATCH 86/91] Change slightly console output of LaTeX deprecation macro --- sphinx/texinputs/sphinx.sty | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty index 7a142c116..b7a514a5d 100644 --- a/sphinx/texinputs/sphinx.sty +++ b/sphinx/texinputs/sphinx.sty @@ -25,15 +25,16 @@ %% for deprecation warnings \newcommand\sphinxdeprecationwarning[4]{% #1 the deprecated macro or name, -% #2 = version when deprecated, #3 = version when removed, #4 = message +% #2 = when deprecated, #3 = when removed, #4 = additional info \edef\spx@tempa{\detokenize{#1}}% \spx@ifundefined{sphinx_depr_\spx@tempa}{% \global\expandafter\let\csname sphinx_depr_\spx@tempa\endcsname\spx@tempa \expandafter\AtEndDocument\expandafter{\expandafter\let\expandafter \sphinxdeprecatedmacro\csname sphinx_depr_\spx@tempa\endcsname \PackageWarningNoLine{sphinx}{^^J**** SPHINX DEPRECATION WARNING:^^J - \sphinxdeprecatedmacro\space will be (or has been) - deprecated at Sphinx #2^^J and will be removed at Sphinx #3.^^J + \sphinxdeprecatedmacro^^J + \@spaces- is deprecated at Sphinx #2^^J + \@spaces- and removed at Sphinx #3.^^J #4^^J****}}% }{% warning already emitted (at end of latex log), don't repeat }} @@ -1361,8 +1362,10 @@ Anyhow, Sphinx mark-up uses only \string\sphinx\@tempa.}% % and also at end of log for better visibility \expandafter\sphinxdeprecationwarning\expandafter{\csname\@tempa\endcsname}{1.6}{1.7} - {\sphinxdeprecatedmacro\space already existed at Sphinx loading time! Not redefined!^^J - Sphinx mark-up uses only \string\sphinx\expandafter\@gobble\sphinxdeprecatedmacro.}% + {\sphinxdeprecatedmacro already existed at Sphinx loading time! Not redefined!^^J + Sphinx mark-up uses only \string\sphinx\expandafter\@gobble\sphinxdeprecatedmacro.^^J + Note: if this warning is about macro \string\strong, it presumably results^^J + from fontspec 2.6 having defined it prior to Sphinx. No need for alarm!}% }% \fi \fi From 527f5777ba563a3186611eed5f99708bd5d8fe77 Mon Sep 17 00:00:00 2001 From: Iacchus Mercurius Date: Sun, 7 May 2017 00:29:38 -0300 Subject: [PATCH 87/91] please see #3700 --- sphinx/ext/viewcode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/ext/viewcode.py b/sphinx/ext/viewcode.py index 6ac8c76be..25e5fe82a 100644 --- a/sphinx/ext/viewcode.py +++ b/sphinx/ext/viewcode.py @@ -156,7 +156,7 @@ def collect_pages(app): # construct a page name for the highlighted source pagename = '_modules/' + modname.replace('.', '/') # highlight the source using the builder's highlighter - if env.config.highlight_language in ('python3', 'default'): + if env.config.highlight_language in ('python3', 'default', 'none'): lexer = env.config.highlight_language else: lexer = 'python' From 579a79660e843cd86278832c9aad3fec8f8a5fb3 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 7 May 2017 14:09:54 +0900 Subject: [PATCH 88/91] Update type annotations for new mypy --- mypy.ini | 1 - sphinx/__init__.py | 2 +- sphinx/builders/applehelp.py | 2 +- sphinx/builders/gettext.py | 4 ++-- sphinx/builders/html.py | 6 +++--- sphinx/builders/linkcheck.py | 6 +++--- sphinx/builders/qthelp.py | 4 ++-- sphinx/ext/autodoc.py | 2 +- sphinx/ext/autosummary/generate.py | 5 ++--- sphinx/ext/doctest.py | 2 +- sphinx/ext/ifconfig.py | 2 +- sphinx/ext/inheritance_diagram.py | 2 +- sphinx/quickstart.py | 2 +- sphinx/registry.py | 2 +- sphinx/search/ja.py | 6 +++--- sphinx/transforms/__init__.py | 2 +- sphinx/util/fileutil.py | 2 +- sphinx/util/images.py | 2 +- sphinx/util/parallel.py | 2 +- sphinx/util/pycompat.py | 2 +- sphinx/util/requests.py | 2 +- sphinx/writers/latex.py | 2 +- sphinx/writers/manpage.py | 2 +- sphinx/writers/texinfo.py | 2 +- sphinx/writers/text.py | 2 +- 25 files changed, 33 insertions(+), 35 deletions(-) diff --git a/mypy.ini b/mypy.ini index 8fbf24f30..ec12ea587 100644 --- a/mypy.ini +++ b/mypy.ini @@ -2,7 +2,6 @@ python_version = 2.7 ignore_missing_imports = True follow_imports = skip -fast_parser = True incremental = True check_untyped_defs = True warn_unused_ignores = True diff --git a/sphinx/__init__.py b/sphinx/__init__.py index cd8ad2c0d..f8ee222c3 100644 --- a/sphinx/__init__.py +++ b/sphinx/__init__.py @@ -58,7 +58,7 @@ if __version__.endswith('+'): stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = p.communicate() if out: - __display_version__ += '/' + out.decode().strip() # type: ignore + __display_version__ += '/' + out.decode().strip() except Exception: pass diff --git a/sphinx/builders/applehelp.py b/sphinx/builders/applehelp.py index fa47429e2..f8df9310c 100644 --- a/sphinx/builders/applehelp.py +++ b/sphinx/builders/applehelp.py @@ -190,7 +190,7 @@ class AppleHelpBuilder(StandaloneHTMLBuilder): # Build the access page logger.info(bold('building access page...'), nonl=True) - with codecs.open(path.join(language_dir, '_access.html'), 'w') as f: + with codecs.open(path.join(language_dir, '_access.html'), 'w') as f: # type: ignore f.write(access_page_template % { 'toc': htmlescape(toc, quote=True), 'title': htmlescape(self.config.applehelp_title) diff --git a/sphinx/builders/gettext.py b/sphinx/builders/gettext.py index 4da19bd2c..b684104c1 100644 --- a/sphinx/builders/gettext.py +++ b/sphinx/builders/gettext.py @@ -31,7 +31,7 @@ from sphinx.locale import pairindextypes if False: # For type annotation - from typing import Any, Dict, Iterable, List, Set, Tuple # NOQA + from typing import Any, DefaultDict, Dict, Iterable, List, Set, Tuple # NOQA from docutils import nodes # NOQA from sphinx.util.i18n import CatalogInfo # NOQA from sphinx.application import Sphinx # NOQA @@ -122,7 +122,7 @@ class I18nBuilder(Builder): self.env.set_versioning_method(self.versioning_method, self.env.config.gettext_uuid) self.tags = I18nTags() - self.catalogs = defaultdict(Catalog) # type: defaultdict[unicode, Catalog] + self.catalogs = defaultdict(Catalog) # type: DefaultDict[unicode, Catalog] def get_target_uri(self, docname, typ=None): # type: (unicode, unicode) -> unicode diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index c98f5fee8..c8ca51c87 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -390,7 +390,7 @@ class StandaloneHTMLBuilder(Builder): # typically doesn't include the time of day lufmt = self.config.html_last_updated_fmt if lufmt is not None: - self.last_updated = format_date(lufmt or _('%b %d, %Y'), + self.last_updated = format_date(lufmt or _('%b %d, %Y'), # type: ignore language=self.config.language) else: self.last_updated = None @@ -823,7 +823,7 @@ class StandaloneHTMLBuilder(Builder): else: f = open(searchindexfn, 'rb') # type: ignore with f: - self.indexer.load(f, self.indexer_format) # type: ignore + self.indexer.load(f, self.indexer_format) except (IOError, OSError, ValueError): if keep: logger.warning('search index couldn\'t be loaded, but not all ' @@ -993,7 +993,7 @@ class StandaloneHTMLBuilder(Builder): else: f = open(searchindexfn + '.tmp', 'wb') # type: ignore with f: - self.indexer.dump(f, self.indexer_format) # type: ignore + self.indexer.dump(f, self.indexer_format) movefile(searchindexfn + '.tmp', searchindexfn) logger.info('done') diff --git a/sphinx/builders/linkcheck.py b/sphinx/builders/linkcheck.py index 722041d5b..c52b808cd 100644 --- a/sphinx/builders/linkcheck.py +++ b/sphinx/builders/linkcheck.py @@ -16,7 +16,7 @@ import threading from os import path from requests.exceptions import HTTPError -from six.moves import queue, html_parser # type: ignore +from six.moves import queue, html_parser from six.moves.urllib.parse import unquote from docutils import nodes @@ -105,8 +105,8 @@ class CheckExternalLinksBuilder(Builder): open(path.join(self.outdir, 'output.txt'), 'w').close() # create queues and worker threads - self.wqueue = queue.Queue() - self.rqueue = queue.Queue() + self.wqueue = queue.Queue() # type: queue.Queue + self.rqueue = queue.Queue() # type: queue.Queue self.workers = [] # type: List[threading.Thread] for i in range(self.app.config.linkcheck_workers): thread = threading.Thread(target=self.check_thread) diff --git a/sphinx/builders/qthelp.py b/sphinx/builders/qthelp.py index ac3117733..b1c94c548 100644 --- a/sphinx/builders/qthelp.py +++ b/sphinx/builders/qthelp.py @@ -206,7 +206,7 @@ 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 % { # type: ignore + f.write(project_template % { 'outname': htmlescape(outname), 'title': htmlescape(self.config.html_title), 'version': htmlescape(self.config.version), @@ -223,7 +223,7 @@ 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 % { # type: ignore + f.write(collection_template % { 'outname': htmlescape(outname), 'title': htmlescape(self.config.html_short_title), 'homepage': htmlescape(homepage), diff --git a/sphinx/ext/autodoc.py b/sphinx/ext/autodoc.py index 58c7317a3..1b314bd0e 100644 --- a/sphinx/ext/autodoc.py +++ b/sphinx/ext/autodoc.py @@ -48,7 +48,7 @@ try: if sys.version_info >= (3,): import typing else: - typing = None # type: ignore + typing = None except ImportError: typing = None diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py index 3dd4a2fcf..d40b4787d 100644 --- a/sphinx/ext/autosummary/generate.py +++ b/sphinx/ext/autosummary/generate.py @@ -259,8 +259,7 @@ def find_autosummary_in_files(filenames): with codecs.open(filename, 'r', encoding='utf-8', # type: ignore errors='ignore') as f: lines = f.read().splitlines() - documented.extend(find_autosummary_in_lines(lines, # type: ignore - filename=filename)) + documented.extend(find_autosummary_in_lines(lines, filename=filename)) return documented @@ -273,7 +272,7 @@ def find_autosummary_in_docstring(name, module=None, filename=None): try: real_name, obj, parent, modname = import_by_name(name) lines = pydoc.getdoc(obj).splitlines() - return find_autosummary_in_lines(lines, module=name, filename=filename) + return find_autosummary_in_lines(lines, module=name, filename=filename) # type: ignore except AttributeError: pass except ImportError as e: diff --git a/sphinx/ext/doctest.py b/sphinx/ext/doctest.py index 27672ff43..42363fdfd 100644 --- a/sphinx/ext/doctest.py +++ b/sphinx/ext/doctest.py @@ -307,7 +307,7 @@ class DocTestBuilder(Builder): self.outfile = None # type: IO self.outfile = codecs.open(path.join(self.outdir, 'output.txt'), # type: ignore 'w', encoding='utf-8') - self.outfile.write(('Results of doctest builder run on %s\n' # type: ignore + self.outfile.write(('Results of doctest builder run on %s\n' '==================================%s\n') % (date, '=' * len(date))) diff --git a/sphinx/ext/ifconfig.py b/sphinx/ext/ifconfig.py index 036cbdf67..c700649dd 100644 --- a/sphinx/ext/ifconfig.py +++ b/sphinx/ext/ifconfig.py @@ -66,7 +66,7 @@ def process_ifconfig_nodes(app, doctree, docname): except Exception as err: # handle exceptions in a clean fashion from traceback import format_exception_only - msg = ''.join(format_exception_only(err.__class__, err)) # type: ignore + msg = ''.join(format_exception_only(err.__class__, err)) newnode = doctree.reporter.error('Exception occured in ' 'ifconfig expression: \n%s' % msg, base_node=node) diff --git a/sphinx/ext/inheritance_diagram.py b/sphinx/ext/inheritance_diagram.py index 09b60655d..f5b0228a5 100644 --- a/sphinx/ext/inheritance_diagram.py +++ b/sphinx/ext/inheritance_diagram.py @@ -42,7 +42,7 @@ import inspect try: from hashlib import md5 except ImportError: - from md5 import md5 # type: ignore + from md5 import md5 from six import text_type from six.moves import builtins diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py index 50d200e60..e70de64b6 100644 --- a/sphinx/quickstart.py +++ b/sphinx/quickstart.py @@ -557,7 +557,7 @@ def valid_dir(d): class MyFormatter(optparse.IndentedHelpFormatter): - def format_usage(self, usage): + def format_usage(self, usage): # type: ignore # type: (str) -> str return usage diff --git a/sphinx/registry.py b/sphinx/registry.py index a86fd8915..2efd5e08a 100644 --- a/sphinx/registry.py +++ b/sphinx/registry.py @@ -133,7 +133,7 @@ class SphinxComponentRegistry(object): directive = type(directivename, # type: ignore (GenericObject, object), {'indextemplate': indextemplate, - 'parse_node': staticmethod(parse_node), # type: ignore + 'parse_node': staticmethod(parse_node), 'doc_field_types': doc_field_types}) stddomain = self.domains['std'] diff --git a/sphinx/search/ja.py b/sphinx/search/ja.py index a2703441b..d1d922dd4 100644 --- a/sphinx/search/ja.py +++ b/sphinx/search/ja.py @@ -41,7 +41,7 @@ from sphinx.util import import_object if False: # For type annotation - from typing import Dict, List # NOQA + from typing import Any, Dict, List # NOQA class BaseSplitter(object): @@ -65,8 +65,8 @@ class MecabSplitter(BaseSplitter): def __init__(self, options): # type: (Dict) -> None super(MecabSplitter, self).__init__(options) - self.ctypes_libmecab = None # type: ignore - self.ctypes_mecab = None # type: ignore + self.ctypes_libmecab = None # type: Any + self.ctypes_mecab = None # type: Any if not native_module: self.init_ctypes(options) else: diff --git a/sphinx/transforms/__init__.py b/sphinx/transforms/__init__.py index a60b717b9..5109a530c 100644 --- a/sphinx/transforms/__init__.py +++ b/sphinx/transforms/__init__.py @@ -116,7 +116,7 @@ class DefaultSubstitutions(SphinxTransform): text = self.config[refname] if refname == 'today' and not text: # special handling: can also specify a strftime format - text = format_date(self.config.today_fmt or _('%b %d, %Y'), + text = format_date(self.config.today_fmt or _('%b %d, %Y'), # type: ignore language=self.config.language) ref.replace_self(nodes.Text(text, text)) diff --git a/sphinx/util/fileutil.py b/sphinx/util/fileutil.py index 772e41331..04fd338b7 100644 --- a/sphinx/util/fileutil.py +++ b/sphinx/util/fileutil.py @@ -51,7 +51,7 @@ def copy_asset_file(source, destination, context=None, renderer=None): if destination.lower().endswith('_t'): destination = destination[:-2] with codecs.open(destination, 'w', encoding='utf-8') as fdst: # type: ignore - fdst.write(renderer.render_string(fsrc.read(), context)) # type: ignore + fdst.write(renderer.render_string(fsrc.read(), context)) else: copyfile(source, destination) diff --git a/sphinx/util/images.py b/sphinx/util/images.py index 7bb904d22..2d228e66d 100644 --- a/sphinx/util/images.py +++ b/sphinx/util/images.py @@ -118,5 +118,5 @@ def parse_data_uri(uri): elif prop: mimetype = prop - image_data = base64.b64decode(data) # type: ignore + image_data = base64.b64decode(data) return DataURI(mimetype, charset, image_data) diff --git a/sphinx/util/parallel.py b/sphinx/util/parallel.py index bcc6117f6..7ca4e94e6 100644 --- a/sphinx/util/parallel.py +++ b/sphinx/util/parallel.py @@ -88,7 +88,7 @@ class ParallelTasks(object): failed = False except BaseException as err: failed = True - errmsg = traceback.format_exception_only(err.__class__, err)[0].strip() # type: ignore # NOQA + errmsg = traceback.format_exception_only(err.__class__, err)[0].strip() ret = (errmsg, traceback.format_exc()) logging.convert_serializable(collector.logs) pipe.send((failed, collector.logs, ret)) diff --git a/sphinx/util/pycompat.py b/sphinx/util/pycompat.py index 5a1fda909..6122c1697 100644 --- a/sphinx/util/pycompat.py +++ b/sphinx/util/pycompat.py @@ -79,7 +79,7 @@ if PY3: return text_type(tree) else: # no need to refactor on 2.x versions - convert_with_2to3 = None # type: ignore + convert_with_2to3 = None # htmlescape() diff --git a/sphinx/util/requests.py b/sphinx/util/requests.py index 6834e6368..3dc1a30b2 100644 --- a/sphinx/util/requests.py +++ b/sphinx/util/requests.py @@ -120,7 +120,7 @@ def _get_tls_cacert(url, config): certs = getattr(config, 'tls_cacerts', None) if not certs: return True - elif isinstance(certs, (string_types, tuple)): # type: ignore + elif isinstance(certs, (string_types, tuple)): return certs # type: ignore else: hostname = urlsplit(url)[1] diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 5dea34c54..728d5c94e 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -557,7 +557,7 @@ class LaTeXTranslator(nodes.NodeVisitor): if builder.config.today: self.elements['date'] = builder.config.today else: - self.elements['date'] = format_date(builder.config.today_fmt or _('%b %d, %Y'), + self.elements['date'] = format_date(builder.config.today_fmt or _('%b %d, %Y'), # type: ignore # NOQA language=builder.config.language) if builder.config.latex_logo: # no need for \\noindent here, used in flushright diff --git a/sphinx/writers/manpage.py b/sphinx/writers/manpage.py index 830b1148a..71c2aac0b 100644 --- a/sphinx/writers/manpage.py +++ b/sphinx/writers/manpage.py @@ -107,7 +107,7 @@ class ManualPageTranslator(BaseTranslator): if builder.config.today: self._docinfo['date'] = builder.config.today else: - self._docinfo['date'] = format_date(builder.config.today_fmt or _('%b %d, %Y'), + self._docinfo['date'] = format_date(builder.config.today_fmt or _('%b %d, %Y'), # type: ignore # NOQA language=builder.config.language) self._docinfo['copyright'] = builder.config.copyright self._docinfo['version'] = builder.config.version diff --git a/sphinx/writers/texinfo.py b/sphinx/writers/texinfo.py index 13fac45d3..d7e08510e 100644 --- a/sphinx/writers/texinfo.py +++ b/sphinx/writers/texinfo.py @@ -238,7 +238,7 @@ class TexinfoTranslator(nodes.NodeVisitor): 'project': self.escape(self.builder.config.project), 'copyright': self.escape(self.builder.config.copyright), 'date': self.escape(self.builder.config.today or - format_date(self.builder.config.today_fmt or _('%b %d, %Y'), + format_date(self.builder.config.today_fmt or _('%b %d, %Y'), # type: ignore # NOQA language=self.builder.config.language)) }) # title diff --git a/sphinx/writers/text.py b/sphinx/writers/text.py index 687eecfd7..d2b2f9045 100644 --- a/sphinx/writers/text.py +++ b/sphinx/writers/text.py @@ -661,7 +661,7 @@ class TextTranslator(nodes.NodeVisitor): self.add_text(''.join(out) + self.nl) def writerow(row): - # type: (list[List[unicode]]) -> None + # type: (List[List[unicode]]) -> None lines = zip_longest(*row) for line in lines: out = ['|'] From 59412d7f6ba9f20331db988e60f203feffcfd0b0 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 7 May 2017 14:42:17 +0900 Subject: [PATCH 89/91] Update CHANGES for PR #3700, #3714 --- CHANGES | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES b/CHANGES index 354f90e47..69e811829 100644 --- a/CHANGES +++ b/CHANGES @@ -27,6 +27,7 @@ Bugs fixed 1.5 series, due to hard-coded usage of ``--halt-on-error`` option. (refs #3695) * #3683: sphinx.websupport module is not provided by default * #3683: Failed to build document if builder.css_file.insert() is called +* #3714: viewcode extension not taking ``highlight_code='none'`` in account Testing -------- From 69891bd9fbaac3b44a444d4df27dfe2a8ce5b3f3 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 3 May 2017 19:02:16 +0900 Subject: [PATCH 90/91] Cherry-pick #3699 into 1.6-release branch --- CHANGES | 1 + sphinx/transforms/post_transforms/__init__.py | 34 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/CHANGES b/CHANGES index 69e811829..06c13453f 100644 --- a/CHANGES +++ b/CHANGES @@ -28,6 +28,7 @@ Bugs fixed * #3683: sphinx.websupport module is not provided by default * #3683: Failed to build document if builder.css_file.insert() is called * #3714: viewcode extension not taking ``highlight_code='none'`` in account +* #3698: Moving :doc: to std domain broke backwards compatibility Testing -------- diff --git a/sphinx/transforms/post_transforms/__init__.py b/sphinx/transforms/post_transforms/__init__.py index 8b2a5b22c..8e2580a2d 100644 --- a/sphinx/transforms/post_transforms/__init__.py +++ b/sphinx/transforms/post_transforms/__init__.py @@ -9,9 +9,13 @@ :license: BSD, see LICENSE for details. """ +import warnings + from docutils import nodes +from docutils.utils import get_source_line from sphinx import addnodes +from sphinx.deprecation import RemovedInSphinx20Warning from sphinx.environment import NoUri from sphinx.locale import _ from sphinx.transforms import SphinxTransform @@ -27,6 +31,35 @@ if False: logger = logging.getLogger(__name__) +class DocReferenceMigrator(SphinxTransform): + """Migrate :doc: reference to std domain.""" + + default_priority = 5 # before ReferencesResolver + + def apply(self): + # type: () -> None + for node in self.document.traverse(addnodes.pending_xref): + if node.get('reftype') == 'doc' and node.get('refdomain') is None: + source, line = get_source_line(node) + if source and line: + location = "%s:%s" % (source, line) + elif source: + location = "%s:" % source + elif line: + location = ":%s" % line + else: + location = None + + message = ('Invalid pendig_xref node detected. ' + ':doc: reference should have refdomain=std attribute.') + if location: + warnings.warn("%s: %s" % (location, message), + RemovedInSphinx20Warning) + else: + warnings.warn(message, RemovedInSphinx20Warning) + node['refdomain'] = 'std' + + class ReferencesResolver(SphinxTransform): """ Resolves cross-references on doctrees. @@ -165,6 +198,7 @@ class OnlyNodeTransform(SphinxTransform): def setup(app): # type: (Sphinx) -> Dict[unicode, Any] + app.add_post_transform(DocReferenceMigrator) app.add_post_transform(ReferencesResolver) app.add_post_transform(OnlyNodeTransform) From 9612dd6fa72f9a48219e9856f7a8e0a140501fbd Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 7 May 2017 15:44:46 +0900 Subject: [PATCH 91/91] Add sqlalchemy and whoosh to test-reqs --- test-reqs.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test-reqs.txt b/test-reqs.txt index 874948dd5..3a7bde8ea 100644 --- a/test-reqs.txt +++ b/test-reqs.txt @@ -8,6 +8,8 @@ Pygments>=2.0 docutils>=0.11 snowballstemmer>=1.1 babel +sqlalchemy>=0.9 +whoosh>=2.0 alabaster sphinx_rtd_theme sphinxcontrib-websupport