Merge branch 'stable'

This commit is contained in:
Takeshi KOMIYA 2017-01-10 01:04:21 +09:00
commit 8cbe1efe8d
23 changed files with 1973 additions and 1588 deletions

View File

@ -82,6 +82,7 @@ Bugs fixed
* #3268: Sphinx crashes with requests package from Debian jessie * #3268: Sphinx crashes with requests package from Debian jessie
* #3284: Sphinx crashes on parallel build with an extension which raises * #3284: Sphinx crashes on parallel build with an extension which raises
unserializable exception unserializable exception
* #3315: Bibliography crashes on latex build with docclass 'memoir'
Release 1.5.1 (released Dec 13, 2016) Release 1.5.1 (released Dec 13, 2016)

View File

@ -6,7 +6,7 @@ PYTHON ?= python
DONT_CHECK = -i build -i dist -i sphinx/style/jquery.js \ DONT_CHECK = -i build -i dist -i sphinx/style/jquery.js \
-i sphinx/pycode/pgen2 -i sphinx/util/smartypants.py \ -i sphinx/pycode/pgen2 -i sphinx/util/smartypants.py \
-i .ropeproject -i doc/_build -i tests/path.py \ -i .ropeproject -i doc/_build -i tests/path.py \
-i tests/coverage.py -i utils/convert.py \ -i utils/convert.py \
-i tests/typing_test_data.py \ -i tests/typing_test_data.py \
-i tests/test_autodoc_py35.py \ -i tests/test_autodoc_py35.py \
-i tests/roots/test-warnings/undecodable.rst \ -i tests/roots/test-warnings/undecodable.rst \

View File

@ -712,13 +712,13 @@ class StandardDomain(Domain):
else: else:
title = env.config.numfig_format.get(figtype, '') title = env.config.numfig_format.get(figtype, '')
if figname is None and '%{name}' in title: if figname is None and '{name}' in title:
logger.warning('the link has no caption: %s', title, location=node) logger.warning('the link has no caption: %s', title, location=node)
return contnode return contnode
else: else:
fignum = '.'.join(map(str, fignumber)) fignum = '.'.join(map(str, fignumber))
if '{name}' in title or 'number' in title: if '{name}' in title or 'number' in title:
# new style format (cf. "Fig.%{number}") # new style format (cf. "Fig.{number}")
if figname: if figname:
newtitle = title.format(name=figname, number=fignum) newtitle = title.format(name=figname, number=fignum)
else: else:

View File

@ -1082,8 +1082,14 @@
% make commands known to non-Sphinx document classes % make commands known to non-Sphinx document classes
\providecommand*{\sphinxtableofcontents}{\tableofcontents} \providecommand*{\sphinxtableofcontents}{\tableofcontents}
\providecommand*{\sphinxthebibliography}{\thebibliography} \spx@ifundefined{sphinxthebibliography}
\providecommand*{\sphinxtheindex}{\theindex} {\newenvironment
{sphinxthebibliography}{\begin{thebibliography}}{\end{thebibliography}}%
}
{}% else clause of ifundefined
\spx@ifundefined{sphinxtheindex}
{\newenvironment{sphinxtheindex}{\begin{theindex}}{\end{theindex}}}%
{}% else clause of ifundefined
% remove LaTeX's cap on nesting depth if 'maxlistdepth' key used. % remove LaTeX's cap on nesting depth if 'maxlistdepth' key used.
% This is a hack, which works with the standard classes: it assumes \@toodeep % This is a hack, which works with the standard classes: it assumes \@toodeep

View File

@ -3,7 +3,7 @@ pytest>=3.0
pytest-cov pytest-cov
mock mock
six>=1.4 six>=1.4
Jinja2>=2.3,<2.9 Jinja2>=2.3
Pygments>=2.0 Pygments>=2.0
docutils>=0.11 docutils>=0.11
snowballstemmer>=1.1 snowballstemmer>=1.1

View File

@ -1,19 +1,25 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import print_function
import sys import sys
import subprocess import subprocess
from collections import namedtuple
import pytest import pytest
from six import StringIO from six import StringIO, string_types
from util import SphinxTestApp, path import util
@pytest.fixture @pytest.fixture
def app_params(request): def app_params(request, test_params, shared_result):
""" """
parameters that is specified by 'pytest.mark.sphinx' for parameters that is specified by 'pytest.mark.sphinx' for
sphinx.application.Sphinx initialization sphinx.application.Sphinx initialization
""" """
# ##### process pytest.mark.sphinx
markers = request.node.get_marker("sphinx") markers = request.node.get_marker("sphinx")
pargs = {} pargs = {}
kwargs = {} kwargs = {}
@ -26,17 +32,99 @@ def app_params(request):
kwargs.update(info.kwargs) kwargs.update(info.kwargs)
args = [pargs[i] for i in sorted(pargs.keys())] args = [pargs[i] for i in sorted(pargs.keys())]
return args, kwargs
# ##### process pytest.mark.test_params
if test_params['shared_result']:
if 'srcdir' in kwargs:
raise pytest.Exception('You can not spcify shared_result and '
'srcdir in same time.')
kwargs['srcdir'] = test_params['shared_result']
restore = shared_result.restore(test_params['shared_result'])
kwargs.update(restore)
# ##### prepare Application params
if 'srcdir' in kwargs:
srcdir = util.tempdir / kwargs['srcdir']
else:
srcdir = util.tempdir / kwargs.get('testroot', 'root')
kwargs['srcdir'] = srcdir
if kwargs.get('testroot') is None:
testroot_path = util.rootdir / 'root'
else:
testroot_path = util.rootdir / 'roots' / ('test-' + kwargs['testroot'])
if not srcdir.exists():
testroot_path.copytree(srcdir)
return namedtuple('app_params', 'args,kwargs')(args, kwargs)
@pytest.fixture
def test_params(request):
"""
test parameters that is specified by 'pytest.mark.test_params'
:param Union[str] shared_result:
If the value is provided, app._status and app._warning objects will be
shared in the parametrized test functions and/or test functions that
have same 'shared_result' value.
**NOTE**: You can not specify shared_result and srcdir in same time.
"""
env = request.node.get_marker('test_params')
kwargs = env.kwargs if env else {}
result = {
'shared_result': None,
}
result.update(kwargs)
if (result['shared_result'] and
not isinstance(result['shared_result'], string_types)):
raise pytest.Exception('You can only provide a string type of value '
'for "shared_result" ')
return result
class SphinxTestAppWrapperForSkipBuilding(object):
"""
This class is a wrapper for SphinxTestApp to speed up the test by skipping
`app.build` process if it is already built and there is even one output
file.
"""
def __init__(self, app_):
self.app = app_
def __getattr__(self, name):
return getattr(self.app, name)
def build(self, *args, **kw):
if not self.app.outdir.listdir():
# if listdir is empty, do build.
self.app.build(*args, **kw)
# otherwise, we can use built cache
@pytest.fixture(scope='function') @pytest.fixture(scope='function')
def app(app_params, make_app): def app(test_params, app_params, make_app, shared_result):
""" """
provides sphinx.application.Sphinx object provides sphinx.application.Sphinx object
""" """
args, kwargs = app_params args, kwargs = app_params
app_ = make_app(*args, **kwargs) app_ = make_app(*args, **kwargs)
return app_ yield app_
print('# testroot:', kwargs.get('testroot', 'root'))
print('# builder:', app_.buildername)
print('# srcdir:', app_.srcdir)
print('# outdir:', app_.outdir)
print('# status:', '\n' + app_._status.getvalue())
print('# warning:', '\n' + app_._warning.getvalue())
if test_params['shared_result']:
shared_result.store(test_params['shared_result'], app_)
@pytest.fixture(scope='function') @pytest.fixture(scope='function')
@ -56,7 +144,7 @@ def warning(app):
@pytest.fixture() @pytest.fixture()
def make_app(): def make_app(test_params):
""" """
provides make_app function to initialize SphinxTestApp instance. provides make_app function to initialize SphinxTestApp instance.
if you want to initialize 'app' in your test function. please use this if you want to initialize 'app' in your test function. please use this
@ -69,8 +157,10 @@ def make_app():
status, warning = StringIO(), StringIO() status, warning = StringIO(), StringIO()
kwargs.setdefault('status', status) kwargs.setdefault('status', status)
kwargs.setdefault('warning', warning) kwargs.setdefault('warning', warning)
app_ = SphinxTestApp(*args, **kwargs) app_ = util.SphinxTestApp(*args, **kwargs)
apps.append(app_) apps.append(app_)
if test_params['shared_result']:
app_ = SphinxTestAppWrapperForSkipBuilding(app_)
return app_ return app_
yield make yield make
@ -79,6 +169,38 @@ def make_app():
app_.cleanup() app_.cleanup()
class SharedResult(object):
cache = {}
def store(self, key, app_):
if key in self.cache:
return
data = {
'status': app_._status.getvalue(),
'warning': app_._warning.getvalue(),
}
self.cache[key] = data
def restore(self, key):
if key not in self.cache:
return {}
data = self.cache[key]
return {
'status': StringIO(data['status']),
'warning': StringIO(data['warning']),
}
@pytest.fixture
def shared_result():
return SharedResult()
@pytest.fixture(scope='module', autouse=True)
def _shared_result_cache():
SharedResult.cache.clear()
@pytest.fixture @pytest.fixture
def if_graphviz_found(app): def if_graphviz_found(app):
""" """
@ -105,4 +227,4 @@ def tempdir(tmpdir):
temporary directory that wrapped with `path` class. temporary directory that wrapped with `path` class.
this fixture is for compat with old test implementation. this fixture is for compat with old test implementation.
""" """
return path(tmpdir) return util.path(tmpdir)

View File

@ -13,7 +13,7 @@
# "raises" imported for usage by autodoc # "raises" imported for usage by autodoc
import six import six
import sys import sys
from util import TestApp, Struct, raises, SkipTest from util import SphinxTestApp, Struct
import pytest import pytest
from six import StringIO from six import StringIO
@ -27,7 +27,7 @@ app = None
def setup_module(): def setup_module():
global app global app
app = TestApp() app = SphinxTestApp()
app.builder.env.app = app app.builder.env.app = app
app.builder.env.temp_data['docname'] = 'dummy' app.builder.env.temp_data['docname'] = 'dummy'
app.connect('autodoc-process-docstring', process_docstring) app.connect('autodoc-process-docstring', process_docstring)
@ -185,7 +185,7 @@ def test_generate():
'Class.meth', more_content=add_content) 'Class.meth', more_content=add_content)
# test check_module # test check_module
inst = FunctionDocumenter(directive, 'raises') inst = FunctionDocumenter(directive, 'add_documenter')
inst.generate(check_module=True) inst.generate(check_module=True)
assert len(directive.result) == 0 assert len(directive.result) == 0

View File

@ -17,11 +17,15 @@ import warnings
import traceback import traceback
from path import path from path import path
import pytest
testroot = os.path.dirname(__file__) or '.' testroot = os.path.dirname(__file__) or '.'
sys.path.insert(0, os.path.abspath(os.path.join(testroot, os.path.pardir))) 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=PendingDeprecationWarning, module=r'_pytest\..*')
# check dependencies before testing # check dependencies before testing
print('Checking dependencies...') print('Checking dependencies...')
for modname in ('pytest', 'mock', 'six', 'docutils', 'jinja2', 'pygments', for modname in ('pytest', 'mock', 'six', 'docutils', 'jinja2', 'pygments',
@ -49,9 +53,6 @@ tempdir.makedirs()
print('Running Sphinx test suite (with Python %s)...' % sys.version.split()[0]) print('Running Sphinx test suite (with Python %s)...' % sys.version.split()[0])
sys.stdout.flush() sys.stdout.flush()
# filter warnings of test dependencies
warnings.filterwarnings('ignore', category=DeprecationWarning, module='site') # virtualenv
# exclude 'root' and 'roots' dirs for pytest test collector # exclude 'root' and 'roots' dirs for pytest test collector
ignore_paths = [ ignore_paths = [
os.path.relpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), sub)) os.path.relpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), sub))
@ -61,4 +62,5 @@ args = sys.argv[1:]
for path in ignore_paths: for path in ignore_paths:
args.extend(['--ignore', path]) args.extend(['--ignore', path])
import pytest
sys.exit(pytest.main(args)) sys.exit(pytest.main(args))

View File

@ -10,8 +10,7 @@
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
# "raises" imported for usage by autodoc from util import SphinxTestApp, Struct # NOQA
from util import TestApp, Struct, raises, SkipTest # NOQA
import pytest import pytest
import enum import enum
@ -26,7 +25,7 @@ app = None
def setup_module(): def setup_module():
global app global app
app = TestApp() app = SphinxTestApp()
app.builder.env.app = app app.builder.env.app = app
app.builder.env.temp_data['docname'] = 'dummy' app.builder.env.temp_data['docname'] = 'dummy'
app.connect('autodoc-process-docstring', process_docstring) app.connect('autodoc-process-docstring', process_docstring)
@ -125,26 +124,27 @@ def test_parse_name():
del _warnings[:] del _warnings[:]
# for functions/classes # for functions/classes
verify('function', 'util.raises', ('util', ['raises'], None, None)) verify('function', 'test_autodoc.raises',
verify('function', 'util.raises(exc) -> None', ('test_autodoc', ['raises'], None, None))
('util', ['raises'], 'exc', 'None')) verify('function', 'test_autodoc.raises(exc) -> None',
directive.env.temp_data['autodoc:module'] = 'util' ('test_autodoc', ['raises'], 'exc', 'None'))
verify('function', 'raises', ('util', ['raises'], None, None)) directive.env.temp_data['autodoc:module'] = 'test_autodoc'
verify('function', 'raises', ('test_autodoc', ['raises'], None, None))
del directive.env.temp_data['autodoc:module'] del directive.env.temp_data['autodoc:module']
directive.env.ref_context['py:module'] = 'util' directive.env.ref_context['py:module'] = 'test_autodoc'
verify('function', 'raises', ('util', ['raises'], None, None)) verify('function', 'raises', ('test_autodoc', ['raises'], None, None))
verify('class', 'TestApp', ('util', ['TestApp'], None, None)) verify('class', 'Base', ('test_autodoc', ['Base'], None, None))
# for members # for members
directive.env.ref_context['py:module'] = 'foo' directive.env.ref_context['py:module'] = 'foo'
verify('method', 'util.TestApp.cleanup', verify('method', 'util.SphinxTestApp.cleanup',
('util', ['TestApp', 'cleanup'], None, None)) ('util', ['SphinxTestApp', 'cleanup'], None, None))
directive.env.ref_context['py:module'] = 'util' directive.env.ref_context['py:module'] = 'util'
directive.env.ref_context['py:class'] = 'Foo' directive.env.ref_context['py:class'] = 'Foo'
directive.env.temp_data['autodoc:class'] = 'TestApp' directive.env.temp_data['autodoc:class'] = 'SphinxTestApp'
verify('method', 'cleanup', ('util', ['TestApp', 'cleanup'], None, None)) verify('method', 'cleanup', ('util', ['SphinxTestApp', 'cleanup'], None, None))
verify('method', 'TestApp.cleanup', verify('method', 'SphinxTestApp.cleanup',
('util', ['TestApp', 'cleanup'], None, None)) ('util', ['SphinxTestApp', 'cleanup'], None, None))
# and clean up # and clean up
del directive.env.ref_context['py:module'] del directive.env.ref_context['py:module']
@ -658,7 +658,7 @@ def test_generate():
'Class.meth', more_content=add_content) 'Class.meth', more_content=add_content)
# test check_module # test check_module
inst = FunctionDocumenter(directive, 'raises') inst = FunctionDocumenter(directive, 'add_documenter')
inst.generate(check_module=True) inst.generate(check_module=True)
assert len(directive.result) == 0 assert len(directive.result) == 0
@ -878,6 +878,11 @@ __all__ = ['Class']
integer = 1 integer = 1
def raises(exc, func, *args, **kwds):
"""Raise AssertionError if ``func(*args, **kwds)`` does not raise *exc*."""
pass
class CustomEx(Exception): class CustomEx(Exception):
"""My custom exception.""" """My custom exception."""
@ -1086,7 +1091,7 @@ def test_type_hints():
try: try:
from typing_test_data import f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11 from typing_test_data import f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11
except (ImportError, SyntaxError): except (ImportError, SyntaxError):
raise SkipTest('Cannot import Python code with function annotations') pytest.skip('Cannot import Python code with function annotations')
def verify_arg_spec(f, expected): def verify_arg_spec(f, expected):
assert formatargspec(f, *getargspec(f)) == expected assert formatargspec(f, *getargspec(f)) == expected

View File

@ -15,14 +15,8 @@ import mock
import pytest import pytest
from textwrap import dedent from textwrap import dedent
from sphinx.errors import SphinxError from sphinx.errors import SphinxError
import sphinx.builders.linkcheck
from util import rootdir, tempdir, SkipTest, TestApp, path from util import rootdir, tempdir, path
try:
from docutils.writers.manpage import Writer as ManWriter
except ImportError:
ManWriter = None
def request_session_head(url, **kwargs): def request_session_head(url, **kwargs):
@ -32,24 +26,17 @@ def request_session_head(url, **kwargs):
return response return response
def verify_build(buildername, srcdir): @pytest.fixture
if buildername == 'man' and ManWriter is None: def nonascii_srcdir(request):
raise SkipTest('man writer is not available')
app = TestApp(buildername=buildername, srcdir=srcdir)
try:
app.builder.build_all()
finally:
app.cleanup()
def test_build_all():
# If supported, build in a non-ASCII source dir # If supported, build in a non-ASCII source dir
test_name = u'\u65e5\u672c\u8a9e' test_name = u'\u65e5\u672c\u8a9e'
basedir = tempdir / request.node.originalname
try: try:
srcdir = tempdir / test_name srcdir = basedir / test_name
(rootdir / 'root').copytree(tempdir / test_name) if not srcdir.exists():
(rootdir / 'root').copytree(srcdir)
except UnicodeEncodeError: except UnicodeEncodeError:
srcdir = tempdir / 'all' srcdir = basedir / 'all'
else: else:
# add a doc with a non-ASCII file name to the source dir # add a doc with a non-ASCII file name to the source dir
(srcdir / (test_name + '.txt')).write_text(dedent(""" (srcdir / (test_name + '.txt')).write_text(dedent("""
@ -63,31 +50,33 @@ def test_build_all():
%(test_name)s/%(test_name)s %(test_name)s/%(test_name)s
""" % {'test_name': test_name}) """ % {'test_name': test_name})
) )
return srcdir
with mock.patch('sphinx.builders.linkcheck.requests') as requests:
requests.head = request_session_head
@pytest.mark.parametrize(
"buildername",
[
# note: no 'html' - if it's ok with dirhtml it's ok with html # note: no 'html' - if it's ok with dirhtml it's ok with html
for buildername in ['dirhtml', 'singlehtml', 'latex', 'texinfo', 'pickle', 'dirhtml', 'singlehtml', 'latex', 'texinfo', 'pickle', 'json', 'text',
'json', 'text', 'htmlhelp', 'qthelp', 'epub2', 'epub', 'htmlhelp', 'qthelp', 'epub2', 'epub', 'applehelp', 'changes', 'xml',
'applehelp', 'changes', 'xml', 'pseudoxml', 'man', 'pseudoxml', 'man', 'linkcheck',
'linkcheck']: ],
yield verify_build, buildername, srcdir )
@mock.patch('sphinx.builders.linkcheck.requests.head',
side_effect=request_session_head)
def test_build_all(requests_head, make_app, nonascii_srcdir, buildername):
app = make_app(buildername, srcdir=nonascii_srcdir)
app.build()
def test_master_doc_not_found(tempdir): def test_master_doc_not_found(tempdir, make_app):
(tempdir / 'conf.py').write_text('master_doc = "index"') (tempdir / 'conf.py').write_text('master_doc = "index"')
assert tempdir.listdir() == ['conf.py'] assert tempdir.listdir() == ['conf.py']
try: app = make_app('dummy', srcdir=tempdir)
app = TestApp(buildername='dummy', srcdir=tempdir) with pytest.raises(SphinxError):
app.builder.build_all() app.builder.build_all()
assert False # SphinxError not raised
except Exception as exc:
assert isinstance(exc, SphinxError)
finally:
app.cleanup()
@pytest.mark.sphinx(buildername='text', testroot='circular') @pytest.mark.sphinx(buildername='text', testroot='circular')

View File

@ -17,36 +17,36 @@ from subprocess import Popen, PIPE
import pytest import pytest
from util import ( from sphinx.util.osutil import cd
gen_with_app, SkipTest, assert_in, assert_true, assert_equal
)
@gen_with_app('gettext', srcdir='root-gettext') @pytest.mark.sphinx('gettext', srcdir='root-gettext')
def test_all(app, status, warning): def test_build_gettext(app):
# Generic build; should fail only when the builder is horribly broken. # Generic build; should fail only when the builder is horribly broken.
app.builder.build_all() app.builder.build_all()
# Do messages end up in the correct location? # Do messages end up in the correct location?
# top-level documents end up in a message catalog # top-level documents end up in a message catalog
yield assert_true, (app.outdir / 'extapi.pot').isfile() assert (app.outdir / 'extapi.pot').isfile()
# directory items are grouped into sections # directory items are grouped into sections
yield assert_true, (app.outdir / 'subdir.pot').isfile() assert (app.outdir / 'subdir.pot').isfile()
# regression test for issue #960 # regression test for issue #960
catalog = (app.outdir / 'markup.pot').text(encoding='utf-8') catalog = (app.outdir / 'markup.pot').text(encoding='utf-8')
yield assert_in, 'msgid "something, something else, something more"', catalog assert 'msgid "something, something else, something more"' in catalog
@pytest.mark.sphinx('gettext', srcdir='root-gettext')
def test_msgfmt(app):
app.builder.build_all()
(app.outdir / 'en' / 'LC_MESSAGES').makedirs() (app.outdir / 'en' / 'LC_MESSAGES').makedirs()
cwd = os.getcwd() with cd(app.outdir):
os.chdir(app.outdir)
try:
try: try:
p = Popen(['msginit', '--no-translator', '-i', 'markup.pot', p = Popen(['msginit', '--no-translator', '-i', 'markup.pot',
'--locale', 'en_US'], '--locale', 'en_US'],
stdout=PIPE, stderr=PIPE) stdout=PIPE, stderr=PIPE)
except OSError: except OSError:
raise SkipTest # most likely msginit was not found pytest.skip() # most likely msginit was not found
else: else:
stdout, stderr = p.communicate() stdout, stderr = p.communicate()
if p.returncode != 0: if p.returncode != 0:
@ -54,13 +54,13 @@ def test_all(app, status, warning):
print(stderr) print(stderr)
assert False, 'msginit exited with return code %s' % \ assert False, 'msginit exited with return code %s' % \
p.returncode p.returncode
yield assert_true, (app.outdir / 'en_US.po').isfile(), 'msginit failed' assert (app.outdir / 'en_US.po').isfile(), 'msginit failed'
try: try:
p = Popen(['msgfmt', 'en_US.po', '-o', p = Popen(['msgfmt', 'en_US.po', '-o',
os.path.join('en', 'LC_MESSAGES', 'test_root.mo')], os.path.join('en', 'LC_MESSAGES', 'test_root.mo')],
stdout=PIPE, stderr=PIPE) stdout=PIPE, stderr=PIPE)
except OSError: except OSError:
raise SkipTest # most likely msgfmt was not found pytest.skip() # most likely msgfmt was not found
else: else:
stdout, stderr = p.communicate() stdout, stderr = p.communicate()
if p.returncode != 0: if p.returncode != 0:
@ -68,20 +68,17 @@ def test_all(app, status, warning):
print(stderr) print(stderr)
assert False, 'msgfmt exited with return code %s' % \ assert False, 'msgfmt exited with return code %s' % \
p.returncode p.returncode
yield (assert_true, mo = app.outdir / 'en' / 'LC_MESSAGES' / 'test_root.mo'
(app.outdir / 'en' / 'LC_MESSAGES' / 'test_root.mo').isfile(), assert mo.isfile(), 'msgfmt failed'
'msgfmt failed')
finally:
os.chdir(cwd)
_ = gettext.translation('test_root', app.outdir, languages=['en']).gettext _ = gettext.translation('test_root', app.outdir, languages=['en']).gettext
yield assert_equal, _("Testing various markup"), u"Testing various markup" assert _("Testing various markup") == u"Testing various markup"
@pytest.mark.sphinx( @pytest.mark.sphinx(
'gettext', testroot='intl', srcdir='gettext', 'gettext', testroot='intl', srcdir='gettext',
confoverrides={'gettext_compact': False}) confoverrides={'gettext_compact': False})
def test_gettext_index_entries(app, status, warning): def test_gettext_index_entries(app):
# regression test for #976 # regression test for #976
app.builder.build(['index_entries']) app.builder.build(['index_entries'])
@ -128,8 +125,9 @@ def test_gettext_index_entries(app, status, warning):
@pytest.mark.sphinx( @pytest.mark.sphinx(
'gettext', testroot='intl', srcdir='gettext', 'gettext', testroot='intl', srcdir='gettext',
confoverrides={'gettext_compact': False, 'gettext_additional_targets': []}) confoverrides={'gettext_compact': False,
def test_gettext_disable_index_entries(app, status, warning): 'gettext_additional_targets': []})
def test_gettext_disable_index_entries(app):
# regression test for #976 # regression test for #976
app.builder.build(['index_entries']) app.builder.build(['index_entries'])
@ -160,7 +158,7 @@ def test_gettext_disable_index_entries(app, status, warning):
@pytest.mark.sphinx('gettext', testroot='intl', srcdir='gettext') @pytest.mark.sphinx('gettext', testroot='intl', srcdir='gettext')
def test_gettext_template(app, status, warning): def test_gettext_template(app):
app.builder.build_all() app.builder.build_all()
assert (app.outdir / 'sphinx.pot').isfile() assert (app.outdir / 'sphinx.pot').isfile()

File diff suppressed because it is too large Load Diff

View File

@ -13,9 +13,6 @@ from six import PY3, iteritems
import pytest import pytest
import mock import mock
from util import TestApp, gen_with_app, \
assert_in, assert_not_in
import sphinx import sphinx
from sphinx.config import Config from sphinx.config import Config
from sphinx.errors import ExtensionError, ConfigError, VersionRequirementError from sphinx.errors import ExtensionError, ConfigError, VersionRequirementError
@ -76,7 +73,6 @@ def test_core_config(app, status, warning):
assert cfg['project'] == cfg.project == 'Sphinx Tests' assert cfg['project'] == cfg.project == 'Sphinx Tests'
@pytest.mark.sphinx()
def test_extension_values(app, status, warning): def test_extension_values(app, status, warning):
cfg = app.config cfg = app.config
@ -125,39 +121,39 @@ def test_errors_warnings(logger, tempdir):
assert logger.warning.called is True assert logger.warning.called is True
def test_errors_if_setup_is_not_callable(tempdir): def test_errors_if_setup_is_not_callable(tempdir, make_app):
# test the error to call setup() in the config file # test the error to call setup() in the config file
(tempdir / 'conf.py').write_text(u'setup = 1') (tempdir / 'conf.py').write_text(u'setup = 1')
with pytest.raises(ConfigError) as excinfo: with pytest.raises(ConfigError) as excinfo:
TestApp(srcdir=tempdir) make_app(srcdir=tempdir)
assert 'callable' in str(excinfo.value) assert 'callable' in str(excinfo.value)
@mock.patch.object(sphinx, '__display_version__', '1.3.4') @mock.patch.object(sphinx, '__display_version__', '1.3.4')
def test_needs_sphinx(): def test_needs_sphinx(make_app):
# micro version # micro version
app = TestApp(confoverrides={'needs_sphinx': '1.3.3'}) # OK: less app = make_app(confoverrides={'needs_sphinx': '1.3.3'}) # OK: less
app.cleanup() app.cleanup()
app = TestApp(confoverrides={'needs_sphinx': '1.3.4'}) # OK: equals app = make_app(confoverrides={'needs_sphinx': '1.3.4'}) # OK: equals
app.cleanup() app.cleanup()
with pytest.raises(VersionRequirementError): with pytest.raises(VersionRequirementError):
TestApp(confoverrides={'needs_sphinx': '1.3.5'}) # NG: greater make_app(confoverrides={'needs_sphinx': '1.3.5'}) # NG: greater
# minor version # minor version
app = TestApp(confoverrides={'needs_sphinx': '1.2'}) # OK: less app = make_app(confoverrides={'needs_sphinx': '1.2'}) # OK: less
app.cleanup() app.cleanup()
app = TestApp(confoverrides={'needs_sphinx': '1.3'}) # OK: equals app = make_app(confoverrides={'needs_sphinx': '1.3'}) # OK: equals
app.cleanup() app.cleanup()
with pytest.raises(VersionRequirementError): with pytest.raises(VersionRequirementError):
TestApp(confoverrides={'needs_sphinx': '1.4'}) # NG: greater make_app(confoverrides={'needs_sphinx': '1.4'}) # NG: greater
# major version # major version
app = TestApp(confoverrides={'needs_sphinx': '0'}) # OK: less app = make_app(confoverrides={'needs_sphinx': '0'}) # OK: less
app.cleanup() app.cleanup()
app = TestApp(confoverrides={'needs_sphinx': '1'}) # OK: equals app = make_app(confoverrides={'needs_sphinx': '1'}) # OK: equals
app.cleanup() app.cleanup()
with pytest.raises(VersionRequirementError): with pytest.raises(VersionRequirementError):
TestApp(confoverrides={'needs_sphinx': '2'}) # NG: greater make_app(confoverrides={'needs_sphinx': '2'}) # NG: greater
@mock.patch("sphinx.config.logger") @mock.patch("sphinx.config.logger")
@ -177,12 +173,14 @@ def test_config_eol(logger, tempdir):
'primary_domain': None}) 'primary_domain': None})
def test_builtin_conf(app, status, warning): def test_builtin_conf(app, status, warning):
warnings = warning.getvalue() warnings = warning.getvalue()
assert_in('master_doc', warnings, assert 'master_doc' in warnings, (
'override on builtin "master_doc" should raise a type warning') 'override on builtin "master_doc" should raise a type warning')
assert_not_in('language', warnings, 'explicitly permitted ' assert 'language' not in warnings, (
'override on builtin "language" should NOT raise a type warning') 'explicitly permitted override on builtin "language" should NOT raise '
assert_not_in('primary_domain', warnings, 'override to None on builtin ' 'a type warning')
'"primary_domain" should NOT raise a type warning') assert 'primary_domain' not in warnings, (
'override to None on builtin "primary_domain" should NOT raise a type '
'warning')
# See roots/test-config/conf.py. # See roots/test-config/conf.py.
@ -197,7 +195,7 @@ TYPECHECK_WARNINGS = {
'value8': False, 'value8': False,
'value9': False, 'value9': False,
'value10': False, 'value10': False,
'value11': True, 'value11': False if PY3 else True,
'value12': False, 'value12': False,
'value13': False, 'value13': False,
'value14': False, 'value14': False,
@ -206,15 +204,17 @@ TYPECHECK_WARNINGS = {
} }
@gen_with_app(testroot='config') @pytest.mark.parametrize("key,should", iteritems(TYPECHECK_WARNINGS))
def test_gen_check_types(app, status, warning): @pytest.mark.sphinx(testroot='config')
if PY3: def test_check_types(warning, key, should):
TYPECHECK_WARNINGS['value11'] = False warn = warning.getvalue()
if should:
for key, should in iteritems(TYPECHECK_WARNINGS): assert key in warn, (
yield assert_in if should else assert_not_in, key, warning.getvalue(), ( 'override on "%s" should raise a type warning' % key
'override on "%s" should%s raise a type warning' % )
(key, '' if should else ' NOT') else:
assert key not in warn, (
'override on "%s" should NOT raise a type warning' % key
) )

View File

@ -8,42 +8,30 @@
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
import os import pytest
from util import TestApp
def test_correct_year(): @pytest.fixture(
try: params=[
# save current value of SOURCE_DATE_EPOCH
sde = os.environ.pop('SOURCE_DATE_EPOCH', None)
# test with SOURCE_DATE_EPOCH unset: no modification # test with SOURCE_DATE_EPOCH unset: no modification
app = TestApp(buildername='html', testroot='correct-year') (None, '2006-2009'),
app.builder.build_all() # test with SOURCE_DATE_EPOCH set: copyright year should be updated
content = (app.outdir / 'contents.html').text() ('1293840000', '2006-2011'),
app.cleanup() ('1293839999', '2006-2010'),
assert '2006-2009' in content ],
# test with SOURCE_DATE_EPOCH set: copyright year should be )
# updated def expect_date(request, monkeypatch):
os.environ['SOURCE_DATE_EPOCH'] = "1293840000" sde, expect = request.param
app = TestApp(buildername='html', testroot='correct-year') if sde:
app.builder.build_all() monkeypatch.setenv('SOURCE_DATE_EPOCH', sde)
content = (app.outdir / 'contents.html').text() else:
app.cleanup() monkeypatch.delenv('SOURCE_DATE_EPOCH', raising=False)
assert '2006-2011' in content yield expect
os.environ['SOURCE_DATE_EPOCH'] = "1293839999"
app = TestApp(buildername='html', testroot='correct-year')
app.builder.build_all()
content = (app.outdir / 'contents.html').text()
app.cleanup()
assert '2006-2010' in content
finally: @pytest.mark.sphinx('html', testroot='correct-year')
# Restores SOURCE_DATE_EPOCH def test_correct_year(expect_date, app):
if sde is None: app.build()
os.environ.pop('SOURCE_DATE_EPOCH', None) content = (app.outdir / 'contents.html').text()
else: assert expect_date in content
os.environ['SOURCE_DATE_EPOCH'] = sde

View File

@ -9,9 +9,7 @@
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
from six import StringIO from util import SphinxTestApp, path
from util import TestApp, path
from sphinx.builders.html import StandaloneHTMLBuilder from sphinx.builders.html import StandaloneHTMLBuilder
from sphinx.builders.latex import LaTeXBuilder from sphinx.builders.latex import LaTeXBuilder
@ -21,7 +19,7 @@ app = env = None
def setup_module(): def setup_module():
global app, env global app, env
app = TestApp(srcdir='root-envtest', warning=StringIO()) app = SphinxTestApp(srcdir='root-envtest')
env = app.env env = app.env

View File

@ -16,23 +16,13 @@ from sphinx.addnodes import compact_paragraph, only
from sphinx.builders.html import StandaloneHTMLBuilder from sphinx.builders.html import StandaloneHTMLBuilder
import pytest import pytest
from util import gen_with_app, assert_node from util import assert_node
@gen_with_app('xml', testroot='toctree') @pytest.mark.sphinx('xml', testroot='toctree')
def test_basic(app, status, warning): @pytest.mark.test_params(shared_result='test_environment_toctree_basic')
def test_process_doc(app):
app.build() app.build()
yield _test_process_doc, app
yield _test_get_toc_for, app
yield _test_get_toc_for_only, app
yield _test_get_toc_for_tocdepth, app
yield _test_get_toctree_for, app
yield _test_get_toctree_for_collapse, app
yield _test_get_toctree_for_maxdepth, app
yield _test_get_toctree_for_includehidden, app
def _test_process_doc(app):
# tocs # tocs
toctree = app.env.tocs['index'] toctree = app.env.tocs['index']
assert_node(toctree, assert_node(toctree,
@ -99,7 +89,7 @@ def _test_process_doc(app):
@pytest.mark.sphinx('dummy', testroot='toctree-glob') @pytest.mark.sphinx('dummy', testroot='toctree-glob')
def test_glob(app, status, warning): def test_glob(app):
includefiles = ['foo', 'bar/index', 'bar/bar_1', 'bar/bar_2', includefiles = ['foo', 'bar/index', 'bar/bar_1', 'bar/bar_2',
'bar/bar_3', 'baz', 'qux/index'] 'bar/bar_3', 'baz', 'qux/index']
@ -144,7 +134,10 @@ def test_glob(app, status, warning):
assert app.env.numbered_toctrees == set() assert app.env.numbered_toctrees == set()
def _test_get_toc_for(app): @pytest.mark.sphinx('xml', testroot='toctree')
@pytest.mark.test_params(shared_result='test_environment_toctree_basic')
def test_get_toc_for(app):
app.build()
toctree = app.env.get_toc_for('index', app.builder) toctree = app.env.get_toc_for('index', app.builder)
assert_node(toctree, assert_node(toctree,
@ -167,7 +160,10 @@ def _test_get_toc_for(app):
[compact_paragraph, reference, "Indices and tables"]) [compact_paragraph, reference, "Indices and tables"])
def _test_get_toc_for_only(app): @pytest.mark.sphinx('xml', testroot='toctree')
@pytest.mark.test_params(shared_result='test_environment_toctree_basic')
def test_get_toc_for_only(app):
app.build()
builder = StandaloneHTMLBuilder(app) builder = StandaloneHTMLBuilder(app)
toctree = app.env.get_toc_for('index', builder) toctree = app.env.get_toc_for('index', builder)
@ -194,7 +190,10 @@ def _test_get_toc_for_only(app):
[compact_paragraph, reference, "Indices and tables"]) [compact_paragraph, reference, "Indices and tables"])
def _test_get_toc_for_tocdepth(app): @pytest.mark.sphinx('xml', testroot='toctree')
@pytest.mark.test_params(shared_result='test_environment_toctree_basic')
def test_get_toc_for_tocdepth(app):
app.build()
toctree = app.env.get_toc_for('tocdepth', app.builder) toctree = app.env.get_toc_for('tocdepth', app.builder)
assert_node(toctree, assert_node(toctree,
@ -206,7 +205,10 @@ def _test_get_toc_for_tocdepth(app):
[bullet_list, list_item, compact_paragraph, reference, "level 2"]) [bullet_list, list_item, compact_paragraph, reference, "level 2"])
def _test_get_toctree_for(app): @pytest.mark.sphinx('xml', testroot='toctree')
@pytest.mark.test_params(shared_result='test_environment_toctree_basic')
def test_get_toctree_for(app):
app.build()
toctree = app.env.get_toctree_for('index', app.builder, collapse=False) toctree = app.env.get_toctree_for('index', app.builder, collapse=False)
assert_node(toctree, assert_node(toctree,
[compact_paragraph, ([caption, "Table of Contents"], [compact_paragraph, ([caption, "Table of Contents"],
@ -240,7 +242,10 @@ def _test_get_toctree_for(app):
assert_node(toctree[3][1][0][0], reference, refuri="http://python.org/") assert_node(toctree[3][1][0][0], reference, refuri="http://python.org/")
def _test_get_toctree_for_collapse(app): @pytest.mark.sphinx('xml', testroot='toctree')
@pytest.mark.test_params(shared_result='test_environment_toctree_basic')
def test_get_toctree_for_collapse(app):
app.build()
toctree = app.env.get_toctree_for('index', app.builder, collapse=True) toctree = app.env.get_toctree_for('index', app.builder, collapse=True)
assert_node(toctree, assert_node(toctree,
[compact_paragraph, ([caption, "Table of Contents"], [compact_paragraph, ([caption, "Table of Contents"],
@ -265,7 +270,10 @@ def _test_get_toctree_for_collapse(app):
assert_node(toctree[3][1][0][0], reference, refuri="http://python.org/") assert_node(toctree[3][1][0][0], reference, refuri="http://python.org/")
def _test_get_toctree_for_maxdepth(app): @pytest.mark.sphinx('xml', testroot='toctree')
@pytest.mark.test_params(shared_result='test_environment_toctree_basic')
def test_get_toctree_for_maxdepth(app):
app.build()
toctree = app.env.get_toctree_for('index', app.builder, collapse=False, maxdepth=3) toctree = app.env.get_toctree_for('index', app.builder, collapse=False, maxdepth=3)
assert_node(toctree, assert_node(toctree,
[compact_paragraph, ([caption, "Table of Contents"], [compact_paragraph, ([caption, "Table of Contents"],
@ -304,7 +312,10 @@ def _test_get_toctree_for_maxdepth(app):
assert_node(toctree[3][1][0][0], reference, refuri="http://python.org/") assert_node(toctree[3][1][0][0], reference, refuri="http://python.org/")
def _test_get_toctree_for_includehidden(app): @pytest.mark.sphinx('xml', testroot='toctree')
@pytest.mark.test_params(shared_result='test_environment_toctree_basic')
def test_get_toctree_for_includehidden(app):
app.build()
toctree = app.env.get_toctree_for('index', app.builder, collapse=False, toctree = app.env.get_toctree_for('index', app.builder, collapse=False,
includehidden=False) includehidden=False)
assert_node(toctree, assert_node(toctree,

File diff suppressed because it is too large Load Diff

View File

@ -13,7 +13,7 @@ import re
import pickle import pickle
from docutils import frontend, utils, nodes from docutils import frontend, utils, nodes
from docutils.parsers import rst from docutils.parsers.rst import Parser as RstParser
from sphinx import addnodes from sphinx import addnodes
from sphinx.util import texescape from sphinx.util import texescape
@ -22,31 +22,37 @@ from sphinx.writers.html import HTMLWriter, SmartyPantsHTMLTranslator
from sphinx.writers.latex import LaTeXWriter, LaTeXTranslator from sphinx.writers.latex import LaTeXWriter, LaTeXTranslator
import pytest import pytest
from util import TestApp, assert_node from util import assert_node
app = settings = parser = domain_context = None @pytest.fixture
def settings(app):
def setup_module():
global app, settings, parser, domain_context
texescape.init() # otherwise done by the latex builder texescape.init() # otherwise done by the latex builder
app = TestApp()
optparser = frontend.OptionParser( optparser = frontend.OptionParser(
components=(rst.Parser, HTMLWriter, LaTeXWriter)) components=(RstParser, HTMLWriter, LaTeXWriter))
settings = optparser.get_default_values() settings = optparser.get_default_values()
settings.env = app.builder.env settings.env = app.builder.env
settings.env.temp_data['docname'] = 'dummy' settings.env.temp_data['docname'] = 'dummy'
parser = rst.Parser()
domain_context = sphinx_domains(settings.env) domain_context = sphinx_domains(settings.env)
domain_context.enable() domain_context.enable()
yield settings
def teardown_module():
app.cleanup()
domain_context.disable() domain_context.disable()
@pytest.fixture
def parse(settings):
def parse_(rst):
document = utils.new_document(b'test data', settings)
document['file'] = 'dummy'
parser = RstParser()
parser.parse(rst, document)
for msg in document.traverse(nodes.system_message):
if msg['level'] == 1:
msg.replace_self([])
return document
return parse_
# since we're not resolving the markup afterwards, these nodes may remain # since we're not resolving the markup afterwards, these nodes may remain
class ForgivingTranslator: class ForgivingTranslator:
def visit_pending_xref(self, node): def visit_pending_xref(self, node):
@ -64,93 +70,158 @@ class ForgivingLaTeXTranslator(LaTeXTranslator, ForgivingTranslator):
pass pass
def verify_re(rst, html_expected, latex_expected): @pytest.fixture
document = utils.new_document(b'test data', settings) def verify_re_html(app, parse):
document['file'] = 'dummy' def verify(rst, html_expected):
parser.parse(rst, document) document = parse(rst)
for msg in document.traverse(nodes.system_message):
if msg['level'] == 1:
msg.replace_self([])
if html_expected:
html_translator = ForgivingHTMLTranslator(app.builder, document) html_translator = ForgivingHTMLTranslator(app.builder, document)
document.walkabout(html_translator) document.walkabout(html_translator)
html_translated = ''.join(html_translator.fragment).strip() html_translated = ''.join(html_translator.fragment).strip()
assert re.match(html_expected, html_translated), 'from ' + rst assert re.match(html_expected, html_translated), 'from ' + rst
return verify
if latex_expected:
@pytest.fixture
def verify_re_latex(app, parse):
def verify(rst, latex_expected):
document = parse(rst)
latex_translator = ForgivingLaTeXTranslator(document, app.builder) latex_translator = ForgivingLaTeXTranslator(document, app.builder)
latex_translator.first_document = -1 # don't write \begin{document} latex_translator.first_document = -1 # don't write \begin{document}
document.walkabout(latex_translator) document.walkabout(latex_translator)
latex_translated = ''.join(latex_translator.body).strip() latex_translated = ''.join(latex_translator.body).strip()
assert re.match(latex_expected, latex_translated), 'from ' + repr(rst) assert re.match(latex_expected, latex_translated), 'from ' + repr(rst)
return verify
def verify(rst, html_expected, latex_expected): @pytest.fixture
if html_expected: def verify_re(verify_re_html, verify_re_latex):
html_expected = re.escape(html_expected) + '$' def verify_re_(rst, html_expected, latex_expected):
if latex_expected: if html_expected:
latex_expected = re.escape(latex_expected) + '$' return verify_re_html(rst, html_expected)
verify_re(rst, html_expected, latex_expected) if latex_expected:
return verify_re_latex(rst, latex_expected)
return verify_re_
def test_inline(): @pytest.fixture
# correct interpretation of code with whitespace def verify(verify_re_html, verify_re_latex):
_html = ('<p><code class="(samp )?docutils literal"><span class="pre">' def verify_(rst, html_expected, latex_expected):
'code</span>&#160;&#160; <span class="pre">sample</span></code></p>') if html_expected:
yield verify_re, '``code sample``', _html, r'\\sphinxcode{code sample}' return verify_re_html(rst, re.escape(html_expected) + '$')
yield verify_re, ':samp:`code sample`', _html, r'\\sphinxcode{code sample}' if latex_expected:
return verify_re_latex(rst, re.escape(latex_expected) + '$')
# interpolation of braces in samp and file roles (HTML only) return verify_
yield (verify, ':samp:`a{b}c`',
'<p><code class="samp docutils literal"><span class="pre">a</span>'
'<em><span class="pre">b</span></em>'
'<span class="pre">c</span></code></p>',
'\\sphinxcode{a\\sphinxstyleemphasis{b}c}')
# interpolation of arrows in menuselection
yield (verify, ':menuselection:`a --> b`',
u'<p><span class="menuselection">a \N{TRIANGULAR BULLET} b</span></p>',
'\\sphinxmenuselection{a \\(\\rightarrow\\) b}')
# interpolation of ampersands in guilabel/menuselection
yield (verify, ':guilabel:`&Foo -&&- &Bar`',
u'<p><span class="guilabel"><span class="accelerator">F</span>oo '
'-&amp;- <span class="accelerator">B</span>ar</span></p>',
r'\sphinxmenuselection{\sphinxaccelerator{F}oo -\&- \sphinxaccelerator{B}ar}')
# non-interpolation of dashes in option role
yield (verify_re, ':option:`--with-option`',
'<p><code( class="xref std std-option docutils literal")?>'
'<span class="pre">--with-option</span></code></p>$',
r'\\sphinxcode{-{-}with-option}$')
# verify smarty-pants quotes
yield verify, '"John"', '<p>&#8220;John&#8221;</p>', "``John''"
# ... but not in literal text
yield (verify, '``"John"``',
'<p><code class="docutils literal"><span class="pre">'
'&quot;John&quot;</span></code></p>',
'\\sphinxcode{"John"}')
# verify classes for inline roles
yield (verify, ':manpage:`mp(1)`',
'<p><em class="manpage">mp(1)</em></p>',
'\\sphinxstyleliteralemphasis{mp(1)}')
def test_latex_escaping(): @pytest.fixture
# correct escaping in normal mode def get_verifier(verify, verify_re):
yield (verify, u'Γ\\\\∞$', None, v = {
r'\(\Gamma\)\textbackslash{}\(\infty\)\$') 'verify': verify,
# in verbatim code fragments 'verify_re': verify_re,
yield (verify, u'::\n\n\\∞${}', None, }
u'\\begin{sphinxVerbatim}[commandchars=\\\\\\{\\}]\n' def get(name):
u'@\\(\\Gamma\\)\\PYGZbs{}\\(\\infty\\)\\PYGZdl{}\\PYGZob{}\\PYGZcb{}\n' return v[name]
u'\\end{sphinxVerbatim}') return get
# in URIs
yield (verify_re, u'`test <http://example.com/~me/>`_', None,
r'\\href{http://example.com/~me/}{test}.*') @pytest.mark.parametrize('type,rst,html_expected,latex_expected', [
(
# correct interpretation of code with whitespace
'verify_re',
'``code sample``',
('<p><code class="(samp )?docutils literal"><span class="pre">'
'code</span>&#160;&#160; <span class="pre">sample</span></code></p>'),
r'\\sphinxcode{code sample}',
),
(
# correct interpretation of code with whitespace
'verify_re',
':samp:`code sample`',
('<p><code class="(samp )?docutils literal"><span class="pre">'
'code</span>&#160;&#160; <span class="pre">sample</span></code></p>'),
r'\\sphinxcode{code sample}',
),
(
# interpolation of braces in samp and file roles (HTML only)
'verify',
':samp:`a{b}c`',
('<p><code class="samp docutils literal"><span class="pre">a</span>'
'<em><span class="pre">b</span></em>'
'<span class="pre">c</span></code></p>'),
'\\sphinxcode{a\\sphinxstyleemphasis{b}c}',
),
(
# interpolation of arrows in menuselection
'verify',
':menuselection:`a --> b`',
(u'<p><span class="menuselection">a \N{TRIANGULAR BULLET} b</span></p>'),
'\\sphinxmenuselection{a \\(\\rightarrow\\) b}',
),
(
# interpolation of ampersands in guilabel/menuselection
'verify',
':guilabel:`&Foo -&&- &Bar`',
(u'<p><span class="guilabel"><span class="accelerator">F</span>oo '
'-&amp;- <span class="accelerator">B</span>ar</span></p>'),
r'\sphinxmenuselection{\sphinxaccelerator{F}oo -\&- \sphinxaccelerator{B}ar}',
),
(
# non-interpolation of dashes in option role
'verify_re',
':option:`--with-option`',
('<p><code( class="xref std std-option docutils literal")?>'
'<span class="pre">--with-option</span></code></p>$'),
r'\\sphinxcode{-{-}with-option}$',
),
(
# verify smarty-pants quotes
'verify',
'"John"',
'<p>&#8220;John&#8221;</p>',
"``John''",
),
(
# ... but not in literal text
'verify',
'``"John"``',
('<p><code class="docutils literal"><span class="pre">'
'&quot;John&quot;</span></code></p>'),
'\\sphinxcode{"John"}',
),
(
# verify classes for inline roles
'verify',
':manpage:`mp(1)`',
'<p><em class="manpage">mp(1)</em></p>',
'\\sphinxstyleliteralemphasis{mp(1)}',
),
(
# correct escaping in normal mode
'verify',
u'Γ\\\\∞$',
None,
r'\(\Gamma\)\textbackslash{}\(\infty\)\$',
),
(
# in verbatim code fragments
'verify',
u'::\n\n\\∞${}',
None,
(u'\\begin{sphinxVerbatim}[commandchars=\\\\\\{\\}]\n'
u'@\\(\\Gamma\\)\\PYGZbs{}\\(\\infty\\)\\PYGZdl{}\\PYGZob{}\\PYGZcb{}\n'
u'\\end{sphinxVerbatim}'),
),
(
# in URIs
'verify_re',
u'`test <http://example.com/~me/>`_',
None,
r'\\href{http://example.com/~me/}{test}.*',
),
])
def test_inline(get_verifier, type, rst, html_expected, latex_expected):
verifier = get_verifier(type)
verifier(rst, html_expected, latex_expected)
@pytest.mark.sphinx('dummy', testroot='prolog') @pytest.mark.sphinx('dummy', testroot='prolog')

View File

@ -12,30 +12,27 @@ from __future__ import print_function
import os import os
import datetime import datetime
from os import path
import pytest
from babel.messages.mofile import read_mo from babel.messages.mofile import read_mo
from sphinx.util import i18n from sphinx.util import i18n
from sphinx.errors import SphinxError from sphinx.errors import SphinxError
import pytest
from util import TestApp
def test_catalog_info_for_file_and_path(): def test_catalog_info_for_file_and_path():
cat = i18n.CatalogInfo('path', 'domain', 'utf-8') cat = i18n.CatalogInfo('path', 'domain', 'utf-8')
assert cat.po_file == 'domain.po' assert cat.po_file == 'domain.po'
assert cat.mo_file == 'domain.mo' assert cat.mo_file == 'domain.mo'
assert cat.po_path == path.join('path', 'domain.po') assert cat.po_path == os.path.join('path', 'domain.po')
assert cat.mo_path == path.join('path', 'domain.mo') assert cat.mo_path == os.path.join('path', 'domain.mo')
def test_catalog_info_for_sub_domain_file_and_path(): def test_catalog_info_for_sub_domain_file_and_path():
cat = i18n.CatalogInfo('path', 'sub/domain', 'utf-8') cat = i18n.CatalogInfo('path', 'sub/domain', 'utf-8')
assert cat.po_file == 'sub/domain.po' assert cat.po_file == 'sub/domain.po'
assert cat.mo_file == 'sub/domain.mo' assert cat.mo_file == 'sub/domain.mo'
assert cat.po_path == path.join('path', 'sub/domain.po') assert cat.po_path == os.path.join('path', 'sub/domain.po')
assert cat.mo_path == path.join('path', 'sub/domain.mo') assert cat.mo_path == os.path.join('path', 'sub/domain.mo')
def test_catalog_outdated(tempdir): def test_catalog_outdated(tempdir):
@ -55,7 +52,7 @@ def test_catalog_write_mo(tempdir):
(tempdir / 'test.po').write_text('#') (tempdir / 'test.po').write_text('#')
cat = i18n.CatalogInfo(tempdir, 'test', 'utf-8') cat = i18n.CatalogInfo(tempdir, 'test', 'utf-8')
cat.write_mo('en') cat.write_mo('en')
assert path.exists(cat.mo_path) assert os.path.exists(cat.mo_path)
with open(cat.mo_path, 'rb') as f: with open(cat.mo_path, 'rb') as f:
assert read_mo(f) is not None assert read_mo(f) is not None
@ -189,9 +186,7 @@ def test_format_date():
assert i18n.format_date(format, date=date) == 'Feb 7, 2016' assert i18n.format_date(format, date=date) == 'Feb 7, 2016'
def test_get_filename_for_language(): def test_get_filename_for_language(app):
app = TestApp()
# language is None # language is None
app.env.config.language = None app.env.config.language = None
assert app.env.config.language is None assert app.env.config.language is None

View File

@ -17,6 +17,7 @@ from docutils import frontend
from sphinx.util.nodes import extract_messages, clean_astext from sphinx.util.nodes import extract_messages, clean_astext
from sphinx.transforms import ApplySourceWorkaround from sphinx.transforms import ApplySourceWorkaround
import pytest
def _transform(doctree): def _transform(doctree):
@ -49,84 +50,63 @@ def assert_node_count(messages, node_type, expect_count):
% (node_type, node_list, count, expect_count)) % (node_type, node_list, count, expect_count))
def test_extract_messages(): @pytest.mark.parametrize(
text = dedent( 'rst,node_cls,count',
""" [
.. admonition:: admonition title (
"""
.. admonition:: admonition title
admonition body admonition body
""" """,
) nodes.title, 1
yield ( ),
assert_node_count, (
extract_messages(_get_doctree(text)), """
nodes.title, 1, .. figure:: foo.jpg
)
text = dedent( this is title
""" """,
.. figure:: foo.jpg nodes.caption, 1,
),
(
"""
.. rubric:: spam
""",
nodes.rubric, 1,
),
(
"""
| spam
| egg
""",
nodes.line, 2,
),
(
"""
section
=======
this is title +----------------+
""" | | **Title 1** |
) | | Message 1 |
yield ( +----------------+
assert_node_count, """,
extract_messages(_get_doctree(text)), nodes.line, 2,
nodes.caption, 1, ),
) (
"""
* | **Title 1**
| Message 1
""",
nodes.line, 2,
text = dedent( ),
""" ]
.. rubric:: spam )
""" def test_extract_messages(rst, node_cls, count):
) msg = extract_messages(_get_doctree(dedent(rst)))
yield ( assert_node_count(msg, node_cls, count)
assert_node_count,
extract_messages(_get_doctree(text)),
nodes.rubric, 1,
)
text = dedent(
"""
| spam
| egg
"""
)
yield (
assert_node_count,
extract_messages(_get_doctree(text)),
nodes.line, 2,
)
text = dedent(
"""
section
=======
+----------------+
| | **Title 1** |
| | Message 1 |
+----------------+
"""
)
yield (
assert_node_count,
extract_messages(_get_doctree(text)),
nodes.line, 2,
)
text = dedent(
"""
* | **Title 1**
| Message 1
"""
)
yield (
assert_node_count,
extract_messages(_get_doctree(text)),
nodes.line, 2,
)
def test_extract_messages_without_rawsource(): def test_extract_messages_without_rawsource():

View File

@ -16,7 +16,7 @@ from docutils.parsers.rst.directives.html import MetaBody
from sphinx import addnodes from sphinx import addnodes
from sphinx.versioning import add_uids, merge_doctrees, get_ratio from sphinx.versioning import add_uids, merge_doctrees, get_ratio
from util import TestApp from util import SphinxTestApp
app = original = original_uids = None app = original = original_uids = None
@ -24,7 +24,7 @@ app = original = original_uids = None
def setup_module(): def setup_module():
global app, original, original_uids global app, original, original_uids
app = TestApp(testroot='versioning') app = SphinxTestApp(testroot='versioning')
app.builder.env.app = app app.builder.env.app = app
app.connect('doctree-resolved', on_doctree_resolved) app.connect('doctree-resolved', on_doctree_resolved)
app.build() app.build()

View File

@ -23,7 +23,7 @@ except ImportError:
sqlalchemy_missing = True sqlalchemy_missing = True
import pytest import pytest
from util import rootdir, tempdir, skip_if from util import rootdir, tempdir
@pytest.fixture @pytest.fixture
@ -57,13 +57,13 @@ def test_no_srcdir(support):
support.build() support.build()
@skip_if(sqlalchemy_missing, 'needs sqlalchemy') @pytest.mark.skipif(sqlalchemy_missing, reason='needs sqlalchemy')
@with_support() @with_support()
def test_build(support): def test_build(support):
support.build() support.build()
@skip_if(sqlalchemy_missing, 'needs sqlalchemy') @pytest.mark.skipif(sqlalchemy_missing, reason='needs sqlalchemy')
@with_support() @with_support()
def test_get_document(support): def test_get_document(support):
with pytest.raises(DocumentNotFoundError): with pytest.raises(DocumentNotFoundError):
@ -74,7 +74,7 @@ def test_get_document(support):
and contents['sidebar'] and contents['relbar'] and contents['sidebar'] and contents['relbar']
@skip_if(sqlalchemy_missing, 'needs sqlalchemy') @pytest.mark.skipif(sqlalchemy_missing, reason='needs sqlalchemy')
@with_support() @with_support()
def test_comments(support): def test_comments(support):
session = Session() session = Session()
@ -123,7 +123,7 @@ def test_comments(support):
assert children[0]['text'] == '<p>Child test comment</p>\n' assert children[0]['text'] == '<p>Child test comment</p>\n'
@skip_if(sqlalchemy_missing, 'needs sqlalchemy') @pytest.mark.skipif(sqlalchemy_missing, reason='needs sqlalchemy')
@with_support() @with_support()
def test_user_delete_comments(support): def test_user_delete_comments(support):
def get_comment(): def get_comment():
@ -152,7 +152,7 @@ def moderation_callback(comment):
called = True called = True
@skip_if(sqlalchemy_missing, 'needs sqlalchemy') @pytest.mark.skipif(sqlalchemy_missing, reason='needs sqlalchemy')
@with_support(moderation_callback=moderation_callback) @with_support(moderation_callback=moderation_callback)
def test_moderation(support): def test_moderation(support):
session = Session() session = Session()
@ -178,7 +178,7 @@ def test_moderation(support):
assert len(comments) == 1 assert len(comments) == 1
@skip_if(sqlalchemy_missing, 'needs sqlalchemy') @pytest.mark.skipif(sqlalchemy_missing, reason='needs sqlalchemy')
@with_support() @with_support()
def test_moderator_delete_comments(support): def test_moderator_delete_comments(support):
def get_comment(): def get_comment():
@ -194,7 +194,7 @@ def test_moderator_delete_comments(support):
get_comment() get_comment()
@skip_if(sqlalchemy_missing, 'needs sqlalchemy') @pytest.mark.skipif(sqlalchemy_missing, reason='needs sqlalchemy')
@with_support() @with_support()
def test_update_username(support): def test_update_username(support):
support.update_username('user_two', 'new_user_two') support.update_username('user_two', 'new_user_two')
@ -213,7 +213,7 @@ def test_update_username(support):
assert len(votes) == 0 assert len(votes) == 0
@skip_if(sqlalchemy_missing, 'needs sqlalchemy') @pytest.mark.skipif(sqlalchemy_missing, reason='needs sqlalchemy')
@with_support() @with_support()
def test_proposals(support): def test_proposals(support):
session = Session() session = Session()
@ -229,7 +229,7 @@ def test_proposals(support):
proposal=proposal) proposal=proposal)
@skip_if(sqlalchemy_missing, 'needs sqlalchemy') @pytest.mark.skipif(sqlalchemy_missing, reason='needs sqlalchemy')
@with_support() @with_support()
def test_voting(support): def test_voting(support):
session = Session() session = Session()

View File

@ -207,7 +207,6 @@ def strip_escseq(text):
# ############################################# # #############################################
# DEPRECATED implementations # DEPRECATED implementations
import tempfile
from six import StringIO from six import StringIO