mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
merge with trunk
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
{% extends "!layout.html" %}
|
||||
|
||||
{% block extrahead %}
|
||||
{# html_context variable from conf.py #}
|
||||
<meta name="hc" content="{{ hckey }}" />
|
||||
@@ -6,3 +7,9 @@
|
||||
<meta name="hc_co" content="{{ hckey_co }}" />
|
||||
{{ super() }}
|
||||
{% endblock %}
|
||||
|
||||
{% block sidebartoc %}
|
||||
{# display global TOC in addition to local TOC #}
|
||||
{{ super() }}
|
||||
{{ toctree(collapse=False, maxdepth=-1) }}
|
||||
{% endblock %}
|
||||
@@ -25,6 +25,7 @@ today_fmt = '%B %d, %Y'
|
||||
exclude_trees = ['_build']
|
||||
keep_warnings = True
|
||||
pygments_style = 'sphinx'
|
||||
show_authors = True
|
||||
|
||||
rst_epilog = '.. |subst| replace:: global substitution'
|
||||
|
||||
@@ -49,6 +50,7 @@ latex_additional_files = ['svgimg.svg']
|
||||
value_from_conf_py = 84
|
||||
|
||||
coverage_c_path = ['special/*.h']
|
||||
# XXX cfunction?
|
||||
coverage_c_regexes = {'cfunction': r'^PyAPI_FUNC\(.*\)\s+([^_][\w_]+)'}
|
||||
|
||||
autosummary_generate = ['autosummary']
|
||||
@@ -59,7 +61,12 @@ extlinks = {'issue': ('http://bugs.python.org/issue%s', 'issue '),
|
||||
# modify tags from conf.py
|
||||
tags.add('confpytag')
|
||||
|
||||
|
||||
# -- extension API
|
||||
|
||||
from docutils import nodes
|
||||
from sphinx import addnodes
|
||||
from sphinx.util.compat import Directive
|
||||
|
||||
def userdesc_parse(env, sig, signode):
|
||||
x, y = sig.split(':')
|
||||
@@ -68,7 +75,18 @@ def userdesc_parse(env, sig, signode):
|
||||
signode[-1] += addnodes.desc_parameter(y, y)
|
||||
return x
|
||||
|
||||
def functional_directive(name, arguments, options, content, lineno,
|
||||
content_offset, block_text, state, state_machine):
|
||||
return [nodes.strong(text='from function: %s' % options['opt'])]
|
||||
|
||||
class ClassDirective(Directive):
|
||||
option_spec = {'opt': lambda x: x}
|
||||
def run(self):
|
||||
return [nodes.strong(text='from class: %s' % self.options['opt'])]
|
||||
|
||||
def setup(app):
|
||||
app.add_config_value('value_from_conf_py', 42, False)
|
||||
app.add_description_unit('userdesc', 'userdescrole', '%s (userdesc)',
|
||||
userdesc_parse)
|
||||
app.add_directive('funcdir', functional_directive, opt=lambda x: x)
|
||||
app.add_directive('clsdir', ClassDirective)
|
||||
app.add_object_type('userdesc', 'userdescrole', '%s (userdesc)',
|
||||
userdesc_parse, objname='user desc')
|
||||
|
||||
@@ -11,12 +11,13 @@ Contents:
|
||||
:maxdepth: 2
|
||||
:numbered:
|
||||
|
||||
extapi
|
||||
images
|
||||
subdir/images
|
||||
subdir/includes
|
||||
includes
|
||||
markup
|
||||
desc
|
||||
objects
|
||||
bom
|
||||
math
|
||||
autodoc
|
||||
|
||||
10
tests/root/extapi.txt
Normal file
10
tests/root/extapi.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
Extension API tests
|
||||
===================
|
||||
|
||||
Testing directives:
|
||||
|
||||
.. funcdir::
|
||||
:opt: Foo
|
||||
|
||||
.. clsdir::
|
||||
:opt: Bar
|
||||
@@ -5,7 +5,11 @@
|
||||
Testing various markup
|
||||
======================
|
||||
|
||||
Meta markup
|
||||
-----------
|
||||
|
||||
.. sectionauthor:: Georg Brandl
|
||||
.. moduleauthor:: Georg Brandl
|
||||
|
||||
.. contents:: TOC
|
||||
|
||||
@@ -13,7 +17,11 @@ Testing various markup
|
||||
:author: Me
|
||||
:keywords: docs, sphinx
|
||||
|
||||
A |subst|.
|
||||
|
||||
Generic reST
|
||||
------------
|
||||
|
||||
A |subst| (the definition is in rst_epilog).
|
||||
|
||||
.. _label:
|
||||
|
||||
@@ -21,22 +29,14 @@ A |subst|.
|
||||
|
||||
some code
|
||||
|
||||
Admonitions
|
||||
-----------
|
||||
Option list:
|
||||
|
||||
.. note:: Note
|
||||
Note text.
|
||||
|
||||
.. warning:: Warning
|
||||
|
||||
Warning text.
|
||||
|
||||
.. tip::
|
||||
Tip text.
|
||||
-h help
|
||||
--help also help
|
||||
|
||||
|
||||
Body directives
|
||||
---------------
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
.. topic:: Title
|
||||
|
||||
@@ -69,7 +69,67 @@ Body directives
|
||||
|
||||
b
|
||||
|
||||
|
||||
|
||||
Admonitions
|
||||
^^^^^^^^^^^
|
||||
|
||||
.. admonition:: My Admonition
|
||||
|
||||
Admonition text.
|
||||
|
||||
.. note::
|
||||
Note text.
|
||||
|
||||
.. warning::
|
||||
|
||||
Warning text.
|
||||
|
||||
.. tip::
|
||||
Tip text.
|
||||
|
||||
|
||||
Inline markup
|
||||
-------------
|
||||
|
||||
*Generic inline markup*
|
||||
|
||||
* :command:`command`
|
||||
* :dfn:`dfn`
|
||||
* :guilabel:`guilabel`
|
||||
* :kbd:`kbd`
|
||||
* :mailheader:`mailheader`
|
||||
* :makevar:`makevar`
|
||||
* :manpage:`manpage`
|
||||
* :mimetype:`mimetype`
|
||||
* :newsgroup:`newsgroup`
|
||||
* :program:`program`
|
||||
* :regexp:`regexp`
|
||||
* :menuselection:`File --> Close`
|
||||
* :file:`a/{varpart}/b`
|
||||
* :samp:`print {i}`
|
||||
|
||||
*Linking inline markup*
|
||||
|
||||
* :pep:`8`
|
||||
* :rfc:`1`
|
||||
* :envvar:`HOME`
|
||||
* :keyword:`with`
|
||||
* :token:`try statement <try_stmt>`
|
||||
* :doc:`subdir/includes`
|
||||
* ``:download:`` is tested in includes.txt
|
||||
* :option:`Python -c option <python -c>`
|
||||
|
||||
Test :abbr:`abbr (abbreviation)` and another :abbr:`abbr (abbreviation)`.
|
||||
|
||||
|
||||
.. _with:
|
||||
|
||||
With
|
||||
----
|
||||
|
||||
(Empty section.)
|
||||
|
||||
|
||||
Tables
|
||||
------
|
||||
|
||||
@@ -96,6 +156,17 @@ Version markup
|
||||
Boring stuff.
|
||||
|
||||
|
||||
Code blocks
|
||||
-----------
|
||||
|
||||
.. code-block:: ruby
|
||||
:linenos:
|
||||
|
||||
def ruby?
|
||||
false
|
||||
end
|
||||
|
||||
|
||||
Misc stuff
|
||||
----------
|
||||
|
||||
@@ -124,11 +195,6 @@ This is a side note.
|
||||
|
||||
This tests :CLASS:`role names in uppercase`.
|
||||
|
||||
Option list:
|
||||
|
||||
-h help
|
||||
--help also help
|
||||
|
||||
.. centered:: LICENSE AGREEMENT
|
||||
|
||||
.. acks::
|
||||
@@ -146,7 +212,7 @@ Option list:
|
||||
Particle with half-integer spin.
|
||||
|
||||
.. productionlist::
|
||||
try_stmt: try1_stmt | try2_stmt
|
||||
try_stmt: `try1_stmt` | `try2_stmt`
|
||||
try1_stmt: "try" ":" `suite`
|
||||
: ("except" [`expression` ["," `target`]] ":" `suite`)+
|
||||
: ["else" ":" `suite`]
|
||||
@@ -154,7 +220,6 @@ Option list:
|
||||
try2_stmt: "try" ":" `suite`
|
||||
: "finally" ":" `suite`
|
||||
|
||||
Test :abbr:`abbr (abbreviation)` and another :abbr:`abbr (abbreviation)`.
|
||||
|
||||
Index markup
|
||||
------------
|
||||
@@ -180,11 +245,6 @@ Invalid index markup...
|
||||
Testing öäü...
|
||||
|
||||
|
||||
Object markup
|
||||
-------------
|
||||
|
||||
:cfunc:`CFunction`.
|
||||
|
||||
Only directive
|
||||
--------------
|
||||
|
||||
@@ -208,3 +268,4 @@ Only directive
|
||||
.. rubric:: Footnotes
|
||||
|
||||
.. [#] Like footnotes.
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Testing description units
|
||||
=========================
|
||||
Testing object descriptions
|
||||
===========================
|
||||
|
||||
.. function:: func_without_module(a, b, *c[, d])
|
||||
|
||||
@@ -43,22 +43,42 @@ Testing description units
|
||||
C items
|
||||
=======
|
||||
|
||||
.. cfunction:: Sphinx_DoSomething()
|
||||
.. c:function:: Sphinx_DoSomething()
|
||||
|
||||
.. cmember:: SphinxStruct.member
|
||||
.. c:member:: SphinxStruct.member
|
||||
|
||||
.. cmacro:: SPHINX_USE_PYTHON
|
||||
.. c:macro:: SPHINX_USE_PYTHON
|
||||
|
||||
.. ctype:: SphinxType
|
||||
.. c:type:: SphinxType
|
||||
|
||||
.. cvar:: sphinx_global
|
||||
.. c:var:: sphinx_global
|
||||
|
||||
|
||||
Testing references
|
||||
==================
|
||||
References
|
||||
==========
|
||||
|
||||
Referencing :class:`mod.Cls` or :Class:`mod.Cls` should be the same.
|
||||
|
||||
With target: :c:func:`Sphinx_DoSomething()` (parentheses are handled),
|
||||
:c:member:`SphinxStruct.member`, :c:macro:`SPHINX_USE_PYTHON`,
|
||||
:c:type:`SphinxType *` (pointer is handled), :c:data:`sphinx_global`.
|
||||
|
||||
Without target: :c:func:`CFunction`. :c:func:`!malloc`.
|
||||
|
||||
|
||||
Others
|
||||
======
|
||||
|
||||
.. envvar:: HOME
|
||||
|
||||
.. program:: python
|
||||
|
||||
.. cmdoption:: -c command
|
||||
|
||||
.. program:: perl
|
||||
|
||||
.. cmdoption:: -c
|
||||
|
||||
|
||||
User markup
|
||||
===========
|
||||
@@ -97,28 +97,28 @@ def test_parse_name():
|
||||
verify('function', 'util.raises', ('util', ['raises'], None, None))
|
||||
verify('function', 'util.raises(exc) -> None',
|
||||
('util', ['raises'], 'exc', 'None'))
|
||||
directive.env.autodoc_current_module = 'util'
|
||||
directive.env.doc_read_data['autodoc_module'] = 'util'
|
||||
verify('function', 'raises', ('util', ['raises'], None, None))
|
||||
directive.env.autodoc_current_module = None
|
||||
directive.env.currmodule = 'util'
|
||||
del directive.env.doc_read_data['autodoc_module']
|
||||
directive.env.doc_read_data['py_module'] = 'util'
|
||||
verify('function', 'raises', ('util', ['raises'], None, None))
|
||||
verify('class', 'TestApp', ('util', ['TestApp'], None, None))
|
||||
|
||||
# for members
|
||||
directive.env.currmodule = 'foo'
|
||||
directive.env.doc_read_data['py_module'] = 'foo'
|
||||
verify('method', 'util.TestApp.cleanup',
|
||||
('util', ['TestApp', 'cleanup'], None, None))
|
||||
directive.env.currmodule = 'util'
|
||||
directive.env.currclass = 'Foo'
|
||||
directive.env.autodoc_current_class = 'TestApp'
|
||||
directive.env.doc_read_data['py_module'] = 'util'
|
||||
directive.env.doc_read_data['py_class'] = 'Foo'
|
||||
directive.env.doc_read_data['autodoc_class'] = 'TestApp'
|
||||
verify('method', 'cleanup', ('util', ['TestApp', 'cleanup'], None, None))
|
||||
verify('method', 'TestApp.cleanup',
|
||||
('util', ['TestApp', 'cleanup'], None, None))
|
||||
|
||||
# and clean up
|
||||
directive.env.currmodule = None
|
||||
directive.env.currclass = None
|
||||
directive.env.autodoc_current_class = None
|
||||
del directive.env.doc_read_data['py_module']
|
||||
del directive.env.doc_read_data['py_class']
|
||||
del directive.env.doc_read_data['autodoc_class']
|
||||
|
||||
|
||||
def test_format_signature():
|
||||
@@ -306,7 +306,7 @@ def test_new_documenter():
|
||||
del directive.result[:]
|
||||
|
||||
options.members = ['integer']
|
||||
assert_result_contains('.. data:: integer', 'module', 'test_autodoc')
|
||||
assert_result_contains('.. py:data:: integer', 'module', 'test_autodoc')
|
||||
|
||||
|
||||
def test_generate():
|
||||
@@ -353,7 +353,7 @@ def test_generate():
|
||||
'function', 'util.foobar', more_content=None)
|
||||
|
||||
# test auto and given content mixing
|
||||
directive.env.currmodule = 'test_autodoc'
|
||||
directive.env.doc_read_data['py_module'] = 'test_autodoc'
|
||||
assert_result_contains(' Function.', 'method', 'Class.meth')
|
||||
add_content = ViewList()
|
||||
add_content.append('Content.', '', 0)
|
||||
@@ -394,7 +394,8 @@ def test_generate():
|
||||
|
||||
options.members = []
|
||||
# test module flags
|
||||
assert_result_contains('.. module:: test_autodoc', 'module', 'test_autodoc')
|
||||
assert_result_contains('.. py:module:: test_autodoc',
|
||||
'module', 'test_autodoc')
|
||||
options.synopsis = 'Synopsis'
|
||||
assert_result_contains(' :synopsis: Synopsis', 'module', 'test_autodoc')
|
||||
options.deprecated = True
|
||||
@@ -403,9 +404,9 @@ def test_generate():
|
||||
assert_result_contains(' :platform: Platform', 'module', 'test_autodoc')
|
||||
# test if __all__ is respected for modules
|
||||
options.members = ALL
|
||||
assert_result_contains('.. class:: Class', 'module', 'test_autodoc')
|
||||
assert_result_contains('.. py:class:: Class', 'module', 'test_autodoc')
|
||||
try:
|
||||
assert_result_contains('.. exception:: CustomEx',
|
||||
assert_result_contains('.. py:exception:: CustomEx',
|
||||
'module', 'test_autodoc')
|
||||
except AssertionError:
|
||||
pass
|
||||
@@ -419,7 +420,7 @@ def test_generate():
|
||||
assert_result_contains(' :noindex:', 'class', 'Base')
|
||||
|
||||
# okay, now let's get serious about mixing Python and C signature stuff
|
||||
assert_result_contains('.. class:: CustomDict', 'class', 'CustomDict',
|
||||
assert_result_contains('.. py:class:: CustomDict', 'class', 'CustomDict',
|
||||
all_members=True)
|
||||
|
||||
# test inner class handling
|
||||
@@ -433,7 +434,7 @@ def test_generate():
|
||||
'attribute', 'test_autodoc.Class.descr')
|
||||
|
||||
# test generation for C modules (which have no source file)
|
||||
directive.env.currmodule = 'time'
|
||||
directive.env.doc_read_data['py_module'] = 'time'
|
||||
assert_processes([('function', 'time.asctime')], 'function', 'asctime')
|
||||
assert_processes([('function', 'time.asctime')], 'function', 'asctime')
|
||||
|
||||
|
||||
@@ -13,28 +13,19 @@ import os
|
||||
import re
|
||||
import sys
|
||||
import difflib
|
||||
import htmlentitydefs
|
||||
from StringIO import StringIO
|
||||
from subprocess import Popen, PIPE
|
||||
|
||||
from util import *
|
||||
from etree13 import ElementTree as ET
|
||||
|
||||
try:
|
||||
import pygments
|
||||
except ImportError:
|
||||
pygments = None
|
||||
|
||||
from sphinx.builders.html import StandaloneHTMLBuilder
|
||||
from sphinx.builders.latex import LaTeXBuilder
|
||||
from sphinx.writers.latex import LaTeXTranslator
|
||||
|
||||
from util import *
|
||||
|
||||
|
||||
def teardown_module():
|
||||
(test_root / '_build').rmtree(True)
|
||||
|
||||
|
||||
html_warnfile = StringIO()
|
||||
latex_warnfile = StringIO()
|
||||
|
||||
ENV_WARNINGS = """\
|
||||
@@ -46,164 +37,11 @@ included file u'wrongenc.inc' seems to be wrong, try giving an :encoding: option
|
||||
%(root)s/includes.txt:4: WARNING: download file not readable: nonexisting.png
|
||||
"""
|
||||
|
||||
HTML_WARNINGS = ENV_WARNINGS + """\
|
||||
%(root)s/images.txt:20: WARNING: no matching candidate for image URI u'foo.*'
|
||||
%(root)s/markup.txt:: WARNING: invalid index entry u''
|
||||
%(root)s/markup.txt:: WARNING: invalid pair index entry u''
|
||||
%(root)s/markup.txt:: WARNING: invalid pair index entry u'keyword; '
|
||||
"""
|
||||
|
||||
LATEX_WARNINGS = ENV_WARNINGS + """\
|
||||
None:None: WARNING: no matching candidate for image URI u'foo.*'
|
||||
WARNING: invalid pair index entry u''
|
||||
"""
|
||||
|
||||
HTML_XPATH = {
|
||||
'images.html': {
|
||||
".//img[@src='_images/img.png']": '',
|
||||
".//img[@src='_images/img1.png']": '',
|
||||
".//img[@src='_images/simg.png']": '',
|
||||
".//object[@data='_images/svgimg.svg']": '',
|
||||
".//embed[@src='_images/svgimg.svg']": '',
|
||||
},
|
||||
'subdir/images.html': {
|
||||
".//img[@src='../_images/img1.png']": '',
|
||||
".//img[@src='../_images/rimg.png']": '',
|
||||
},
|
||||
'subdir/includes.html': {
|
||||
".//a[@href='../_downloads/img.png']": '',
|
||||
},
|
||||
'includes.html': {
|
||||
".//pre": u'Max Strauß',
|
||||
".//a[@href='_downloads/img.png']": '',
|
||||
".//a[@href='_downloads/img1.png']": '',
|
||||
".//pre": u'"quotes"',
|
||||
".//pre": u"'included'",
|
||||
},
|
||||
'autodoc.html': {
|
||||
".//dt[@id='test_autodoc.Class']": '',
|
||||
".//dt[@id='test_autodoc.function']/em": r'\*\*kwds',
|
||||
".//dd": r'Return spam\.',
|
||||
},
|
||||
'markup.html': {
|
||||
".//meta[@name='author'][@content='Me']": '',
|
||||
".//meta[@name='keywords'][@content='docs, sphinx']": '',
|
||||
".//a[@href='contents.html#ref1']": '',
|
||||
".//div[@id='label']": '',
|
||||
".//span[@class='option']": '--help',
|
||||
".//p": 'A global substitution.',
|
||||
".//p": 'In HTML.',
|
||||
".//p": 'In both.',
|
||||
".//p": 'Always present',
|
||||
".//title": 'set by title directive',
|
||||
".//span[@class='pre']": 'CFunction()',
|
||||
},
|
||||
'desc.html': {
|
||||
".//dt[@id='mod.Cls.meth1']": '',
|
||||
".//dt[@id='errmod.Error']": '',
|
||||
".//a[@href='#mod.Cls']": '',
|
||||
".//dl[@class='userdesc']": '',
|
||||
".//dt[@id='userdescrole-myobj']": '',
|
||||
".//a[@href='#userdescrole-myobj']": '',
|
||||
},
|
||||
'contents.html': {
|
||||
".//meta[@name='hc'][@content='hcval']": '',
|
||||
".//meta[@name='hc_co'][@content='hcval_co']": '',
|
||||
".//meta[@name='testopt'][@content='testoverride']": '',
|
||||
#".//td[@class='label']": r'\[Ref1\]', # docutils 0.5 only
|
||||
".//td[@class='label']": '',
|
||||
".//li[@class='toctree-l1']/a": 'Testing various markup',
|
||||
".//li[@class='toctree-l2']/a": 'Admonitions',
|
||||
".//title": 'Sphinx <Tests>',
|
||||
".//div[@class='footer']": 'Georg Brandl & Team',
|
||||
".//a[@href='http://python.org/']": '',
|
||||
},
|
||||
'bom.html': {
|
||||
".//title": " File with UTF-8 BOM",
|
||||
},
|
||||
'extensions.html': {
|
||||
".//a[@href='http://python.org/dev/']": "http://python.org/dev/",
|
||||
".//a[@href='http://bugs.python.org/issue1000']": "issue 1000",
|
||||
".//a[@href='http://bugs.python.org/issue1042']": "explicit caption",
|
||||
},
|
||||
'_static/statictmpl.html': {
|
||||
".//project": 'Sphinx <Tests>',
|
||||
},
|
||||
}
|
||||
|
||||
if pygments:
|
||||
HTML_XPATH['includes.html'].update({
|
||||
".//pre/span[@class='s']": u'üöä',
|
||||
".//div[@class='inc-pyobj1 highlight-text']/div/pre":
|
||||
r'^class Foo:\n pass\n\s*$',
|
||||
".//div[@class='inc-pyobj2 highlight-text']/div/pre":
|
||||
r'^ def baz\(\):\n pass\n\s*$',
|
||||
".//div[@class='inc-lines highlight-text']/div/pre":
|
||||
r'^class Foo:\n pass\nclass Bar:\n$',
|
||||
".//div[@class='inc-startend highlight-text']/div/pre":
|
||||
ur'^foo = u"Including Unicode characters: üöä"\n$',
|
||||
".//div[@class='inc-preappend highlight-text']/div/pre":
|
||||
r'(?m)^START CODE$',
|
||||
".//div[@class='inc-pyobj-dedent highlight-python']/div/pre/span":
|
||||
r'def',
|
||||
})
|
||||
HTML_XPATH['subdir/includes.html'].update({
|
||||
".//pre/span": 'line 1',
|
||||
".//pre/span": 'line 2',
|
||||
})
|
||||
|
||||
class NslessParser(ET.XMLParser):
|
||||
"""XMLParser that throws away namespaces in tag names."""
|
||||
|
||||
def _fixname(self, key):
|
||||
try:
|
||||
return self._names[key]
|
||||
except KeyError:
|
||||
name = key
|
||||
br = name.find('}')
|
||||
if br > 0:
|
||||
name = name[br+1:]
|
||||
self._names[key] = name = self._fixtext(name)
|
||||
return name
|
||||
|
||||
|
||||
def check_xpath(etree, fname, path, check):
|
||||
nodes = list(etree.findall(path))
|
||||
assert nodes != [], ('did not find any node matching xpath '
|
||||
'%r in file %s' % (path, fname))
|
||||
if hasattr(check, '__call__'):
|
||||
check(nodes)
|
||||
elif not check:
|
||||
# only check for node presence
|
||||
pass
|
||||
else:
|
||||
rex = re.compile(check)
|
||||
for node in nodes:
|
||||
if node.text and rex.search(node.text):
|
||||
break
|
||||
else:
|
||||
assert False, ('%r not found in any node matching '
|
||||
'path %s in %s: %r' % (check, path, fname,
|
||||
[node.text for node in nodes]))
|
||||
|
||||
@gen_with_app(buildername='html', warning=html_warnfile, cleanenv=True,
|
||||
confoverrides={'html_context.hckey_co': 'hcval_co'},
|
||||
tags=['testtag'])
|
||||
def test_html(app):
|
||||
app.builder.build_all()
|
||||
html_warnings = html_warnfile.getvalue().replace(os.sep, '/')
|
||||
html_warnings_exp = HTML_WARNINGS % {'root': app.srcdir}
|
||||
assert html_warnings == html_warnings_exp, 'Warnings don\'t match:\n' + \
|
||||
'\n'.join(difflib.ndiff(html_warnings_exp.splitlines(),
|
||||
html_warnings.splitlines()))
|
||||
|
||||
for fname, paths in HTML_XPATH.iteritems():
|
||||
parser = NslessParser()
|
||||
parser.entity.update(htmlentitydefs.entitydefs)
|
||||
etree = ET.parse(os.path.join(app.outdir, fname), parser)
|
||||
for path, check in paths.iteritems():
|
||||
yield check_xpath, etree, fname, path, check
|
||||
|
||||
|
||||
@with_app(buildername='latex', warning=latex_warnfile, cleanenv=True)
|
||||
def test_latex(app):
|
||||
|
||||
252
tests/test_build_html.py
Normal file
252
tests/test_build_html.py
Normal file
@@ -0,0 +1,252 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
test_build_html
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Test the HTML builder and check output against XPath.
|
||||
|
||||
:copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import difflib
|
||||
import htmlentitydefs
|
||||
from StringIO import StringIO
|
||||
|
||||
try:
|
||||
import pygments
|
||||
except ImportError:
|
||||
pygments = None
|
||||
|
||||
from sphinx.builders.html import StandaloneHTMLBuilder
|
||||
|
||||
from util import *
|
||||
from test_build import ENV_WARNINGS
|
||||
from etree13 import ElementTree as ET
|
||||
|
||||
|
||||
def teardown_module():
|
||||
(test_root / '_build').rmtree(True)
|
||||
|
||||
|
||||
html_warnfile = StringIO()
|
||||
|
||||
HTML_WARNINGS = ENV_WARNINGS + """\
|
||||
%(root)s/images.txt:20: WARNING: no matching candidate for image URI u'foo.*'
|
||||
%(root)s/markup.txt:: WARNING: invalid index entry u''
|
||||
%(root)s/markup.txt:: WARNING: invalid pair index entry u''
|
||||
%(root)s/markup.txt:: WARNING: invalid pair index entry u'keyword; '
|
||||
"""
|
||||
|
||||
HTML_XPATH = {
|
||||
'images.html': {
|
||||
".//img[@src='_images/img.png']": '',
|
||||
".//img[@src='_images/img1.png']": '',
|
||||
".//img[@src='_images/simg.png']": '',
|
||||
".//object[@data='_images/svgimg.svg']": '',
|
||||
".//embed[@src='_images/svgimg.svg']": '',
|
||||
},
|
||||
'subdir/images.html': {
|
||||
".//img[@src='../_images/img1.png']": '',
|
||||
".//img[@src='../_images/rimg.png']": '',
|
||||
},
|
||||
'subdir/includes.html': {
|
||||
".//a[@href='../_downloads/img.png']": '',
|
||||
},
|
||||
'includes.html': {
|
||||
".//pre": u'Max Strauß',
|
||||
".//a[@href='_downloads/img.png']": '',
|
||||
".//a[@href='_downloads/img1.png']": '',
|
||||
".//pre": u'"quotes"',
|
||||
".//pre": u"'included'",
|
||||
},
|
||||
'autodoc.html': {
|
||||
".//dt[@id='test_autodoc.Class']": '',
|
||||
".//dt[@id='test_autodoc.function']/em": r'\*\*kwds',
|
||||
".//dd": r'Return spam\.',
|
||||
},
|
||||
'extapi.html': {
|
||||
".//strong": 'from function: Foo',
|
||||
".//strong": 'from class: Bar',
|
||||
},
|
||||
'markup.html': {
|
||||
".//title": 'set by title directive',
|
||||
".//p/em": 'Section author: Georg Brandl',
|
||||
".//p/em": 'Module author: Georg Brandl',
|
||||
# created by the meta directive
|
||||
".//meta[@name='author'][@content='Me']": '',
|
||||
".//meta[@name='keywords'][@content='docs, sphinx']": '',
|
||||
# a label created by ``.. _label:``
|
||||
".//div[@id='label']": '',
|
||||
# code with standard code blocks
|
||||
".//pre": '^some code$',
|
||||
# an option list
|
||||
".//span[@class='option']": '--help',
|
||||
# admonitions
|
||||
".//p[@class='first admonition-title']": 'My Admonition',
|
||||
".//p[@class='last']": 'Note text.',
|
||||
".//p[@class='last']": 'Warning text.',
|
||||
# inline markup
|
||||
".//li/strong": '^command$',
|
||||
".//li/strong": '^program$',
|
||||
".//li/em": '^dfn$',
|
||||
".//li/tt/span[@class='pre']": '^kbd$',
|
||||
".//li/em": u'File \N{TRIANGULAR BULLET} Close',
|
||||
".//li/tt/span[@class='pre']": '^a/$',
|
||||
".//li/tt/em/span[@class='pre']": '^varpart$',
|
||||
".//li/tt/em/span[@class='pre']": '^i$',
|
||||
".//a[@href='http://www.python.org/dev/peps/pep-0008']/strong": 'PEP 8',
|
||||
".//a[@href='http://tools.ietf.org/html/rfc1.html']/strong": 'RFC 1',
|
||||
".//a[@href='objects.html#envvar-HOME']/tt/span[@class='pre']": 'HOME',
|
||||
".//a[@href='#with']/tt/span[@class='pre']": '^with$',
|
||||
".//a[@href='#grammar-token-try_stmt']/tt/span": '^statement$',
|
||||
".//a[@href='subdir/includes.html']/em": 'Including in subdir',
|
||||
".//a[@href='objects.html#cmdoption-python-c']/em": 'Python -c option',
|
||||
# abbreviations
|
||||
".//abbr[@title='abbreviation']": '^abbr$',
|
||||
# version stuff
|
||||
".//span[@class='versionmodified']": 'New in version 0.6',
|
||||
# footnote reference
|
||||
".//a[@class='footnote-reference']": r'\[1\]',
|
||||
# created by reference lookup
|
||||
".//a[@href='contents.html#ref1']": '',
|
||||
# ``seealso`` directive
|
||||
".//div/p[@class='first admonition-title']": 'See also',
|
||||
# a ``hlist`` directive
|
||||
".//table[@class='hlist']/tr/td/ul/li": '^This$',
|
||||
# a ``centered`` directive
|
||||
".//p[@class='centered']/strong": 'LICENSE',
|
||||
# a glossary
|
||||
".//dl/dt[@id='term-boson']": 'boson',
|
||||
# a production list
|
||||
".//pre/strong": 'try_stmt',
|
||||
".//pre/a[@href='#grammar-token-try1_stmt']/tt/span": 'try1_stmt',
|
||||
# tests for ``only`` directive
|
||||
".//p": 'A global substitution.',
|
||||
".//p": 'In HTML.',
|
||||
".//p": 'In both.',
|
||||
".//p": 'Always present',
|
||||
},
|
||||
'objects.html': {
|
||||
".//dt[@id='mod.Cls.meth1']": '',
|
||||
".//dt[@id='errmod.Error']": '',
|
||||
".//a[@href='#mod.Cls']": '',
|
||||
".//dl[@class='userdesc']": '',
|
||||
".//dt[@id='userdesc-myobj']": '',
|
||||
".//a[@href='#userdesc-myobj']": '',
|
||||
# C references
|
||||
".//span[@class='pre']": 'CFunction()',
|
||||
".//a[@href='#Sphinx_DoSomething']": '',
|
||||
".//a[@href='#SphinxStruct.member']": '',
|
||||
".//a[@href='#SPHINX_USE_PYTHON']": '',
|
||||
".//a[@href='#SphinxType']": '',
|
||||
".//a[@href='#sphinx_global']": '',
|
||||
# test global TOC created by toctree()
|
||||
".//ul[@class='current']/li[@class='toctree-l1 current']/a[@href='']":
|
||||
'Testing object descriptions',
|
||||
".//li[@class='toctree-l1']/a[@href='markup.html']":
|
||||
'Testing various markup',
|
||||
},
|
||||
'contents.html': {
|
||||
".//meta[@name='hc'][@content='hcval']": '',
|
||||
".//meta[@name='hc_co'][@content='hcval_co']": '',
|
||||
".//meta[@name='testopt'][@content='testoverride']": '',
|
||||
#".//td[@class='label']": r'\[Ref1\]', # docutils 0.5 only
|
||||
".//td[@class='label']": '',
|
||||
".//li[@class='toctree-l1']/a": 'Testing various markup',
|
||||
".//li[@class='toctree-l2']/a": 'Inline markup',
|
||||
".//title": 'Sphinx <Tests>',
|
||||
".//div[@class='footer']": 'Georg Brandl & Team',
|
||||
".//a[@href='http://python.org/']": '',
|
||||
".//li/a[@href='genindex.html']/em": 'Index',
|
||||
".//li/a[@href='modindex.html']/em": 'Module Index',
|
||||
".//li/a[@href='search.html']/em": 'Search Page',
|
||||
},
|
||||
'bom.html': {
|
||||
".//title": " File with UTF-8 BOM",
|
||||
},
|
||||
'extensions.html': {
|
||||
".//a[@href='http://python.org/dev/']": "http://python.org/dev/",
|
||||
".//a[@href='http://bugs.python.org/issue1000']": "issue 1000",
|
||||
".//a[@href='http://bugs.python.org/issue1042']": "explicit caption",
|
||||
},
|
||||
'_static/statictmpl.html': {
|
||||
".//project": 'Sphinx <Tests>',
|
||||
},
|
||||
}
|
||||
|
||||
if pygments:
|
||||
HTML_XPATH['includes.html'].update({
|
||||
".//pre/span[@class='s']": u'üöä',
|
||||
".//div[@class='inc-pyobj1 highlight-text']/div/pre":
|
||||
r'^class Foo:\n pass\n\s*$',
|
||||
".//div[@class='inc-pyobj2 highlight-text']/div/pre":
|
||||
r'^ def baz\(\):\n pass\n\s*$',
|
||||
".//div[@class='inc-lines highlight-text']/div/pre":
|
||||
r'^class Foo:\n pass\nclass Bar:\n$',
|
||||
".//div[@class='inc-startend highlight-text']/div/pre":
|
||||
ur'^foo = u"Including Unicode characters: üöä"\n$',
|
||||
".//div[@class='inc-preappend highlight-text']/div/pre":
|
||||
r'(?m)^START CODE$',
|
||||
".//div[@class='inc-pyobj-dedent highlight-python']/div/pre/span":
|
||||
r'def',
|
||||
})
|
||||
HTML_XPATH['subdir/includes.html'].update({
|
||||
".//pre/span": 'line 1',
|
||||
".//pre/span": 'line 2',
|
||||
})
|
||||
|
||||
class NslessParser(ET.XMLParser):
|
||||
"""XMLParser that throws away namespaces in tag names."""
|
||||
|
||||
def _fixname(self, key):
|
||||
try:
|
||||
return self._names[key]
|
||||
except KeyError:
|
||||
name = key
|
||||
br = name.find('}')
|
||||
if br > 0:
|
||||
name = name[br+1:]
|
||||
self._names[key] = name = self._fixtext(name)
|
||||
return name
|
||||
|
||||
|
||||
def check_xpath(etree, fname, path, check):
|
||||
nodes = list(etree.findall(path))
|
||||
assert nodes != [], ('did not find any node matching xpath '
|
||||
'%r in file %s' % (path, fname))
|
||||
if hasattr(check, '__call__'):
|
||||
check(nodes)
|
||||
elif not check:
|
||||
# only check for node presence
|
||||
pass
|
||||
else:
|
||||
rex = re.compile(check)
|
||||
for node in nodes:
|
||||
if node.text and rex.search(node.text):
|
||||
break
|
||||
else:
|
||||
assert False, ('%r not found in any node matching '
|
||||
'path %s in %s: %r' % (check, path, fname,
|
||||
[node.text for node in nodes]))
|
||||
|
||||
@gen_with_app(buildername='html', warning=html_warnfile, cleanenv=True,
|
||||
confoverrides={'html_context.hckey_co': 'hcval_co'},
|
||||
tags=['testtag'])
|
||||
def test_html(app):
|
||||
app.builder.build_all()
|
||||
html_warnings = html_warnfile.getvalue().replace(os.sep, '/')
|
||||
html_warnings_exp = HTML_WARNINGS % {'root': app.srcdir}
|
||||
assert html_warnings == html_warnings_exp, 'Warnings don\'t match:\n' + \
|
||||
'\n'.join(difflib.ndiff(html_warnings_exp.splitlines(),
|
||||
html_warnings.splitlines()))
|
||||
|
||||
for fname, paths in HTML_XPATH.iteritems():
|
||||
parser = NslessParser()
|
||||
parser.entity.update(htmlentitydefs.entitydefs)
|
||||
etree = ET.parse(os.path.join(app.outdir, fname), parser)
|
||||
for path, check in paths.iteritems():
|
||||
yield check_xpath, etree, fname, path, check
|
||||
@@ -32,7 +32,7 @@ def test_core_config(app):
|
||||
# simple default values
|
||||
assert 'exclude_dirs' not in cfg.__dict__
|
||||
assert cfg.exclude_dirs == []
|
||||
assert cfg.show_authors == False
|
||||
assert cfg.trim_footnote_reference_space == False
|
||||
|
||||
# complex default values
|
||||
assert 'html_title' not in cfg.__dict__
|
||||
|
||||
@@ -36,6 +36,7 @@ def test_build(app):
|
||||
undoc_py, undoc_c = pickle.loads((app.outdir / 'undoc.pickle').text())
|
||||
assert len(undoc_c) == 1
|
||||
# the key is the full path to the header file, which isn't testable
|
||||
# XXX this should fail right now
|
||||
assert undoc_c.values()[0] == [('cfunction', 'Py_SphinxTest')]
|
||||
|
||||
assert 'test_autodoc' in undoc_py
|
||||
|
||||
@@ -20,8 +20,8 @@ warnings = []
|
||||
|
||||
def setup_module():
|
||||
global app, env
|
||||
app = TestApp(srcdir='(temp)')
|
||||
env = BuildEnvironment(app.srcdir, app.doctreedir, app.config)
|
||||
app = TestApp(srcdir='(temp)', freshenv=True)
|
||||
env = app.env
|
||||
env.set_warnfunc(lambda *args: warnings.append(args))
|
||||
|
||||
def teardown_module():
|
||||
@@ -51,7 +51,7 @@ def test_images():
|
||||
|
||||
tree = env.get_doctree('images')
|
||||
app._warning.reset()
|
||||
htmlbuilder = StandaloneHTMLBuilder(app, env)
|
||||
htmlbuilder = StandaloneHTMLBuilder(app)
|
||||
htmlbuilder.post_process_images(tree)
|
||||
assert "no matching candidate for image URI u'foo.*'" in \
|
||||
app._warning.content[-1]
|
||||
@@ -61,7 +61,7 @@ def test_images():
|
||||
set(['img.png', 'img1.png', 'simg.png', 'svgimg.svg'])
|
||||
|
||||
app._warning.reset()
|
||||
latexbuilder = LaTeXBuilder(app, env)
|
||||
latexbuilder = LaTeXBuilder(app)
|
||||
latexbuilder.post_process_images(tree)
|
||||
assert "no matching candidate for image URI u'foo.*'" in \
|
||||
app._warning.content[-1]
|
||||
@@ -92,10 +92,10 @@ def test_second_update():
|
||||
assert 'autodoc' not in env.found_docs
|
||||
|
||||
def test_object_inventory():
|
||||
refs = env.descrefs
|
||||
refs = env.domaindata['py']['objects']
|
||||
|
||||
assert 'func_without_module' in refs
|
||||
assert refs['func_without_module'] == ('desc', 'function')
|
||||
assert refs['func_without_module'] == ('objects', 'function')
|
||||
assert 'func_without_module2' in refs
|
||||
assert 'mod.func_in_module' in refs
|
||||
assert 'mod.Cls' in refs
|
||||
@@ -109,5 +109,8 @@ def test_object_inventory():
|
||||
assert 'func_in_module' not in refs
|
||||
assert 'func_noindex' not in refs
|
||||
|
||||
assert 'mod' in env.modules
|
||||
assert env.modules['mod'] == ('desc', 'Module synopsis.', 'UNIX', False)
|
||||
assert env.domaindata['py']['modules']['mod'] == \
|
||||
('objects', 'Module synopsis.', 'UNIX', False)
|
||||
|
||||
assert env.domains['py'].data is env.domaindata['py']
|
||||
assert env.domains['c'].data is env.domaindata['c']
|
||||
|
||||
112
tests/test_intersphinx.py
Normal file
112
tests/test_intersphinx.py
Normal file
@@ -0,0 +1,112 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
test_intersphinx
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Test the intersphinx extension.
|
||||
|
||||
:copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
import zlib
|
||||
import posixpath
|
||||
from cStringIO import StringIO
|
||||
|
||||
from docutils import nodes
|
||||
|
||||
from sphinx import addnodes
|
||||
from sphinx.ext.intersphinx import read_inventory_v1, read_inventory_v2, \
|
||||
fetch_inventory, load_mappings, missing_reference
|
||||
|
||||
from util import *
|
||||
|
||||
|
||||
inventory_v1 = '''\
|
||||
# Sphinx inventory version 1
|
||||
# Project: foo
|
||||
# Version: 1.0
|
||||
module mod foo.html
|
||||
module.cls class foo.html
|
||||
'''
|
||||
|
||||
inventory_v2 = '''\
|
||||
# Sphinx inventory version 2
|
||||
# Project: foo
|
||||
# Version: 2.0
|
||||
# The remainder of this file is compressed with zlib.
|
||||
''' + zlib.compress('''\
|
||||
module1 py:module 0 foo.html#module-module1
|
||||
module2 py:module 0 foo.html#module-$
|
||||
module1.func py:function 1 sub/foo.html#$
|
||||
CFunc c:function 2 cfunc.html#CFunc
|
||||
''')
|
||||
|
||||
|
||||
def test_read_inventory_v1():
|
||||
f = StringIO(inventory_v1)
|
||||
f.readline()
|
||||
invdata = read_inventory_v1(f, '/util', posixpath.join)
|
||||
assert invdata['py:module']['module'] == \
|
||||
('foo', '1.0', '/util/foo.html#module-module')
|
||||
assert invdata['py:class']['module.cls'] == \
|
||||
('foo', '1.0', '/util/foo.html#module.cls')
|
||||
|
||||
|
||||
def test_read_inventory_v2():
|
||||
f = StringIO(inventory_v2)
|
||||
f.readline()
|
||||
invdata1 = read_inventory_v2(f, '/util', posixpath.join)
|
||||
|
||||
# try again with a small buffer size to test the chunking algorithm
|
||||
f = StringIO(inventory_v2)
|
||||
f.readline()
|
||||
invdata2 = read_inventory_v2(f, '/util', posixpath.join, bufsize=5)
|
||||
|
||||
assert invdata1 == invdata2
|
||||
|
||||
assert len(invdata1['py:module']) == 2
|
||||
assert invdata1['py:module']['module1'] == \
|
||||
('foo', '2.0', '/util/foo.html#module-module1')
|
||||
assert invdata1['py:module']['module2'] == \
|
||||
('foo', '2.0', '/util/foo.html#module-module2')
|
||||
assert invdata1['py:function']['module1.func'][2] == \
|
||||
'/util/sub/foo.html#module1.func'
|
||||
assert invdata1['c:function']['CFunc'][2] == '/util/cfunc.html#CFunc'
|
||||
|
||||
|
||||
@with_app(confoverrides={'extensions': 'sphinx.ext.intersphinx'})
|
||||
@with_tempdir
|
||||
def test_missing_reference(tempdir, app):
|
||||
inv_file = tempdir / 'inventory'
|
||||
write_file(inv_file, inventory_v2)
|
||||
app.config.intersphinx_mapping = {'http://docs.python.org/': inv_file}
|
||||
app.config.intersphinx_cache_limit = 0
|
||||
|
||||
# load the inventory and check if it's done correctly
|
||||
load_mappings(app)
|
||||
inv = app.env.intersphinx_inventory
|
||||
|
||||
assert inv['py:module']['module2'] == \
|
||||
('foo', '2.0', 'http://docs.python.org/foo.html#module-module2')
|
||||
|
||||
# create fake nodes and check referencing
|
||||
contnode = nodes.emphasis('foo')
|
||||
refnode = addnodes.pending_xref('')
|
||||
refnode['reftarget'] = 'module1.func'
|
||||
refnode['reftype'] = 'func'
|
||||
refnode['refdomain'] = 'py'
|
||||
|
||||
rn = missing_reference(app, app.env, refnode, contnode)
|
||||
assert isinstance(rn, nodes.reference)
|
||||
assert rn['refuri'] == 'http://docs.python.org/sub/foo.html#module1.func'
|
||||
assert rn['reftitle'] == '(in foo v2.0)'
|
||||
assert rn[0] is contnode
|
||||
|
||||
# create unresolvable nodes and check None return value
|
||||
refnode['reftype'] = 'foo'
|
||||
assert missing_reference(app, app.env, refnode, contnode) is None
|
||||
|
||||
refnode['reftype'] = 'function'
|
||||
refnode['reftarget'] = 'foo.func'
|
||||
assert missing_reference(app, app.env, refnode, contnode) is None
|
||||
@@ -29,6 +29,7 @@ def setup_module():
|
||||
components=(rst.Parser, HTMLWriter, LaTeXWriter))
|
||||
settings = optparser.get_default_values()
|
||||
settings.env = app.builder.env
|
||||
settings.env.patch_lookup_functions()
|
||||
parser = rst.Parser()
|
||||
|
||||
def teardown_module():
|
||||
@@ -60,7 +61,7 @@ def verify_re(rst, html_expected, latex_expected):
|
||||
html_translator = ForgivingHTMLTranslator(app.builder, document)
|
||||
document.walkabout(html_translator)
|
||||
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
|
||||
|
||||
if latex_expected:
|
||||
latex_translator = ForgivingLaTeXTranslator(document, app.builder)
|
||||
|
||||
@@ -184,9 +184,9 @@ def gen_with_app(*args, **kwargs):
|
||||
|
||||
|
||||
def with_tempdir(func):
|
||||
def new_func():
|
||||
def new_func(*args, **kwds):
|
||||
tempdir = path(tempfile.mkdtemp())
|
||||
func(tempdir)
|
||||
func(tempdir, *args, **kwds)
|
||||
tempdir.rmtree()
|
||||
new_func.__name__ = func.__name__
|
||||
return new_func
|
||||
|
||||
Reference in New Issue
Block a user