Merge branch '1.7'

This commit is contained in:
Takeshi KOMIYA 2018-02-23 23:44:00 +09:00
commit a2bce15989
14 changed files with 236 additions and 62 deletions

View File

@ -4,3 +4,7 @@ coverage:
default:
# allowed to drop X% and still result in a "success" commit status
threshold: 0.05
patch:
default:
# allowed to drop X% and still result in a "success" commit status
threshold: 0.05

33
CHANGES
View File

@ -45,7 +45,7 @@ Bugs fixed
Testing
--------
Release 1.7.1 (in development)
Release 1.7.2 (in development)
==============================
Dependencies
@ -57,13 +57,27 @@ Incompatible changes
Deprecated
----------
Features added
--------------
Bugs fixed
----------
Testing
--------
Release 1.7.1 (released Feb 23, 2018)
=====================================
Deprecated
----------
* #4623: ``sphinx.build_main()`` is deprecated. Use
``sphinx.cmd.build.build_main()`` instead.
* autosummary: The interface of ``sphinx.ext.autosummary.get_documenter()`` has
been changed (Since 1.7.0)
Features added
--------------
* #4664: ``sphinx.ext.intersphinx.debug()`` is deprecated. Use
``sphinx.ext.intersphinx.inspect_main()`` instead.
Bugs fixed
----------
@ -81,9 +95,14 @@ Bugs fixed
* #4630: Have order on msgids in sphinx.pot deterministic
* #4563: autosummary: Incorrect end of line punctuation detection
* #4577: Enumerated sublists with explicit start with wrong number
Testing
--------
* #4641: A external link in TOC cannot contain "?" with ``:glob:`` option
* C++, add missing parsing of explicit casts and typeid in expression parsing.
* C++, add missing parsing of ``this`` in expression parsing.
* #4655: Fix incomplete localization strings in Polish
* #4653: Fix error reporting for parameterless ImportErrors
* #4664: Reading objects.inv fails again
* #4662: ``any`` refs with ``term`` targets crash when an ambiguity is
encountered
Release 1.7.0 (released Feb 12, 2018)
=====================================

View File

@ -134,6 +134,7 @@ class Epub3Builder(_epub_base.EpubBuilder):
metadata['ibook_scroll_axis'] = IBOOK_SCROLL_AXIS.get(writing_mode)
metadata['date'] = self.esc(format_date("%Y-%m-%dT%H:%M:%SZ"))
metadata['version'] = self.esc(self.config.version)
metadata['epub_version'] = self.config.epub_version
return metadata
def prepare_writing(self, docnames):
@ -230,6 +231,7 @@ def setup(app):
# config values
app.add_config_value('epub_basename', lambda self: make_filename(self.project), None)
app.add_config_value('epub_version', 3.0, 'epub') # experimental
app.add_config_value('epub_theme', 'epub', 'epub')
app.add_config_value('epub_theme_options', {}, 'epub')
app.add_config_value('epub_title', lambda self: self.html_title, 'epub')

View File

@ -73,7 +73,9 @@ class TocTree(Directive):
for entry in self.content:
if not entry:
continue
if glob and ('*' in entry or '?' in entry or '[' in entry):
# look for explicit titles ("Some Title <document>")
explicit = explicit_title_re.match(entry)
if glob and ('*' in entry or '?' in entry or '[' in entry) and not explicit:
patname = docname_join(env.docname, entry)
docnames = sorted(patfilter(all_docnames, patname))
for docname in docnames:
@ -85,11 +87,9 @@ class TocTree(Directive):
'toctree glob pattern %r didn\'t match any documents'
% entry, line=self.lineno))
else:
# look for explicit titles ("Some Title <document>")
m = explicit_title_re.match(entry)
if m:
ref = m.group(2)
title = m.group(1)
if explicit:
ref = explicit.group(2)
title = explicit.group(1)
docname = ref
else:
ref = docname = entry

View File

@ -530,6 +530,12 @@ _expression_bin_ops = [
_expression_unary_ops = ["++", "--", "*", "&", "+", "-", "!", "~"]
_expression_assignment_ops = ["=", "*=", "/=", "%=", "+=", "-=",
">>=", "<<=", "&=", "^=", "|="]
_id_explicit_cast = {
'dynamic_cast': 'dc',
'static_cast': 'sc',
'const_cast': 'cc',
'reinterpret_cast': 'rc'
}
class NoOldIdError(UnicodeMixin, Exception):
@ -779,6 +785,17 @@ class ASTStringLiteral(ASTBase):
signode.append(nodes.Text(txt, txt))
class ASTThisLiteral(ASTBase):
def __unicode__(self):
return "this"
def get_id(self, version):
return "fpT"
def describe_signature(self, signode, mode, env, symbol):
signode.append(nodes.Text("this"))
class ASTParenExpr(ASTBase):
def __init__(self, expr):
self.expr = expr
@ -1028,6 +1045,56 @@ class ASTNoexceptExpr(ASTBase):
signode.append(nodes.Text(')'))
class ASTExplicitCast(ASTBase):
def __init__(self, cast, typ, expr):
assert cast in _id_explicit_cast
self.cast = cast
self.typ = typ
self.expr = expr
def __unicode__(self):
res = [self.cast]
res.append('<')
res.append(text_type(self.typ))
res.append('>(')
res.append(text_type(self.expr))
res.append(')')
return u''.join(res)
def get_id(self, version):
return (_id_explicit_cast[self.cast] +
self.typ.get_id(version) +
self.expr.get_id(version))
def describe_signature(self, signode, mode, env, symbol):
signode.append(nodes.Text(self.cast))
signode.append(nodes.Text('<'))
self.typ.describe_signature(signode, mode, env, symbol)
signode.append(nodes.Text('>'))
signode.append(nodes.Text('('))
self.expr.describe_signature(signode, mode, env, symbol)
signode.append(nodes.Text(')'))
class ASTTypeId(ASTBase):
def __init__(self, typeOrExpr, isType):
self.typeOrExpr = typeOrExpr
self.isType = isType
def __unicode__(self):
return 'typeid(' + text_type(self.typeOrExpr) + ')'
def get_id(self, version):
prefix = 'ti' if self.isType else 'te'
return prefix + self.typeOrExpr.get_id(version)
def describe_signature(self, signode, mode, env, symbol):
signode.append(nodes.Text('typeid'))
signode.append(nodes.Text('('))
self.typeOrExpr.describe_signature(signode, mode, env, symbol)
signode.append(nodes.Text(')'))
class ASTPostfixCallExpr(ASTBase):
def __init__(self, exprs):
self.exprs = exprs
@ -4069,7 +4136,10 @@ class DefinitionParser(object):
res = self._parse_literal()
if res is not None:
return res
# TODO: try 'this' and lambda expression
self.skip_ws()
if self.skip_word("this"):
return ASTThisLiteral()
# TODO: try lambda expression
res = self._parse_fold_or_paren_expression()
if res is not None:
return res
@ -4097,39 +4167,94 @@ class DefinitionParser(object):
# | "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
prefix = None # type: Any
self.skip_ws()
cast = None
for c in _id_explicit_cast:
if self.skip_word_and_ws(c):
cast = c
break
if cast is not None:
prefixType = "cast"
if not self.skip_string("<"):
self.fail("Expected '<' afer '%s'." % cast)
typ = self._parse_type(False)
self.skip_ws()
if not self.skip_string_and_ws(">"):
self.fail("Expected '>' after type in '%s'." % cast)
if not self.skip_string("("):
self.fail("Expected '(' in '%s'." % cast)
def parser():
return self._parse_expression(inTemplate=False)
expr = self._parse_expression_fallback([')'], parser)
self.skip_ws()
if not self.skip_string(")"):
self.fail("Expected ')' to end '%s'." % cast)
prefix = ASTExplicitCast(cast, typ, expr)
elif self.skip_word_and_ws("typeid"):
prefixType = "typeid"
if not self.skip_string_and_ws('('):
self.fail("Expected '(' after 'typeid'.")
pos = self.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'
# | simple-type-specifier "(" expression-list [opt] ")"
# | simple-type-specifier braced-init-list
# | typename-specifier "(" expression-list [opt] ")"
# | typename-specifier braced-init-list
self.skip_ws()
if self.current_char != '(' and self.current_char != '{':
self.fail("Expecting '(' or '{' after type in cast expression.")
except DefinitionError as eInner:
typ = self._parse_type(False)
prefix = ASTTypeId(typ, isType=True)
if not self.skip_string(')'):
self.fail("Expected ')' to end 'typeid' of type.")
except DefinitionError as eType:
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)
try:
def parser():
return self._parse_expression(inTemplate=False)
expr = self._parse_expression_fallback([')'], parser)
prefix = ASTTypeId(expr, isType=False)
if not self.skip_string(')'):
self.fail("Expected ')' to end 'typeid' of expression.")
except DefinitionError as eExpr:
self.pos = pos
header = "Error in 'typeid(...)'."
header += " Expected type or expression."
errors = []
errors.append((eType, "If type"))
errors.append((eExpr, "If expression"))
raise self._make_multi_error(errors, header)
else: # a primary expression or a type
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'
# | simple-type-specifier "(" expression-list [opt] ")"
# | simple-type-specifier braced-init-list
# | typename-specifier "(" expression-list [opt] ")"
# | typename-specifier braced-init-list
self.skip_ws()
if self.current_char != '(' and self.current_char != '{':
self.fail("Expecting '(' or '{' after type in cast expression.")
except DefinitionError as eInner:
self.pos = pos
header = "Error in postfix expression,"
header += " 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 prefixType in ['expr', 'cast', 'typeid']:
if self.skip_string_and_ws('['):
expr = self._parse_expression(inTemplate=False)
self.skip_ws()

View File

@ -181,7 +181,7 @@ def import_object(modname, objpath, objtype='', attrgetter=safe_getattr, warning
if isinstance(real_exc, SystemExit):
errmsg += ('; the module executes module level statement '
'and it might call sys.exit().')
elif isinstance(real_exc, ImportError):
elif isinstance(real_exc, ImportError) and real_exc.args:
errmsg += '; the following exception was raised:\n%s' % real_exc.args[0]
else:
errmsg += '; the following exception was raised:\n%s' % traceback_msg

View File

@ -30,6 +30,7 @@ import functools
import posixpath
import sys
import time
import warnings
from os import path
from typing import TYPE_CHECKING
@ -40,6 +41,7 @@ from six.moves.urllib.parse import urlsplit, urlunsplit
import sphinx
from sphinx.builders.html import INVENTORY_FILENAME
from sphinx.deprecation import RemovedInSphinx20Warning
from sphinx.locale import _
from sphinx.util import requests, logging
from sphinx.util.inventory import InventoryFile
@ -381,7 +383,16 @@ def setup(app):
def debug(argv):
# type: (List[unicode]) -> None
"""Debug functionality to print out an inventory"""
if len(argv) < 2:
warnings.warn('sphinx.ext.intersphinx.debug() is deprecated. '
'Please use inspect_main() instead',
RemovedInSphinx20Warning)
inspect_main(argv[1:])
def inspect_main(argv):
# type: (List[unicode]) -> None
"""Debug functionality to print out an inventory"""
if len(argv) < 1:
print("Print out an inventory file.\n"
"Error: must specify local path or URL to an inventory file.",
file=sys.stderr)
@ -399,7 +410,7 @@ def debug(argv):
# type: (unicode) -> None
print(msg, file=sys.stderr)
filename = argv[1]
filename = argv[0]
invdata = fetch_inventory(MockApp(), '', filename) # type: ignore
for key in sorted(invdata or {}):
print(key)
@ -413,4 +424,4 @@ if __name__ == '__main__':
import logging # type: ignore
logging.basicConfig()
debug(argv=sys.argv[1:]) # type: ignore
inspect_main(argv=sys.argv[1:]) # type: ignore

View File

@ -1298,12 +1298,12 @@ msgstr "znaleziono więcej niż jeden cel dla cross-referencji „any” %r: mo
#: sphinx/transforms/post_transforms/__init__.py:169
#, python-format
msgid "%s:%s reference target not found: %%(target)s"
msgstr "nie znaleziono celu referencji %s:%s: %%(cel)e"
msgstr "nie znaleziono celu referencji %s:%s: %%(target)s"
#: sphinx/transforms/post_transforms/__init__.py:172
#, python-format
msgid "%r reference target not found: %%(target)s"
msgstr "nie znaleziono celu referencji %r: %%(cel)e"
msgstr "nie znaleziono celu referencji %r: %%(target)s"
#: sphinx/util/docutils.py:202
msgid "when adding directive classes, no additional arguments may be given"

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<package xmlns="http://www.idpf.org/2007/opf" version="3.0" xml:lang="{{ lang }}"
<package xmlns="http://www.idpf.org/2007/opf" version="{{ epub_version }}" xml:lang="{{ lang }}"
unique-identifier="{{ uid }}"
prefix="ibooks: http://vocabulary.itunes.apple.com/rdf/ibooks/vocabulary-extensions-1.0/">
<metadata xmlns:opf="http://www.idpf.org/2007/opf"
@ -11,7 +11,11 @@
<dc:contributor>{{ contributor }}</dc:contributor>
<dc:publisher>{{ publisher }}</dc:publisher>
<dc:rights>{{ copyright }}</dc:rights>
{%- if epub_version == 3.1 %}
<dc:identifier id="{{ uid }}" opf:scheme="{{ scheme }}">{{ id }}</dc:identifier>
{%- else %}
<dc:identifier id="{{ uid }}">{{ id }}</dc:identifier>
{%- endif %}
<dc:date>{{ date }}</dc:date>
<meta property="dcterms:modified">{{ date }}</meta>
<meta property="ibooks:version">{{ version }}</meta>

View File

@ -135,10 +135,12 @@ class ReferencesResolver(SphinxTransform):
if not results:
return None
if len(results) > 1:
nice_results = ' or '.join(':%s:`%s`' % (name, role["reftitle"])
for name, role in results)
def stringify(name, node):
reftitle = node.get('reftitle', node.astext())
return ':%s:`%s`' % (name, reftitle)
candidates = ' or '.join(stringify(name, role) for name, role in results)
logger.warning(__('more than one target found for \'any\' cross-'
'reference %r: could be %s'), target, nice_results,
'reference %r: could be %s'), target, candidates,
location=node)
res_role, newnode = results[0]
# Override "any" class with the actual role type to get the styling

View File

@ -12,6 +12,7 @@ normal order
bar/*
baz
qux/index
hyperref <https://sphinx-doc.org/?q=sphinx>
reversed order
-------------

View File

@ -126,6 +126,7 @@ def test_expressions():
expr = '5.0' + suffix
exprCheck(expr, 'L' + expr + 'E')
exprCheck('"abc\\"cba"', 'LA8_KcE') # string
exprCheck('this', 'fpT')
# TODO: test the rest
exprCheck('(... + Ns)', '(... + Ns)')
exprCheck('(5)', 'L5E')
@ -137,7 +138,12 @@ def test_expressions():
exprCheck('a->b->c', 'ptpt1a1b1c')
exprCheck('i++', 'pp1i')
exprCheck('i--', 'mm1i')
# TODO, those with prefixes
exprCheck('dynamic_cast<T&>(i)++', 'ppdcR1T1i')
exprCheck('static_cast<T&>(i)++', 'ppscR1T1i')
exprCheck('reinterpret_cast<T&>(i)++', 'pprcR1T1i')
exprCheck('const_cast<T&>(i)++', 'ppccR1T1i')
exprCheck('typeid(T).name', 'dtti1T4name')
exprCheck('typeid(a + b).name', 'dttepl1a1b4name')
# unary
exprCheck('++5', 'pp_L5E')
exprCheck('--5', 'mm_L5E')

View File

@ -113,7 +113,7 @@ def test_glob(app):
maxdepth=-1, numbered=0, includefiles=includefiles,
entries=[(None, 'foo'), (None, 'bar/index'), (None, 'bar/bar_1'),
(None, 'bar/bar_2'), (None, 'bar/bar_3'), (None, 'baz'),
(None, 'qux/index')])
(None, 'qux/index'), ('hyperref', 'https://sphinx-doc.org/?q=sphinx')])
assert_node(toctree[0][1][1],
[list_item, ([compact_paragraph, reference, "reversed order"],
[bullet_list, addnodes.toctree])]) # [0][1][1][1][0]

View File

@ -22,7 +22,7 @@ from test_util_inventory import inventory_v2, inventory_v2_not_having_version
from sphinx import addnodes
from sphinx.ext.intersphinx import (
load_mappings, missing_reference, _strip_basic_auth,
_get_safe_url, fetch_inventory, INVENTORY_FILENAME, debug
_get_safe_url, fetch_inventory, INVENTORY_FILENAME, inspect_main
)
from sphinx.ext.intersphinx import setup as intersphinx_setup
@ -393,10 +393,10 @@ def test_getsafeurl_unauthed():
assert expected == actual
def test_debug_noargs(capsys):
"""debug interface, without arguments"""
def test_inspect_main_noargs(capsys):
"""inspect_main interface, without arguments"""
with pytest.raises(SystemExit):
debug(['sphinx/ext/intersphinx.py'])
inspect_main([])
expected = (
"Print out an inventory file.\n"
@ -407,12 +407,12 @@ def test_debug_noargs(capsys):
assert stderr == expected + "\n"
def test_debug_file(capsys, tempdir):
"""debug interface, with file argument"""
def test_inspect_main_file(capsys, tempdir):
"""inspect_main interface, with file argument"""
inv_file = tempdir / 'inventory'
inv_file.write_bytes(inventory_v2)
debug(['sphinx/ext/intersphinx.py', str(inv_file)])
inspect_main([str(inv_file)])
stdout, stderr = capsys.readouterr()
assert stdout.startswith("c:function\n")
@ -420,8 +420,8 @@ def test_debug_file(capsys, tempdir):
@mock.patch('requests.get')
def test_debug_url(fake_get, capsys):
"""debug interface, with url argument"""
def test_inspect_main_url(fake_get, capsys):
"""inspect_main interface, with url argument"""
raw = BytesIO(inventory_v2)
real_read = raw.read
@ -436,7 +436,7 @@ def test_debug_url(fake_get, capsys):
resp.raw = raw
fake_get.return_value = resp
debug(['sphinx/ext/intersphinx.py', url])
inspect_main([url])
stdout, stderr = capsys.readouterr()
assert stdout.startswith("c:function\n")