summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakeshi KOMIYA <i.tkomiya@gmail.com>2016-11-23 13:31:02 +0900
committerTakeshi KOMIYA <i.tkomiya@gmail.com>2016-11-23 13:31:02 +0900
commite63afbc60ec6534f3f09bb5cad1f5400da0988f1 (patch)
treed269de11f798dede9f6216829d1cb4f98081d352
parent43fe104501912077dde3890b392a2518f784bef9 (diff)
parent0ee524e3922089b7ffbd6081c92968ae7dd510c7 (diff)
downloadsphinx-git-e63afbc60ec6534f3f09bb5cad1f5400da0988f1.tar.gz
Merge branch '1.5-release'
-rw-r--r--.gitignore1
-rw-r--r--CHANGES16
-rw-r--r--EXAMPLES1
-rw-r--r--doc/config.rst21
-rw-r--r--doc/markup/inline.rst6
-rw-r--r--sphinx/application.py8
-rw-r--r--sphinx/builders/applehelp.py4
-rw-r--r--sphinx/builders/epub.py2
-rw-r--r--sphinx/builders/html.py1
-rw-r--r--sphinx/builders/linkcheck.py11
-rw-r--r--sphinx/config.py3
-rw-r--r--sphinx/environment/__init__.py2
-rw-r--r--sphinx/ext/imgmath.py5
-rw-r--r--sphinx/ext/inheritance_diagram.py114
-rw-r--r--sphinx/ext/intersphinx.py11
-rw-r--r--sphinx/ext/pngmath.py5
-rw-r--r--sphinx/locale/fr/LC_MESSAGES/sphinx.po2
-rw-r--r--sphinx/search/__init__.py3
-rw-r--r--sphinx/search/jssplitter.py110
-rw-r--r--sphinx/themes/basic/defindex.html2
-rw-r--r--sphinx/themes/basic/static/searchtools.js_t12
-rw-r--r--sphinx/transforms/__init__.py4
-rw-r--r--sphinx/util/nodes.py3
-rw-r--r--sphinx/util/requests.py60
-rw-r--r--sphinx/writers/latex.py90
-rw-r--r--tests/roots/test-ext-inheritance_diagram/example/__init__.py1
-rw-r--r--tests/roots/test-ext-inheritance_diagram/example/sphinx.py5
-rw-r--r--tests/roots/test-intl/figure.po7
-rw-r--r--tests/roots/test-intl/figure.txt17
-rw-r--r--tests/roots/test-latex-title/conf.py8
-rw-r--r--tests/roots/test-latex-title/index.rst12
-rw-r--r--tests/test_build_latex.py20
-rw-r--r--tests/test_ext_inheritance_diagram.py50
-rw-r--r--tests/test_intl.py12
-rw-r--r--utils/jssplitter_generator.py142
35 files changed, 639 insertions, 132 deletions
diff --git a/.gitignore b/.gitignore
index 86a8baf9d..0f9acd743 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,3 +20,4 @@ Sphinx.egg-info/
doc/_build/
tests/.coverage
tests/build/
+utils/regression_test.js
diff --git a/CHANGES b/CHANGES
index 952c312ff..fa81c4039 100644
--- a/CHANGES
+++ b/CHANGES
@@ -18,12 +18,22 @@ Release 1.5 beta2 (in development)
Incompatible changes
--------------------
+* #2986: ``themes/basic/defindex.html`` is now deprecated
+
Features added
--------------
+* #3095: Add :confval:`tls_verify` and :confval:`tls_cacerts` to support
+ self-signed HTTPS servers in linkcheck and intersphinx
+
Bugs fixed
----------
+* #3069: Even if ``'babel'`` key is set to empty string, LaTeX output contains
+ one ``\addto\captions...``
+* #3123: user ``'babel'`` key setting is not obeyed anymore
+* #3155: Fix JavaScript for `html_sourcelink_suffix` fails with IE and Opera
+
Release 1.5 beta1 (released Nov 6, 2016)
========================================
@@ -281,6 +291,12 @@ Bugs fixed
* #3068: Allow the '=' character in the -D option of sphinx-build.py
* #3074: ``add_source_parser()`` crashes in debug mode
* #3135: ``sphinx.ext.autodoc`` crashes with plain Callable
+* #3150: Fix query word splitter in JavaScript. It behaves as same as Python's regular expression.
+* #3093: gettext build broken on substituted images.
+* #3093: gettext build broken on image node under ``note`` directive.
+* imgmath: crashes on showing error messages if image generation failed
+* #3117: LaTeX writer crashes if admonition is placed before first section title
+* #3164: Change search order of ``sphinx.ext.inheritance_diagram``
Release 1.4.8 (released Oct 1, 2016)
====================================
diff --git a/EXAMPLES b/EXAMPLES
index e84e0db26..d6770436b 100644
--- a/EXAMPLES
+++ b/EXAMPLES
@@ -135,6 +135,7 @@ Documentation using another builtin theme
* jsFiddle: http://doc.jsfiddle.net/ (nature)
* libLAS: http://www.liblas.org/ (nature)
* Linguistica: http://linguistica-uchicago.github.io/lxa5/ (sphinx_rtd_theme)
+* MoinMoin: https://moin-20.readthedocs.io/en/latest/ (sphinx_rtd_theme)
* MPipe: http://vmlaker.github.io/mpipe/ (sphinx13)
* pip: https://pip.pypa.io/en/latest/ (sphinx_rtd_theme)
* Pyramid web framework:
diff --git a/doc/config.rst b/doc/config.rst
index 34900535f..a3f45abe9 100644
--- a/doc/config.rst
+++ b/doc/config.rst
@@ -297,8 +297,7 @@ General configuration
A dictionary mapping ``'figure'``, ``'table'``, ``'code-block'`` and
``'section'`` to strings that are used for format of figure numbers.
- As a special character, `%s` and `{number}` will be replaced to figure
- number. `{name}` will be replaced to figure caption.
+ As a special character, `%s` will be replaced to figure number.
Default is to use ``'Fig. %s'`` for ``'figure'``, ``'Table %s'`` for
``'table'``, ``'Listing %s'`` for ``'code-block'`` and ``'Section'`` for
@@ -306,9 +305,6 @@ General configuration
.. versionadded:: 1.3
- .. versionchanged:: 1.5
- Support format of section. Allow to refer the caption of figures.
-
.. confval:: numfig_secnum_depth
The scope of figure numbers, that is, the numfig feature numbers figures
@@ -318,6 +314,21 @@ General configuration
.. versionadded:: 1.3
+.. confval:: tls_verify
+
+ If true, Sphinx verifies server certifications. Default is ``True``.
+
+ .. versionadded:: 1.5
+
+.. confval:: tls_cacerts
+
+ A path to a certification file of CA or a path to directory which
+ contains the certificates. This also allows a dictionary mapping
+ hostname to the path to certificate file.
+ The certificates are used to verify server certifications.
+
+ .. versionadded:: 1.5
+
Project information
-------------------
diff --git a/doc/markup/inline.rst b/doc/markup/inline.rst
index bd02dfa08..a59585bab 100644
--- a/doc/markup/inline.rst
+++ b/doc/markup/inline.rst
@@ -214,6 +214,7 @@ Cross-referencing figures by figure number
.. versionchanged:: 1.5
`numref` role can also refer sections.
+ And `numref` allows `{name}` for the link text.
.. rst:role:: numref
@@ -223,7 +224,10 @@ Cross-referencing figures by figure number
If an explicit link text is given (like usual: ``:numref:`Image of Sphinx (Fig.
%s) <my-figure>```), the link caption will be the title of the reference.
- The format of link text is same as :confval:`numfig_format`.
+ As a special character, `%s` and `{number}` will be replaced to figure
+ number. `{name}` will be replaced to figure caption.
+ If no explicit link text is given, the value of :confval:`numfig_format` is
+ used to default value of link text.
If :confval:`numfig` is ``False``, figures are not numbered.
so this role inserts not a reference but labels or link text.
diff --git a/sphinx/application.py b/sphinx/application.py
index e8e212696..a486069f9 100644
--- a/sphinx/application.py
+++ b/sphinx/application.py
@@ -315,7 +315,7 @@ class Sphinx(object):
self.info('not yet created')
else:
self.info('failed: %s' % err)
- return self._init_env(freshenv=True)
+ self._init_env(freshenv=True)
def _init_builder(self, buildername):
# type: (unicode) -> None
@@ -488,9 +488,9 @@ class Sphinx(object):
l = 0
for item in iterable:
if l == 0:
- self.info(bold(summary), nonl=1)
+ self.info(bold(summary), nonl=True)
l = 1
- self.info(colorfunc(stringify_func(item)) + ' ', nonl=1)
+ self.info(colorfunc(stringify_func(item)) + ' ', nonl=True)
yield item
if l == 1:
self.info()
@@ -514,7 +514,7 @@ class Sphinx(object):
s += '\n'
else:
s = term_width_line(s)
- self.info(s, nonl=1)
+ self.info(s, nonl=True)
yield item
if l > 0:
self.info()
diff --git a/sphinx/builders/applehelp.py b/sphinx/builders/applehelp.py
index c674204fc..05a5a2977 100644
--- a/sphinx/builders/applehelp.py
+++ b/sphinx/builders/applehelp.py
@@ -294,10 +294,10 @@ def setup(app):
app.add_config_value('applehelp_title', lambda self: self.project + ' Help', 'applehelp')
app.add_config_value('applehelp_codesign_identity',
lambda self: environ.get('CODE_SIGN_IDENTITY', None),
- 'applehelp'),
+ 'applehelp')
app.add_config_value('applehelp_codesign_flags',
lambda self: shlex.split(environ.get('OTHER_CODE_SIGN_FLAGS', '')),
- 'applehelp'),
+ 'applehelp')
app.add_config_value('applehelp_indexer_path', '/usr/bin/hiutil', 'applehelp')
app.add_config_value('applehelp_codesign_path', '/usr/bin/codesign', 'applehelp')
app.add_config_value('applehelp_disable_external_tools', False, None)
diff --git a/sphinx/builders/epub.py b/sphinx/builders/epub.py
index f9abd53fb..2b8aba7bd 100644
--- a/sphinx/builders/epub.py
+++ b/sphinx/builders/epub.py
@@ -432,7 +432,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
"""
self.fix_ids(doctree)
self.add_visible_links(doctree, self.config.epub_show_urls)
- return StandaloneHTMLBuilder.write_doc(self, docname, doctree)
+ StandaloneHTMLBuilder.write_doc(self, docname, doctree)
def fix_genindex(self, tree):
# type: (nodes.Node) -> None
diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py
index d5d522dd0..b5d68c843 100644
--- a/sphinx/builders/html.py
+++ b/sphinx/builders/html.py
@@ -817,6 +817,7 @@ class StandaloneHTMLBuilder(Builder):
outfilename=None, event_arg=None):
# type: (unicode, Dict, unicode, unicode, Any) -> None
ctx = self.globalcontext.copy()
+ ctx['warn'] = self.warn
# current_page_name is backwards compatibility
ctx['pagename'] = ctx['current_page_name'] = pagename
default_baseuri = self.get_target_uri(pagename)
diff --git a/sphinx/builders/linkcheck.py b/sphinx/builders/linkcheck.py
index 0a3b2fe9d..6df9480a4 100644
--- a/sphinx/builders/linkcheck.py
+++ b/sphinx/builders/linkcheck.py
@@ -33,11 +33,11 @@ except ImportError:
pass
from sphinx.builders import Builder
-from sphinx.util import encode_uri
+from sphinx.util import encode_uri, requests
from sphinx.util.console import ( # type: ignore
purple, red, darkgreen, darkgray, darkred, turquoise
)
-from sphinx.util.requests import requests, useragent_header, is_ssl_error
+from sphinx.util.requests import is_ssl_error
if False:
# For type annotation
@@ -98,7 +98,6 @@ class CheckExternalLinksBuilder(Builder):
self.good = set() # type: Set[unicode]
self.broken = {} # type: Dict[unicode, unicode]
self.redirected = {} # type: Dict[unicode, Tuple[unicode, int]]
- self.headers = dict(useragent_header)
# set a timeout for non-responding servers
socket.setdefaulttimeout(5.0)
# create output file
@@ -144,7 +143,7 @@ class CheckExternalLinksBuilder(Builder):
try:
if anchor and self.app.config.linkcheck_anchors:
# Read the whole document and see if #anchor exists
- response = requests.get(req_url, stream=True, headers=self.headers,
+ response = requests.get(req_url, stream=True, config=self.app.config,
**kwargs)
found = check_anchor(response, unquote(anchor))
@@ -154,12 +153,12 @@ class CheckExternalLinksBuilder(Builder):
try:
# try a HEAD request first, which should be easier on
# the server and the network
- response = requests.head(req_url, headers=self.headers, **kwargs)
+ response = requests.head(req_url, config=self.app.config, **kwargs)
response.raise_for_status()
except HTTPError as err:
# retry with GET request if that fails, some servers
# don't like HEAD requests.
- response = requests.get(req_url, stream=True, headers=self.headers,
+ response = requests.get(req_url, stream=True, config=self.app.config,
**kwargs)
response.raise_for_status()
except HTTPError as err:
diff --git a/sphinx/config.py b/sphinx/config.py
index 9bfdd2976..5872ce8bb 100644
--- a/sphinx/config.py
+++ b/sphinx/config.py
@@ -119,6 +119,9 @@ class Config(object):
'code-block': l_('Listing %s')},
'env'),
+ tls_verify = (True, 'env'),
+ tls_cacerts = (None, 'env'),
+
# pre-initialized confval for HTML builder
html_translator_class = (None, 'html', string_classes),
) # type: Dict[unicode, Tuple]
diff --git a/sphinx/environment/__init__.py b/sphinx/environment/__init__.py
index 6dc39e945..27939db0d 100644
--- a/sphinx/environment/__init__.py
+++ b/sphinx/environment/__init__.py
@@ -545,7 +545,7 @@ class BuildEnvironment(object):
# this cache also needs to be updated every time
self._nitpick_ignore = set(self.config.nitpick_ignore)
- app.info(bold('updating environment: '), nonl=1)
+ app.info(bold('updating environment: '), nonl=True)
added, changed, removed = self.get_outdated_files(config_changed)
diff --git a/sphinx/ext/imgmath.py b/sphinx/ext/imgmath.py
index 9b75e7ee3..11d41d426 100644
--- a/sphinx/ext/imgmath.py
+++ b/sphinx/ext/imgmath.py
@@ -258,10 +258,11 @@ def html_visit_displaymath(self, node):
try:
fname, depth = render_math(self, latex)
except MathExtError as exc:
- sm = nodes.system_message(str(exc), type='WARNING', level=2,
+ msg = text_type(exc)
+ sm = nodes.system_message(msg, type='WARNING', level=2,
backrefs=[], source=node['latex'])
sm.walkabout(self)
- self.builder.warn('inline latex %r: ' % node['latex'] + str(exc))
+ self.builder.warn('inline latex %r: ' % node['latex'] + msg)
raise nodes.SkipNode
self.body.append(self.starttag(node, 'div', CLASS='math'))
self.body.append('<p>')
diff --git a/sphinx/ext/inheritance_diagram.py b/sphinx/ext/inheritance_diagram.py
index 341780473..f37d7cf8b 100644
--- a/sphinx/ext/inheritance_diagram.py
+++ b/sphinx/ext/inheritance_diagram.py
@@ -64,9 +64,63 @@ if False:
from sphinx.environment import BuildEnvironment # NOQA
-class_sig_re = re.compile(r'''^([\w.]*\.)? # module names
- (\w+) \s* $ # class/final module name
- ''', re.VERBOSE)
+module_sig_re = re.compile(r'''^(?:([\w.]*)\.)? # module names
+ (\w+) \s* $ # class/final module name
+ ''', re.VERBOSE)
+
+
+def try_import(objname):
+ # type: (unicode) -> Any
+ """Import a object or module using *name* and *currentmodule*.
+ *name* should be a relative name from *currentmodule* or
+ a fully-qualified name.
+
+ Returns imported object or module. If failed, returns None value.
+ """
+ try:
+ __import__(objname)
+ return sys.modules.get(objname)
+ except ImportError:
+ modname, attrname = module_sig_re.match(objname).groups()
+ if modname is None:
+ return None
+ try:
+ __import__(modname)
+ return getattr(sys.modules.get(modname), attrname, None)
+ except ImportError:
+ return None
+
+
+def import_classes(name, currmodule):
+ # type: (unicode, unicode) -> Any
+ """Import a class using its fully-qualified *name*."""
+ target = None
+
+ # import class or module using currmodule
+ if currmodule:
+ target = try_import(currmodule + '.' + name)
+
+ # import class or module without currmodule
+ if target is None:
+ target = try_import(name)
+
+ if target is None:
+ raise InheritanceException(
+ 'Could not import class or module %r specified for '
+ 'inheritance diagram' % name)
+
+ if inspect.isclass(target):
+ # If imported object is a class, just return it
+ return [target]
+ elif inspect.ismodule(target):
+ # If imported object is a module, return classes defined on it
+ classes = []
+ for cls in target.__dict__.values():
+ if inspect.isclass(cls) and cls.__module__ == target.__name__:
+ classes.append(cls)
+ return classes
+ raise InheritanceException('%r specified for inheritance diagram is '
+ 'not a class or module' % name)
class InheritanceException(Exception):
@@ -95,58 +149,12 @@ class InheritanceGraph(object):
raise InheritanceException('No classes found for '
'inheritance diagram')
- def _import_class_or_module(self, name, currmodule):
- # type: (unicode, str) -> Any
- """Import a class using its fully-qualified *name*."""
- try:
- path, base = class_sig_re.match(name).groups() # type: ignore
- except (AttributeError, ValueError):
- raise InheritanceException('Invalid class or module %r specified '
- 'for inheritance diagram' % name)
-
- fullname = (path or '') + base
- path = (path and path.rstrip('.') or '')
-
- # two possibilities: either it is a module, then import it
- try:
- __import__(fullname)
- todoc = sys.modules[fullname]
- except ImportError:
- # else it is a class, then import the module
- if not path:
- if currmodule:
- # try the current module
- path = currmodule
- else:
- raise InheritanceException(
- 'Could not import class %r specified for '
- 'inheritance diagram' % base)
- try:
- __import__(path)
- todoc = getattr(sys.modules[path], base)
- except (ImportError, AttributeError):
- raise InheritanceException(
- 'Could not import class or module %r specified for '
- 'inheritance diagram' % (path + '.' + base))
-
- # If a class, just return it
- if inspect.isclass(todoc):
- return [todoc]
- elif inspect.ismodule(todoc):
- classes = []
- for cls in todoc.__dict__.values(): # type: ignore
- if inspect.isclass(cls) and cls.__module__ == todoc.__name__:
- classes.append(cls)
- return classes
- raise InheritanceException('%r specified for inheritance diagram is '
- 'not a class or module' % name)
-
def _import_classes(self, class_names, currmodule):
# type: (unicode, str) -> List[Any]
"""Import a list of classes."""
classes = [] # type: List[Any]
for name in class_names:
- classes.extend(self._import_class_or_module(name, currmodule))
+ classes.extend(import_classes(name, currmodule))
return classes
def _class_info(self, classes, show_builtins, private_bases, parts):
@@ -443,7 +451,7 @@ def setup(app):
man=(skip, None),
texinfo=(texinfo_visit_inheritance_diagram, None))
app.add_directive('inheritance-diagram', InheritanceDiagram)
- app.add_config_value('inheritance_graph_attrs', {}, False),
- app.add_config_value('inheritance_node_attrs', {}, False),
- app.add_config_value('inheritance_edge_attrs', {}, False),
+ app.add_config_value('inheritance_graph_attrs', {}, False)
+ app.add_config_value('inheritance_node_attrs', {}, False)
+ app.add_config_value('inheritance_edge_attrs', {}, False)
return {'version': sphinx.__display_version__, 'parallel_read_safe': True}
diff --git a/sphinx/ext/intersphinx.py b/sphinx/ext/intersphinx.py
index 42aafdf94..d24290436 100644
--- a/sphinx/ext/intersphinx.py
+++ b/sphinx/ext/intersphinx.py
@@ -42,12 +42,13 @@ from docutils.utils import relative_path
import sphinx
from sphinx.locale import _
from sphinx.builders.html import INVENTORY_FILENAME
-from sphinx.util.requests import requests, useragent_header
+from sphinx.util import requests
if False:
# For type annotation
from typing import Any, Callable, Dict, IO, Iterator, Tuple, Union # NOQA
from sphinx.application import Sphinx # NOQA
+ from sphinx.config import Config # NOQA
from sphinx.environment import BuildEnvironment # NOQA
if PY3:
@@ -163,8 +164,8 @@ def _strip_basic_auth(url):
return urlunsplit(frags)
-def _read_from_url(url, timeout=None):
- # type: (unicode, int) -> IO
+def _read_from_url(url, config=None):
+ # type: (unicode, Config) -> IO
"""Reads data from *url* with an HTTP *GET*.
This function supports fetching from resources which use basic HTTP auth as
@@ -180,7 +181,7 @@ def _read_from_url(url, timeout=None):
:return: data read from resource described by *url*
:rtype: ``file``-like object
"""
- r = requests.get(url, stream=True, timeout=timeout, headers=dict(useragent_header))
+ r = requests.get(url, stream=True, config=config, timeout=config.intersphinx_timeout)
r.raise_for_status()
r.raw.url = r.url
return r.raw
@@ -223,7 +224,7 @@ def fetch_inventory(app, uri, inv):
uri = _strip_basic_auth(uri)
try:
if '://' in inv:
- f = _read_from_url(inv, timeout=app.config.intersphinx_timeout)
+ f = _read_from_url(inv, config=app.config)
else:
f = open(path.join(app.srcdir, inv), 'rb')
except Exception as err:
diff --git a/sphinx/ext/pngmath.py b/sphinx/ext/pngmath.py
index a02b61b9b..49c81b233 100644
--- a/sphinx/ext/pngmath.py
+++ b/sphinx/ext/pngmath.py
@@ -230,10 +230,11 @@ def html_visit_displaymath(self, node):
try:
fname, depth = render_math(self, latex)
except MathExtError as exc:
- sm = nodes.system_message(str(exc), type='WARNING', level=2,
+ msg = text_type(exc)
+ sm = nodes.system_message(msg, type='WARNING', level=2,
backrefs=[], source=node['latex'])
sm.walkabout(self)
- self.builder.warn('inline latex %r: ' % node['latex'] + str(exc))
+ self.builder.warn('inline latex %r: ' % node['latex'] + msg)
raise nodes.SkipNode
self.body.append(self.starttag(node, 'div', CLASS='math'))
self.body.append('<p>')
diff --git a/sphinx/locale/fr/LC_MESSAGES/sphinx.po b/sphinx/locale/fr/LC_MESSAGES/sphinx.po
index c476663be..2778dffec 100644
--- a/sphinx/locale/fr/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/fr/LC_MESSAGES/sphinx.po
@@ -112,7 +112,7 @@ msgstr "Auteur du module : "
#: sphinx/directives/other.py:153
msgid "Code author: "
-msgstr "Auteur du code :"
+msgstr "Auteur du code : "
#: sphinx/directives/other.py:155
msgid "Author: "
diff --git a/sphinx/search/__init__.py b/sphinx/search/__init__.py
index 959e335c3..df374ae46 100644
--- a/sphinx/search/__init__.py
+++ b/sphinx/search/__init__.py
@@ -19,6 +19,7 @@ from docutils.nodes import raw, comment, title, Text, NodeVisitor, SkipNode
import sphinx
from sphinx.util import jsdump, rpartition
from sphinx.util.pycompat import htmlescape
+from sphinx.search.jssplitter import splitter_code
if False:
# For type annotation
@@ -280,6 +281,7 @@ class IndexBuilder(object):
self.js_scorer_code = fp.read().decode('utf-8')
else:
self.js_scorer_code = u''
+ self.js_splitter_code = splitter_code
def load(self, stream, format):
# type: (IO, Any) -> None
@@ -439,6 +441,7 @@ class IndexBuilder(object):
search_language_stemming_code = self.lang.js_stemmer_code,
search_language_stop_words = jsdump.dumps(sorted(self.lang.stopwords)),
search_scorer_tool = self.js_scorer_code,
+ search_word_splitter_code = self.js_splitter_code,
)
def get_js_stemmer_rawcode(self):
diff --git a/sphinx/search/jssplitter.py b/sphinx/search/jssplitter.py
new file mode 100644
index 000000000..a3bd8b767
--- /dev/null
+++ b/sphinx/search/jssplitter.py
@@ -0,0 +1,110 @@
+
+"""# -*- coding: utf-8 -*-
+ sphinx.search.jssplitter
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Provides Python compatible word splitter to JavaScript
+
+ :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+
+ DO NOT EDIT. This is generated by utils/jssplitter_generator.py
+"""
+
+splitter_code = """
+
+var splitChars = (function() {
+ var result = {};
+ var singles = [96, 180, 187, 191, 215, 247, 749, 885, 903, 907, 909, 930, 1014, 1648,
+ 1748, 1809, 2416, 2473, 2481, 2526, 2601, 2609, 2612, 2615, 2653, 2702,
+ 2706, 2729, 2737, 2740, 2857, 2865, 2868, 2910, 2928, 2948, 2961, 2971,
+ 2973, 3085, 3089, 3113, 3124, 3213, 3217, 3241, 3252, 3295, 3341, 3345,
+ 3369, 3506, 3516, 3633, 3715, 3721, 3736, 3744, 3748, 3750, 3756, 3761,
+ 3781, 3912, 4239, 4347, 4681, 4695, 4697, 4745, 4785, 4799, 4801, 4823,
+ 4881, 5760, 5901, 5997, 6313, 7405, 8024, 8026, 8028, 8030, 8117, 8125,
+ 8133, 8181, 8468, 8485, 8487, 8489, 8494, 8527, 11311, 11359, 11687, 11695,
+ 11703, 11711, 11719, 11727, 11735, 12448, 12539, 43010, 43014, 43019, 43587,
+ 43696, 43713, 64286, 64297, 64311, 64317, 64319, 64322, 64325, 65141];
+ var i, j, start, end;
+ for (i = 0; i < singles.length; i++) {
+ result[singles[i]] = true;
+ }
+ var ranges = [[0, 47], [58, 64], [91, 94], [123, 169], [171, 177], [182, 184], [706, 709],
+ [722, 735], [741, 747], [751, 879], [888, 889], [894, 901], [1154, 1161],
+ [1318, 1328], [1367, 1368], [1370, 1376], [1416, 1487], [1515, 1519], [1523, 1568],
+ [1611, 1631], [1642, 1645], [1750, 1764], [1767, 1773], [1789, 1790], [1792, 1807],
+ [1840, 1868], [1958, 1968], [1970, 1983], [2027, 2035], [2038, 2041], [2043, 2047],
+ [2070, 2073], [2075, 2083], [2085, 2087], [2089, 2307], [2362, 2364], [2366, 2383],
+ [2385, 2391], [2402, 2405], [2419, 2424], [2432, 2436], [2445, 2446], [2449, 2450],
+ [2483, 2485], [2490, 2492], [2494, 2509], [2511, 2523], [2530, 2533], [2546, 2547],
+ [2554, 2564], [2571, 2574], [2577, 2578], [2618, 2648], [2655, 2661], [2672, 2673],
+ [2677, 2692], [2746, 2748], [2750, 2767], [2769, 2783], [2786, 2789], [2800, 2820],
+ [2829, 2830], [2833, 2834], [2874, 2876], [2878, 2907], [2914, 2917], [2930, 2946],
+ [2955, 2957], [2966, 2968], [2976, 2978], [2981, 2983], [2987, 2989], [3002, 3023],
+ [3025, 3045], [3059, 3076], [3130, 3132], [3134, 3159], [3162, 3167], [3170, 3173],
+ [3184, 3191], [3199, 3204], [3258, 3260], [3262, 3293], [3298, 3301], [3312, 3332],
+ [3386, 3388], [3390, 3423], [3426, 3429], [3446, 3449], [3456, 3460], [3479, 3481],
+ [3518, 3519], [3527, 3584], [3636, 3647], [3655, 3663], [3674, 3712], [3717, 3718],
+ [3723, 3724], [3726, 3731], [3752, 3753], [3764, 3772], [3774, 3775], [3783, 3791],
+ [3802, 3803], [3806, 3839], [3841, 3871], [3892, 3903], [3949, 3975], [3980, 4095],
+ [4139, 4158], [4170, 4175], [4182, 4185], [4190, 4192], [4194, 4196], [4199, 4205],
+ [4209, 4212], [4226, 4237], [4250, 4255], [4294, 4303], [4349, 4351], [4686, 4687],
+ [4702, 4703], [4750, 4751], [4790, 4791], [4806, 4807], [4886, 4887], [4955, 4968],
+ [4989, 4991], [5008, 5023], [5109, 5120], [5741, 5742], [5787, 5791], [5867, 5869],
+ [5873, 5887], [5906, 5919], [5938, 5951], [5970, 5983], [6001, 6015], [6068, 6102],
+ [6104, 6107], [6109, 6111], [6122, 6127], [6138, 6159], [6170, 6175], [6264, 6271],
+ [6315, 6319], [6390, 6399], [6429, 6469], [6510, 6511], [6517, 6527], [6572, 6592],
+ [6600, 6607], [6619, 6655], [6679, 6687], [6741, 6783], [6794, 6799], [6810, 6822],
+ [6824, 6916], [6964, 6980], [6988, 6991], [7002, 7042], [7073, 7085], [7098, 7167],
+ [7204, 7231], [7242, 7244], [7294, 7400], [7410, 7423], [7616, 7679], [7958, 7959],
+ [7966, 7967], [8006, 8007], [8014, 8015], [8062, 8063], [8127, 8129], [8141, 8143],
+ [8148, 8149], [8156, 8159], [8173, 8177], [8189, 8303], [8306, 8307], [8314, 8318],
+ [8330, 8335], [8341, 8449], [8451, 8454], [8456, 8457], [8470, 8472], [8478, 8483],
+ [8506, 8507], [8512, 8516], [8522, 8525], [8586, 9311], [9372, 9449], [9472, 10101],
+ [10132, 11263], [11493, 11498], [11503, 11516], [11518, 11519], [11558, 11567],
+ [11622, 11630], [11632, 11647], [11671, 11679], [11743, 11822], [11824, 12292],
+ [12296, 12320], [12330, 12336], [12342, 12343], [12349, 12352], [12439, 12444],
+ [12544, 12548], [12590, 12592], [12687, 12689], [12694, 12703], [12728, 12783],
+ [12800, 12831], [12842, 12880], [12896, 12927], [12938, 12976], [12992, 13311],
+ [19894, 19967], [40908, 40959], [42125, 42191], [42238, 42239], [42509, 42511],
+ [42540, 42559], [42592, 42593], [42607, 42622], [42648, 42655], [42736, 42774],
+ [42784, 42785], [42889, 42890], [42893, 43002], [43043, 43055], [43062, 43071],
+ [43124, 43137], [43188, 43215], [43226, 43249], [43256, 43258], [43260, 43263],
+ [43302, 43311], [43335, 43359], [43389, 43395], [43443, 43470], [43482, 43519],
+ [43561, 43583], [43596, 43599], [43610, 43615], [43639, 43641], [43643, 43647],
+ [43698, 43700], [43703, 43704], [43710, 43711], [43715, 43738], [43742, 43967],
+ [44003, 44015], [44026, 44031], [55204, 55215], [55239, 55242], [55292, 55295],
+ [57344, 63743], [64046, 64047], [64110, 64111], [64218, 64255], [64263, 64274],
+ [64280, 64284], [64434, 64466], [64830, 64847], [64912, 64913], [64968, 65007],
+ [65020, 65135], [65277, 65295], [65306, 65312], [65339, 65344], [65371, 65381],
+ [65471, 65473], [65480, 65481], [65488, 65489], [65496, 65497]];
+ for (i = 0; i < ranges.length; i++) {
+ start = ranges[i][0];
+ end = ranges[i][1];
+ for (j = start; j <= end; j++) {
+ result[j] = true;
+ }
+ }
+ return result;
+})();
+
+function splitQuery(query) {
+ var result = [];
+ var start = -1;
+ for (var i = 0; i < query.length; i++) {
+ if (splitChars[query.charCodeAt(i)]) {
+ if (start !== -1) {
+ result.push(query.slice(start, i));
+ start = -1;
+ }
+ } else if (start === -1) {
+ start = i;
+ }
+ }
+ if (start !== -1) {
+ result.push(query.slice(start));
+ }
+ return result;
+}
+
+"""
diff --git a/sphinx/themes/basic/defindex.html b/sphinx/themes/basic/defindex.html
index 33becfa0d..190680724 100644
--- a/sphinx/themes/basic/defindex.html
+++ b/sphinx/themes/basic/defindex.html
@@ -6,7 +6,7 @@
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
-#}
+#}{{ warn('Now base template defindex.html is deprecated.') }}
{%- extends "layout.html" %}
{% set title = _('Overview') %}
{% block body %}
diff --git a/sphinx/themes/basic/static/searchtools.js_t b/sphinx/themes/basic/static/searchtools.js_t
index f521c3794..63323fd0b 100644
--- a/sphinx/themes/basic/static/searchtools.js_t
+++ b/sphinx/themes/basic/static/searchtools.js_t
@@ -47,6 +47,14 @@ var Scorer = {
};
{% endif %}
+{% if search_word_splitter_code %}
+{{ search_word_splitter_code }}
+{% else %}
+function splitQuery(query) {
+ return query.split(/\s+/);
+}
+{% endif %}
+
/**
* Search Module
*/
@@ -145,7 +153,7 @@ var Search = {
var searchterms = [];
var excluded = [];
var hlterms = [];
- var tmp = query.split(/\W+/);
+ var tmp = splitQuery(query);
var objectterms = [];
for (i = 0; i < tmp.length; i++) {
if (tmp[i] !== "") {
@@ -261,7 +269,7 @@ var Search = {
});
} else if (DOCUMENTATION_OPTIONS.HAS_SOURCE) {
var suffix = DOCUMENTATION_OPTIONS.SOURCELINK_SUFFIX;
- $.ajax({url: DOCUMENTATION_OPTIONS.URL_ROOT + '_sources/' + item[5] + (item[5].endsWith(suffix) ? '' : suffix),
+ $.ajax({url: DOCUMENTATION_OPTIONS.URL_ROOT + '_sources/' + item[5] + (item[5].slice(-suffix.length) === suffix ? '' : suffix),
dataType: "text",
complete: function(jqxhr, textstatus) {
var data = jqxhr.responseText;
diff --git a/sphinx/transforms/__init__.py b/sphinx/transforms/__init__.py
index ab8f86500..88efd4c47 100644
--- a/sphinx/transforms/__init__.py
+++ b/sphinx/transforms/__init__.py
@@ -135,7 +135,7 @@ class CitationReferences(Transform):
# type: () -> None
for citnode in self.document.traverse(nodes.citation_reference):
cittext = citnode.astext()
- refnode = addnodes.pending_xref(cittext, refdomain='std', reftype='citation',
+ refnode = addnodes.pending_xref(cittext, reftype='citation',
reftarget=cittext, refwarn=True,
ids=citnode["ids"])
refnode.source = citnode.source or citnode.parent.source
@@ -162,7 +162,7 @@ class ApplySourceWorkaround(Transform):
def apply(self):
# type: () -> None
for n in self.document.traverse():
- if isinstance(n, nodes.TextElement):
+ if isinstance(n, (nodes.TextElement, nodes.image)):
apply_source_workaround(n)
diff --git a/sphinx/util/nodes.py b/sphinx/util/nodes.py
index 2568ea4aa..0bfecce9e 100644
--- a/sphinx/util/nodes.py
+++ b/sphinx/util/nodes.py
@@ -57,6 +57,8 @@ def apply_source_workaround(node):
node.source = definition_list_item.source
node.line = definition_list_item.line - 1
node.rawsource = node.astext() # set 'classifier1' (or 'classifier2')
+ if isinstance(node, nodes.image) and node.source is None:
+ node.source, node.line = node.parent.source, node.parent.line
if isinstance(node, nodes.term):
# strip classifier from rawsource of term
for classifier in reversed(node.parent.traverse(nodes.classifier)):
@@ -80,6 +82,7 @@ def apply_source_workaround(node):
nodes.title,
nodes.rubric,
nodes.line,
+ nodes.image,
))):
node.source = find_source_node(node)
node.line = 0 # need fix docutils to get `node.line`
diff --git a/sphinx/util/requests.py b/sphinx/util/requests.py
index 36ac1e0e7..e2ac94e80 100644
--- a/sphinx/util/requests.py
+++ b/sphinx/util/requests.py
@@ -14,7 +14,10 @@ from __future__ import absolute_import
import requests
import warnings
import pkg_resources
-from requests.packages.urllib3.exceptions import SSLError
+
+from six import string_types
+from six.moves.urllib.parse import urlsplit
+from requests.packages.urllib3.exceptions import SSLError, InsecureRequestWarning
# try to load requests[security]
try:
@@ -45,6 +48,7 @@ useragent_header = [('User-Agent',
def is_ssl_error(exc):
+ """Check an exception is SSLError."""
if isinstance(exc, SSLError):
return True
else:
@@ -53,3 +57,57 @@ def is_ssl_error(exc):
return True
else:
return False
+
+
+def _get_tls_cacert(url, config):
+ """Get addiotinal CA cert for a specific URL.
+
+ This also returns ``False`` if verification is disabled.
+ And returns ``True`` if additional CA cert not found.
+ """
+ if not config.tls_verify:
+ return False
+
+ certs = getattr(config, 'tls_cacerts', None)
+ if not certs:
+ return True
+ elif isinstance(certs, (string_types, tuple)):
+ return certs
+ else:
+ hostname = urlsplit(url)[1]
+ if '@' in hostname:
+ hostname = hostname.split('@')[1]
+
+ return certs.get(hostname, True)
+
+
+def get(url, **kwargs):
+ """Sends a GET request like requests.get().
+
+ This sets up User-Agent header and TLS verification automatically."""
+ kwargs.setdefault('headers', dict(useragent_header))
+ config = kwargs.pop('config', None)
+ if config:
+ kwargs.setdefault('verify', _get_tls_cacert(url, config))
+
+ with warnings.catch_warnings():
+ if not kwargs.get('verify'):
+ # ignore InsecureRequestWarning if verify=False
+ warnings.filterwarnings("ignore", category=InsecureRequestWarning)
+ return requests.get(url, **kwargs)
+
+
+def head(url, **kwargs):
+ """Sends a HEAD request like requests.head().
+
+ This sets up User-Agent header and TLS verification automatically."""
+ kwargs.setdefault('headers', dict(useragent_header))
+ config = kwargs.pop('config', None)
+ if config:
+ kwargs.setdefault('verify', _get_tls_cacert(url, config))
+
+ with warnings.catch_warnings():
+ if not kwargs.get('verify'):
+ # ignore InsecureRequestWarning if verify=False
+ warnings.filterwarnings("ignore", category=InsecureRequestWarning)
+ return requests.get(url, **kwargs)
diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py
index 9bae7a4b4..555775636 100644
--- a/sphinx/writers/latex.py
+++ b/sphinx/writers/latex.py
@@ -113,6 +113,7 @@ ADDITIONAL_SETTINGS = {
'xelatex': {
'latex_engine': 'xelatex',
'polyglossia': '\\usepackage{polyglossia}',
+ 'babel': '',
'fontenc': '\\usepackage{fontspec}',
'fontpkg': '',
'utf8extra': ('\\catcode`^^^^00a0\\active\\protected\\def^^^^00a0'
@@ -182,7 +183,7 @@ class ExtBabel(Babel):
'italian'):
return '\\if\\catcode`\\"\\active\\shorthandoff{"}\\fi'
elif shortlang in ('tr', 'turkish'):
- return '\\shorthandoff{=}'
+ return '\\if\\catcode`\\=\\active\\shorthandoff{=}\\fi'
return ''
def uses_cyrillic(self):
@@ -396,6 +397,11 @@ class LaTeXTranslator(nodes.NodeVisitor):
# sort out some elements
self.elements = DEFAULT_SETTINGS.copy()
self.elements.update(ADDITIONAL_SETTINGS.get(builder.config.latex_engine, {}))
+ # allow the user to override them all
+ self.check_latex_elements()
+ self.elements.update(builder.config.latex_elements)
+
+ # but some have other interface in config file
self.elements.update({
'wrapperclass': self.format_docclass(document.settings.docclass),
# if empty, the title is set to the first section title
@@ -422,7 +428,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.elements['logo'] = '\\sphinxincludegraphics{%s}\\par' % \
path.basename(builder.config.latex_logo)
- if builder.config.language:
+ if builder.config.language \
+ and 'fncychap' not in builder.config.latex_elements:
# use Sonny style if any language specified
self.elements['fncychap'] = '\\usepackage[Sonny]{fncychap}'
@@ -438,17 +445,16 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.elements['classoptions'] += ',' + self.babel.get_language()
# set up multilingual module...
- if self.elements['polyglossia']:
- self.elements['babel'] = '' # disable babel
- self.elements['multilingual'] = '%s\n\\setmainlanguage{%s}' % \
- (self.elements['polyglossia'], self.babel.get_language())
- elif self.elements['babel']:
+ # 'babel' key is public and user setting must be obeyed
+ if self.elements['babel']:
+ # this branch is not taken for xelatex with writer default settings
self.elements['multilingual'] = self.elements['babel']
if builder.config.language:
self.elements['shorthandoff'] = self.babel.get_shorthandoff()
# Times fonts don't work with Cyrillic languages
- if self.babel.uses_cyrillic():
+ if self.babel.uses_cyrillic() \
+ and 'fontpkg' not in builder.config.latex_elements:
self.elements['fontpkg'] = ''
# pTeX (Japanese TeX) for support
@@ -460,6 +466,9 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.elements['multilingual'] = ''
# disable fncychap in Japanese documents
self.elements['fncychap'] = ''
+ elif self.elements['polyglossia']:
+ self.elements['multilingual'] = '%s\n\\setmainlanguage{%s}' % \
+ (self.elements['polyglossia'], self.babel.get_language())
if getattr(builder, 'usepackages', None):
def declare_package(packagename, options=None):
@@ -487,12 +496,11 @@ class LaTeXTranslator(nodes.NodeVisitor):
if tocdepth >= SECNUMDEPTH:
# Increase secnumdepth if tocdepth is depther than default SECNUMDEPTH
self.elements['secnumdepth'] = '\\setcounter{secnumdepth}{%d}' % tocdepth
+
if getattr(document.settings, 'contentsname', None):
self.elements['contentsname'] = \
self.babel_renewcommand('\\contentsname', document.settings.contentsname)
- # allow the user to override them all
- self.check_latex_elements()
- self.elements.update(builder.config.latex_elements)
+
if self.elements['maxlistdepth']:
self.elements['sphinxpkgoptions'] += (',maxlistdepth=%s' %
self.elements['maxlistdepth'])
@@ -622,7 +630,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
def babel_renewcommand(self, command, definition):
# type: (unicode, unicode) -> unicode
- if self.elements['babel']:
+ if self.elements['multilingual']:
prefix = '\\addto\\captions%s{' % self.babel.get_language()
suffix = '}'
else: # babel is disabled (mainly for Japanese environment)
@@ -882,36 +890,36 @@ class LaTeXTranslator(nodes.NodeVisitor):
if isinstance(parent, addnodes.seealso):
# the environment already handles this
raise nodes.SkipNode
- elif self.this_is_the_title:
- if len(node.children) != 1 and not isinstance(node.children[0],
- nodes.Text):
- self.builder.warn('document title is not a single Text node',
- (self.curfilestack[-1], node.line))
- if not self.elements['title']:
- # text needs to be escaped since it is inserted into
- # the output literally
- self.elements['title'] = node.astext().translate(tex_escape_map)
- self.this_is_the_title = 0
- raise nodes.SkipNode
elif isinstance(parent, nodes.section):
- short = ''
- if node.traverse(nodes.image):
- short = ('[%s]' %
- u' '.join(clean_astext(node).split()).translate(tex_escape_map))
-
- try:
- self.body.append(r'\%s%s{' % (self.sectionnames[self.sectionlevel], short))
- except IndexError:
- # just use "subparagraph", it's not numbered anyway
- self.body.append(r'\%s%s{' % (self.sectionnames[-1], short))
- self.context.append('}\n')
-
- self.restrict_footnote(node)
- if self.next_section_ids:
- for id in self.next_section_ids:
- self.context[-1] += self.hypertarget(id, anchor=False)
- self.next_section_ids.clear()
-
+ if self.this_is_the_title:
+ if len(node.children) != 1 and not isinstance(node.children[0],
+ nodes.Text):
+ self.builder.warn('document title is not a single Text node',
+ (self.curfilestack[-1], node.line))
+ if not self.elements['title']:
+ # text needs to be escaped since it is inserted into
+ # the output literally
+ self.elements['title'] = node.astext().translate(tex_escape_map)
+ self.this_is_the_title = 0
+ raise nodes.SkipNode
+ else:
+ short = ''
+ if node.traverse(nodes.image):
+ short = ('[%s]' %
+ u' '.join(clean_astext(node).split()).translate(tex_escape_map))
+
+ try:
+ self.body.append(r'\%s%s{' % (self.sectionnames[self.sectionlevel], short))
+ except IndexError:
+ # just use "subparagraph", it's not numbered anyway
+ self.body.append(r'\%s%s{' % (self.sectionnames[-1], short))
+ self.context.append('}\n')
+
+ self.restrict_footnote(node)
+ if self.next_section_ids:
+ for id in self.next_section_ids:
+ self.context[-1] += self.hypertarget(id, anchor=False)
+ self.next_section_ids.clear()
elif isinstance(parent, nodes.topic):
self.body.append(r'\sphinxstyletopictitle{')
self.context.append('}\n')
diff --git a/tests/roots/test-ext-inheritance_diagram/example/__init__.py b/tests/roots/test-ext-inheritance_diagram/example/__init__.py
new file mode 100644
index 000000000..2f85c0876
--- /dev/null
+++ b/tests/roots/test-ext-inheritance_diagram/example/__init__.py
@@ -0,0 +1 @@
+# example.py
diff --git a/tests/roots/test-ext-inheritance_diagram/example/sphinx.py b/tests/roots/test-ext-inheritance_diagram/example/sphinx.py
new file mode 100644
index 000000000..5eb8a2291
--- /dev/null
+++ b/tests/roots/test-ext-inheritance_diagram/example/sphinx.py
@@ -0,0 +1,5 @@
+# example.sphinx
+
+
+class DummyClass(object):
+ pass
diff --git a/tests/roots/test-intl/figure.po b/tests/roots/test-intl/figure.po
index cbd9c8837..449b15e3f 100644
--- a/tests/roots/test-intl/figure.po
+++ b/tests/roots/test-intl/figure.po
@@ -50,3 +50,10 @@ msgid ""
msgstr ""
".. image:: img.png\n"
" :alt: I18N -> IMG"
+
+msgid "image on substitution"
+msgstr "IMAGE ON SUBSTITUTION"
+
+msgid "image under note"
+msgstr "IMAGE UNDER NOTE"
+
diff --git a/tests/roots/test-intl/figure.txt b/tests/roots/test-intl/figure.txt
index b639a4ac1..633e12ee8 100644
--- a/tests/roots/test-intl/figure.txt
+++ b/tests/roots/test-intl/figure.txt
@@ -34,3 +34,20 @@ image url and alt
.. figure:: img.png
:alt: img
+
+image on substitution
+---------------------
+
+.. |sub image| image:: i18n.png
+
+image under note
+-----------------
+
+.. note::
+
+ .. image:: i18n.png
+ :alt: i18n under note
+
+ .. figure:: img.png
+ :alt: img under note
+
diff --git a/tests/roots/test-latex-title/conf.py b/tests/roots/test-latex-title/conf.py
new file mode 100644
index 000000000..709f1be48
--- /dev/null
+++ b/tests/roots/test-latex-title/conf.py
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+
+master_doc = 'index'
+
+# set empty string to the third column to use the first section title to document title
+latex_documents = [
+ (master_doc, 'test.tex', '', 'Sphinx', 'report')
+]
diff --git a/tests/roots/test-latex-title/index.rst b/tests/roots/test-latex-title/index.rst
new file mode 100644
index 000000000..411ad0009
--- /dev/null
+++ b/tests/roots/test-latex-title/index.rst
@@ -0,0 +1,12 @@
+.. admonition:: Notice
+
+ This generates nodes.title node before first section title.
+
+test-latex-title
+================
+
+.. toctree::
+ :numbered:
+
+ foo
+ bar
diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py
index fcef77be6..85be43d0d 100644
--- a/tests/test_build_latex.py
+++ b/tests/test_build_latex.py
@@ -149,6 +149,26 @@ def test_latex_warnings(app, status, warning):
'--- Got:\n' + warnings
+@with_app(buildername='latex', testroot='basic')
+def test_latex_title(app, status, warning):
+ app.builder.build_all()
+ result = (app.outdir / 'test.tex').text(encoding='utf8')
+ print(result)
+ print(status.getvalue())
+ print(warning.getvalue())
+ assert '\\title{The basic Sphinx documentation for testing}' in result
+
+
+@with_app(buildername='latex', testroot='latex-title')
+def test_latex_title_after_admonitions(app, status, warning):
+ app.builder.build_all()
+ result = (app.outdir / 'test.tex').text(encoding='utf8')
+ print(result)
+ print(status.getvalue())
+ print(warning.getvalue())
+ assert '\\title{test-latex-title}' in result
+
+
@with_app(buildername='latex', testroot='numfig',
confoverrides={'numfig': True})
def test_numref(app, status, warning):
diff --git a/tests/test_ext_inheritance_diagram.py b/tests/test_ext_inheritance_diagram.py
index bf1bbbac0..968bb1473 100644
--- a/tests/test_ext_inheritance_diagram.py
+++ b/tests/test_ext_inheritance_diagram.py
@@ -9,9 +9,10 @@
:license: BSD, see LICENSE for details.
"""
-import re
-from util import with_app
+import sys
+from util import with_app, rootdir, raises
from test_ext_graphviz import skip_if_graphviz_not_found
+from sphinx.ext.inheritance_diagram import InheritanceException, import_classes
@with_app('html', testroot='ext-inheritance_diagram')
@@ -40,3 +41,48 @@ def test_inheritance_diagram_latex(app, status, warning):
'\\\\includegraphics{inheritance-\\w+.pdf}\n'
'\\\\caption{Test Foo!}\\\\label{index:id1}\\\\end{figure}')
assert re.search(pattern, content, re.M)
+
+
+def test_import_classes():
+ from sphinx.application import Sphinx, TemplateBridge
+ from sphinx.util.i18n import CatalogInfo
+
+ try:
+ sys.path.append(rootdir / 'roots/test-ext-inheritance_diagram')
+ from example.sphinx import DummyClass
+
+ # got exception for unknown class or module
+ raises(InheritanceException, import_classes, 'unknown', None)
+ raises(InheritanceException, import_classes, 'unknown.Unknown', None)
+
+ # a module having no classes
+ classes = import_classes('sphinx', None)
+ assert classes == []
+
+ classes = import_classes('sphinx', 'foo')
+ assert classes == []
+
+ # all of classes in the module
+ classes = import_classes('sphinx.application', None)
+ assert set(classes) == set([Sphinx, TemplateBridge])
+
+ # specified class in the module
+ classes = import_classes('sphinx.application.Sphinx', None)
+ assert classes == [Sphinx]
+
+ # specified class in current module
+ classes = import_classes('Sphinx', 'sphinx.application')
+ assert classes == [Sphinx]
+
+ # relative module name to current module
+ classes = import_classes('i18n.CatalogInfo', 'sphinx.util')
+ assert classes == [CatalogInfo]
+
+ # got exception for functions
+ raises(InheritanceException, import_classes, 'encode_uri', 'sphinx.util')
+
+ # import submodule on current module (refs: #3164)
+ classes = import_classes('sphinx', 'example')
+ assert classes == [DummyClass]
+ finally:
+ sys.path.pop()
diff --git a/tests/test_intl.py b/tests/test_intl.py
index 43cd17c8e..68fae6b15 100644
--- a/tests/test_intl.py
+++ b/tests/test_intl.py
@@ -266,6 +266,18 @@ def test_text_builder(app, status, warning):
u"[image: i18n][image]\n"
u"\n"
u" [image: img][image]\n"
+ u"\n"
+ u"\n"
+ u"IMAGE ON SUBSTITUTION\n"
+ u"=====================\n"
+ u"\n"
+ u"\n"
+ u"IMAGE UNDER NOTE\n"
+ u"================\n"
+ u"\n"
+ u"Note: [image: i18n under note][image]\n"
+ u"\n"
+ u" [image: img under note][image]\n"
)
yield assert_equal, result, expect
diff --git a/utils/jssplitter_generator.py b/utils/jssplitter_generator.py
new file mode 100644
index 000000000..6eef86cc3
--- /dev/null
+++ b/utils/jssplitter_generator.py
@@ -0,0 +1,142 @@
+# -*- coding: utf-8 -*-
+import re
+import json
+import subprocess
+import sys
+import six
+
+# find char codes they are matched with Python's \\w(?u)
+
+match = re.compile(r'\w(?u)')
+begin = -1
+
+ranges = []
+singles = []
+
+for i in range(65536):
+ # 0xd800-0xdfff is surrogate pair area. skip this.
+ if not match.match(six.unichr(i)) and not (0xd800 <= i <= 0xdfff):
+ if begin == -1:
+ begin = i
+ elif begin != -1:
+ if begin + 1 == i:
+ singles.append(begin)
+ else:
+ ranges.append((begin, i - 1))
+ begin = -1
+
+
+# fold json within almost 80 chars per line
+def fold(jsonData, splitter):
+ code = json.dumps(jsonData)
+ lines = []
+ while True:
+ if len(code) < 71:
+ lines.append(' ' + code)
+ break
+ index = code.index(splitter, 70)
+ lines.append(' ' + code[:index+len(splitter)])
+ code = code[index+len(splitter):]
+ lines[0] = lines[0][8:]
+ return '\n'.join(lines)
+
+
+# JavaScript code
+js_src = '''
+var splitChars = (function() {
+ var result = {};
+ var singles = %s;
+ var i, j, start, end;
+ for (i = 0; i < singles.length; i++) {
+ result[singles[i]] = true;
+ }
+ var ranges = %s;
+ for (i = 0; i < ranges.length; i++) {
+ start = ranges[i][0];
+ end = ranges[i][1];
+ for (j = start; j <= end; j++) {
+ result[j] = true;
+ }
+ }
+ return result;
+})();
+
+function splitQuery(query) {
+ var result = [];
+ var start = -1;
+ for (var i = 0; i < query.length; i++) {
+ if (splitChars[query.charCodeAt(i)]) {
+ if (start !== -1) {
+ result.push(query.slice(start, i));
+ start = -1;
+ }
+ } else if (start === -1) {
+ start = i;
+ }
+ }
+ if (start !== -1) {
+ result.push(query.slice(start));
+ }
+ return result;
+}
+''' % (fold(singles, ','), fold(ranges, '],'))
+
+js_test_src = u'''
+// This is regression test for https://github.com/sphinx-doc/sphinx/issues/3150
+// generated by compat_regexp_generator.py
+// it needs node.js for testing
+var assert = require('assert');
+
+%s
+
+console.log("test splitting English words")
+assert.deepEqual(['Hello', 'World'], splitQuery(' Hello World '));
+console.log(' ... ok\\n')
+
+console.log("test splitting special characters")
+assert.deepEqual(['Pin', 'Code'], splitQuery('Pin-Code'));
+console.log(' ... ok\\n')
+
+console.log("test splitting Chinese characters")
+assert.deepEqual(['Hello', 'from', '中国', '上海'], splitQuery('Hello from 中国 上海'));
+console.log(' ... ok\\n')
+
+console.log("test splitting Emoji(surrogate pair) characters. It should keep emojis.")
+assert.deepEqual(['😁😁'], splitQuery('😁😁'));
+console.log(' ... ok\\n')
+
+console.log("test splitting umlauts. It should keep umlauts.")
+assert.deepEqual(
+ ['Löschen', 'Prüfung', 'Abändern', 'ærlig', 'spørsmål'],
+ splitQuery('Löschen Prüfung Abändern ærlig spørsmål'));
+console.log(' ... ok\\n')
+
+''' % js_src
+
+python_src = '''
+"""# -*- coding: utf-8 -*-
+ sphinx.search.jssplitter
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Provides Python compatible word splitter to JavaScript
+
+ :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+
+ DO NOT EDIT. This is generated by utils/jssplitter_generator.py
+"""
+
+splitter_code = """
+%s
+"""
+''' % js_src
+
+with open('../sphinx/search/jssplitter.py', 'w') as f:
+ f.write(python_src)
+
+with open('./regression_test.js', 'w') as f:
+ f.write(js_test_src.encode('utf-8'))
+
+print("starting test...")
+result = subprocess.call(['node', './regression_test.js'])
+sys.exit(result)