diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 000000000..54e27e201
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,7 @@
+language: python
+python:
+ - "2.7"
+ - "3.3"
+script: make test
+install:
+ - python setup.py -q install
diff --git a/AUTHORS b/AUTHORS
index a24d6a545..2a9dbbac9 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -13,6 +13,7 @@ Other contributors, listed alphabetically, are:
* Charles Duffy -- original graphviz extension
* Kevin Dunn -- MathJax extension
* Josip Dzolonga -- coverage builder
+* Hernan Grecco -- search improvements
* Horst Gutmann -- internationalization support
* Martin Hans -- autodoc improvements
* Doug Hellmann -- graphviz improvements
diff --git a/CHANGES b/CHANGES
index 25bc6daf4..54033b0cb 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,74 @@
Release 1.2 (in development)
============================
+* New locales: #1097: Added Basque locale.
+
+* Fix text builder did not respect wide/fullwidth characters:
+ title underline width, table layout width and text wrap width.
+
+* #1062: sphinx.ext.autodoc use __init__ method signature for class signature.
+
+* PR#111: Respect add_autodoc_attrgetter() even when inherited-members is set.
+ Thanks to A. Jesse Jiryu Davis.
+
+* #1090: Fix gettext does not extract glossary terms.
+
+* #1070: Avoid un-pickling issues when running Python 3 and the saved
+ environment was created under Python 2.
+
+* #1069: Fixed error caused when autodoc would try to format signatures of
+ "partial" functions without keyword arguments (patch by Artur Gaspar).
+
+* The :confval:`latex_documents`, :confval:`texinfo_documents`, and
+ :confval:`man_pages` configuration values will be set to default values based
+ on the :confval:`master_doc` if not explicitly set in :file:`conf.py`.
+ Previously, if these values were not set, no output would be genereted by
+ their respective builders.
+
+* The :rst:dir:`toctree` directive and the ``toctree()`` template function now
+ have an ``includehidden`` option that includes hidden toctree entries (bugs
+ #790 and #1047). A bug in the ``maxdepth`` option for the ``toctree()``
+ template function has been fixed (bug #1046).
+
+* PR#99: Strip down seealso directives to normal admonitions. This removes
+ their unusual CSS classes (admonition-see-also), inconsistent LaTeX
+ admonition title ("See Also" instead of "See also"), and spurious indentation
+ in the text builder.
+
+* sphinx-build now has a verbose option :option:`-v` which can be
+ repeated for greater effect. A single occurrance provides a
+ slightly more verbose output than normal. Two or more occurrences
+ of this option provides more detailed output which may be useful for
+ debugging.
+
+* sphinx-build now provides more specific error messages when called with
+ invalid options or arguments.
+
+* sphinx-build now supports the standard :option:`--help` and
+ :option:`--version` options.
+
+* #869: sphinx-build now has the option :option:`-T` for printing the full
+ traceback after an unhandled exception.
+
+* #976: Fix gettext does not extract index entries.
+
+* #940: Fix gettext does not extract figure caption.
+
+* #1067: Improve the ordering of the JavaScript search results: matches in titles
+ come before matches in full text, and object results are better categorized.
+ Also implement a pluggable search scorer.
+
+* Fix text writer can not handle visit_legend for figure directive contents.
+
+* PR#72: #975: Fix gettext does not extract definition terms before docutils 0.10.0
+
+* PR#25: In inheritance diagrams, the first line of the class docstring
+ is now the tooltip for the class.
+
+* PR#47: Added :mod:`sphinx.ext.linkcode` extension.
+
+* PR#75: Added ``--follow-links`` option to sphinx-apidoc.
+
* PR#45: The linkcheck builder now checks ``#anchor``\ s for existence.
* PR#28: Added Hungarian translation.
@@ -11,11 +79,55 @@ Release 1.2 (in development)
* PR#52: ``special_members`` flag to autodoc now behaves like ``members``.
+* #955: Fix i18n transformation.
+
+* Handle duplicate domain indices in texinfo.
+
+* PR#74: Fix some Russian translation.
+
+* PR#97: Fix footnote handling in translated documents.
+
* Update to jQuery 1.7.1 and Underscore.js 1.3.1.
+* #1055: Fix web support with relative path to source directory.
-Release 1.1.4 (in development)
-==============================
+* #1053: The "rightsidebar" and "collapsiblesidebar" HTML theme options now work together.
+
+* #1015: Stop overriding jQuery contains() in the JavaScript.
+
+* #1028: Fix line block output in the text builder.
+
+* #1018: Fix "container" directive handling in the text builder.
+
+* #1012: Update Estonian translation.
+
+* #1010: Make pngmath images transparent by default; IE7+ should handle it.
+
+* #440: Fix coarse timestamp resolution in some filesystem generate wrong outdated file-list.
+
+* #1008: Fix test failures with Python 3.3.
+
+* #1029: Fix intersphinx_mapping values are not stable if mapping have plural key/value set with Python 3.3.
+
+* #920: Rescue PIL packaging issue that allow import Image without PIL namespace. Thanks to Marc Schlaich.
+
+* #1024: Improve Makefile/make.bat error message if Sphinx is not found. Thanks to anatoly techtonik.
+
+* #1037: Fix typos in Polish translation. Thanks to Jakub Wilk.
+
+* #1038: Fix cpp domain parser fails to parse C+11 "static constexpr" declarations. Thanks to Jakub Wilk.
+
+* #1043: Fix sphinx-quickstart asks again and again Y|N because input() return value with extra '\r' on Python-3.2.0 + Windows. Thanks to Régis Décamps.
+
+* #1041: Fix cpp domain parser fails to parse a const type with a modifier.
+
+* #958: Do not preserve ``environment.pickle`` after a failed build.
+
+* PR#88: Added the "Sphinx Developer's Guide" (:file:`doc/devguide.rst`)
+ which outlines the basic development process of the Sphinx project.
+
+* Added the Docutils-native XML and pseudo-XML builders. See
+ :class:`XMLBuilder` and :class:`PseudoXMLBuilder`.
Release 1.1.3 (Mar 10, 2012)
diff --git a/EXAMPLES b/EXAMPLES
index bb5712d49..3e9e8265f 100644
--- a/EXAMPLES
+++ b/EXAMPLES
@@ -4,7 +4,7 @@ Projects using Sphinx
This is an (incomplete) alphabetic list of projects that use Sphinx or
are experimenting with using it for their documentation. If you like to
be included, please mail to `the Google group
-`_.
+`_.
I've grouped the list into sections to make it easier to find
interesting examples.
@@ -70,6 +70,7 @@ Documentation using a customized version of the default theme
* Chaco: http://code.enthought.com/projects/chaco/docs/html/
* Djagios: http://djagios.org/
* GetFEM++: http://home.gna.org/getfem/
+* Google or-tools: https://or-tools.googlecode.com/svn/trunk/documentation/user_manual/index.html
* GPAW: https://wiki.fysik.dtu.dk/gpaw/
* Grok: http://grok.zope.org/doc/current/
* IFM: http://fluffybunny.memebot.com/ifm-docs/index.html
@@ -105,10 +106,11 @@ Documentation using the sphinxdoc theme
http://www.tango-controls.org/static/PyTango/latest/doc/html/index.html
* Reteisi: http://www.reteisi.org/contents.html
* Satchmo: http://www.satchmoproject.com/docs/dev/
-* Sphinx: http://sphinx.pocoo.org/
+* Sphinx: http://sphinx-doc.org/
* Sqlkit: http://sqlkit.argolinux.org/
* Tau: http://www.tango-controls.org/static/tau/latest/doc/html/index.html
* Total Open Station: http://tops.berlios.de/
+* Turbulenz: http://docs.turbulenz.com/
* WebFaction: http://docs.webfaction.com/
@@ -128,6 +130,7 @@ Documentation using another builtin theme
(agogo)
* Sylli: http://sylli.sourceforge.net/ (nature)
* libLAS: http://liblas.org/ (nature)
+* Valence: http://docs.valence.desire2learn.com/ (haiku)
Documentation using a custom theme/integrated in a site
@@ -135,7 +138,8 @@ Documentation using a custom theme/integrated in a site
* Blender: http://www.blender.org/documentation/250PythonDoc/
* Blinker: http://discorporate.us/projects/Blinker/docs/
-* Classy: classy: http://classy.pocoo.org/
+* Classy: http://classy.pocoo.org/
+* DEAP: http://deap.gel.ulaval.ca/doc/0.8/index.html
* Django: http://docs.djangoproject.com/
* e-cidadania: http://e-cidadania.readthedocs.org/en/latest/
* Flask: http://flask.pocoo.org/docs/
@@ -143,13 +147,17 @@ Documentation using a custom theme/integrated in a site
* Gameduino: http://excamera.com/sphinx/gameduino/
* GeoServer: http://docs.geoserver.org/
* Glashammer: http://glashammer.org/
+* Istihza (Turkish Python documentation project): http://www.istihza.com/py2/icindekiler_python.html
+* MathJax: http://docs.mathjax.org/en/latest/
* MirrorBrain: http://mirrorbrain.org/docs/
* nose: http://somethingaboutorange.com/mrl/projects/nose/
* ObjectListView: http://objectlistview.sourceforge.net/python
* Open ERP: http://doc.openerp.com/
+* OpenCV: http://docs.opencv.org/
* OpenLayers: http://docs.openlayers.org/
* PyEphem: http://rhodesmill.org/pyephem/
* German Plone 4.0 user manual: http://www.hasecke.com/plone-benutzerhandbuch/4.0/
+* PSI4: http://sirius.chem.vt.edu/psi4manual/latest/index.html
* Pylons: http://pylonshq.com/docs/en/0.9.7/
* PyMOTW: http://www.doughellmann.com/PyMOTW/
* pypol: http://pypol.altervista.org/ (celery)
@@ -161,6 +169,7 @@ Documentation using a custom theme/integrated in a site
* SQLAlchemy: http://www.sqlalchemy.org/docs/
* tinyTiM: http://tinytim.sourceforge.net/docs/2.0/
* tipfy: http://www.tipfy.org/docs/
+* Ubuntu packaging guide: http://developer.ubuntu.com/packaging/html/
* Werkzeug: http://werkzeug.pocoo.org/docs/
* WFront: http://discorporate.us/projects/WFront/
diff --git a/LICENSE b/LICENSE
index 260c20fb1..7aa7620b5 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,7 +1,7 @@
License for Sphinx
==================
-Copyright (c) 2007-2011 by the Sphinx team (see AUTHORS file).
+Copyright (c) 2007-2013 by the Sphinx team (see AUTHORS file).
All rights reserved.
Redistribution and use in source and binary forms, with or without
diff --git a/README.rst b/README.rst
index 850204d3c..5963a0ae8 100644
--- a/README.rst
+++ b/README.rst
@@ -21,7 +21,7 @@ After installing::
Then, direct your browser to ``_build/html/index.html``.
-Or read them online at .
+Or read them online at .
Testing
diff --git a/distribute_setup.py b/distribute_setup.py
index 37117b34e..cebd80b19 100644
--- a/distribute_setup.py
+++ b/distribute_setup.py
@@ -14,11 +14,14 @@ the appropriate options to ``use_setuptools()``.
This file can also be run as a script to install or upgrade setuptools.
"""
import os
+import shutil
import sys
import time
import fnmatch
import tempfile
import tarfile
+import optparse
+
from distutils import log
try:
@@ -46,7 +49,7 @@ except ImportError:
args = [quote(arg) for arg in args]
return os.spawnl(os.P_WAIT, sys.executable, *args) == 0
-DEFAULT_VERSION = "0.6.13"
+DEFAULT_VERSION = "0.6.30"
DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/"
SETUPTOOLS_FAKED_VERSION = "0.6c11"
@@ -63,7 +66,7 @@ Description: xxx
""" % SETUPTOOLS_FAKED_VERSION
-def _install(tarball):
+def _install(tarball, install_args=()):
# extracting the tarball
tmpdir = tempfile.mkdtemp()
log.warn('Extracting in %s', tmpdir)
@@ -81,11 +84,14 @@ def _install(tarball):
# installing
log.warn('Installing Distribute')
- if not _python_cmd('setup.py', 'install'):
+ if not _python_cmd('setup.py', 'install', *install_args):
log.warn('Something went wrong during the installation.')
log.warn('See the error message above.')
+ # exitcode will be 2
+ return 2
finally:
os.chdir(old_wd)
+ shutil.rmtree(tmpdir)
def _build_egg(egg, tarball, to_dir):
@@ -110,6 +116,7 @@ def _build_egg(egg, tarball, to_dir):
finally:
os.chdir(old_wd)
+ shutil.rmtree(tmpdir)
# returning the result
log.warn(egg)
if not os.path.exists(egg):
@@ -144,7 +151,7 @@ def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
except ImportError:
return _do_download(version, download_base, to_dir, download_delay)
try:
- pkg_resources.require("distribute>="+version)
+ pkg_resources.require("distribute>=" + version)
return
except pkg_resources.VersionConflict:
e = sys.exc_info()[1]
@@ -167,6 +174,7 @@ def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
if not no_fake:
_create_fake_setuptools_pkg_info(to_dir)
+
def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
to_dir=os.curdir, delay=15):
"""Download distribute from a specified location and return its filename
@@ -203,6 +211,7 @@ def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
dst.close()
return os.path.realpath(saveto)
+
def _no_sandbox(function):
def __no_sandbox(*args, **kw):
try:
@@ -227,6 +236,7 @@ def _no_sandbox(function):
return __no_sandbox
+
def _patch_file(path, content):
"""Will backup the file then patch it"""
existing_content = open(path).read()
@@ -245,15 +255,18 @@ def _patch_file(path, content):
_patch_file = _no_sandbox(_patch_file)
+
def _same_content(path, content):
return open(path).read() == content
+
def _rename_path(path):
new_name = path + '.OLD.%s' % time.time()
- log.warn('Renaming %s into %s', path, new_name)
+ log.warn('Renaming %s to %s', path, new_name)
os.rename(path, new_name)
return new_name
+
def _remove_flat_installation(placeholder):
if not os.path.isdir(placeholder):
log.warn('Unkown installation at %s', placeholder)
@@ -267,7 +280,7 @@ def _remove_flat_installation(placeholder):
log.warn('Could not locate setuptools*.egg-info')
return
- log.warn('Removing elements out of the way...')
+ log.warn('Moving elements out of the way...')
pkg_info = os.path.join(placeholder, file)
if os.path.isdir(pkg_info):
patched = _patch_egg_dir(pkg_info)
@@ -289,11 +302,13 @@ def _remove_flat_installation(placeholder):
_remove_flat_installation = _no_sandbox(_remove_flat_installation)
+
def _after_install(dist):
log.warn('After install bootstrap.')
placeholder = dist.get_command_obj('install').install_purelib
_create_fake_setuptools_pkg_info(placeholder)
+
def _create_fake_setuptools_pkg_info(placeholder):
if not placeholder or not os.path.exists(placeholder):
log.warn('Could not find the install location')
@@ -307,7 +322,11 @@ def _create_fake_setuptools_pkg_info(placeholder):
return
log.warn('Creating %s', pkg_info)
- f = open(pkg_info, 'w')
+ try:
+ f = open(pkg_info, 'w')
+ except EnvironmentError:
+ log.warn("Don't have permissions to write %s, skipping", pkg_info)
+ return
try:
f.write(SETUPTOOLS_PKG_INFO)
finally:
@@ -321,7 +340,10 @@ def _create_fake_setuptools_pkg_info(placeholder):
finally:
f.close()
-_create_fake_setuptools_pkg_info = _no_sandbox(_create_fake_setuptools_pkg_info)
+_create_fake_setuptools_pkg_info = _no_sandbox(
+ _create_fake_setuptools_pkg_info
+)
+
def _patch_egg_dir(path):
# let's check if it's already patched
@@ -343,6 +365,7 @@ def _patch_egg_dir(path):
_patch_egg_dir = _no_sandbox(_patch_egg_dir)
+
def _before_install():
log.warn('Before install bootstrap.')
_fake_setuptools()
@@ -351,7 +374,7 @@ def _before_install():
def _under_prefix(location):
if 'install' not in sys.argv:
return True
- args = sys.argv[sys.argv.index('install')+1:]
+ args = sys.argv[sys.argv.index('install') + 1:]
for index, arg in enumerate(args):
for option in ('--root', '--prefix'):
if arg.startswith('%s=' % option):
@@ -359,7 +382,7 @@ def _under_prefix(location):
return location.startswith(top_dir)
elif arg == option:
if len(args) > index:
- top_dir = args[index+1]
+ top_dir = args[index + 1]
return location.startswith(top_dir)
if arg == '--user' and USER_SITE is not None:
return location.startswith(USER_SITE)
@@ -376,11 +399,14 @@ def _fake_setuptools():
return
ws = pkg_resources.working_set
try:
- setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools',
- replacement=False))
+ setuptools_dist = ws.find(
+ pkg_resources.Requirement.parse('setuptools', replacement=False)
+ )
except TypeError:
# old distribute API
- setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools'))
+ setuptools_dist = ws.find(
+ pkg_resources.Requirement.parse('setuptools')
+ )
if setuptools_dist is None:
log.warn('No setuptools distribution found')
@@ -414,7 +440,7 @@ def _fake_setuptools():
res = _patch_egg_dir(setuptools_location)
if not res:
return
- log.warn('Patched done.')
+ log.warn('Patching complete.')
_relaunch()
@@ -422,7 +448,9 @@ def _relaunch():
log.warn('Relaunching...')
# we have to relaunch the process
# pip marker to avoid a relaunch bug
- if sys.argv[:3] == ['-c', 'install', '--single-version-externally-managed']:
+ _cmd1 = ['-c', 'install', '--single-version-externally-managed']
+ _cmd2 = ['-c', 'install', '--record']
+ if sys.argv[:3] == _cmd1 or sys.argv[:3] == _cmd2:
sys.argv[0] = 'setup.py'
args = [sys.executable] + sys.argv
sys.exit(subprocess.call(args))
@@ -448,7 +476,7 @@ def _extractall(self, path=".", members=None):
# Extract directories with a safe mode.
directories.append(tarinfo)
tarinfo = copy.copy(tarinfo)
- tarinfo.mode = 448 # decimal for oct 0700
+ tarinfo.mode = 448 # decimal for oct 0700
self.extract(tarinfo, path)
# Reverse sort directories.
@@ -475,11 +503,39 @@ def _extractall(self, path=".", members=None):
self._dbg(1, "tarfile: %s" % e)
-def main(argv, version=DEFAULT_VERSION):
- """Install or upgrade setuptools and EasyInstall"""
- tarball = download_setuptools()
- _install(tarball)
+def _build_install_args(options):
+ """
+ Build the arguments to 'python setup.py install' on the distribute package
+ """
+ install_args = []
+ if options.user_install:
+ if sys.version_info < (2, 6):
+ log.warn("--user requires Python 2.6 or later")
+ raise SystemExit(1)
+ install_args.append('--user')
+ return install_args
+def _parse_args():
+ """
+ Parse the command line for options
+ """
+ parser = optparse.OptionParser()
+ parser.add_option(
+ '--user', dest='user_install', action='store_true', default=False,
+ help='install in user site package (requires Python 2.6 or later)')
+ parser.add_option(
+ '--download-base', dest='download_base', metavar="URL",
+ default=DEFAULT_URL,
+ help='alternative URL from where to download the distribute package')
+ options, args = parser.parse_args()
+ # positional arguments are ignored
+ return options
+
+def main(version=DEFAULT_VERSION):
+ """Install or upgrade setuptools and EasyInstall"""
+ options = _parse_args()
+ tarball = download_setuptools(download_base=options.download_base)
+ return _install(tarball, _build_install_args(options))
if __name__ == '__main__':
- main(sys.argv[1:])
+ sys.exit(main())
diff --git a/doc/Makefile b/doc/Makefile
index 47951316a..831c12c52 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -13,7 +13,8 @@ ALLSPHINXOPTS = -d _build/doctrees $(PAPEROPT_$(PAPER)) \
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(O) .
.PHONY: help clean html dirhtml singlehtml text man pickle json htmlhelp \
- qthelp devhelp epub latex latexpdf changes linkcheck doctest
+ qthelp devhelp epub latex latexpdf changes linkcheck doctest xml \
+ pseudoxml
help:
@echo "Please use \`make ' where is one of"
@@ -37,7 +38,7 @@ help:
@echo " linkcheck to check all external links for integrity"
clean:
- -rm -rf _build/*
+ rm -rf _build/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) _build/html
@@ -147,3 +148,13 @@ info:
@echo "Running Texinfo files through makeinfo..."
make -C _build/texinfo info
@echo "makeinfo finished; the Info files are in _build/texinfo."
+
+xml:
+ $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) _build/xml
+ @echo
+ @echo "Build finished. The XML files are in _build/XML."
+
+pseudoxml:
+ $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) _build/pseudoxml
+ @echo
+ @echo "Build finished. The pseudo-XML files are in _build/pseudoxml."
diff --git a/doc/_static/pocoo.png b/doc/_static/pocoo.png
index 297dcd5e0..eeb18eafe 100644
Binary files a/doc/_static/pocoo.png and b/doc/_static/pocoo.png differ
diff --git a/doc/_templates/index.html b/doc/_templates/index.html
index 34dead7e6..cf2761545 100644
--- a/doc/_templates/index.html
+++ b/doc/_templates/index.html
@@ -17,25 +17,23 @@
documentation of Python projects, but C/C++ is already supported as well,
and it is planned to add special support for other languages as well. Of
course, this site is also created from reStructuredText sources using
- Sphinx!
-
-
- Sphinx is under constant development. The following features are present,
- work fine and can be seen “in action” in the Python docs:
+ Sphinx! The following features should be highlighted:
Output formats: HTML (including Windows HTML Help), LaTeX (for
- printable PDF versions), manual pages, plain text
+ printable PDF versions), Texinfo, manual pages, plain text
Extensive cross-references: semantic markup and automatic links
for functions, classes, citations, glossary terms and similar pieces of
information
Hierarchical structure: easy definition of a document tree, with
automatic links to siblings, parents and children
-
Automatic indices: general index as well as a module index
+
Automatic indices: general index as well as a language-specific
+ module indices
Code handling: automatic highlighting using the Pygments highlighter
Extensions: automatic testing of code snippets, inclusion of
- docstrings from Python modules (API docs), and more
+ docstrings from Python modules (API docs), and
+ more
You can also download PDF versions of the Sphinx documentation:
- a version generated from
+ a version generated from
the LaTeX Sphinx produces, and
- a version generated
+ a version generated
by rst2pdf.
@@ -86,14 +84,4 @@
There is a Japanese translation
of this documentation, thanks to Yoshiki Shibukawa.
+{% endblock %}
diff --git a/doc/_themes/sphinx13/static/bodybg.png b/doc/_themes/sphinx13/static/bodybg.png
new file mode 100644
index 000000000..506b6f908
Binary files /dev/null and b/doc/_themes/sphinx13/static/bodybg.png differ
diff --git a/doc/_themes/sphinx13/static/footerbg.png b/doc/_themes/sphinx13/static/footerbg.png
new file mode 100644
index 000000000..d1922b446
Binary files /dev/null and b/doc/_themes/sphinx13/static/footerbg.png differ
diff --git a/doc/_themes/sphinx13/static/headerbg.png b/doc/_themes/sphinx13/static/headerbg.png
new file mode 100644
index 000000000..6d3e1d5e6
Binary files /dev/null and b/doc/_themes/sphinx13/static/headerbg.png differ
diff --git a/doc/_themes/sphinx13/static/listitem.png b/doc/_themes/sphinx13/static/listitem.png
new file mode 100644
index 000000000..e45715f91
Binary files /dev/null and b/doc/_themes/sphinx13/static/listitem.png differ
diff --git a/doc/_themes/sphinx13/static/relbg.png b/doc/_themes/sphinx13/static/relbg.png
new file mode 100644
index 000000000..47225851b
Binary files /dev/null and b/doc/_themes/sphinx13/static/relbg.png differ
diff --git a/doc/_themes/sphinx13/static/sphinx13.css b/doc/_themes/sphinx13/static/sphinx13.css
new file mode 100644
index 000000000..bb81b67b5
--- /dev/null
+++ b/doc/_themes/sphinx13/static/sphinx13.css
@@ -0,0 +1,396 @@
+/*
+ * sphinx13.css
+ * ~~~~~~~~~~~~
+ *
+ * Sphinx stylesheet -- sphinx13 theme.
+ *
+ * :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
+ * :license: BSD, see LICENSE for details.
+ *
+ */
+
+@import url("basic.css");
+
+/* -- page layout ----------------------------------------------------------- */
+
+body {
+ font-family: 'Open Sans', 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva',
+ 'Verdana', sans-serif;
+ font-size: 14px;
+ text-align: center;
+ background-image: url(bodybg.png);
+ color: black;
+ padding: 0;
+ border-right: 1px solid #0a507a;
+ border-left: 1px solid #0a507a;
+
+ margin: 0 auto;
+ min-width: 780px;
+ max-width: 1080px;
+}
+
+.pageheader {
+ background-image: url(headerbg.png);
+ text-align: left;
+ padding: 10px 15px;
+}
+
+.pageheader ul {
+ float: right;
+ color: white;
+ list-style-type: none;
+ padding-left: 0;
+ margin-top: 30px;
+ margin-right: 10px;
+}
+
+.pageheader li {
+ float: left;
+ margin: 0 0 0 10px;
+}
+
+.pageheader li a {
+ border-radius: 1px;
+ padding: 8px 12px;
+ color: #f9f9f0;
+ text-shadow: 0 0 5px rgba(0, 0, 0, 0.5);
+}
+
+.pageheader li a:hover {
+ background-color: #f9f9f0;
+ color: #0a507a;
+ text-shadow: none;
+}
+
+div.document {
+ background-color: white;
+ text-align: left;
+}
+
+div.bodywrapper {
+ margin: 0 240px 0 0;
+ border-right: 1px solid #0a507a;
+}
+
+div.body {
+ margin: 0;
+ padding: 0.5em 20px 20px 20px;
+}
+
+div.related {
+ font-size: 1em;
+ color: white;
+}
+
+div.related ul {
+ background-image: url(relbg.png);
+ height: 1.9em;
+ border-top: 1px solid #002e50;
+ border-bottom: 1px solid #002e50;
+}
+
+div.related ul li {
+ margin: 0 5px 0 0;
+ padding: 0;
+ float: left;
+}
+
+div.related ul li.right {
+ float: right;
+ margin-right: 5px;
+}
+
+div.related ul li a {
+ margin: 0;
+ padding: 0 5px 0 5px;
+ line-height: 1.75em;
+ color: #f9f9f0;
+ text-shadow: 0px 0px 1px rgba(0, 0, 0, 0.5);
+}
+
+div.related ul li a:hover {
+ color: white;
+ /*text-decoration: underline;*/
+ text-shadow: 0px 0px 1px rgba(255, 255, 255, 0.5);
+}
+
+div.sphinxsidebarwrapper {
+ position: relative;
+ top: 0px;
+ padding: 0;
+}
+
+div.sphinxsidebar {
+ margin: 0;
+ padding: 0 15px 15px 0;
+ width: 210px;
+ float: right;
+ font-size: 1em;
+ text-align: left;
+}
+
+div.sphinxsidebar .logo {
+ font-size: 1.8em;
+ color: #0A507A;
+ font-weight: 300;
+ text-align: center;
+}
+
+div.sphinxsidebar .logo img {
+ vertical-align: middle;
+}
+
+div.sphinxsidebar input {
+ border: 1px solid #aaa;
+ font-family: 'Open Sans', 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva',
+ 'Verdana', sans-serif;
+ font-size: 1em;
+}
+
+div.sphinxsidebar h3 {
+ font-size: 1.5em;
+ border-top: 1px solid #0a507a;
+ margin-top: 1em;
+ margin-bottom: 0.5em;
+ padding-top: 0.5em;
+}
+
+div.sphinxsidebar h4 {
+ font-size: 1.2em;
+ margin-bottom: 0;
+}
+
+div.sphinxsidebar h3, div.sphinxsidebar h4 {
+ margin-right: -15px;
+ margin-left: -15px;
+ padding-right: 14px;
+ padding-left: 14px;
+ color: #333;
+ font-weight: 300;
+ /*text-shadow: 0px 0px 0.5px rgba(0, 0, 0, 0.4);*/
+}
+
+div.sphinxsidebarwrapper > h3:first-child {
+ margin-top: 0.5em;
+ border: none;
+}
+
+div.sphinxsidebar h3 a {
+ color: #333;
+}
+
+div.sphinxsidebar ul {
+ color: #444;
+ margin-top: 7px;
+ padding: 0;
+ line-height: 130%;
+}
+
+div.sphinxsidebar ul ul {
+ margin-left: 20px;
+ list-style-image: url(listitem.png);
+}
+
+div.footer {
+ background-image: url(footerbg.png);
+ color: #ccc;
+ text-shadow: 0 0 .2px rgba(255, 255, 255, 0.8);
+ padding: 3px 8px 3px 0;
+ clear: both;
+ font-size: 0.8em;
+ text-align: right;
+}
+
+/* no need to make a visible link to Sphinx on the Sphinx page */
+div.footer a {
+ color: #ccc;
+}
+
+/* -- body styles ----------------------------------------------------------- */
+
+p {
+ margin: 0.8em 0 0.5em 0;
+}
+
+a {
+ color: #A2881D;
+ text-decoration: none;
+}
+
+a:hover {
+ color: #E1C13F;
+}
+
+div.body a {
+ text-decoration: underline;
+}
+
+h1 {
+ margin: 10px 0 0 0;
+ font-size: 2.4em;
+ color: #0A507A;
+ font-weight: 300;
+}
+
+h2 {
+ margin: 1.em 0 0.2em 0;
+ font-size: 1.5em;
+ font-weight: 300;
+ padding: 0;
+ color: #174967;
+}
+
+h3 {
+ margin: 1em 0 -0.3em 0;
+ font-size: 1.3em;
+ font-weight: 300;
+}
+
+div.body h1 a, div.body h2 a, div.body h3 a, div.body h4 a, div.body h5 a, div.body h6 a {
+ text-decoration: none;
+}
+
+div.body h1 a tt, div.body h2 a tt, div.body h3 a tt, div.body h4 a tt, div.body h5 a tt, div.body h6 a tt {
+ color: #0A507A !important;
+ font-size: inherit !important;
+}
+
+a.headerlink {
+ color: #0A507A !important;
+ font-size: 12px;
+ margin-left: 6px;
+ padding: 0 4px 0 4px;
+ text-decoration: none !important;
+ float: right;
+}
+
+a.headerlink:hover {
+ background-color: #ccc;
+ color: white!important;
+}
+
+cite, code, tt {
+ font-family: 'Consolas', 'DejaVu Sans Mono',
+ 'Bitstream Vera Sans Mono', monospace;
+ font-size: 14px;
+ letter-spacing: -0.02em;
+}
+
+tt {
+ background-color: #f2f2f2;
+ border: 1px solid #ddd;
+ border-radius: 2px;
+ color: #333;
+ padding: 1px;
+}
+
+tt.descname, tt.descclassname, tt.xref {
+ border: 0;
+}
+
+hr {
+ border: 1px solid #abc;
+ margin: 2em;
+}
+
+a tt {
+ border: 0;
+ color: #a2881d;
+}
+
+a tt:hover {
+ color: #e1c13f;
+}
+
+pre {
+ font-family: 'Consolas', 'DejaVu Sans Mono',
+ 'Bitstream Vera Sans Mono', monospace;
+ font-size: 13px;
+ letter-spacing: 0.015em;
+ line-height: 120%;
+ padding: 0.5em;
+ border: 1px solid #ccc;
+ border-radius: 2px;
+ background-color: #f8f8f8;
+}
+
+pre a {
+ color: inherit;
+ text-decoration: underline;
+}
+
+td.linenos pre {
+ padding: 0.5em 0;
+}
+
+div.quotebar {
+ background-color: #f8f8f8;
+ max-width: 250px;
+ float: right;
+ padding: 0px 7px;
+ border: 1px solid #ccc;
+ margin-left: 1em;
+}
+
+div.topic {
+ background-color: #f8f8f8;
+}
+
+table {
+ border-collapse: collapse;
+ margin: 0 -0.5em 0 -0.5em;
+}
+
+table td, table th {
+ padding: 0.2em 0.5em 0.2em 0.5em;
+}
+
+div.admonition, div.warning {
+ font-size: 0.9em;
+ margin: 1em 0 1em 0;
+ border: 1px solid #86989B;
+ border-radius: 2px;
+ background-color: #f7f7f7;
+ padding: 0;
+}
+
+div.admonition p, div.warning p {
+ margin: 0.5em 1em 0.5em 1em;
+ padding: 0;
+}
+
+div.admonition pre, div.warning pre {
+ margin: 0.4em 1em 0.4em 1em;
+}
+
+div.admonition p.admonition-title,
+div.warning p.admonition-title {
+ margin-top: 1em;
+ padding-top: 0.5em;
+ font-weight: bold;
+}
+
+div.warning {
+ border: 1px solid #940000;
+/* background-color: #FFCCCF;*/
+}
+
+div.warning p.admonition-title {
+}
+
+div.admonition ul, div.admonition ol,
+div.warning ul, div.warning ol {
+ margin: 0.1em 0.5em 0.5em 3em;
+ padding: 0;
+}
+
+.viewcode-back {
+ font-family: 'Open Sans', 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva',
+ 'Verdana', sans-serif;
+}
+
+div.viewcode-block:target {
+ background-color: #f4debf;
+ border-top: 1px solid #ac9;
+ border-bottom: 1px solid #ac9;
+}
diff --git a/doc/_themes/sphinx13/static/sphinxheader.png b/doc/_themes/sphinx13/static/sphinxheader.png
new file mode 100644
index 000000000..2b33f09d9
Binary files /dev/null and b/doc/_themes/sphinx13/static/sphinxheader.png differ
diff --git a/doc/_themes/sphinx13/theme.conf b/doc/_themes/sphinx13/theme.conf
new file mode 100644
index 000000000..876b19803
--- /dev/null
+++ b/doc/_themes/sphinx13/theme.conf
@@ -0,0 +1,4 @@
+[theme]
+inherit = basic
+stylesheet = sphinx13.css
+pygments_style = trac
diff --git a/doc/builders.rst b/doc/builders.rst
index 6600807d3..0075ad810 100644
--- a/doc/builders.rst
+++ b/doc/builders.rst
@@ -272,6 +272,29 @@ Note that a direct PDF builder using ReportLab is available in `rst2pdf
Its name is ``linkcheck``.
+.. module:: sphinx.builders.xml
+.. class:: XMLBuilder
+
+ This builder produces Docutils-native XML files. The output can be
+ transformed with standard XML tools such as XSLT processors into arbitrary
+ final forms.
+
+ Its name is ``xml``.
+
+ .. versionadded:: 1.2
+
+.. class:: PseudoXMLBuilder
+
+ This builder is used for debugging the Sphinx/Docutils "Reader to Transform
+ to Writer" pipeline. It produces compact pretty-printed "pseudo-XML", files
+ where nesting is indicated by indentation (no end-tags). External
+ attributes for all elements are output, and internal attributes for any
+ leftover "pending" elements are also given.
+
+ Its name is ``pseudoxml``.
+
+ .. versionadded:: 1.2
+
Built-in Sphinx extensions that offer more builders are:
diff --git a/doc/conf.py b/doc/conf.py
index 6b547edde..e9998d237 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -13,24 +13,25 @@ templates_path = ['_templates']
exclude_patterns = ['_build']
project = 'Sphinx'
-copyright = '2007-2011, Georg Brandl'
+copyright = '2007-2013, Georg Brandl'
version = sphinx.__released__
release = version
show_authors = True
-html_theme = 'sphinxdoc'
+html_theme = 'sphinx13'
+html_theme_path = ['_themes']
modindex_common_prefix = ['sphinx.']
html_static_path = ['_static']
html_sidebars = {'index': ['indexsidebar.html', 'searchbox.html']}
html_additional_pages = {'index': 'index.html'}
-html_use_opensearch = 'http://sphinx.pocoo.org'
+html_use_opensearch = 'http://sphinx-doc.org'
htmlhelp_basename = 'Sphinxdoc'
epub_theme = 'epub'
epub_basename = 'sphinx'
epub_author = 'Georg Brandl'
-epub_publisher = 'http://sphinx.pocoo.org/'
+epub_publisher = 'http://sphinx-doc.org/'
epub_scheme = 'url'
epub_identifier = epub_publisher
epub_pre_files = [('index.html', 'Welcome')]
@@ -39,6 +40,7 @@ epub_exclude_files = ['_static/opensearch.xml', '_static/doctools.js',
'_static/basic.css', 'search.html', '_static/websupport.js']
epub_fix_images = False
epub_max_image_width = 0
+epub_show_urls = 'inline'
latex_documents = [('contents', 'sphinx.tex', 'Sphinx Documentation',
'Georg Brandl', 'manual', 1)]
diff --git a/doc/config.rst b/doc/config.rst
index 6178d459b..34d862e9a 100644
--- a/doc/config.rst
+++ b/doc/config.rst
@@ -373,6 +373,7 @@ documentation on :ref:`intl` for details.
* ``en`` -- English
* ``es`` -- Spanish
* ``et`` -- Estonian
+ * ``eu`` -- Basque
* ``fa`` -- Iranian
* ``fi`` -- Finnish
* ``fr`` -- French
@@ -383,6 +384,7 @@ documentation on :ref:`intl` for details.
* ``ko`` -- Korean
* ``lt`` -- Lithuanian
* ``lv`` -- Latvian
+ * ``nb_NO`` -- Norwegian Bokmal
* ``ne`` -- Nepali
* ``nl`` -- Dutch
* ``pl`` -- Polish
@@ -759,6 +761,15 @@ that use Sphinx' HTMLWriter class.
.. versionadded:: 1.1
+.. confval:: html_search_scorer
+
+ The name of a javascript file (relative to the configuration directory) that
+ implements a search results scorer. If empty, the default will be used.
+
+ .. XXX describe interface for scorer here
+
+ .. versionadded:: 1.2
+
.. confval:: htmlhelp_basename
Output file base name for HTML help builder. Default is ``'pydoc'``.
@@ -926,6 +937,17 @@ the `Dublin Core metadata `_.
.. versionadded:: 1.2
+.. confval:: epub_show_urls
+
+ Control whether to display URL addresses. This is very useful for
+ readers that have no other means to display the linked URL. The
+ settings can have the following values:
+
+ * ``'inline'`` -- display URLs inline in parentheses (default)
+ * ``'no'`` -- do not display URLs
+
+ .. versionadded:: 1.2
+
.. _latex-options:
@@ -1341,6 +1363,16 @@ Options for the linkcheck builder
.. versionadded:: 1.2
+Options for the XML builder
+---------------------------
+
+.. confval:: xml_pretty
+
+ If True, pretty-print the XML. Default is ``True``.
+
+ .. versionadded:: 1.2
+
+
.. rubric:: Footnotes
.. [1] A note on available globbing syntax: you can use the standard shell
diff --git a/doc/contents.rst b/doc/contents.rst
index 3bbc28350..5e0ae6c12 100644
--- a/doc/contents.rst
+++ b/doc/contents.rst
@@ -22,6 +22,7 @@ Sphinx documentation contents
faq
glossary
+ devguide
changes
examples
diff --git a/doc/develop.rst b/doc/develop.rst
new file mode 100644
index 000000000..4181cde86
--- /dev/null
+++ b/doc/develop.rst
@@ -0,0 +1,103 @@
+:orphan:
+
+Sphinx development
+==================
+
+Sphinx is a maintained by a group of volunteers. We value every contribution!
+
+* The code can be found in a Mercurial repository, at
+ http://bitbucket.org/birkenfeld/sphinx/.
+* Issues and feature requests should be raised in the `tracker
+ `_.
+* The mailing list for development is at `Google Groups
+ `_.
+
+For more about our development process and methods, see the :doc:`devguide`.
+
+Extensions
+==========
+
+The `sphinx-contrib `_
+repository contains many contributed extensions. Some of them have their own
+releases on PyPI, others you can install from a checkout.
+
+This is the current list of contributed extensions in that repository:
+
+- aafig: render embeded ASCII art as nice images using aafigure_.
+- actdiag: embed activity diagrams by using actdiag_
+- adadomain: an extension for Ada support (Sphinx 1.0 needed)
+- ansi: parse ANSI color sequences inside documents
+- autorun: Execute code in a runblock directive.
+- blockdiag: embed block diagrams by using blockdiag_
+- cheeseshop: easily link to PyPI packages
+- clearquest: create tables from ClearQuest_ queries.
+- coffeedomain: a domain for (auto)documenting CoffeeScript source code.
+- context: a builder for ConTeXt.
+- doxylink: Link to external Doxygen-generated HTML documentation
+- email: obfuscate email addresses
+- erlangdomain: an extension for Erlang support (Sphinx 1.0 needed)
+- exceltable: embed Excel spreadsheets into documents using exceltable_
+- feed: an extension for creating syndication feeds and time-based overviews
+ from your site content
+- gnuplot: produces images using gnuplot_ language.
+- googleanalytics: track html visitors statistics
+- googlechart: embed charts by using `Google Chart`_
+- googlemaps: embed maps by using `Google Maps`_
+- httpdomain: a domain for documenting RESTful HTTP APIs.
+- hyphenator: client-side hyphenation of HTML using hyphenator_
+- lilypond: an extension inserting music scripts from Lilypond_ in PNG format.
+- mscgen: embed mscgen-formatted MSC (Message Sequence Chart)s.
+- nicoviceo: embed videos from nicovideo
+- nwdiag: embed network diagrams by using nwdiag_
+- omegat: support tools to collaborate with OmegaT_ (Sphinx 1.1 needed)
+- osaka: convert standard Japanese doc to Osaka dialect (it is joke extension)
+- paverutils: an alternate integration of Sphinx with Paver_.
+- phpdomain: an extension for PHP support
+- plantuml: embed UML diagram by using PlantUML_
+- rawfiles: copy raw files, like a CNAME.
+- requirements: declare requirements wherever you need (e.g. in test
+ docstrings), mark statuses and collect them in a single list
+- rubydomain: an extension for Ruby support (Sphinx 1.0 needed)
+- sadisplay: display SqlAlchemy model sadisplay_
+- sdedit: an extension inserting sequence diagram by using Quick Sequence.
+ Diagram Editor (sdedit_)
+- seqdiag: embed sequence diagrams by using seqdiag_
+- slide: embed presentation slides on slideshare_ and other sites.
+- swf: embed flash files
+- sword: an extension inserting Bible verses from Sword_.
+- tikz: draw pictures with the `TikZ/PGF LaTeX package`_.
+- traclinks: create TracLinks_ to a Trac_ instance from within Sphinx
+- whooshindex: whoosh indexer extension
+- youtube: embed videos from YouTube_
+- zopeext: provide an ``autointerface`` directive for using `Zope interfaces`_.
+
+
+See the :ref:`extension tutorial ` on getting started with writing your
+own extensions.
+
+
+.. _aafigure: https://launchpad.net/aafigure
+.. _gnuplot: http://www.gnuplot.info/
+.. _paver: http://www.blueskyonmars.com/projects/paver/
+.. _Sword: http://www.crosswire.org/sword/
+.. _Lilypond: http://lilypond.org/web/
+.. _sdedit: http://sdedit.sourceforge.net/
+.. _Trac: http://trac.edgewall.org
+.. _TracLinks: http://trac.edgewall.org/wiki/TracLinks
+.. _OmegaT: http://www.omegat.org/
+.. _PlantUML: http://plantuml.sourceforge.net/
+.. _PyEnchant: http://www.rfk.id.au/software/pyenchant/
+.. _sadisplay: http://bitbucket.org/estin/sadisplay/wiki/Home
+.. _blockdiag: http://blockdiag.com/
+.. _seqdiag: http://blockdiag.com/
+.. _actdiag: http://blockdiag.com/
+.. _nwdiag: http://blockdiag.com/
+.. _Google Chart: http://code.google.com/intl/ja/apis/chart/
+.. _Google Maps: http://maps.google.com/
+.. _hyphenator: http://code.google.com/p/hyphenator/
+.. _exceltable: http://packages.python.org/sphinxcontrib-exceltable/
+.. _YouTube: http://www.youtube.com/
+.. _ClearQuest: http://www-01.ibm.com/software/awdtools/clearquest/
+.. _Zope interfaces: http://docs.zope.org/zope.interface/README.html
+.. _slideshare: http://www.slideshare.net/
+.. _TikZ/PGF LaTeX package: http://sourceforge.net/projects/pgf/
diff --git a/doc/devguide.rst b/doc/devguide.rst
new file mode 100644
index 000000000..ab4bc7409
--- /dev/null
+++ b/doc/devguide.rst
@@ -0,0 +1,214 @@
+Sphinx Developer's Guide
+========================
+
+.. topic:: Abstract
+
+ This document describes the development process of Sphinx, a documentation
+ system used by developers to document systems used by other developers to
+ develop other systems that may also be documented using Sphinx.
+
+The Sphinx source code is managed using `Mercurial`_ and is hosted on
+`BitBucket`_.
+
+ hg clone https://bitbucket.org/birkenfeld/sphinx
+
+.. rubric:: Community
+
+sphinx-users
+ Mailing list for user support.
+
+sphinx-dev
+ Mailing list for development related discussions.
+
+#pocoo on irc.freenode.net
+ IRC channel for development questions and user support.
+
+ This channel is shared with other Pocoo projects. Archived logs are
+ available `here `_.
+
+.. _`BitBucket`: http://bitbucket.org
+.. _`Mercurial`: http://mercurial.selenic.com/
+
+
+Bug Reports and Feature Requests
+--------------------------------
+
+If you have encountered a problem with Sphinx or have an idea for a new
+feature, please submit it to the `issue tracker`_ on BitBucket or discuss it
+on the sphinx-dev mailing list.
+
+For bug reports, please include the output produced during the build process
+and also the log file Sphinx creates after it encounters an un-handled
+exception. The location of this file should be shown towards the end of the
+error message.
+
+Including or providing a link to the source files involved may help us fix the
+issue. If possible, try to create a minimal project that produces the error
+and post that instead.
+
+.. _`issue tracker`: http://bitbucket.org/birkenfeld/sphinx/issues
+
+
+Contributing to Sphinx
+----------------------
+
+The recommended way for new contributors to submit code to Sphinx is to fork
+the Mercurial repository on BitBucket and then submit a pull request after
+committing the changes. The pull request will then need to be approved by one
+of the core developers before it is merged into the main repository.
+
+
+Getting Started
+~~~~~~~~~~~~~~~
+
+These are the basic steps needed to start developing on Sphinx.
+
+#. Create an account on BitBucket.
+
+#. Fork the main Sphinx repository (`birkenfeld/sphinx
+ `_) using the BitBucket interface.
+
+#. Clone the forked repository to your machine. ::
+
+ hg clone https://bitbucket.org/USERNAME/sphinx-fork
+ cd sphinx-fork
+
+#. Checkout the appropriate branch.
+
+ For changes that should be included in the next minor release (namely bug
+ fixes), use the ``stable`` branch. ::
+
+ hg checkout stable
+
+ For new features or other substantial changes that should wait until the
+ next major release, use the ``default`` branch.
+
+#. Setup your Python environment. ::
+
+ virtualenv ~/sphinxenv
+ . ~/sphinxenv/bin/activate
+ pip install -e .
+
+#. Hack, hack, hack.
+
+ For tips on working with the code, see the `Coding Guide`_.
+
+#. Test, test, test.
+
+ Run the unit tests::
+
+ pip install nose
+ make test
+
+ Build the documentation and check the output for different builders::
+
+ cd docs
+ make clean html text man info latexpdf
+
+ Run the unit tests under different Python environments using
+ :program:`tox`::
+
+ pip install tox
+ tox -v
+
+ Add a new unit test in the ``tests`` directory if you can.
+
+ For bug fixes, first add a test that fails without your changes and passes
+ after they are applied.
+
+#. Commit your changes. ::
+
+ hg commit -m 'Add useful new feature that does this.'
+
+ BitBucket recognizes `certain phrases`__ that can be used to automatically
+ update the issue tracker.
+
+ For example::
+
+ hg commit -m 'Closes #42: Fix invalid markup in docstring of Foo.bar.'
+
+ would close issue #42.
+
+ __ https://confluence.atlassian.com/display/BITBUCKET/Automatically+Resolving+Issues+when+Users+Push+Code
+
+#. Push changes to your forked repository on BitBucket. ::
+
+ hg push
+
+#. Submit a pull request from your repository to ``birkenfeld/sphinx`` using
+ the BitBucket interface.
+
+#. Wait for a core developer to review your changes.
+
+
+Core Developers
+~~~~~~~~~~~~~~~
+
+The core developers of Sphinx have write access to the main repository. They
+can commit changes, accept/reject pull requests, and manage items on the issue
+tracker.
+
+You do not need to be a core developer or have write access to be involved in
+the development of Sphinx. You can submit patches or create pull requests
+from forked repositories and have a core developer add the changes for you.
+
+The following are some general guidelines for core developers:
+
+* Questionable or extensive changes should be submitted as a pull request
+ instead of being committed directly to the main repository. The pull
+ request should be reviewed by another core developer before it is merged.
+
+* Trivial changes can be committed directly but be sure to keep the repository
+ in a good working state and that all tests pass before pushing your changes.
+
+* When committing code written by someone else, please attribute the original
+ author in the commit message and any relevant :file:`CHANGES` entry.
+
+* Using Mercurial named branches other than ``default`` and ``stable`` is not
+ encouraged.
+
+
+Coding Guide
+------------
+
+* Try to use the same code style as used in the rest of the project. See the
+ `Pocoo Styleguide`__ for more information.
+
+ __ http://flask.pocoo.org/docs/styleguide/
+
+* For non-trivial changes, please update the :file:`CHANGES` file. If your
+ changes alter existing behavior, please document this.
+
+* New features should be documented. Include examples and use cases where
+ appropriate. If possible, include a sample that is displayed in the
+ generated output.
+
+* When adding a new configuration variable, be sure to document it and update
+ :file:`sphinx/quickstart.py`.
+
+* Use the included :program:`utils/check_sources.py` script to check for
+ common formatting issues (trailing whitespace, lengthy lines, etc).
+
+* Add appropriate unit tests.
+
+
+Debugging Tips
+~~~~~~~~~~~~~~
+
+* Delete the build cache before building documents if you make changes in the
+ code by running the command ``make clean`` or using the
+ :option:`sphinx-build -E` option.
+
+* Use the :option:`sphinx-build -P` option to run Pdb on exceptions.
+
+* Use ``node.pformat()`` and ``node.asdom().toxml()`` to generate a printable
+ representation of the document structure.
+
+* Set the configuration variable :confval:`keep_warnings` to True so warnings
+ will be displayed in the generated output.
+
+* Set the configuration variable :confval:`nitpicky` to True so that Sphinx
+ will complain about references without a known target.
+
+* Set the debugging options in the `Docutils configuration file
+ `_.
diff --git a/doc/domains.rst b/doc/domains.rst
index 3d52db3fd..bd99a4c5e 100644
--- a/doc/domains.rst
+++ b/doc/domains.rst
@@ -812,7 +812,15 @@ More domains
------------
The sphinx-contrib_ repository contains more domains available as extensions;
-currently Ada, Erlang, HTTP, PHP, and Ruby domains.
+currently Ada, CoffeeScript_, Erlang_, HTTP_, Jinja_, PHP_, Ruby, and Scala_
+domains.
.. _sphinx-contrib: https://bitbucket.org/birkenfeld/sphinx-contrib/
+
+.. _CoffeeScript: http://pypi.python.org/pypi/sphinxcontrib-coffee
+.. _Erlang: http://pypi.python.org/pypi/sphinxcontrib-erlangdomain
+.. _HTTP: http://pypi.python.org/pypi/sphinxcontrib-httpdomain
+.. _Jinja: http://pypi.python.org/pypi/sphinxcontrib-jinjadomain
+.. _Scala: http://pypi.python.org/pypi/sphinxcontrib-scaladomain
+.. _PHP: http://pypi.python.org/pypi/sphinxcontrib-phpdomain
diff --git a/doc/ext/linkcode.rst b/doc/ext/linkcode.rst
new file mode 100644
index 000000000..a69a5b1c5
--- /dev/null
+++ b/doc/ext/linkcode.rst
@@ -0,0 +1,46 @@
+:mod:`sphinx.ext.linkcode` -- Add external links to source code
+===============================================================
+
+.. module:: sphinx.ext.linkcode
+ :synopsis: Add external links to source code.
+.. moduleauthor:: Pauli Virtanen
+
+.. versionadded:: 1.2
+
+This extension looks at your object descriptions (``.. class::``,
+``.. function::`` etc.) and adds external links to code hosted
+somewhere on the web. The intent is similar to the
+``sphinx.ext.viewcode`` extension, but assumes the source code can be
+found somewhere on the Internet.
+
+In your configuration, you need to specify a :confval:`linkcode_resolve`
+function that returns an URL based on the object.
+
+.. confval:: linkcode_resolve
+
+ This is a function ``linkcode_resolve(domain, info)``,
+ which should return the URL to source code corresponding to
+ the object in given domain with given information.
+
+ The function should return ``None`` if no link is to be added.
+
+ The argument ``domain`` specifies the language domain the object is
+ in. ``info`` is a dictionary with the following keys guaranteed to
+ be present (dependent on the domain):
+
+ - ``py``: ``module`` (name of the module), ``fullname`` (name of the object)
+ - ``c``: ``names`` (list of names for the object)
+ - ``cpp``: ``names`` (list of names for the object)
+ - ``javascript``: ``object`` (name of the object), ``fullname`` (name of the item)
+
+ Example:
+
+ .. code-block:: python
+
+ def linkcode_resolve(domain, info):
+ if domain != 'py':
+ return None
+ if not info['module']:
+ return None
+ filename = info['module'].replace('.', '/')
+ return "http://somesite/sourcerepo/%s.py" % filename
diff --git a/doc/ext/math.rst b/doc/ext/math.rst
index 3652b55e8..91376d15d 100644
--- a/doc/ext/math.rst
+++ b/doc/ext/math.rst
@@ -148,19 +148,12 @@ built:
.. confval:: pngmath_dvipng_args
Additional arguments to give to dvipng, as a list. The default value is
- ``['-gamma 1.5', '-D 110']`` which makes the image a bit darker and larger
- then it is by default.
+ ``['-gamma', '1.5', '-D', '110', '-bg', 'Transparent']`` which makes the
+ image a bit darker and larger then it is by default, and produces PNGs with a
+ transparent background.
- An arguments you might want to add here is e.g. ``'-bg Transparent'``,
- which produces PNGs with a transparent background. This is not enabled by
- default because some Internet Explorer versions don't like transparent PNGs.
-
- .. note::
-
- When you "add" an argument, you need to reproduce the default arguments if
- you want to keep them; that is, like this::
-
- pngmath_dvipng_args = ['-gamma 1.5', '-D 110', '-bg Transparent']
+ .. versionchanged:: 1.2
+ Now includes ``-bg Transparent`` by default.
.. confval:: pngmath_use_preview
diff --git a/doc/extensions.rst b/doc/extensions.rst
index b93974486..07bc7fe4b 100644
--- a/doc/extensions.rst
+++ b/doc/extensions.rst
@@ -53,6 +53,7 @@ These extensions are built in and can be activated by respective entries in the
ext/todo
ext/extlinks
ext/viewcode
+ ext/linkcode
ext/oldcmarkup
diff --git a/doc/faq.rst b/doc/faq.rst
index bf2327d70..1d9487377 100644
--- a/doc/faq.rst
+++ b/doc/faq.rst
@@ -36,6 +36,13 @@ How do I...
still need to mark up classes and such, but the headings and code examples
come through cleanly.
+... create HTML slides from Sphinx documents?
+ See the "Hieroglyph" package at http://github.com/nyergler/hieroglyph.
+
+For many more extensions and other contributed stuff, see the sphinx-contrib_
+repository.
+
+.. _sphinx-contrib: https://bitbucket.org/birkenfeld/sphinx-contrib/
.. _usingwith:
diff --git a/doc/install.rst b/doc/install.rst
new file mode 100644
index 000000000..3af60dad5
--- /dev/null
+++ b/doc/install.rst
@@ -0,0 +1,166 @@
+:orphan:
+
+Installing Sphinx
+==================
+
+Sphinx is written by Python, you need to install Python and Sphinx.
+
+Sphinx package is available as a package on the `Python Package Index
+`_.
+
+You can also download a snapshot from the Mercurial development repository:
+
+* as a `.tar.bz2 `_
+ file or
+* as a `.zip `_ file
+
+There is introductions for each environments:
+
+.. contents::
+ :depth: 1
+ :local:
+ :backlinks: none
+
+
+Install by your own
+--------------------
+
+If you use system installed Python or build your own Python, you can
+use that python to install Sphinx. The actual command list is same as
+these install.
+
+* `Install easy_install command`_
+* `Install Sphinx`_
+
+
+Debian/Ubuntu: Install Sphinx using packaging system
+-----------------------------------------------------
+
+You may install using this command if you use Debian/Ubuntu.
+
+.. code-block:: bash
+
+ $ aptitude install python-sphinx
+
+
+Mac OS X: Install Sphinx using MacPorts
+----------------------------------------
+
+If you use Mac OS X `MacPorts `_ , use this
+command to install all software.
+
+.. code-block:: bash
+
+ $ sudo port install py27-sphinx
+
+However, the execution path is not added, use select command to use
+Python2.7 as default.
+
+.. code-block:: bash
+
+ $ sudo port select --set python python27
+ $ sudo port select --set sphinx py27-sphinx
+
+Type :command:`which sphinx-quickstart` to check the installation.
+
+
+Windows: Install Python and Sphinx
+-----------------------------------
+
+Intall Python
+^^^^^^^^^^^^^^
+
+Almost every Windows user do not have Python, we begin Python
+installation. If you already install python, please skip this section.
+
+Go to http://python.org . This site is a headquarter of the
+Python. Look at Left sidebar and "Quick Links", Click "Windows
+Installer" to download.
+
+.. image:: pythonorg.jpg
+
+.. note::
+
+ Currently, Python has two version, 2.X and 3.X. Sphinx-1.2 can
+ run under Python-2.5, 2.6, 2.7, 3.1, 3.2, 3.3.
+ You may get some advice from ML or other places.
+
+ This chapter assumes Python-2.7.
+
+
+Follow the normal Windows installer, the Python install will be completed.
+
+.. image:: installpython.jpg
+
+After installation, you have better to add PATH to the Environment
+Variable in order to run Python from Command Prompt.
+
+* Right-Click the My Computer Icon and open Property Dialog
+* Click Environment Variable button under detail tab
+* Edit and add the path to the system variables PATH
+
+Add these variables. This is for Python-2.7. If you use another version
+of Python, change the "27" number. Add these pathes separeted by ";".
+
+.. list-table:: Adding PATH
+ :widths: 10 40
+ :header-rows: 1
+
+ * - PATH
+ - description
+ * - C:\\Python27
+ - Folder which includes Python Command
+ * - C:\\Python27\\Scripts
+ - Folder which includes easy_install (described later) or sphinx commands
+
+Run **Command Prompt** or enter ``cmd`` to the "search program and
+files" text box. After command prompt window appear, type
+``python[Enter]``. If you can get installed python version and prompt
+about ``>>>``, the Python installation is succeeded. Enter ``Ctrl+Z``
+key to quit.
+
+
+Install easy_install command
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Python has very useful :command:`easy_install` command which install 3rd
+party library.
+
+* http://pypi.python.org/pypi/distribute
+
+easy_install downloads and install software which you want to need by only
+one command.
+
+
+Save http://distribute.org/distribute_setup.py link by Right-click.
+Some browsers can download just open the URL.
+If you can read the file iteslf, calm down, Right-click and choose "Save".
+
+After download, invoke command prompt, go to the distribute_setup.py saved
+directory and run this command:
+
+.. code-block:: bat
+
+ C:\> python distribute_setup.py
+
+Now :command:`easy_insall` command is installed. OK, Let's go to the Sphinx
+install!
+
+
+Install Sphinx
+^^^^^^^^^^^^^^^
+
+If you finshed easy_install install, for the rest is just a moment.
+Type this line.
+
+.. code-block:: bat
+
+ C:\> easy_install sphinx
+
+After installation, type :command:`sphinx-quickstart` on the command
+prompt. If you get interactive messages which starts with
+``Welcome to the Sphinx quickstart utility.``,
+installation is succeeded. Quit by hitting ``Ctrl+C``.
+
+That it. Install is over. Let's go to :doc:`tutorial` to make Sphinx project.
+
diff --git a/doc/installpython.jpg b/doc/installpython.jpg
new file mode 100644
index 000000000..b0e458e40
Binary files /dev/null and b/doc/installpython.jpg differ
diff --git a/doc/intro.rst b/doc/intro.rst
index 5d76dd29c..4d052c818 100644
--- a/doc/intro.rst
+++ b/doc/intro.rst
@@ -50,19 +50,15 @@ See the :ref:`pertinent section in the FAQ list `.
Prerequisites
-------------
-Sphinx needs at least **Python 2.4** or **Python 3.1** to run, as well as the
+Sphinx needs at least **Python 2.5** or **Python 3.1** to run, as well as the
docutils_ and Jinja2_ libraries. Sphinx should work with docutils version 0.7
or some (not broken) SVN trunk snapshot. If you like to have source code
highlighting support, you must also install the Pygments_ library.
-If you use **Python 2.4** you also need uuid_.
-
.. _reStructuredText: http://docutils.sf.net/rst.html
.. _docutils: http://docutils.sf.net/
.. _Jinja2: http://jinja.pocoo.org/
.. _Pygments: http://pygments.org/
-.. The given homepage is only a directory listing so I'm using the pypi site.
-.. _uuid: http://pypi.python.org/pypi/uuid/
Usage
diff --git a/doc/invocation.rst b/doc/invocation.rst
index 2c69d32f9..4cfa48433 100644
--- a/doc/invocation.rst
+++ b/doc/invocation.rst
@@ -59,6 +59,13 @@ The :program:`sphinx-build` script has several options:
**linkcheck**
Check the integrity of all external links.
+ **xml**
+ Build Docutils-native XML files.
+
+ **pseudoxml**
+ Build compact pretty-printed "pseudo-XML" files displaying the
+ internal structure of the intermediate document trees.
+
See :ref:`builders` for a list of all builders shipped with Sphinx.
Extensions can add their own builders.
@@ -131,6 +138,13 @@ The :program:`sphinx-build` script has several options:
Do not emit colored output. (On Windows, colored output is disabled in any
case.)
+.. option:: -v
+
+ Increase verbosity. This option can be given up to three times to get more
+ debug output. It implies :option:`-T`.
+
+ .. versionadded:: 1.2
+
.. option:: -q
Do not output anything on standard output, only write warnings and errors to
@@ -150,11 +164,24 @@ The :program:`sphinx-build` script has several options:
Turn warnings into errors. This means that the build stops at the first
warning and ``sphinx-build`` exits with exit status 1.
+.. option:: -T
+
+ Display the full traceback when an unhandled exception occurs. Otherwise,
+ only a summary is displayed and the traceback information is saved to a file
+ for further analysis.
+
+ .. versionadded:: 1.2
+
.. option:: -P
(Useful for debugging only.) Run the Python debugger, :mod:`pdb`, if an
unhandled exception occurs while building.
+.. option:: -h, --help, --version
+
+ Display usage summary or Sphinx version.
+
+ .. versionadded:: 1.2
You can also give one or more filenames on the command line after the source and
build directories. Sphinx will then try to build only these output files (and
@@ -227,6 +254,17 @@ The :program:`sphinx-apidoc` script has several options:
This sets the maximum depth of the table of contents, if one is generated.
+.. option:: -l, --follow-links
+
+ This option makes sphinx-apidoc follow symbolic links when recursing the
+ filesystem to discover packages and modules. You may need it if you want
+ to generate documentation from a source directory managed by
+ `collective.recipe.omelette
+ `_.
+ By default, symbolic links are skipped.
+
+ .. versionadded:: 1.2
+
.. option:: -T, --no-toc
This prevents the generation of a table-of-contents file ``modules.rst``.
diff --git a/doc/man/sphinx-build.rst b/doc/man/sphinx-build.rst
index b7212a84b..0a5d4abb9 100644
--- a/doc/man/sphinx-build.rst
+++ b/doc/man/sphinx-build.rst
@@ -64,6 +64,13 @@ linkcheck
pickle / json
Generates serialized HTML files for use in web applications.
+xml
+ Generates Docutils-native XML files.
+
+pseudoxml
+ Generates compact pretty-printed "pseudo-XML" files displaying the
+ internal structure of the intermediate document trees.
+
Options
-------
diff --git a/doc/markup/misc.rst b/doc/markup/misc.rst
index 3a2ffa32e..f5eaac9ce 100644
--- a/doc/markup/misc.rst
+++ b/doc/markup/misc.rst
@@ -182,13 +182,6 @@ Including content based on tags
The format of the current builder (``html``, ``latex`` or ``text``) is always
set as a tag.
- .. note::
-
- Due to docutils' specifics of parsing of directive content, you cannot put
- a section with the same level as the main document heading inside an
- ``only`` directive. Such sections will appear to be ignored in the parsed
- document.
-
.. versionadded:: 0.6
diff --git a/doc/markup/toctree.rst b/doc/markup/toctree.rst
index 625fbb611..fe27e019d 100644
--- a/doc/markup/toctree.rst
+++ b/doc/markup/toctree.rst
@@ -126,6 +126,18 @@ tables of contents. The ``toctree`` directive is the central element.
intend to insert these links yourself, in a different style, or in the HTML
sidebar.
+ In cases where you want to have only one top-level toctree and hide all other
+ lower level toctrees you can add the "includehidden" option to the top-level
+ toctree entry::
+
+ .. toctree::
+ :includehidden:
+
+ doc_1
+ doc_2
+
+ All other toctree entries can then be eliminated by the "hidden" option.
+
In the end, all documents in the :term:`source directory` (or subdirectories)
must occur in some ``toctree`` directive; Sphinx will emit a warning if it
finds a file that is not included, because that means that this file will not
@@ -150,6 +162,8 @@ tables of contents. The ``toctree`` directive is the central element.
.. versionchanged:: 1.1
Added numeric argument to "numbered".
+ .. versionchanged:: 1.2
+ Added "includehidden" option.
Special names
-------------
diff --git a/doc/pythonorg.jpg b/doc/pythonorg.jpg
new file mode 100644
index 000000000..83b54df0b
Binary files /dev/null and b/doc/pythonorg.jpg differ
diff --git a/doc/templating.rst b/doc/templating.rst
index 05a1346c0..b9dfc683b 100644
--- a/doc/templating.rst
+++ b/doc/templating.rst
@@ -391,3 +391,6 @@ are in HTML form), these variables are also available:
* ``titles_only`` (false by default): if true, put only toplevel document
titles in the tree
+
+ * ``includehidden`` (false by default): if true, the TOC tree will also
+ contain hidden entries.
diff --git a/doc/theming.rst b/doc/theming.rst
index 334f6ffe7..0375bc710 100644
--- a/doc/theming.rst
+++ b/doc/theming.rst
@@ -111,8 +111,7 @@ These themes are:
- **collapsiblesidebar** (true or false): Add an *experimental* JavaScript
snippet that makes the sidebar collapsible via a button on its side.
- *Doesn't work together with "rightsidebar" or "stickysidebar".* Defaults to
- false.
+ *Doesn't work with "stickysidebar".* Defaults to false.
- **externalrefs** (true or false): Display external links differently from
internal links. Defaults to false.
diff --git a/doc/tutorial.rst b/doc/tutorial.rst
index b0e1d4869..9fea11dbe 100644
--- a/doc/tutorial.rst
+++ b/doc/tutorial.rst
@@ -13,10 +13,10 @@ the described task.
Setting up the documentation sources
------------------------------------
-The root directory of a documentation collection is called the :term:`source
-directory`. This directory also contains the Sphinx configuration file
-:file:`conf.py`, where you can configure all aspects of how Sphinx reads your
-sources and builds your documentation. [#]_
+The root directory of a Sphinx collection of reStructuredText document sources
+is called the :term:`source directory`. This directory also contains the Sphinx
+configuration file :file:`conf.py`, where you can configure all aspects of how
+Sphinx reads your sources and builds your documentation. [#]_
Sphinx comes with a script called :program:`sphinx-quickstart` that sets up a
source directory and creates a default :file:`conf.py` with the most useful
diff --git a/setup.cfg b/setup.cfg
index 525e24ea3..5dac60b4d 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -4,6 +4,7 @@ tag_date = true
[aliases]
release = egg_info -RDb ''
+upload = upload --sign --identity=36580288
[extract_messages]
mapping_file = babel.cfg
diff --git a/setup.py b/setup.py
index ca41aa431..03f8e8e14 100644
--- a/setup.py
+++ b/setup.py
@@ -46,26 +46,20 @@ A development egg can be found `here
requires = ['Pygments>=1.2', 'Jinja2>=2.3', 'docutils>=0.7']
-if sys.version_info < (2, 4):
- print('ERROR: Sphinx requires at least Python 2.4 to run.')
- sys.exit(1)
+if sys.version_info[:3] >= (3, 3, 0):
+ requires[2] = 'docutils>=0.10'
if sys.version_info < (2, 5):
- # Python 2.4's distutils doesn't automatically install an egg-info,
- # so an existing docutils install won't be detected -- in that case,
- # remove the dependency from setup.py
- try:
- import docutils
- if int(docutils.__version__[2]) < 4:
- raise ValueError('docutils not recent enough')
- except:
- pass
- else:
- del requires[-1]
-
- # The uuid module is new in the stdlib in 2.5
- requires.append('uuid>=1.30')
+ print('ERROR: Sphinx requires at least Python 2.5 to run.')
+ sys.exit(1)
+# tell distribute to use 2to3 with our own fixers
+extra = {}
+if sys.version_info >= (3, 0):
+ extra.update(
+ use_2to3=True,
+ use_2to3_fixers=['custom_fixers']
+ )
# Provide a "compile_catalog" command that also creates the translated
# JavaScript files if Babel is available.
@@ -164,7 +158,7 @@ else:
setup(
name='Sphinx',
version=sphinx.__version__,
- url='http://sphinx.pocoo.org/',
+ url='http://sphinx-doc.org/',
download_url='http://pypi.python.org/pypi/Sphinx',
license='BSD',
author='Georg Brandl',
@@ -203,6 +197,5 @@ setup(
},
install_requires=requires,
cmdclass=cmdclass,
- use_2to3=True,
- use_2to3_fixers=['custom_fixers'],
+ **extra
)
diff --git a/sphinx-apidoc.py b/sphinx-apidoc.py
index 9cafb497d..31e57487f 100755
--- a/sphinx-apidoc.py
+++ b/sphinx-apidoc.py
@@ -4,7 +4,7 @@
Sphinx - Python documentation toolchain
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx-autogen.py b/sphinx-autogen.py
index b1e467e77..baf3ed65e 100755
--- a/sphinx-autogen.py
+++ b/sphinx-autogen.py
@@ -4,7 +4,7 @@
Sphinx - Python documentation toolchain
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx-build.py b/sphinx-build.py
index 70822d732..6737d0729 100755
--- a/sphinx-build.py
+++ b/sphinx-build.py
@@ -4,7 +4,7 @@
Sphinx - Python documentation toolchain
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx-quickstart.py b/sphinx-quickstart.py
index 5d7c15443..0ae67adfc 100755
--- a/sphinx-quickstart.py
+++ b/sphinx-quickstart.py
@@ -4,7 +4,7 @@
Sphinx - Python documentation toolchain
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/__init__.py b/sphinx/__init__.py
index e1ffc2ed6..0c68ad322 100644
--- a/sphinx/__init__.py
+++ b/sphinx/__init__.py
@@ -5,7 +5,7 @@
The Sphinx documentation toolchain.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -38,11 +38,20 @@ if '+' in __version__ or 'pre' in __version__:
def main(argv=sys.argv):
"""Sphinx build "main" command-line entry."""
- if sys.version_info[:3] < (2, 4, 0):
+ if sys.version_info[:3] < (2, 5, 0):
sys.stderr.write('Error: Sphinx requires at least '
- 'Python 2.4 to run.\n')
+ 'Python 2.5 to run.\n')
return 1
-
+ if sys.version_info[:3] >= (3, 3, 0):
+ try:
+ import docutils
+ x, y = docutils.__version__.split('.')[:2]
+ if (int(x), int(y)) < (0, 10):
+ sys.stderr.write('Error: Sphinx requires at least '
+ 'Docutils 0.10 for Python 3.3 and above.\n')
+ return 1
+ except Exception:
+ pass
try:
from sphinx import cmdline
except ImportError:
diff --git a/sphinx/addnodes.py b/sphinx/addnodes.py
index 94f1d615f..d8dca2ce4 100644
--- a/sphinx/addnodes.py
+++ b/sphinx/addnodes.py
@@ -5,7 +5,7 @@
Additional docutils nodes.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/apidoc.py b/sphinx/apidoc.py
index ec1a8a33f..151608288 100644
--- a/sphinx/apidoc.py
+++ b/sphinx/apidoc.py
@@ -11,7 +11,7 @@
Copyright 2008 Société des arts technologiques (SAT),
http://www.sat.qc.ca/
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import os
@@ -157,7 +157,8 @@ def recurse_tree(rootpath, excludes, opts):
root_package = None
toplevels = []
- for root, subs, files in os.walk(rootpath):
+ followlinks = getattr(opts, 'followlinks', False)
+ for root, subs, files in os.walk(rootpath, followlinks=followlinks):
if is_excluded(root, excludes):
del subs[:]
continue
@@ -246,6 +247,10 @@ Note: By default this script will not overwrite already created files.""")
'(default: 4)', type='int', default=4)
parser.add_option('-f', '--force', action='store_true', dest='force',
help='Overwrite all files')
+ parser.add_option('-l', '--follow-links', action='store_true',
+ dest='followlinks', default=False,
+ help='Follow symbolic links. Powerful when combined '
+ 'with collective.recipe.omelette.')
parser.add_option('-n', '--dry-run', action='store_true', dest='dryrun',
help='Run the script without creating files')
parser.add_option('-T', '--no-toc', action='store_true', dest='notoc',
diff --git a/sphinx/application.py b/sphinx/application.py
index bfb39a70f..c2ebb535e 100644
--- a/sphinx/application.py
+++ b/sphinx/application.py
@@ -7,10 +7,11 @@
Gracefully adapted from the TextPress system by Armin.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
+import os
import sys
import types
import posixpath
@@ -34,7 +35,7 @@ from sphinx.environment import BuildEnvironment, SphinxStandaloneReader
from sphinx.util import pycompat # imported for side-effects
from sphinx.util.tags import Tags
from sphinx.util.osutil import ENOENT
-from sphinx.util.console import bold
+from sphinx.util.console import bold, lightgray, darkgray
# List of all known core events. Maps name to arguments description.
@@ -60,7 +61,8 @@ class Sphinx(object):
def __init__(self, srcdir, confdir, outdir, doctreedir, buildername,
confoverrides=None, status=sys.stdout, warning=sys.stderr,
- freshenv=False, warningiserror=False, tags=None):
+ freshenv=False, warningiserror=False, tags=None, verbosity=0):
+ self.verbosity = verbosity
self.next_listener_id = 0
self._extensions = {}
self._listeners = {}
@@ -194,6 +196,8 @@ class Sphinx(object):
self.builder = builderclass(self)
self.emit('builder-inited')
+ # ---- main "build" method -------------------------------------------------
+
def build(self, force_all=False, filenames=None):
try:
if force_all:
@@ -203,12 +207,29 @@ class Sphinx(object):
else:
self.builder.build_update()
except Exception, err:
+ # delete the saved env to force a fresh build next time
+ envfile = path.join(self.doctreedir, ENV_PICKLE_FILENAME)
+ if path.isfile(envfile):
+ os.unlink(envfile)
self.emit('build-finished', err)
raise
else:
self.emit('build-finished', None)
self.builder.cleanup()
+ # ---- logging handling ----------------------------------------------------
+
+ def _log(self, message, wfile, nonl=False):
+ try:
+ wfile.write(message)
+ except UnicodeEncodeError:
+ encoding = getattr(wfile, 'encoding', 'ascii') or 'ascii'
+ wfile.write(message.encode(encoding, 'replace'))
+ if not nonl:
+ wfile.write('\n')
+ if hasattr(wfile, 'flush'):
+ wfile.flush()
+
def warn(self, message, location=None, prefix='WARNING: '):
if isinstance(location, tuple):
docname, lineno = location
@@ -221,26 +242,37 @@ class Sphinx(object):
if self.warningiserror:
raise SphinxWarning(warntext)
self._warncount += 1
- try:
- self._warning.write(warntext)
- except UnicodeEncodeError:
- encoding = getattr(self._warning, 'encoding', 'ascii') or 'ascii'
- self._warning.write(warntext.encode(encoding, 'replace'))
+ self._log(warntext, self._warning, True)
def info(self, message='', nonl=False):
- try:
- self._status.write(message)
- except UnicodeEncodeError:
- encoding = getattr(self._status, 'encoding', 'ascii') or 'ascii'
- self._status.write(message.encode(encoding, 'replace'))
- if not nonl:
- self._status.write('\n')
- self._status.flush()
+ self._log(message, self._status, nonl)
- # general extensibility interface
+ def verbose(self, message, *args, **kwargs):
+ if self.verbosity < 1:
+ return
+ if args or kwargs:
+ message = message % (args or kwargs)
+ self._log(message, self._status)
+
+ def debug(self, message, *args, **kwargs):
+ if self.verbosity < 2:
+ return
+ if args or kwargs:
+ message = message % (args or kwargs)
+ self._log(darkgray(message), self._status)
+
+ def debug2(self, message, *args, **kwargs):
+ if self.verbosity < 3:
+ return
+ if args or kwargs:
+ message = message % (args or kwargs)
+ self._log(lightgray(message), self._status)
+
+ # ---- general extensibility interface -------------------------------------
def setup_extension(self, extension):
"""Import and setup a Sphinx extension module. No-op if called twice."""
+ self.debug('[app] setting up extension: %r', extension)
if extension in self._extensions:
return
try:
@@ -301,13 +333,17 @@ class Sphinx(object):
else:
self._listeners[event][listener_id] = callback
self.next_listener_id += 1
+ self.debug('[app] connecting event %r: %r [id=%s]',
+ event, callback, listener_id)
return listener_id
def disconnect(self, listener_id):
+ self.debug('[app] disconnecting event: [id=%s]', listener_id)
for event in self._listeners.itervalues():
event.pop(listener_id, None)
def emit(self, event, *args):
+ self.debug2('[app] emitting event: %r%s', event, repr(args)[:100])
results = []
if event in self._listeners:
for _, callback in self._listeners[event].iteritems():
@@ -323,6 +359,7 @@ class Sphinx(object):
# registering addon parts
def add_builder(self, builder):
+ self.debug('[app] adding builder: %r', builder)
if not hasattr(builder, 'name'):
raise ExtensionError('Builder class %s has no "name" attribute'
% builder)
@@ -337,6 +374,7 @@ class Sphinx(object):
self.builderclasses[builder.name] = builder
def add_config_value(self, name, default, rebuild):
+ self.debug('[app] adding config value: %r', (name, default, rebuild))
if name in self.config.values:
raise ExtensionError('Config value %r already present' % name)
if rebuild in (False, True):
@@ -344,11 +382,13 @@ class Sphinx(object):
self.config.values[name] = (default, rebuild)
def add_event(self, name):
+ self.debug('[app] adding event: %r', name)
if name in self._events:
raise ExtensionError('Event %r already present' % name)
self._events[name] = ''
def add_node(self, node, **kwds):
+ self.debug('[app] adding node: %r', (node, kwds))
nodes._add_node_class_names([node.__name__])
for key, val in kwds.iteritems():
try:
@@ -388,44 +428,54 @@ class Sphinx(object):
return obj
def add_directive(self, name, obj, content=None, arguments=None, **options):
+ self.debug('[app] adding directive: %r',
+ (name, obj, content, arguments, options))
directives.register_directive(
name, self._directive_helper(obj, content, arguments, **options))
def add_role(self, name, role):
+ self.debug('[app] adding role: %r', (name, role))
roles.register_local_role(name, role)
def add_generic_role(self, name, nodeclass):
# don't use roles.register_generic_role because it uses
# register_canonical_role
+ self.debug('[app] adding generic role: %r', (name, nodeclass))
role = roles.GenericRole(name, nodeclass)
roles.register_local_role(name, role)
def add_domain(self, domain):
+ self.debug('[app] adding domain: %r', domain)
if domain.name in self.domains:
raise ExtensionError('domain %s already registered' % domain.name)
self.domains[domain.name] = domain
def override_domain(self, domain):
+ self.debug('[app] overriding domain: %r', domain)
if domain.name not in self.domains:
raise ExtensionError('domain %s not yet registered' % domain.name)
if not issubclass(domain, self.domains[domain.name]):
- raise ExtensionError('new domain not a subclass of registered '
+ raise ExtensionError('new domain not a subclass of registered %s '
'domain' % domain.name)
self.domains[domain.name] = domain
def add_directive_to_domain(self, domain, name, obj,
content=None, arguments=None, **options):
+ self.debug('[app] adding directive to domain: %r',
+ (domain, name, obj, content, arguments, options))
if domain not in self.domains:
raise ExtensionError('domain %s not yet registered' % domain)
self.domains[domain].directives[name] = \
self._directive_helper(obj, content, arguments, **options)
def add_role_to_domain(self, domain, name, role):
+ self.debug('[app] adding role to domain: %r', (domain, name, role))
if domain not in self.domains:
raise ExtensionError('domain %s not yet registered' % domain)
self.domains[domain].roles[name] = role
def add_index_to_domain(self, domain, index):
+ self.debug('[app] adding index to domain: %r', (domain, index))
if domain not in self.domains:
raise ExtensionError('domain %s not yet registered' % domain)
self.domains[domain].indices.append(index)
@@ -433,6 +483,9 @@ class Sphinx(object):
def add_object_type(self, directivename, rolename, indextemplate='',
parse_node=None, ref_nodeclass=None, objname='',
doc_field_types=[]):
+ self.debug('[app] adding object type: %r',
+ (directivename, rolename, indextemplate, parse_node,
+ ref_nodeclass, objname, doc_field_types))
StandardDomain.object_types[directivename] = \
ObjType(objname or directivename, rolename)
# create a subclass of GenericObject as the new directive
@@ -449,6 +502,9 @@ class Sphinx(object):
def add_crossref_type(self, directivename, rolename, indextemplate='',
ref_nodeclass=None, objname=''):
+ self.debug('[app] adding crossref type: %r',
+ (directivename, rolename, indextemplate, ref_nodeclass,
+ objname))
StandardDomain.object_types[directivename] = \
ObjType(objname or directivename, rolename)
# create a subclass of Target as the new directive
@@ -459,9 +515,11 @@ class Sphinx(object):
StandardDomain.roles[rolename] = XRefRole(innernodeclass=ref_nodeclass)
def add_transform(self, transform):
+ self.debug('[app] adding transform: %r', transform)
SphinxStandaloneReader.transforms.append(transform)
def add_javascript(self, filename):
+ self.debug('[app] adding javascript: %r', filename)
from sphinx.builders.html import StandaloneHTMLBuilder
if '://' in filename:
StandaloneHTMLBuilder.script_files.append(filename)
@@ -470,6 +528,7 @@ class Sphinx(object):
posixpath.join('_static', filename))
def add_stylesheet(self, filename):
+ self.debug('[app] adding stylesheet: %r', filename)
from sphinx.builders.html import StandaloneHTMLBuilder
if '://' in filename:
StandaloneHTMLBuilder.css_files.append(filename)
@@ -478,21 +537,25 @@ class Sphinx(object):
posixpath.join('_static', filename))
def add_lexer(self, alias, lexer):
+ self.debug('[app] adding lexer: %r', (alias, lexer))
from sphinx.highlighting import lexers
if lexers is None:
return
lexers[alias] = lexer
def add_autodocumenter(self, cls):
+ self.debug('[app] adding autodocumenter: %r', cls)
from sphinx.ext import autodoc
autodoc.add_documenter(cls)
self.add_directive('auto' + cls.objtype, autodoc.AutoDirective)
def add_autodoc_attrgetter(self, type, getter):
+ self.debug('[app] adding autodoc attrgetter: %r', (type, getter))
from sphinx.ext import autodoc
autodoc.AutoDirective._special_attrgetters[type] = getter
def add_search_language(self, cls):
+ self.debug('[app] adding search language: %r', cls)
from sphinx.search import languages, SearchLanguage
assert isinstance(cls, SearchLanguage)
languages[cls.lang] = cls
diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py
index 5240a1c73..97932c4c7 100644
--- a/sphinx/builders/__init__.py
+++ b/sphinx/builders/__init__.py
@@ -5,7 +5,7 @@
Builder superclass for all builders.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -119,9 +119,13 @@ class Builder(object):
summary = bold(summary)
for item in iterable:
l += 1
- self.info(term_width_line('%s[%3d%%] %s' %
- (summary, 100*l/length,
- colorfunc(item))), nonl=1)
+ s = '%s[%3d%%] %s' % (summary, 100*l/length,
+ colorfunc(item))
+ if self.app.verbosity:
+ s += '\n'
+ else:
+ s = term_width_line(s)
+ self.info(s, nonl=1)
yield item
if l > 0:
self.info()
@@ -334,4 +338,6 @@ BUILTIN_BUILDERS = {
'linkcheck': ('linkcheck', 'CheckExternalLinksBuilder'),
'websupport': ('websupport', 'WebSupportBuilder'),
'gettext': ('gettext', 'MessageCatalogBuilder'),
+ 'xml': ('xml', 'XMLBuilder'),
+ 'pseudoxml': ('xml', 'PseudoXMLBuilder'),
}
diff --git a/sphinx/builders/changes.py b/sphinx/builders/changes.py
index 22fcab869..05e7166ed 100644
--- a/sphinx/builders/changes.py
+++ b/sphinx/builders/changes.py
@@ -5,7 +5,7 @@
Changelog builder.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/builders/devhelp.py b/sphinx/builders/devhelp.py
index 62bd22377..81d2e6cee 100644
--- a/sphinx/builders/devhelp.py
+++ b/sphinx/builders/devhelp.py
@@ -7,9 +7,10 @@
.. _Devhelp: http://live.gnome.org/devhelp
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
+from __future__ import absolute_import
import re
from os import path
diff --git a/sphinx/builders/epub.py b/sphinx/builders/epub.py
index b4c3b2776..6803753bf 100644
--- a/sphinx/builders/epub.py
+++ b/sphinx/builders/epub.py
@@ -6,7 +6,7 @@
Build epub files.
Originally derived from qthelp.py.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -30,7 +30,7 @@ from docutils import nodes
from sphinx import addnodes
from sphinx.builders.html import StandaloneHTMLBuilder
-from sphinx.util.osutil import ensuredir, EEXIST
+from sphinx.util.osutil import ensuredir, copyfile, EEXIST
from sphinx.util.smartypants import sphinx_smarty_pants as ssp
from sphinx.util.console import brown
@@ -281,8 +281,11 @@ class EpubBuilder(StandaloneHTMLBuilder):
newids.append(self.fix_fragment('', id))
node.attributes['ids'] = newids
- def add_visible_links(self, tree):
- """Append visible link targets after external links."""
+ def add_visible_links(self, tree, show_urls='inline'):
+ """Append visible link targets after external links"""
+ if show_urls == 'no':
+ return
+
for node in tree.traverse(nodes.reference):
uri = node.get('refuri', '')
if (uri.startswith('http:') or uri.startswith('https:') or
@@ -290,9 +293,10 @@ class EpubBuilder(StandaloneHTMLBuilder):
uri = _link_target_template % {'uri': uri}
if uri:
idx = node.parent.index(node) + 1
- link = nodes.inline(uri, uri)
- link['classes'].append(_css_link_target_class)
- node.parent.insert(idx, link)
+ if show_urls == 'inline':
+ link = nodes.inline(uri, uri)
+ link['classes'].append(_css_link_target_class)
+ node.parent.insert(idx, link)
def write_doc(self, docname, doctree):
"""Write one document file.
@@ -301,7 +305,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
and to add visible external links.
"""
self.fix_ids(doctree)
- self.add_visible_links(doctree)
+ self.add_visible_links(doctree, self.config.epub_show_urls)
return StandaloneHTMLBuilder.write_doc(self, docname, doctree)
def fix_genindex(self, tree):
@@ -662,7 +666,12 @@ class EpubBuilder(StandaloneHTMLBuilder):
zipfile.ZIP_STORED)
for file in projectfiles:
fp = path.join(outdir, file)
- if isinstance(fp, unicode):
- fp = fp.encode(sys.getfilesystemencoding())
+ if sys.version_info < (2, 6):
+ # When zipile.ZipFile.write call with unicode filename, ZipFile
+ # encode filename to 'utf-8' (only after Python-2.6).
+ if isinstance(file, unicode):
+ # OEBPS Container Format (OCF) 2.0.1 specification require
+ # "File Names MUST be UTF-8 encoded".
+ file = file.encode('utf-8')
epub.write(fp, file, zipfile.ZIP_DEFLATED)
epub.close()
diff --git a/sphinx/builders/gettext.py b/sphinx/builders/gettext.py
index a5f71a5f6..7a6e1abe6 100644
--- a/sphinx/builders/gettext.py
+++ b/sphinx/builders/gettext.py
@@ -5,7 +5,7 @@
The MessageCatalogBuilder class.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -15,9 +15,11 @@ from datetime import datetime
from collections import defaultdict
from sphinx.builders import Builder
-from sphinx.util.nodes import extract_messages
-from sphinx.util.osutil import SEP, safe_relpath, ensuredir, find_catalog
+from sphinx.util import split_index_msg
+from sphinx.util.nodes import extract_messages, traverse_translatable_index
+from sphinx.util.osutil import safe_relpath, ensuredir, find_catalog
from sphinx.util.console import darkgreen
+from sphinx.locale import pairindextypes
POHEADER = ur"""
# SOME DESCRIPTIVE TITLE.
@@ -82,6 +84,16 @@ class I18nBuilder(Builder):
for node, msg in extract_messages(doctree):
catalog.add(msg, node)
+ # Extract translatable messages from index entries.
+ for node, entries in traverse_translatable_index(doctree):
+ for typ, msg, tid, main in entries:
+ for m in split_index_msg(typ, msg):
+ if typ == 'pair' and m in pairindextypes.values():
+ # avoid built-in translated message was incorporated
+ # in 'sphinx.util.nodes.process_index_entry'
+ continue
+ catalog.add(m, node)
+
class MessageCatalogBuilder(I18nBuilder):
"""
diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py
index f5218673f..1d1dfa799 100644
--- a/sphinx/builders/html.py
+++ b/sphinx/builders/html.py
@@ -5,7 +5,7 @@
Several HTML builders.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -53,6 +53,23 @@ INVENTORY_FILENAME = 'objects.inv'
LAST_BUILD_FILENAME = 'last_build'
+def get_object_hash(obj):
+ """
+ In python3.3, unicode(dict_instance) retun another string per process.
+ get_object_hash(dict_instance) return same hash for same dict_instance.
+ """
+ if isinstance(obj, dict):
+ return get_object_hash(list(obj.items()))
+
+ elif isinstance(obj, (list, tuple)):
+ obj = sorted(get_object_hash(o) for o in obj)
+
+ else: # int or other objects
+ pass
+
+ return md5(unicode(obj).encode('utf8')).hexdigest()
+
+
class StandaloneHTMLBuilder(Builder):
"""
Builds standalone HTML docs.
@@ -156,9 +173,8 @@ class StandaloneHTMLBuilder(Builder):
cfgdict = dict((name, self.config[name])
for (name, desc) in self.config.values.iteritems()
if desc[1] == 'html')
- self.config_hash = md5(unicode(cfgdict).encode('utf-8')).hexdigest()
- self.tags_hash = md5(unicode(sorted(self.tags)).encode('utf-8')) \
- .hexdigest()
+ self.config_hash = get_object_hash(cfgdict)
+ self.tags_hash = get_object_hash(sorted(self.tags))
old_config_hash = old_tags_hash = ''
try:
fp = open(path.join(self.outdir, '.buildinfo'))
@@ -240,7 +256,8 @@ class StandaloneHTMLBuilder(Builder):
if not lang or lang not in languages:
lang = 'en'
self.indexer = IndexBuilder(self.env, lang,
- self.config.html_search_options)
+ self.config.html_search_options,
+ self.config.html_search_scorer)
self.load_indexer(docnames)
self.docwriter = HTMLWriter(self)
@@ -653,6 +670,8 @@ class StandaloneHTMLBuilder(Builder):
self.indexer.feed(pagename, title, doctree)
def _get_local_toctree(self, docname, collapse=True, **kwds):
+ if 'includehidden' not in kwds:
+ kwds['includehidden'] = False
return self.render_partial(self.env.get_toctree_for(
docname, self, collapse, **kwds))['fragment']
diff --git a/sphinx/builders/htmlhelp.py b/sphinx/builders/htmlhelp.py
index a1dcef239..605012f8f 100644
--- a/sphinx/builders/htmlhelp.py
+++ b/sphinx/builders/htmlhelp.py
@@ -6,7 +6,7 @@
Build HTML help support files.
Parts adapted from Python's Doc/tools/prechm.py.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/builders/latex.py b/sphinx/builders/latex.py
index 3369338c2..dcc3b3603 100644
--- a/sphinx/builders/latex.py
+++ b/sphinx/builders/latex.py
@@ -5,7 +5,7 @@
LaTeX builder.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/builders/linkcheck.py b/sphinx/builders/linkcheck.py
index a8adcdac7..c567401c5 100644
--- a/sphinx/builders/linkcheck.py
+++ b/sphinx/builders/linkcheck.py
@@ -5,7 +5,7 @@
The CheckExternalLinksBuilder class.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -102,7 +102,8 @@ class CheckExternalLinksBuilder(Builder):
def check():
# check for various conditions without bothering the network
- if len(uri) == 0 or uri[0] == '#' or uri[0:7] == 'mailto:' or uri[0:4] == 'ftp:':
+ if len(uri) == 0 or uri[0] == '#' or \
+ uri[0:7] == 'mailto:' or uri[0:4] == 'ftp:':
return 'unchecked', ''
elif not (uri[0:5] == 'http:' or uri[0:6] == 'https:'):
return 'local', ''
diff --git a/sphinx/builders/manpage.py b/sphinx/builders/manpage.py
index 93b56a01c..32a097574 100644
--- a/sphinx/builders/manpage.py
+++ b/sphinx/builders/manpage.py
@@ -5,7 +5,7 @@
Manual pages builder.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/builders/qthelp.py b/sphinx/builders/qthelp.py
index 5e5deaf1a..ce07315db 100644
--- a/sphinx/builders/qthelp.py
+++ b/sphinx/builders/qthelp.py
@@ -5,7 +5,7 @@
Build input files for the Qt collection generator.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/builders/texinfo.py b/sphinx/builders/texinfo.py
index 535c527e0..a606fc6cc 100644
--- a/sphinx/builders/texinfo.py
+++ b/sphinx/builders/texinfo.py
@@ -5,7 +5,7 @@
Texinfo builder.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -72,9 +72,9 @@ uninstall-info: info
\t-$(TEXI2PDF) '$<'
clean:
-\t-rm -f *.info *.pdf *.txt *.html
-\t-rm -f *.log *.ind *.aux *.toc *.syn *.idx *.out *.ilg *.pla *.ky *.pg
-\t-rm -f *.vr *.tp *.fn *.fns *.def *.defs *.cp *.cps *.ge *.ges *.mo
+\trm -f *.info *.pdf *.txt *.html
+\trm -f *.log *.ind *.aux *.toc *.syn *.idx *.out *.ilg *.pla *.ky *.pg
+\trm -f *.vr *.tp *.fn *.fns *.def *.defs *.cp *.cps *.ge *.ges *.mo
.PHONY: all info plaintext html pdf install-info uninstall-info clean
'''
diff --git a/sphinx/builders/text.py b/sphinx/builders/text.py
index 40fd3d791..98148a8c6 100644
--- a/sphinx/builders/text.py
+++ b/sphinx/builders/text.py
@@ -5,7 +5,7 @@
Plain-text Sphinx builder.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/builders/websupport.py b/sphinx/builders/websupport.py
index b77573095..d8238bb46 100644
--- a/sphinx/builders/websupport.py
+++ b/sphinx/builders/websupport.py
@@ -5,7 +5,7 @@
Builder for the web support package.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/builders/xml.py b/sphinx/builders/xml.py
new file mode 100644
index 000000000..ec411a79d
--- /dev/null
+++ b/sphinx/builders/xml.py
@@ -0,0 +1,98 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.builders.xml
+ ~~~~~~~~~~~~~~~~~~~
+
+ Docutils-native XML and pseudo-XML builders.
+
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import codecs
+from os import path
+
+from docutils import nodes
+from docutils.io import StringOutput
+
+from sphinx.builders import Builder
+from sphinx.util.osutil import ensuredir, os_path
+from sphinx.writers.xml import XMLWriter, PseudoXMLWriter
+
+class XMLBuilder(Builder):
+ """
+ Builds Docutils-native XML.
+ """
+ name = 'xml'
+ format = 'xml'
+ out_suffix = '.xml'
+
+ _writer_class = XMLWriter
+
+ def init(self):
+ pass
+
+ def get_outdated_docs(self):
+ for docname in self.env.found_docs:
+ if docname not in self.env.all_docs:
+ yield docname
+ continue
+ targetname = self.env.doc2path(docname, self.outdir,
+ self.out_suffix)
+ try:
+ targetmtime = path.getmtime(targetname)
+ except Exception:
+ targetmtime = 0
+ try:
+ srcmtime = path.getmtime(self.env.doc2path(docname))
+ if srcmtime > targetmtime:
+ yield docname
+ except EnvironmentError:
+ # source doesn't exist anymore
+ pass
+
+ def get_target_uri(self, docname, typ=None):
+ return ''
+
+ def prepare_writing(self, docnames):
+ self.writer = self._writer_class(self)
+
+ def write_doc(self, docname, doctree):
+ # work around multiple string % tuple issues in docutils;
+ # replace tuples in attribute values with lists
+ doctree = doctree.deepcopy()
+ for node in doctree.traverse(nodes.Element):
+ for att, value in node.attributes.items():
+ if isinstance(value, tuple):
+ node.attributes[att] = list(value)
+ value = node.attributes[att]
+ if isinstance(value, list):
+ for i, val in enumerate(value):
+ if isinstance(val, tuple):
+ value[i] = list(val)
+ destination = StringOutput(encoding='utf-8')
+ self.writer.write(doctree, destination)
+ outfilename = path.join(self.outdir, os_path(docname) + self.out_suffix)
+ ensuredir(path.dirname(outfilename))
+ try:
+ f = codecs.open(outfilename, 'w', 'utf-8')
+ try:
+ f.write(self.writer.output)
+ finally:
+ f.close()
+ except (IOError, OSError), err:
+ self.warn("error writing file %s: %s" % (outfilename, err))
+
+ def finish(self):
+ pass
+
+
+class PseudoXMLBuilder(XMLBuilder):
+ """
+ Builds pseudo-XML for display purposes.
+ """
+ name = 'pseudoxml'
+ format = 'pseudoxml'
+ out_suffix = '.pseudoxml'
+
+ _writer_class = PseudoXMLWriter
diff --git a/sphinx/cmdline.py b/sphinx/cmdline.py
index 9fc213716..13bd2e9f4 100644
--- a/sphinx/cmdline.py
+++ b/sphinx/cmdline.py
@@ -5,7 +5,7 @@
sphinx-build command-line handling.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -22,9 +22,17 @@ from sphinx.errors import SphinxError
from sphinx.application import Sphinx
from sphinx.util import Tee, format_exception_cut_frames, save_traceback
from sphinx.util.console import red, nocolor, color_terminal
+from sphinx.util.osutil import fs_encoding
from sphinx.util.pycompat import terminal_safe, bytes
+def abspath(pathdir):
+ pathdir = path.abspath(pathdir)
+ if isinstance(pathdir, bytes):
+ pathdir = pathdir.decode(fs_encoding)
+ return pathdir
+
+
def usage(argv, msg=None):
if msg:
print >>sys.stderr, msg
@@ -32,29 +40,47 @@ def usage(argv, msg=None):
print >>sys.stderr, """\
Sphinx v%s
Usage: %s [options] sourcedir outdir [filenames...]
-Options: -b -- builder to use; default is html
- -a -- write all files; default is to only write \
-new and changed files
- -E -- don't use a saved environment, always read all files
- -t -- include "only" blocks with
- -d -- path for the cached environment and doctree files
- (default: outdir/.doctrees)
- -c -- path where configuration file (conf.py) is located
+
+General options
+^^^^^^^^^^^^^^^
+-b builder to use; default is html
+-a write all files; default is to only write new and changed files
+-E don't use a saved environment, always read all files
+-d path for the cached environment and doctree files
+ (default: outdir/.doctrees)
+
+Build configuration options
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+-c path where configuration file (conf.py) is located
(default: same as sourcedir)
- -C -- use no config file at all, only -D options
- -D -- override a setting in configuration
- -A -- pass a value into the templates, for HTML builder
- -n -- nit-picky mode, warn about all missing references
- -N -- do not do colored output
- -q -- no output on stdout, just warnings on stderr
- -Q -- no output at all, not even warnings
- -w -- write warnings (and errors) to given file
- -W -- turn warnings into errors
- -P -- run Pdb on exception
-Modi:
+-C use no config file at all, only -D options
+-D override a setting in configuration file
+-t define tag: include "only" blocks with
+-A pass a value into the templates, for HTML builder
+-n nit-picky mode, warn about all missing references
+
+Console output options
+^^^^^^^^^^^^^^^^^^^^^^
+-v increase verbosity (can be repeated)
+-q no output on stdout, just warnings on stderr
+-Q no output at all, not even warnings
+-w write warnings (and errors) to given file
+-W turn warnings into errors
+-T show full traceback on exception
+-N do not emit colored output
+-P run Pdb on exception
+
+Filename arguments
+^^^^^^^^^^^^^^^^^^
* without -a and without filenames, write new and changed files.
* with -a, write all files.
-* with filenames, write these.""" % (__version__, argv[0])
+* with filenames, write these.
+
+Standard options
+^^^^^^^^^^^^^^^^
+-h, --help show this help and exit
+--version show version information and exit
+""" % (__version__, argv[0])
def main(argv):
@@ -63,9 +89,19 @@ def main(argv):
nocolor()
try:
- opts, args = getopt.getopt(argv[1:], 'ab:t:d:c:CD:A:ng:NEqQWw:P')
+ opts, args = getopt.getopt(argv[1:], 'ab:t:d:c:CD:A:nNEqQWw:PThv',
+ ['help', 'version'])
allopts = set(opt[0] for opt in opts)
- srcdir = confdir = path.abspath(args[0])
+ if '-h' in allopts or '--help' in allopts:
+ usage(argv)
+ print >>sys.stderr
+ print >>sys.stderr, 'For more information, see '\
+ '.'
+ return 0
+ if '--version' in allopts:
+ print 'Sphinx (sphinx-build) %s' % __version__
+ return 0
+ srcdir = confdir = abspath(args[0])
if not path.isdir(srcdir):
print >>sys.stderr, 'Error: Cannot find source directory `%s\'.' % (
srcdir,)
@@ -75,19 +111,19 @@ def main(argv):
print >>sys.stderr, ('Error: Source directory doesn\'t '
'contain conf.py file.')
return 1
- outdir = path.abspath(args[1])
- if not path.isdir(outdir):
- print >>sys.stderr, 'Making output directory...'
- os.makedirs(outdir)
- except (IndexError, getopt.error):
- usage(argv)
+ outdir = abspath(args[1])
+ except getopt.error, err:
+ usage(argv, 'Error: %s' % err)
+ return 1
+ except IndexError:
+ usage(argv, 'Error: Insufficient arguments.')
return 1
filenames = args[2:]
err = 0
for filename in filenames:
if not path.isfile(filename):
- print >>sys.stderr, 'Cannot find file %r.' % filename
+ print >>sys.stderr, 'Error: Cannot find file %r.' % filename
err = 1
if err:
return 1
@@ -101,6 +137,8 @@ def main(argv):
buildername = None
force_all = freshenv = warningiserror = use_pdb = False
+ show_traceback = False
+ verbosity = 0
status = sys.stdout
warning = sys.stderr
error = sys.stderr
@@ -113,15 +151,15 @@ def main(argv):
buildername = val
elif opt == '-a':
if filenames:
- usage(argv, 'Cannot combine -a option and filenames.')
+ usage(argv, 'Error: Cannot combine -a option and filenames.')
return 1
force_all = True
elif opt == '-t':
tags.append(val)
elif opt == '-d':
- doctreedir = path.abspath(val)
+ doctreedir = abspath(val)
elif opt == '-c':
- confdir = path.abspath(val)
+ confdir = abspath(val)
if not path.isfile(path.join(confdir, 'conf.py')):
print >>sys.stderr, ('Error: Configuration directory '
'doesn\'t contain conf.py file.')
@@ -177,26 +215,29 @@ def main(argv):
warnfile = val
elif opt == '-P':
use_pdb = True
+ elif opt == '-T':
+ show_traceback = True
+ elif opt == '-v':
+ verbosity += 1
+ show_traceback = True
if warning and warnfile:
warnfp = open(warnfile, 'w')
warning = Tee(warning, warnfp)
error = warning
+ if not path.isdir(outdir):
+ if status:
+ print >>status, 'Making output directory...'
+ os.makedirs(outdir)
+
try:
app = Sphinx(srcdir, confdir, outdir, doctreedir, buildername,
confoverrides, status, warning, freshenv,
- warningiserror, tags)
+ warningiserror, tags, verbosity)
app.build(force_all, filenames)
return app.statuscode
- except KeyboardInterrupt:
- if use_pdb:
- import pdb
- print >>error, red('Interrupted while building, starting debugger:')
- traceback.print_exc()
- pdb.post_mortem(sys.exc_info()[2])
- return 1
- except Exception, err:
+ except (Exception, KeyboardInterrupt), err:
if use_pdb:
import pdb
print >>error, red('Exception occurred while building, '
@@ -205,7 +246,12 @@ def main(argv):
pdb.post_mortem(sys.exc_info()[2])
else:
print >>error
- if isinstance(err, SystemMessage):
+ if show_traceback:
+ traceback.print_exc(None, error)
+ print >>error
+ if isinstance(err, KeyboardInterrupt):
+ print >>error, 'interrupted!'
+ elif isinstance(err, SystemMessage):
print >>error, red('reST markup error:')
print >>error, terminal_safe(err.args[0])
elif isinstance(err, SphinxError):
@@ -223,7 +269,7 @@ def main(argv):
'can be provided next time.')
print >>error, (
'Either send bugs to the mailing list at '
- ',\n'
+ ',\n'
'or report them in the tracker at '
'. Thanks!')
return 1
diff --git a/sphinx/config.py b/sphinx/config.py
index 2012634e1..36a5b862a 100644
--- a/sphinx/config.py
+++ b/sphinx/config.py
@@ -5,7 +5,7 @@
Build configuration file handling.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -17,7 +17,7 @@ from os import path
from sphinx.errors import ConfigError
from sphinx.locale import l_
from sphinx.util.osutil import make_filename
-from sphinx.util.pycompat import bytes, b, convert_with_2to3
+from sphinx.util.pycompat import bytes, b, execfile_
nonascii_re = re.compile(b(r'[\x80-\xff]'))
@@ -110,6 +110,7 @@ class Config(object):
html_secnumber_suffix = ('. ', 'html'),
html_search_language = (None, 'html'),
html_search_options = ({}, 'html'),
+ html_search_scorer = ('', None),
# HTML help only options
htmlhelp_basename = (lambda self: make_filename(self.project), None),
@@ -141,9 +142,14 @@ class Config(object):
epub_tocdup = (True, 'env'),
epub_fix_images = (False, 'env'),
epub_max_image_width = (0, 'env'),
+ epub_show_urls = ('inline', 'html'),
# LaTeX options
- latex_documents = ([], None),
+ latex_documents = (lambda self: [(self.master_doc,
+ make_filename(self.project) + '.tex',
+ self.project,
+ '', 'manual')],
+ None),
latex_logo = (None, None),
latex_appendices = ([], None),
latex_use_parts = (False, None),
@@ -166,11 +172,22 @@ class Config(object):
text_newlines = ('unix', 'env'),
# manpage options
- man_pages = ([], None),
+ man_pages = (lambda self: [(self.master_doc,
+ make_filename(self.project).lower(),
+ '%s %s' % (self.project, self.release),
+ [], 1)],
+ None),
man_show_urls = (False, None),
# Texinfo options
- texinfo_documents = ([], None),
+ texinfo_documents = (lambda self: [(self.master_doc,
+ make_filename(self.project).lower(),
+ self.project, '',
+ make_filename(self.project),
+ 'The %s reference manual.' %
+ make_filename(self.project),
+ 'Python')],
+ None),
texinfo_appendices = ([], None),
texinfo_elements = ({}, None),
texinfo_domain_indices = (True, None),
@@ -184,6 +201,9 @@ class Config(object):
# gettext options
gettext_compact = (True, 'gettext'),
+
+ # XML options
+ xml_pretty = (True, 'env'),
)
def __init__(self, dirname, filename, overrides, tags):
@@ -199,26 +219,8 @@ class Config(object):
# we promise to have the config dir as current dir while the
# config file is executed
os.chdir(dirname)
- # get config source -- 'b' is a no-op under 2.x, while 'U' is
- # ignored under 3.x (but 3.x compile() accepts \r\n newlines)
- f = open(config_file, 'rbU')
try:
- source = f.read()
- finally:
- f.close()
- try:
- # compile to a code object, handle syntax errors
- try:
- code = compile(source, config_file, 'exec')
- except SyntaxError:
- if convert_with_2to3:
- # maybe the file uses 2.x syntax; try to refactor to
- # 3.x syntax using 2to3
- source = convert_with_2to3(config_file)
- code = compile(source, config_file, 'exec')
- else:
- raise
- exec code in config
+ execfile_(filename, config)
except SyntaxError, err:
raise ConfigError(CONFIG_SYNTAX_ERROR % err)
finally:
diff --git a/sphinx/directives/__init__.py b/sphinx/directives/__init__.py
index 6e0300abc..388522dca 100644
--- a/sphinx/directives/__init__.py
+++ b/sphinx/directives/__init__.py
@@ -5,7 +5,7 @@
Handlers for additional ReST directives.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/directives/code.py b/sphinx/directives/code.py
index c45e1b368..6fb21d146 100644
--- a/sphinx/directives/code.py
+++ b/sphinx/directives/code.py
@@ -3,7 +3,7 @@
sphinx.directives.code
~~~~~~~~~~~~~~~~~~~~~~
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/directives/other.py b/sphinx/directives/other.py
index aa4142d6f..59931a554 100644
--- a/sphinx/directives/other.py
+++ b/sphinx/directives/other.py
@@ -3,14 +3,13 @@
sphinx.directives.other
~~~~~~~~~~~~~~~~~~~~~~~
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
-import os
-
from docutils import nodes
from docutils.parsers.rst import Directive, directives
+from docutils.parsers.rst.directives.admonitions import BaseAdmonition
from docutils.parsers.rst.directives.misc import Class
from docutils.parsers.rst.directives.misc import Include as BaseInclude
@@ -19,7 +18,6 @@ from sphinx.locale import _
from sphinx.util import url_re, docname_join
from sphinx.util.nodes import explicit_title_re, set_source_info, \
process_index_entry
-from sphinx.util.compat import make_admonition
from sphinx.util.matching import patfilter
@@ -42,6 +40,7 @@ class TocTree(Directive):
'maxdepth': int,
'glob': directives.flag,
'hidden': directives.flag,
+ 'includehidden': directives.flag,
'numbered': int_or_nothing,
'titlesonly': directives.flag,
}
@@ -107,6 +106,7 @@ class TocTree(Directive):
subnode['maxdepth'] = self.options.get('maxdepth', -1)
subnode['glob'] = glob
subnode['hidden'] = 'hidden' in self.options
+ subnode['includehidden'] = 'includehidden' in self.options
subnode['numbered'] = self.options.get('numbered', 0)
subnode['titlesonly'] = 'titlesonly' in self.options
set_source_info(self, subnode)
@@ -168,6 +168,7 @@ class Index(Directive):
indexnode = addnodes.index()
indexnode['entries'] = ne = []
indexnode['inline'] = False
+ set_source_info(self, indexnode)
for entry in arguments:
ne.extend(process_index_entry(entry, targetid))
return [indexnode, targetnode]
@@ -204,29 +205,11 @@ class VersionChange(Directive):
return ret
-class SeeAlso(Directive):
+class SeeAlso(BaseAdmonition):
"""
An admonition mentioning things to look at as reference.
"""
- has_content = True
- required_arguments = 0
- optional_arguments = 1
- final_argument_whitespace = True
- option_spec = {}
-
- def run(self):
- ret = make_admonition(
- addnodes.seealso, self.name, [_('See also')], self.options,
- self.content, self.lineno, self.content_offset, self.block_text,
- self.state, self.state_machine)
- if self.arguments:
- argnodes, msgs = self.state.inline_text(self.arguments[0],
- self.lineno)
- para = nodes.paragraph()
- para += argnodes
- para += msgs
- ret[0].insert(1, para)
- return ret
+ node_class = addnodes.seealso
class TabularColumns(Directive):
@@ -338,9 +321,46 @@ class Only(Directive):
node.document = self.state.document
set_source_info(self, node)
node['expr'] = self.arguments[0]
- self.state.nested_parse(self.content, self.content_offset, node,
- match_titles=1)
- return [node]
+
+ # Same as util.nested_parse_with_titles but try to handle nested
+ # sections which should be raised higher up the doctree.
+ surrounding_title_styles = self.state.memo.title_styles
+ surrounding_section_level = self.state.memo.section_level
+ self.state.memo.title_styles = []
+ self.state.memo.section_level = 0
+ try:
+ self.state.nested_parse(self.content, self.content_offset,
+ node, match_titles=1)
+ title_styles = self.state.memo.title_styles
+ if (not surrounding_title_styles
+ or not title_styles
+ or title_styles[0] not in surrounding_title_styles
+ or not self.state.parent):
+ # No nested sections so no special handling needed.
+ return [node]
+ # Calculate the depths of the current and nested sections.
+ current_depth = 0
+ parent = self.state.parent
+ while parent:
+ current_depth += 1
+ parent = parent.parent
+ current_depth -= 2
+ title_style = title_styles[0]
+ nested_depth = len(surrounding_title_styles)
+ if title_style in surrounding_title_styles:
+ nested_depth = surrounding_title_styles.index(title_style)
+ # Use these depths to determine where the nested sections should
+ # be placed in the doctree.
+ n_sects_to_raise = current_depth - nested_depth + 1
+ parent = self.state.parent
+ for i in xrange(n_sects_to_raise):
+ if parent.parent:
+ parent = parent.parent
+ parent.append(node)
+ return []
+ finally:
+ self.state.memo.title_styles = surrounding_title_styles
+ self.state.memo.section_level = surrounding_section_level
class Include(BaseInclude):
diff --git a/sphinx/domains/__init__.py b/sphinx/domains/__init__.py
index c48568eb9..2a0e985fa 100644
--- a/sphinx/domains/__init__.py
+++ b/sphinx/domains/__init__.py
@@ -6,7 +6,7 @@
Support for domains, which are groupings of description directives
and roles describing e.g. constructs of one programming language.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py
index f1848dbdb..f9f2e6647 100644
--- a/sphinx/domains/c.py
+++ b/sphinx/domains/c.py
@@ -5,7 +5,7 @@
The C language domain.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py
index a6392c1cb..9307d3a4d 100644
--- a/sphinx/domains/cpp.py
+++ b/sphinx/domains/cpp.py
@@ -5,7 +5,7 @@
The C++ language domain.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -672,8 +672,9 @@ class DefinitionParser(object):
except ValueError:
return False
- def _parse_builtin(self, modifier):
- path = [modifier]
+ def _parse_builtin(self, modifiers):
+ modifier = modifiers[-1]
+ path = modifiers
following = self._modifiers[modifier]
while 1:
self.skip_ws()
@@ -730,9 +731,10 @@ class DefinitionParser(object):
# impossible for a template to follow, so what
# we do is go to a different function that just
# eats types
- if following is not None:
- return self._parse_builtin(modifier)
modifiers.append(modifier)
+ if following is not None:
+ return self._parse_builtin(modifiers)
+ self.skip_ws()
else:
self.backout()
break
@@ -760,17 +762,33 @@ class DefinitionParser(object):
self.skip_ws()
if self.match(_string_re):
return self.matched_text
- idx1 = self.definition.find(',', self.pos)
- idx2 = self.definition.find(')', self.pos)
- if idx1 < 0:
- idx = idx2
- elif idx2 < 0:
- idx = idx1
- else:
- idx = min(idx1, idx2)
- if idx < 0:
- self.fail('unexpected end in default expression')
- rv = self.definition[self.pos:idx]
+ paren_stack_depth = 0
+ max_pos = len(self.definition)
+ rv_start = self.pos
+ while 1:
+ idx0 = self.definition.find('(', self.pos)
+ idx1 = self.definition.find(',', self.pos)
+ idx2 = self.definition.find(')', self.pos)
+ if idx0 < 0:
+ idx0 = max_pos
+ if idx1 < 0:
+ idx1 = max_pos
+ if idx2 < 0:
+ idx2 = max_pos
+ idx = min(idx0, idx1, idx2)
+ if idx >= max_pos:
+ self.fail('unexpected end in default expression')
+ if idx == idx0:
+ paren_stack_depth += 1
+ elif idx == idx2:
+ paren_stack_depth -= 1
+ if paren_stack_depth < 0:
+ break
+ elif paren_stack_depth == 0:
+ break
+ self.pos = idx+1
+
+ rv = self.definition[rv_start:idx]
self.pos = idx
return rv
@@ -836,7 +854,7 @@ class DefinitionParser(object):
visibility = 'public'
if self.match(_visibility_re):
visibility = self.matched_text
- static = self.skip_word('static')
+ static = self.skip_word_and_ws('static')
return visibility, static
def parse_type(self):
diff --git a/sphinx/domains/javascript.py b/sphinx/domains/javascript.py
index a6f4e87cc..862077b53 100644
--- a/sphinx/domains/javascript.py
+++ b/sphinx/domains/javascript.py
@@ -5,7 +5,7 @@
The JavaScript domain.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py
index 38f521dc4..89b7fded4 100644
--- a/sphinx/domains/python.py
+++ b/sphinx/domains/python.py
@@ -5,7 +5,7 @@
The Python domain.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/domains/rst.py b/sphinx/domains/rst.py
index 9a40b05fa..73e7e48e9 100644
--- a/sphinx/domains/rst.py
+++ b/sphinx/domains/rst.py
@@ -5,7 +5,7 @@
The reStructuredText domain.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/domains/std.py b/sphinx/domains/std.py
index 9148131a4..011a12bf3 100644
--- a/sphinx/domains/std.py
+++ b/sphinx/domains/std.py
@@ -5,7 +5,7 @@
The standard domain.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -307,12 +307,18 @@ class Glossary(Directive):
# add an index entry too
indexnode = addnodes.index()
indexnode['entries'] = [('single', termtext, new_id, 'main')]
- termnodes.append(indexnode)
- termnodes.extend(res[0])
- termnodes.append(addnodes.termsep())
+ _termnodes = []
+ _termnodes.append(indexnode)
+ _termnodes.extend(res[0])
+ _termnodes.append(addnodes.termsep())
+ for termnode in _termnodes:
+ termnode.source, termnode.line = source, lineno
+ termnodes.extend(_termnodes)
# make a single "term" node with all the terms, separated by termsep
# nodes (remove the dangling trailing separator)
term = nodes.term('', '', *termnodes[:-1])
+ term.source, term.line = termnodes[0].source, termnodes[0].line
+ term.rawsource = term.astext()
term['ids'].extend(ids)
term['names'].extend(ids)
term += system_messages
diff --git a/sphinx/environment.py b/sphinx/environment.py
index 5661c468a..32e8eb5bd 100644
--- a/sphinx/environment.py
+++ b/sphinx/environment.py
@@ -5,7 +5,7 @@
Global creation environment.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -26,30 +26,27 @@ from itertools import izip, groupby
from docutils import nodes
from docutils.io import FileInput, NullOutput
from docutils.core import Publisher
-from docutils.utils import Reporter, relative_path, new_document, \
- get_source_line
+from docutils.utils import Reporter, relative_path, get_source_line
from docutils.readers import standalone
-from docutils.parsers.rst import roles, directives, Parser as RSTParser
+from docutils.parsers.rst import roles, directives
from docutils.parsers.rst.languages import en as english
from docutils.parsers.rst.directives.html import MetaBody
from docutils.writers import UnfilteredWriter
-from docutils.transforms import Transform
-from docutils.transforms.parts import ContentsFilter
from sphinx import addnodes
from sphinx.util import url_re, get_matching_docs, docname_join, split_into, \
FilenameUniqDict
-from sphinx.util.nodes import clean_astext, make_refnode, extract_messages, \
- WarningStream
-from sphinx.util.osutil import movefile, SEP, ustrftime, find_catalog
+from sphinx.util.nodes import clean_astext, make_refnode, WarningStream
+from sphinx.util.osutil import SEP, fs_encoding
from sphinx.util.matching import compile_matchers
-from sphinx.util.pycompat import all, class_types
+from sphinx.util.pycompat import class_types
from sphinx.util.websupport import is_commentable
from sphinx.errors import SphinxError, ExtensionError
-from sphinx.locale import _, init as init_locale
+from sphinx.locale import _
from sphinx.versioning import add_uids, merge_doctrees
+from sphinx.transforms import DefaultSubstitutions, MoveModuleTargets, \
+ HandleCodeBlocks, SortIds, CitationReferences, Locale, SphinxContentsFilter
-fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding()
orig_role_function = roles.role
orig_directive_function = directives.directive
@@ -66,19 +63,14 @@ default_settings = {
'doctitle_xform': False,
'sectsubtitle_xform': False,
'halt_level': 5,
+ 'file_insertion_enabled': True,
}
# This is increased every time an environment attribute is added
# or changed to properly invalidate pickle files.
-ENV_VERSION = 41
+ENV_VERSION = 42 + (sys.version_info[0] - 2)
-default_substitutions = set([
- 'version',
- 'release',
- 'today',
-])
-
dummy_reporter = Reporter('', 4, 4)
versioning_conditions = {
@@ -93,141 +85,6 @@ class NoUri(Exception):
pass
-class DefaultSubstitutions(Transform):
- """
- Replace some substitutions if they aren't defined in the document.
- """
- # run before the default Substitutions
- default_priority = 210
-
- def apply(self):
- config = self.document.settings.env.config
- # only handle those not otherwise defined in the document
- to_handle = default_substitutions - set(self.document.substitution_defs)
- for ref in self.document.traverse(nodes.substitution_reference):
- refname = ref['refname']
- if refname in to_handle:
- text = config[refname]
- if refname == 'today' and not text:
- # special handling: can also specify a strftime format
- text = ustrftime(config.today_fmt or _('%B %d, %Y'))
- ref.replace_self(nodes.Text(text, text))
-
-
-class MoveModuleTargets(Transform):
- """
- Move module targets that are the first thing in a section to the section
- title.
-
- XXX Python specific
- """
- default_priority = 210
-
- def apply(self):
- for node in self.document.traverse(nodes.target):
- if not node['ids']:
- continue
- if (node.has_key('ismod') and
- node.parent.__class__ is nodes.section and
- # index 0 is the section title node
- node.parent.index(node) == 1):
- node.parent['ids'][0:0] = node['ids']
- node.parent.remove(node)
-
-
-class HandleCodeBlocks(Transform):
- """
- Several code block related transformations.
- """
- default_priority = 210
-
- def apply(self):
- # move doctest blocks out of blockquotes
- for node in self.document.traverse(nodes.block_quote):
- if all(isinstance(child, nodes.doctest_block) for child
- in node.children):
- node.replace_self(node.children)
- # combine successive doctest blocks
- #for node in self.document.traverse(nodes.doctest_block):
- # if node not in node.parent.children:
- # continue
- # parindex = node.parent.index(node)
- # while len(node.parent) > parindex+1 and \
- # isinstance(node.parent[parindex+1], nodes.doctest_block):
- # node[0] = nodes.Text(node[0] + '\n\n' +
- # node.parent[parindex+1][0])
- # del node.parent[parindex+1]
-
-
-class SortIds(Transform):
- """
- Sort secion IDs so that the "id[0-9]+" one comes last.
- """
- default_priority = 261
-
- def apply(self):
- for node in self.document.traverse(nodes.section):
- if len(node['ids']) > 1 and node['ids'][0].startswith('id'):
- node['ids'] = node['ids'][1:] + [node['ids'][0]]
-
-
-class CitationReferences(Transform):
- """
- Replace citation references by pending_xref nodes before the default
- docutils transform tries to resolve them.
- """
- default_priority = 619
-
- def apply(self):
- for citnode in self.document.traverse(nodes.citation_reference):
- cittext = citnode.astext()
- refnode = addnodes.pending_xref(cittext, reftype='citation',
- reftarget=cittext, refwarn=True)
- refnode.line = citnode.line or citnode.parent.line
- refnode += nodes.Text('[' + cittext + ']')
- citnode.parent.replace(citnode, refnode)
-
-
-class Locale(Transform):
- """
- Replace translatable nodes with their translated doctree.
- """
- default_priority = 0
- def apply(self):
- env = self.document.settings.env
- settings, source = self.document.settings, self.document['source']
- # XXX check if this is reliable
- assert source.startswith(env.srcdir)
- docname = path.splitext(relative_path(env.srcdir, source))[0]
- textdomain = find_catalog(docname,
- self.document.settings.gettext_compact)
-
- # fetch translations
- dirs = [path.join(env.srcdir, directory)
- for directory in env.config.locale_dirs]
- catalog, has_catalog = init_locale(dirs, env.config.language,
- textdomain)
- if not has_catalog:
- return
-
- parser = RSTParser()
-
- for node, msg in extract_messages(self.document):
- patch = new_document(source, settings)
- msgstr = catalog.gettext(msg)
- # XXX add marker to untranslated parts
- if not msgstr or msgstr == msg: # as-of-yet untranslated
- continue
- parser.parse(msgstr, patch)
- patch = patch[0]
- # XXX doctest and other block markup
- if not isinstance(patch, nodes.paragraph):
- continue # skip for now
- for child in patch.children: # update leaves
- child.parent = node
- node.children = patch.children
-
-
class SphinxStandaloneReader(standalone.Reader):
"""
Add our own transforms.
@@ -246,20 +103,6 @@ class SphinxDummyWriter(UnfilteredWriter):
pass
-class SphinxContentsFilter(ContentsFilter):
- """
- Used with BuildEnvironment.add_toc_from() to discard cross-file links
- within table-of-contents link nodes.
- """
- def visit_pending_xref(self, node):
- text = node.astext()
- self.parent.append(nodes.literal(text, text))
- raise nodes.SkipNode
-
- def visit_image(self, node):
- raise nodes.SkipNode
-
-
class BuildEnvironment:
"""
The environment in which the ReST files are translated.
@@ -289,9 +132,7 @@ class BuildEnvironment:
del self.config.values
domains = self.domains
del self.domains
- # first write to a temporary file, so that if dumping fails,
- # the existing environment won't be overwritten
- picklefile = open(filename + '.tmp', 'wb')
+ picklefile = open(filename, 'wb')
# remove potentially pickling-problematic values from config
for key, val in vars(self.config).items():
if key.startswith('_') or \
@@ -303,7 +144,6 @@ class BuildEnvironment:
pickle.dump(self, picklefile, pickle.HIGHEST_PROTOCOL)
finally:
picklefile.close()
- movefile(filename + '.tmp', filename)
# reset attributes
self.domains = domains
self.config.values = values
@@ -782,7 +622,11 @@ class BuildEnvironment:
app.emit('doctree-read', doctree)
# store time of build, for outdated files detection
- self.all_docs[docname] = time.time()
+ # (Some filesystems have coarse timestamp resolution;
+ # therefore time.time() can be older than filesystem's timestamp.
+ # For example, FAT32 has 2sec timestamp resolution.)
+ self.all_docs[docname] = max(
+ time.time(), path.getmtime(self.doc2path(docname)))
if self.versioning_condition:
# get old doctree
@@ -874,6 +718,7 @@ class BuildEnvironment:
filterlevel = self.config.keep_warnings and 2 or 5
for node in doctree.traverse(nodes.system_message):
if node['level'] < filterlevel:
+ self.app.debug('%s [filtered system message]', node.astext())
node.parent.remove(node)
@@ -1149,8 +994,9 @@ class BuildEnvironment:
anchorname=anchorname, *nodetext)
para = addnodes.compact_paragraph('', '', reference)
item = nodes.list_item('', para)
+ sub_item = build_toc(sectionnode, depth + 1)
if maxdepth == 0 or depth < maxdepth:
- item += build_toc(sectionnode, depth+1)
+ item += sub_item
entries.append(item)
if entries:
return nodes.bullet_list('', *entries)
@@ -1259,46 +1105,56 @@ class BuildEnvironment:
if toctree.get('hidden', False) and not includehidden:
return None
- def _walk_depth(node, depth, maxdepth):
+ # For reading the following two helper function, it is useful to keep
+ # in mind the node structure of a toctree (using HTML-like node names
+ # for brevity):
+ #
+ #
+ #
+ #
+ #
+ # ...
+ #
+ # ...
+ #
+ #
+ #
+ #
+ # The transformation is made in two passes in order to avoid
+ # interactions between marking and pruning the tree (see bug #1046).
+
+ def _toctree_prune(node, depth, maxdepth):
"""Utility: Cut a TOC at a specified depth."""
-
- # For reading this function, it is useful to keep in mind the node
- # structure of a toctree (using HTML-like node names for brevity):
- #
- #
- #
- #
- #
- # ...
- #
- # ...
- #
- #
- #
-
for subnode in node.children[:]:
if isinstance(subnode, (addnodes.compact_paragraph,
nodes.list_item)):
- # for
and
, just indicate the depth level and
- # recurse to children
- subnode['classes'].append('toctree-l%d' % (depth-1))
- _walk_depth(subnode, depth, maxdepth)
-
+ # for
and
, just recurse
+ _toctree_prune(subnode, depth, maxdepth)
elif isinstance(subnode, nodes.bullet_list):
# for
, determine if the depth is too large or if the
# entry is to be collapsed
if maxdepth > 0 and depth > maxdepth:
subnode.parent.replace(subnode, [])
else:
- # to find out what to collapse, *first* walk subitems,
- # since that determines which children point to the
- # current page
- _walk_depth(subnode, depth+1, maxdepth)
# cull sub-entries whose parents aren't 'current'
if (collapse and depth > 1 and
'iscurrent' not in subnode.parent):
subnode.parent.remove(subnode)
+ else:
+ # recurse on visible children
+ _toctree_prune(subnode, depth+1, maxdepth)
+ def _toctree_add_classes(node, depth):
+ """Add 'toctree-l%d' and 'current' classes to the toctree."""
+ for subnode in node.children:
+ if isinstance(subnode, (addnodes.compact_paragraph,
+ nodes.list_item)):
+ # for
and
, indicate the depth level and recurse
+ subnode['classes'].append('toctree-l%d' % (depth-1))
+ _toctree_add_classes(subnode, depth)
+ elif isinstance(subnode, nodes.bullet_list):
+ # for
diff --git a/sphinx/themes/agogo/static/agogo.css_t b/sphinx/themes/agogo/static/agogo.css_t
index b069bd224..3fb81178c 100644
--- a/sphinx/themes/agogo/static/agogo.css_t
+++ b/sphinx/themes/agogo/static/agogo.css_t
@@ -4,7 +4,7 @@
*
* Sphinx stylesheet -- agogo theme.
*
- * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
diff --git a/sphinx/themes/agogo/theme.conf b/sphinx/themes/agogo/theme.conf
index 3fc88580f..3052aca30 100644
--- a/sphinx/themes/agogo/theme.conf
+++ b/sphinx/themes/agogo/theme.conf
@@ -10,7 +10,7 @@ pagewidth = 70em
documentwidth = 50em
sidebarwidth = 20em
bgcolor = #eeeeec
-headerbg = url(bgtop.png) top left repeat-x
+headerbg = #555573 url(bgtop.png) top left repeat-x
footerbg = url(bgfooter.png) top left repeat-x
linkcolor = #ce5c00
headercolor1 = #204a87
diff --git a/sphinx/themes/basic/defindex.html b/sphinx/themes/basic/defindex.html
index ce8d3af67..1ae9630de 100644
--- a/sphinx/themes/basic/defindex.html
+++ b/sphinx/themes/basic/defindex.html
@@ -4,10 +4,10 @@
Default template for the "index" page.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
-{% extends "layout.html" %}
+{%- extends "layout.html" %}
{% set title = _('Overview') %}
{% block body %}
{{ docstitle|e }}
diff --git a/sphinx/themes/basic/domainindex.html b/sphinx/themes/basic/domainindex.html
index 947a01ea8..7f99da618 100644
--- a/sphinx/themes/basic/domainindex.html
+++ b/sphinx/themes/basic/domainindex.html
@@ -4,10 +4,10 @@
Template for domain indices (module index, ...).
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
-{% extends "layout.html" %}
+{%- extends "layout.html" %}
{% set title = indextitle %}
{% block extrahead %}
{{ super() }}
diff --git a/sphinx/themes/basic/genindex-single.html b/sphinx/themes/basic/genindex-single.html
index eff8c1ce2..e79212516 100644
--- a/sphinx/themes/basic/genindex-single.html
+++ b/sphinx/themes/basic/genindex-single.html
@@ -4,7 +4,7 @@
Template for a "single" page of a split index.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{% macro indexentries(firstname, links) %}
@@ -28,7 +28,7 @@
{% endmacro %}
-{% extends "layout.html" %}
+{%- extends "layout.html" %}
{% set title = _('Index') %}
{% block body %}
diff --git a/sphinx/themes/basic/genindex-split.html b/sphinx/themes/basic/genindex-split.html
index 6da88a173..20c180687 100644
--- a/sphinx/themes/basic/genindex-split.html
+++ b/sphinx/themes/basic/genindex-split.html
@@ -4,10 +4,10 @@
Template for a "split" index overview page.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
-{% extends "layout.html" %}
+{%- extends "layout.html" %}
{% set title = _('Index') %}
{% block body %}
diff --git a/sphinx/themes/basic/genindex.html b/sphinx/themes/basic/genindex.html
index 7bc002b6c..dd2c96d8e 100644
--- a/sphinx/themes/basic/genindex.html
+++ b/sphinx/themes/basic/genindex.html
@@ -4,7 +4,7 @@
Template for an "all-in-one" index.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{% macro indexentries(firstname, links) %}
@@ -28,7 +28,7 @@
{% endmacro %}
-{% extends "layout.html" %}
+{%- extends "layout.html" %}
{% set title = _('Index') %}
{% block body %}
diff --git a/sphinx/themes/basic/globaltoc.html b/sphinx/themes/basic/globaltoc.html
index ee191faf1..6d117f299 100644
--- a/sphinx/themes/basic/globaltoc.html
+++ b/sphinx/themes/basic/globaltoc.html
@@ -4,7 +4,7 @@
Sphinx sidebar template: global table of contents.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
diff --git a/sphinx/themes/basic/layout.html b/sphinx/themes/basic/layout.html
index 9fb989cbe..9e4e39a18 100644
--- a/sphinx/themes/basic/layout.html
+++ b/sphinx/themes/basic/layout.html
@@ -4,7 +4,7 @@
Master layout template for Sphinx themes.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{%- block doctype -%}
@@ -195,7 +195,7 @@
{% trans last_updated=last_updated|e %}Last updated on {{ last_updated }}.{% endtrans %}
{%- endif %}
{%- if show_sphinx %}
- {% trans sphinx_version=sphinx_version|e %}Created using Sphinx {{ sphinx_version }}.{% endtrans %}
+ {% trans sphinx_version=sphinx_version|e %}Created using Sphinx {{ sphinx_version }}.{% endtrans %}
{%- endif %}
{%- endblock %}
diff --git a/sphinx/themes/basic/localtoc.html b/sphinx/themes/basic/localtoc.html
index aa612ebf6..23a31256e 100644
--- a/sphinx/themes/basic/localtoc.html
+++ b/sphinx/themes/basic/localtoc.html
@@ -4,7 +4,7 @@
Sphinx sidebar template: local table of contents.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{%- if display_toc %}
diff --git a/sphinx/themes/basic/page.html b/sphinx/themes/basic/page.html
index f6e7a688c..185c6c85a 100644
--- a/sphinx/themes/basic/page.html
+++ b/sphinx/themes/basic/page.html
@@ -4,10 +4,10 @@
Master template for simple pages.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
-{% extends "layout.html" %}
+{%- extends "layout.html" %}
{% block body %}
{{ body }}
{% endblock %}
diff --git a/sphinx/themes/basic/relations.html b/sphinx/themes/basic/relations.html
index 82abbeaa2..d7c5fa58e 100644
--- a/sphinx/themes/basic/relations.html
+++ b/sphinx/themes/basic/relations.html
@@ -4,7 +4,7 @@
Sphinx sidebar template: relation links.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{%- if prev %}
diff --git a/sphinx/themes/basic/search.html b/sphinx/themes/basic/search.html
index 4cdc6935c..011c23929 100644
--- a/sphinx/themes/basic/search.html
+++ b/sphinx/themes/basic/search.html
@@ -4,10 +4,10 @@
Template for the search page.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
-{% extends "layout.html" %}
+{%- extends "layout.html" %}
{% set title = _('Search') %}
{% set script_files = script_files + ['_static/searchtools.js'] %}
{% block extrahead %}
diff --git a/sphinx/themes/basic/searchbox.html b/sphinx/themes/basic/searchbox.html
index 13b453c49..609aac83b 100644
--- a/sphinx/themes/basic/searchbox.html
+++ b/sphinx/themes/basic/searchbox.html
@@ -4,7 +4,7 @@
Sphinx sidebar template: quick search box.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{%- if pagename != "search" %}
diff --git a/sphinx/themes/basic/searchresults.html b/sphinx/themes/basic/searchresults.html
index 667abffda..5c42fbaeb 100644
--- a/sphinx/themes/basic/searchresults.html
+++ b/sphinx/themes/basic/searchresults.html
@@ -4,7 +4,7 @@
Template for the body of the search results page.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
Search
diff --git a/sphinx/themes/basic/sourcelink.html b/sphinx/themes/basic/sourcelink.html
index 53f2f6b16..08232efce 100644
--- a/sphinx/themes/basic/sourcelink.html
+++ b/sphinx/themes/basic/sourcelink.html
@@ -4,7 +4,7 @@
Sphinx sidebar template: "show source" link.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{%- if show_source and has_source and sourcename %}
diff --git a/sphinx/themes/basic/static/basic.css_t b/sphinx/themes/basic/static/basic.css_t
index 2937fa49b..b3e36db75 100644
--- a/sphinx/themes/basic/static/basic.css_t
+++ b/sphinx/themes/basic/static/basic.css_t
@@ -4,7 +4,7 @@
*
* Sphinx stylesheet -- basic theme.
*
- * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
diff --git a/sphinx/themes/basic/static/doctools.js b/sphinx/themes/basic/static/doctools.js
index d4619fdfb..8614442eb 100644
--- a/sphinx/themes/basic/static/doctools.js
+++ b/sphinx/themes/basic/static/doctools.js
@@ -4,7 +4,7 @@
*
* Sphinx JavaScript utilities for all documentation.
*
- * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
@@ -32,7 +32,7 @@ if (!window.console || !console.firebug) {
*/
jQuery.urldecode = function(x) {
return decodeURIComponent(x).replace(/\+/g, ' ');
-}
+};
/**
* small helper function to urlencode strings
@@ -61,18 +61,6 @@ jQuery.getQueryParameters = function(s) {
return result;
};
-/**
- * small function to check if an array contains
- * a given item.
- */
-jQuery.contains = function(arr, item) {
- for (var i = 0; i < arr.length; i++) {
- if (arr[i] == item)
- return true;
- }
- return false;
-};
-
/**
* highlight a given string on a jquery object by wrapping it in
* span elements with the given class name.
diff --git a/sphinx/themes/basic/static/searchtools.js_t b/sphinx/themes/basic/static/searchtools.js_t
index 45989c6ae..f96953b44 100644
--- a/sphinx/themes/basic/static/searchtools.js_t
+++ b/sphinx/themes/basic/static/searchtools.js_t
@@ -4,40 +4,47 @@
*
* Sphinx JavaScript utilties for the full-text search.
*
- * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
-/**
- * helper function to return a node containing the
- * search summary for a given text. keywords is a list
- * of stemmed words, hlwords is the list of normal, unstemmed
- * words. the first one is used to find the occurance, the
- * latter for highlighting it.
- */
-
-jQuery.makeSearchSummary = function(text, keywords, hlwords) {
- var textLower = text.toLowerCase();
- var start = 0;
- $.each(keywords, function() {
- var i = textLower.indexOf(this.toLowerCase());
- if (i > -1)
- start = i;
- });
- start = Math.max(start - 120, 0);
- var excerpt = ((start > 0) ? '...' : '') +
- $.trim(text.substr(start, 240)) +
- ((start + 240 - text.length) ? '...' : '');
- var rv = $('').text(excerpt);
- $.each(hlwords, function() {
- rv = rv.highlightText(this, 'highlighted');
- });
- return rv;
-}
-
{{ search_language_stemming_code|safe }}
+{% if search_scorer_tool %}
+{{ search_scorer_tool|safe }}
+{% else %}
+/**
+ * Simple result scoring code.
+ */
+var Scorer = {
+ // Implement the following function to further tweak the score for each result
+ // The function takes a result array [filename, title, anchor, descr, score]
+ // and returns the new score.
+ /*
+ score: function(result) {
+ return result[4];
+ },
+ */
+
+ // query matches the full name of an object
+ objNameMatch: 11,
+ // or matches in the last dotted part of the object name
+ objPartialMatch: 6,
+ // Additive scores depending on the priority of the object
+ objPrio: {0: 15, // used to be importantResults
+ 1: 5, // used to be objectResults
+ 2: -5}, // used to be unimportantResults
+ // Used when the priority is not in the mapping.
+ objPrioDefault: 0,
+
+ // query found in title
+ title: 15,
+ // query found in terms
+ term: 5
+};
+{% endif %}
+
/**
* Search Module
*/
@@ -86,19 +93,20 @@ var Search = {
if (this._pulse_status >= 0)
return;
function pulse() {
+ var i;
Search._pulse_status = (Search._pulse_status + 1) % 4;
var dotString = '';
- for (var i = 0; i < Search._pulse_status; i++)
+ for (i = 0; i < Search._pulse_status; i++)
dotString += '.';
Search.dots.text(dotString);
if (Search._pulse_status > -1)
window.setTimeout(pulse, 500);
- };
+ }
pulse();
},
/**
- * perform a search for something
+ * perform a search for something (or wait until index is loaded)
*/
performSearch : function(query) {
// create the required interface elements
@@ -118,41 +126,46 @@ var Search = {
this.deferQuery(query);
},
+ /**
+ * execute search (requires search index to be loaded)
+ */
query : function(query) {
+ var i;
var stopwords = {{ search_language_stop_words }};
- // Stem the searchterms and add them to the correct list
+ // stem the searchterms and add them to the correct list
var stemmer = new Stemmer();
var searchterms = [];
var excluded = [];
var hlterms = [];
var tmp = query.split(/\s+/);
var objectterms = [];
- for (var i = 0; i < tmp.length; i++) {
- if (tmp[i] != "") {
+ for (i = 0; i < tmp.length; i++) {
+ if (tmp[i] !== "") {
objectterms.push(tmp[i].toLowerCase());
}
if ($u.indexOf(stopwords, tmp[i]) != -1 || tmp[i].match(/^\d+$/) ||
- tmp[i] == "") {
+ tmp[i] === "") {
// skip this "word"
continue;
}
// stem the word
var word = stemmer.stemWord(tmp[i]).toLowerCase();
+ var toAppend;
// select the correct list
if (word[0] == '-') {
- var toAppend = excluded;
+ toAppend = excluded;
word = word.substr(1);
}
else {
- var toAppend = searchterms;
+ toAppend = searchterms;
hlterms.push(tmp[i].toLowerCase());
}
// only add if not already in the list
- if (!$.contains(toAppend, word))
+ if (!$u.contains(toAppend, word))
toAppend.push(word);
- };
+ }
var highlightstring = '?highlight=' + $.urlencode(hlterms.join(" "));
// console.debug('SEARCH: searching for:');
@@ -160,89 +173,51 @@ var Search = {
// console.info('excluded: ', excluded);
// prepare search
- var filenames = this._index.filenames;
- var titles = this._index.titles;
var terms = this._index.terms;
- var fileMap = {};
- var files = null;
- // different result priorities
- var importantResults = [];
- var objectResults = [];
- var regularResults = [];
- var unimportantResults = [];
+ var titleterms = this._index.titleterms;
+
+ // array of [filename, title, anchor, descr, score]
+ var results = [];
$('#search-progress').empty();
// lookup as object
- for (var i = 0; i < objectterms.length; i++) {
- var others = [].concat(objectterms.slice(0,i),
- objectterms.slice(i+1, objectterms.length))
- var results = this.performObjectSearch(objectterms[i], others);
- // Assume first word is most likely to be the object,
- // other words more likely to be in description.
- // Therefore put matches for earlier words first.
- // (Results are eventually used in reverse order).
- objectResults = results[0].concat(objectResults);
- importantResults = results[1].concat(importantResults);
- unimportantResults = results[2].concat(unimportantResults);
+ for (i = 0; i < objectterms.length; i++) {
+ var others = [].concat(objectterms.slice(0, i),
+ objectterms.slice(i+1, objectterms.length));
+ results = results.concat(this.performObjectSearch(objectterms[i], others));
}
- // perform the search on the required terms
- for (var i = 0; i < searchterms.length; i++) {
- var word = searchterms[i];
- // no match but word was a required one
- if ((files = terms[word]) == null)
- break;
- if (files.length == undefined) {
- files = [files];
- }
- // create the mapping
- for (var j = 0; j < files.length; j++) {
- var file = files[j];
- if (file in fileMap)
- fileMap[file].push(word);
- else
- fileMap[file] = [word];
- }
+ // lookup as search terms in fulltext
+ results = results.concat(this.performTermsSearch(searchterms, excluded, terms, Scorer.term))
+ .concat(this.performTermsSearch(searchterms, excluded, titleterms, Scorer.title));
+
+ // let the scorer override scores with a custom scoring function
+ if (Scorer.score) {
+ for (i = 0; i < results.length; i++)
+ results[i][4] = Scorer.score(results[i]);
}
- // now check if the files don't contain excluded terms
- for (var file in fileMap) {
- var valid = true;
-
- // check if all requirements are matched
- if (fileMap[file].length != searchterms.length)
- continue;
-
- // ensure that none of the excluded terms is in the
- // search result.
- for (var i = 0; i < excluded.length; i++) {
- if (terms[excluded[i]] == file ||
- $.contains(terms[excluded[i]] || [], file)) {
- valid = false;
- break;
- }
+ // now sort the results by score (in opposite order of appearance, since the
+ // display function below uses pop() to retrieve items) and then
+ // alphabetically
+ results.sort(function(a, b) {
+ var left = a[4];
+ var right = b[4];
+ if (left > right) {
+ return 1;
+ } else if (left < right) {
+ return -1;
+ } else {
+ // same score: sort alphabetically
+ left = a[1].toLowerCase();
+ right = b[1].toLowerCase();
+ return (left > right) ? -1 : ((left < right) ? 1 : 0);
}
-
- // if we have still a valid result we can add it
- // to the result list
- if (valid)
- regularResults.push([filenames[file], titles[file], '', null]);
- }
-
- // delete unused variables in order to not waste
- // memory until list is retrieved completely
- delete filenames, titles, terms;
-
- // now sort the regular results descending by title
- regularResults.sort(function(a, b) {
- var left = a[1].toLowerCase();
- var right = b[1].toLowerCase();
- return (left > right) ? -1 : ((left < right) ? 1 : 0);
});
- // combine all results
- var results = unimportantResults.concat(regularResults)
- .concat(objectResults).concat(importantResults);
+ // for debugging
+ //Search.lastresults = results.slice(); // a copy
+ //console.info('search results:', Search.lastresults);
// print the results
var resultCount = results.length;
@@ -251,7 +226,7 @@ var Search = {
if (results.length) {
var item = results.pop();
var listItem = $('');
- if (DOCUMENTATION_OPTIONS.FILE_SUFFIX == '') {
+ if (DOCUMENTATION_OPTIONS.FILE_SUFFIX === '') {
// dirhtml builder
var dirname = item[0] + '/';
if (dirname.match(/\/index\/$/)) {
@@ -277,8 +252,8 @@ var Search = {
} else if (DOCUMENTATION_OPTIONS.HAS_SOURCE) {
$.get(DOCUMENTATION_OPTIONS.URL_ROOT + '_sources/' +
item[0] + '.txt', function(data) {
- if (data != '') {
- listItem.append($.makeSearchSummary(data, searchterms, hlterms));
+ if (data !== '') {
+ listItem.append(Search.makeSearchSummary(data, searchterms, hlterms));
Search.output.append(listItem);
}
listItem.slideDown(5, function() {
@@ -307,20 +282,32 @@ var Search = {
displayNextItem();
},
+ /**
+ * search for object names
+ */
performObjectSearch : function(object, otherterms) {
var filenames = this._index.filenames;
var objects = this._index.objects;
var objnames = this._index.objnames;
var titles = this._index.titles;
- var importantResults = [];
- var objectResults = [];
- var unimportantResults = [];
+ var i;
+ var results = [];
for (var prefix in objects) {
for (var name in objects[prefix]) {
var fullname = (prefix ? prefix + '.' : '') + name;
if (fullname.toLowerCase().indexOf(object) > -1) {
+ var score = 0;
+ var parts = fullname.split('.');
+ // check for different match types: exact matches of full name or
+ // "last name" (i.e. last dotted part)
+ if (fullname == object || parts[parts.length - 1] == object) {
+ score += Scorer.objNameMatch;
+ // matches in last name
+ } else if (parts[parts.length - 1].indexOf(object) > -1) {
+ score += Scorer.objPartialMatch;
+ }
var match = objects[prefix][name];
var objname = objnames[match[1]][2];
var title = titles[match[0]];
@@ -330,7 +317,7 @@ var Search = {
var haystack = (prefix + ' ' + name + ' ' +
objname + ' ' + title).toLowerCase();
var allfound = true;
- for (var i = 0; i < otherterms.length; i++) {
+ for (i = 0; i < otherterms.length; i++) {
if (haystack.indexOf(otherterms[i]) == -1) {
allfound = false;
break;
@@ -341,37 +328,107 @@ var Search = {
}
}
var descr = objname + _(', in ') + title;
- anchor = match[3];
- if (anchor == '')
+
+ var anchor = match[3];
+ if (anchor === '')
anchor = fullname;
else if (anchor == '-')
anchor = objnames[match[1]][1] + '-' + fullname;
- result = [filenames[match[0]], fullname, '#'+anchor, descr];
- switch (match[2]) {
- case 1: objectResults.push(result); break;
- case 0: importantResults.push(result); break;
- case 2: unimportantResults.push(result); break;
+ // add custom score for some objects according to scorer
+ if (Scorer.objPrio.hasOwnProperty(match[2])) {
+ score += Scorer.objPrio[match[2]];
+ } else {
+ score += Scorer.objPrioDefault;
}
+ results.push([filenames[match[0]], fullname, '#'+anchor, descr, score]);
}
}
}
- // sort results descending
- objectResults.sort(function(a, b) {
- return (a[1] > b[1]) ? -1 : ((a[1] < b[1]) ? 1 : 0);
- });
+ return results;
+ },
- importantResults.sort(function(a, b) {
- return (a[1] > b[1]) ? -1 : ((a[1] < b[1]) ? 1 : 0);
- });
+ /**
+ * search for full-text terms in the index
+ */
+ performTermsSearch : function(searchterms, excluded, terms, score) {
+ var filenames = this._index.filenames;
+ var titles = this._index.titles;
- unimportantResults.sort(function(a, b) {
- return (a[1] > b[1]) ? -1 : ((a[1] < b[1]) ? 1 : 0);
- });
+ var i, j, file, files;
+ var fileMap = {};
+ var results = [];
- return [importantResults, objectResults, unimportantResults]
+ // perform the search on the required terms
+ for (i = 0; i < searchterms.length; i++) {
+ var word = searchterms[i];
+ // no match but word was a required one
+ if (!(files = terms[word]))
+ break;
+ if (files.length === undefined) {
+ files = [files];
+ }
+ // create the mapping
+ for (j = 0; j < files.length; j++) {
+ file = files[j];
+ if (file in fileMap)
+ fileMap[file].push(word);
+ else
+ fileMap[file] = [word];
+ }
+ }
+
+ // now check if the files don't contain excluded terms
+ for (file in fileMap) {
+ var valid = true;
+
+ // check if all requirements are matched
+ if (fileMap[file].length != searchterms.length)
+ continue;
+
+ // ensure that none of the excluded terms is in the search result
+ for (i = 0; i < excluded.length; i++) {
+ if (terms[excluded[i]] == file ||
+ $u.contains(terms[excluded[i]] || [], file)) {
+ valid = false;
+ break;
+ }
+ }
+
+ // if we have still a valid result we can add it to the result list
+ if (valid) {
+ results.push([filenames[file], titles[file], '', null, score]);
+ }
+ }
+ return results;
+ },
+
+ /**
+ * helper function to return a node containing the
+ * search summary for a given text. keywords is a list
+ * of stemmed words, hlwords is the list of normal, unstemmed
+ * words. the first one is used to find the occurance, the
+ * latter for highlighting it.
+ */
+ makeSearchSummary : function(text, keywords, hlwords) {
+ var textLower = text.toLowerCase();
+ var start = 0;
+ $.each(keywords, function() {
+ var i = textLower.indexOf(this.toLowerCase());
+ if (i > -1)
+ start = i;
+ });
+ start = Math.max(start - 120, 0);
+ var excerpt = ((start > 0) ? '...' : '') +
+ $.trim(text.substr(start, 240)) +
+ ((start + 240 - text.length) ? '...' : '');
+ var rv = $('').text(excerpt);
+ $.each(hlwords, function() {
+ rv = rv.highlightText(this, 'highlighted');
+ });
+ return rv;
}
-}
+};
$(document).ready(function() {
Search.init();
diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js
index e9bd1b851..19fcda564 100644
--- a/sphinx/themes/basic/static/websupport.js
+++ b/sphinx/themes/basic/static/websupport.js
@@ -4,7 +4,7 @@
*
* sphinx.websupport utilties for all documentation.
*
- * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
diff --git a/sphinx/themes/default/layout.html b/sphinx/themes/default/layout.html
index d91a56555..d237f5ed9 100644
--- a/sphinx/themes/default/layout.html
+++ b/sphinx/themes/default/layout.html
@@ -4,10 +4,10 @@
Sphinx layout template for the default theme.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
-{% extends "basic/layout.html" %}
+{%- extends "basic/layout.html" %}
{% if theme_collapsiblesidebar|tobool %}
{% set script_files = script_files + ['_static/sidebar.js'] %}
diff --git a/sphinx/themes/default/static/default.css_t b/sphinx/themes/default/static/default.css_t
index 85c9436a9..5db771089 100644
--- a/sphinx/themes/default/static/default.css_t
+++ b/sphinx/themes/default/static/default.css_t
@@ -4,7 +4,7 @@
*
* Sphinx stylesheet -- default theme.
*
- * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
diff --git a/sphinx/themes/default/static/sidebar.js b/sphinx/themes/default/static/sidebar.js_t
similarity index 79%
rename from sphinx/themes/default/static/sidebar.js
rename to sphinx/themes/default/static/sidebar.js_t
index a45e1926a..242005254 100644
--- a/sphinx/themes/default/static/sidebar.js
+++ b/sphinx/themes/default/static/sidebar.js_t
@@ -16,12 +16,26 @@
* Once the browser is closed the cookie is deleted and the position
* reset to the default (expanded).
*
- * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
$(function() {
+ {% if theme_rightsidebar|tobool %}
+ {% set side = 'right' %}
+ {% set opposite = 'left' %}
+ {% set initial_label = '»' %}
+ {% set expand_label = '«' %}
+ {% set collapse_label = '»' %}
+ {% else %}
+ {% set side = 'left' %}
+ {% set opposite = 'right' %}
+ {% set initial_label = '«' %}
+ {% set expand_label = '»' %}
+ {% set collapse_label = '«' %}
+ {% endif %}
+
// global elements used by the functions.
// the 'sidebarbutton' element is defined as global after its
// creation, in the add_sidebar_button function
@@ -34,7 +48,7 @@ $(function() {
// original margin-left of the bodywrapper and width of the sidebar
// with the sidebar expanded
- var bw_margin_expanded = bodywrapper.css('margin-left');
+ var bw_margin_expanded = bodywrapper.css('margin-{{side}}');
var ssb_width_expanded = sidebar.width();
// margin-left of the bodywrapper and width of the sidebar
@@ -60,38 +74,38 @@ $(function() {
function collapse_sidebar() {
sidebarwrapper.hide();
sidebar.css('width', ssb_width_collapsed);
- bodywrapper.css('margin-left', bw_margin_collapsed);
+ bodywrapper.css('margin-{{side}}', bw_margin_collapsed);
sidebarbutton.css({
- 'margin-left': '0',
+ 'margin-{{side}}': '0',
'height': bodywrapper.height()
});
- sidebarbutton.find('span').text('»');
+ sidebarbutton.find('span').text('{{expand_label}}');
sidebarbutton.attr('title', _('Expand sidebar'));
document.cookie = 'sidebar=collapsed';
}
function expand_sidebar() {
- bodywrapper.css('margin-left', bw_margin_expanded);
+ bodywrapper.css('margin-{{side}}', bw_margin_expanded);
sidebar.css('width', ssb_width_expanded);
sidebarwrapper.show();
sidebarbutton.css({
- 'margin-left': ssb_width_expanded-12,
+ 'margin-{{side}}': ssb_width_expanded-12,
'height': bodywrapper.height()
});
- sidebarbutton.find('span').text('«');
+ sidebarbutton.find('span').text('{{collapse_label}}');
sidebarbutton.attr('title', _('Collapse sidebar'));
document.cookie = 'sidebar=expanded';
}
function add_sidebar_button() {
sidebarwrapper.css({
- 'float': 'left',
- 'margin-right': '0',
+ 'float': '{{side}}',
+ 'margin-{{opposite}}': '0',
'width': ssb_width_expanded - 28
});
// create the button
sidebar.append(
- '
«
'
+ '
{{initial_label}}
'
);
var sidebarbutton = $('#sidebarbutton');
light_color = sidebarbutton.css('background-color');
@@ -110,12 +124,12 @@ $(function() {
sidebarbutton.attr('title', _('Collapse sidebar'));
sidebarbutton.css({
'color': '#FFFFFF',
- 'border-left': '1px solid ' + dark_color,
+ 'border-{{side}}': '1px solid ' + dark_color,
'font-size': '1.2em',
'cursor': 'pointer',
'height': bodywrapper.height(),
'padding-top': '1px',
- 'margin-left': ssb_width_expanded - 12
+ 'margin-{{side}}': ssb_width_expanded - 12
});
sidebarbutton.hover(
diff --git a/sphinx/themes/epub/epub-cover.html b/sphinx/themes/epub/epub-cover.html
index b3202a33f..5872fb7f1 100644
--- a/sphinx/themes/epub/epub-cover.html
+++ b/sphinx/themes/epub/epub-cover.html
@@ -4,10 +4,10 @@
Sample template for the html cover page.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
-{% extends "layout.html" %}
+{%- extends "layout.html" %}
{%- block header %}{% endblock %}
{%- block rootrellink %}{% endblock %}
{%- block relbaritems %}{% endblock %}
diff --git a/sphinx/themes/epub/layout.html b/sphinx/themes/epub/layout.html
index 1f5ad077f..c2e2e0fd5 100644
--- a/sphinx/themes/epub/layout.html
+++ b/sphinx/themes/epub/layout.html
@@ -4,10 +4,10 @@
Sphinx layout template for the epub theme.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
-{% extends "basic/layout.html" %}
+{%- extends "basic/layout.html" %}
{# add only basic navigation links #}
{% block sidebar1 %}{% endblock %}
diff --git a/sphinx/themes/epub/static/epub.css b/sphinx/themes/epub/static/epub.css
index d2c51751a..9ce90fae2 100644
--- a/sphinx/themes/epub/static/epub.css
+++ b/sphinx/themes/epub/static/epub.css
@@ -4,7 +4,7 @@
*
* Sphinx stylesheet -- epub theme.
*
- * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
diff --git a/sphinx/themes/haiku/layout.html b/sphinx/themes/haiku/layout.html
index 719dba770..edbda50b2 100644
--- a/sphinx/themes/haiku/layout.html
+++ b/sphinx/themes/haiku/layout.html
@@ -4,10 +4,10 @@
Sphinx layout template for the haiku theme.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
-{% extends "basic/layout.html" %}
+{%- extends "basic/layout.html" %}
{% set script_files = script_files + ['_static/theme_extras.js'] %}
{% set css_files = css_files + ['_static/print.css'] %}
diff --git a/sphinx/themes/haiku/static/haiku.css_t b/sphinx/themes/haiku/static/haiku.css_t
index 19f4e0bfd..87b116aeb 100644
--- a/sphinx/themes/haiku/static/haiku.css_t
+++ b/sphinx/themes/haiku/static/haiku.css_t
@@ -16,7 +16,7 @@
* Braden Ewing
* Humdinger
*
- * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
diff --git a/sphinx/themes/nature/static/nature.css_t b/sphinx/themes/nature/static/nature.css_t
index a98bd4209..3c4920341 100644
--- a/sphinx/themes/nature/static/nature.css_t
+++ b/sphinx/themes/nature/static/nature.css_t
@@ -4,7 +4,7 @@
*
* Sphinx stylesheet -- nature theme.
*
- * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
diff --git a/sphinx/themes/pyramid/layout.html b/sphinx/themes/pyramid/layout.html
index 1887361ee..8780ceaef 100644
--- a/sphinx/themes/pyramid/layout.html
+++ b/sphinx/themes/pyramid/layout.html
@@ -1,4 +1,4 @@
-{% extends "basic/layout.html" %}
+{%- extends "basic/layout.html" %}
{%- block extrahead %}
diff --git a/sphinx/themes/scrolls/layout.html b/sphinx/themes/scrolls/layout.html
index 92cb694db..97f7ca1db 100644
--- a/sphinx/themes/scrolls/layout.html
+++ b/sphinx/themes/scrolls/layout.html
@@ -5,10 +5,10 @@
Sphinx layout template for the scrolls theme, originally written
by Armin Ronacher.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
-{% extends "basic/layout.html" %}
+{%- extends "basic/layout.html" %}
{% set script_files = script_files + ['_static/theme_extras.js'] %}
{% set css_files = css_files + ['_static/print.css'] %}
{# do not display relbars #}
diff --git a/sphinx/themes/scrolls/static/scrolls.css_t b/sphinx/themes/scrolls/static/scrolls.css_t
index e3d26ea39..c335f167e 100644
--- a/sphinx/themes/scrolls/static/scrolls.css_t
+++ b/sphinx/themes/scrolls/static/scrolls.css_t
@@ -4,7 +4,7 @@
*
* Sphinx stylesheet -- scrolls theme.
*
- * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
diff --git a/sphinx/themes/sphinxdoc/layout.html b/sphinx/themes/sphinxdoc/layout.html
index c93191851..4ffeb6ed2 100644
--- a/sphinx/themes/sphinxdoc/layout.html
+++ b/sphinx/themes/sphinxdoc/layout.html
@@ -4,10 +4,10 @@
Sphinx layout template for the sphinxdoc theme.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
-{% extends "basic/layout.html" %}
+{%- extends "basic/layout.html" %}
{# put the sidebar before the body #}
{% block sidebar1 %}{{ sidebar() }}{% endblock %}
diff --git a/sphinx/themes/sphinxdoc/static/sphinxdoc.css_t b/sphinx/themes/sphinxdoc/static/sphinxdoc.css_t
index f535696b2..af4982577 100644
--- a/sphinx/themes/sphinxdoc/static/sphinxdoc.css_t
+++ b/sphinx/themes/sphinxdoc/static/sphinxdoc.css_t
@@ -5,7 +5,7 @@
* Sphinx stylesheet -- sphinxdoc theme. Originally created by
* Armin Ronacher for Werkzeug.
*
- * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
diff --git a/sphinx/themes/traditional/static/traditional.css_t b/sphinx/themes/traditional/static/traditional.css_t
index 51567255b..6c6bd5f5a 100644
--- a/sphinx/themes/traditional/static/traditional.css_t
+++ b/sphinx/themes/traditional/static/traditional.css_t
@@ -4,7 +4,7 @@
*
* Sphinx stylesheet -- traditional docs.python.org theme.
*
- * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
diff --git a/sphinx/theming.py b/sphinx/theming.py
index 68d11a490..dd20aa2b3 100644
--- a/sphinx/theming.py
+++ b/sphinx/theming.py
@@ -5,7 +5,7 @@
Theming support for HTML builders.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/transforms.py b/sphinx/transforms.py
new file mode 100644
index 000000000..5b96300de
--- /dev/null
+++ b/sphinx/transforms.py
@@ -0,0 +1,319 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.transforms
+ ~~~~~~~~~~~~~~~~~
+
+ Docutils transforms used by Sphinx when reading documents.
+
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+from os import path
+
+from docutils import nodes
+from docutils.utils import new_document, relative_path
+from docutils.parsers.rst import Parser as RSTParser
+from docutils.transforms import Transform
+from docutils.transforms.parts import ContentsFilter
+
+from sphinx import addnodes
+from sphinx.locale import _, init as init_locale
+from sphinx.util import split_index_msg
+from sphinx.util.nodes import traverse_translatable_index, extract_messages
+from sphinx.util.osutil import ustrftime, find_catalog
+from sphinx.util.pycompat import all
+
+
+default_substitutions = set([
+ 'version',
+ 'release',
+ 'today',
+])
+
+class DefaultSubstitutions(Transform):
+ """
+ Replace some substitutions if they aren't defined in the document.
+ """
+ # run before the default Substitutions
+ default_priority = 210
+
+ def apply(self):
+ config = self.document.settings.env.config
+ # only handle those not otherwise defined in the document
+ to_handle = default_substitutions - set(self.document.substitution_defs)
+ for ref in self.document.traverse(nodes.substitution_reference):
+ refname = ref['refname']
+ if refname in to_handle:
+ text = config[refname]
+ if refname == 'today' and not text:
+ # special handling: can also specify a strftime format
+ text = ustrftime(config.today_fmt or _('%B %d, %Y'))
+ ref.replace_self(nodes.Text(text, text))
+
+
+class MoveModuleTargets(Transform):
+ """
+ Move module targets that are the first thing in a section to the section
+ title.
+
+ XXX Python specific
+ """
+ default_priority = 210
+
+ def apply(self):
+ for node in self.document.traverse(nodes.target):
+ if not node['ids']:
+ continue
+ if (node.has_key('ismod') and
+ node.parent.__class__ is nodes.section and
+ # index 0 is the section title node
+ node.parent.index(node) == 1):
+ node.parent['ids'][0:0] = node['ids']
+ node.parent.remove(node)
+
+
+class HandleCodeBlocks(Transform):
+ """
+ Several code block related transformations.
+ """
+ default_priority = 210
+
+ def apply(self):
+ # move doctest blocks out of blockquotes
+ for node in self.document.traverse(nodes.block_quote):
+ if all(isinstance(child, nodes.doctest_block) for child
+ in node.children):
+ node.replace_self(node.children)
+ # combine successive doctest blocks
+ #for node in self.document.traverse(nodes.doctest_block):
+ # if node not in node.parent.children:
+ # continue
+ # parindex = node.parent.index(node)
+ # while len(node.parent) > parindex+1 and \
+ # isinstance(node.parent[parindex+1], nodes.doctest_block):
+ # node[0] = nodes.Text(node[0] + '\n\n' +
+ # node.parent[parindex+1][0])
+ # del node.parent[parindex+1]
+
+
+class SortIds(Transform):
+ """
+ Sort secion IDs so that the "id[0-9]+" one comes last.
+ """
+ default_priority = 261
+
+ def apply(self):
+ for node in self.document.traverse(nodes.section):
+ if len(node['ids']) > 1 and node['ids'][0].startswith('id'):
+ node['ids'] = node['ids'][1:] + [node['ids'][0]]
+
+
+class CitationReferences(Transform):
+ """
+ Replace citation references by pending_xref nodes before the default
+ docutils transform tries to resolve them.
+ """
+ default_priority = 619
+
+ def apply(self):
+ for citnode in self.document.traverse(nodes.citation_reference):
+ cittext = citnode.astext()
+ refnode = addnodes.pending_xref(cittext, reftype='citation',
+ reftarget=cittext, refwarn=True,
+ ids=citnode["ids"])
+ refnode.line = citnode.line or citnode.parent.line
+ refnode += nodes.Text('[' + cittext + ']')
+ citnode.parent.replace(citnode, refnode)
+
+
+class CustomLocaleReporter(object):
+ """
+ Replacer for document.reporter.get_source_and_line method.
+
+ reST text lines for translation not have original source line number.
+ This class provide correct line number at reporting.
+ """
+ def __init__(self, source, line):
+ self.source, self.line = source, line
+
+ try:
+ from docutils import __version__ as du_version
+ v = tuple([int(x) for x in du_version.split('.')[:2]])
+ except ImportError:
+ v = (99, 99)
+ self.du_version = v
+
+ def set_reporter(self, document):
+ if self.du_version < (0, 9):
+ document.reporter.locator = self.get_source_and_line
+ else:
+ document.reporter.get_source_and_line = self.get_source_and_line
+
+ def get_source_and_line(self, lineno=None):
+ return self.source, self.line
+
+
+class Locale(Transform):
+ """
+ Replace translatable nodes with their translated doctree.
+ """
+ default_priority = 0
+
+ def apply(self):
+ env = self.document.settings.env
+ settings, source = self.document.settings, self.document['source']
+ # XXX check if this is reliable
+ assert source.startswith(env.srcdir)
+ docname = path.splitext(relative_path(env.srcdir, source))[0]
+ textdomain = find_catalog(docname,
+ self.document.settings.gettext_compact)
+
+ # fetch translations
+ dirs = [path.join(env.srcdir, directory)
+ for directory in env.config.locale_dirs]
+ catalog, has_catalog = init_locale(dirs, env.config.language,
+ textdomain)
+ if not has_catalog:
+ return
+
+ parser = RSTParser()
+
+ for node, msg in extract_messages(self.document):
+ msgstr = catalog.gettext(msg)
+ # XXX add marker to untranslated parts
+ if not msgstr or msgstr == msg: # as-of-yet untranslated
+ continue
+
+ # Avoid "Literal block expected; none found." warnings.
+ # If msgstr ends with '::' then it cause warning message at
+ # parser.parse() processing.
+ # literal-block-warning is only appear in avobe case.
+ if msgstr.strip().endswith('::'):
+ msgstr += '\n\n dummy literal'
+ # dummy literal node will discard by 'patch = patch[0]'
+
+ patch = new_document(source, settings)
+ CustomLocaleReporter(node.source, node.line).set_reporter(patch)
+ parser.parse(msgstr, patch)
+ patch = patch[0]
+ # XXX doctest and other block markup
+ if not isinstance(patch, nodes.paragraph):
+ continue # skip for now
+
+ # auto-numbered foot note reference should use original 'ids'.
+ def is_autonumber_footnote_ref(node):
+ return isinstance(node, nodes.footnote_reference) and \
+ node.get('auto') == 1
+ old_foot_refs = node.traverse(is_autonumber_footnote_ref)
+ new_foot_refs = patch.traverse(is_autonumber_footnote_ref)
+ if len(old_foot_refs) != len(new_foot_refs):
+ env.warn_node('inconsistent footnote references in '
+ 'translated message', node)
+ for old, new in zip(old_foot_refs, new_foot_refs):
+ new['ids'] = old['ids']
+ for id in new['ids']:
+ self.document.ids[id] = new
+ self.document.autofootnote_refs.remove(old)
+ self.document.note_autofootnote_ref(new)
+
+ # reference should use original 'refname'.
+ # * reference target ".. _Python: ..." is not translatable.
+ # * section refname is not translatable.
+ # * inline reference "`Python <...>`_" has no 'refname'.
+ def is_refnamed_ref(node):
+ return isinstance(node, nodes.reference) and \
+ 'refname' in node
+ old_refs = node.traverse(is_refnamed_ref)
+ new_refs = patch.traverse(is_refnamed_ref)
+ applied_refname_map = {}
+ if len(old_refs) != len(new_refs):
+ env.warn_node('inconsistent references in '
+ 'translated message', node)
+ for new in new_refs:
+ if new['refname'] in applied_refname_map:
+ # 2nd appearance of the reference
+ new['refname'] = applied_refname_map[new['refname']]
+ elif old_refs:
+ # 1st appearance of the reference in old_refs
+ old = old_refs.pop(0)
+ refname = old['refname']
+ new['refname'] = refname
+ applied_refname_map[new['refname']] = refname
+ else:
+ # the reference is not found in old_refs
+ applied_refname_map[new['refname']] = new['refname']
+
+ self.document.note_refname(new)
+
+ # refnamed footnote and citation should use original 'ids'.
+ def is_refnamed_footnote_ref(node):
+ footnote_ref_classes = (nodes.footnote_reference,
+ nodes.citation_reference)
+ return isinstance(node, footnote_ref_classes) and \
+ 'refname' in node
+ old_refs = node.traverse(is_refnamed_footnote_ref)
+ new_refs = patch.traverse(is_refnamed_footnote_ref)
+ refname_ids_map = {}
+ if len(old_refs) != len(new_refs):
+ env.warn_node('inconsistent references in '
+ 'translated message', node)
+ for old in old_refs:
+ refname_ids_map[old["refname"]] = old["ids"]
+ for new in new_refs:
+ refname = new["refname"]
+ if refname in refname_ids_map:
+ new["ids"] = refname_ids_map[refname]
+
+ # Original pending_xref['reftarget'] contain not-translated
+ # target name, new pending_xref must use original one.
+ # This code restricts to change ref-targets in the translation.
+ old_refs = node.traverse(addnodes.pending_xref)
+ new_refs = patch.traverse(addnodes.pending_xref)
+ xref_reftarget_map = {}
+ if len(old_refs) != len(new_refs):
+ env.warn_node('inconsistent term references in '
+ 'translated message', node)
+ for old in old_refs:
+ key = old["reftype"], old["refdomain"]
+ xref_reftarget_map[key] = old["reftarget"]
+ for new in new_refs:
+ key = new["reftype"], new["refdomain"]
+ if key in xref_reftarget_map:
+ new['reftarget'] = xref_reftarget_map[key]
+
+ # update leaves
+ for child in patch.children:
+ child.parent = node
+ node.children = patch.children
+
+ # Extract and translate messages for index entries.
+ for node, entries in traverse_translatable_index(self.document):
+ new_entries = []
+ for type, msg, tid, main in entries:
+ msg_parts = split_index_msg(type, msg)
+ msgstr_parts = []
+ for part in msg_parts:
+ msgstr = catalog.gettext(part)
+ if not msgstr:
+ msgstr = part
+ msgstr_parts.append(msgstr)
+
+ new_entries.append((type, ';'.join(msgstr_parts), tid, main))
+
+ node['raw_entries'] = entries
+ node['entries'] = new_entries
+
+
+class SphinxContentsFilter(ContentsFilter):
+ """
+ Used with BuildEnvironment.add_toc_from() to discard cross-file links
+ within table-of-contents link nodes.
+ """
+ def visit_pending_xref(self, node):
+ text = node.astext()
+ self.parent.append(nodes.literal(text, text))
+ raise nodes.SkipNode
+
+ def visit_image(self, node):
+ raise nodes.SkipNode
diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py
index 6cb83aec2..8bedda126 100644
--- a/sphinx/util/__init__.py
+++ b/sphinx/util/__init__.py
@@ -5,7 +5,7 @@
Utility functions for Sphinx.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -197,13 +197,18 @@ def get_module_source(modname):
except Exception, err:
raise PycodeError('error importing %r' % modname, err)
mod = sys.modules[modname]
- if hasattr(mod, '__loader__'):
+ filename = getattr(mod, '__file__', None)
+ loader = getattr(mod, '__loader__', None)
+ if loader and getattr(loader, 'get_filename', None):
try:
- source = mod.__loader__.get_source(modname)
+ filename = loader.get_filename(modname)
+ except Exception, err:
+ raise PycodeError('error getting filename for %r' % filename, err)
+ if filename is None and loader:
+ try:
+ return 'string', loader.get_source(modname)
except Exception, err:
raise PycodeError('error getting source for %r' % modname, err)
- return 'string', source
- filename = getattr(mod, '__file__', None)
if filename is None:
raise PycodeError('no source found for module %r' % modname)
filename = path.normpath(path.abspath(filename))
@@ -286,6 +291,12 @@ class Tee(object):
self.stream1.write(text)
self.stream2.write(text)
+ def flush(self):
+ if hasattr(self.stream1, 'flush'):
+ self.stream1.flush()
+ if hasattr(self.stream2, 'flush'):
+ self.stream2.flush()
+
def parselinenos(spec, total):
"""Parse a line number spec (such as "1,2,4-6") and return a list of
@@ -349,6 +360,29 @@ def split_into(n, type, value):
return parts
+def split_index_msg(type, value):
+ # new entry types must be listed in directives/other.py!
+ result = []
+ try:
+ if type == 'single':
+ try:
+ result = split_into(2, 'single', value)
+ except ValueError:
+ result = split_into(1, 'single', value)
+ elif type == 'pair':
+ result = split_into(2, 'pair', value)
+ elif type == 'triple':
+ result = split_into(3, 'triple', value)
+ elif type == 'see':
+ result = split_into(2, 'see', value)
+ elif type == 'seealso':
+ result = split_into(2, 'see', value)
+ except ValueError:
+ pass
+
+ return result
+
+
def format_exception_cut_frames(x=1):
"""Format an exception with traceback, but only the last x frames."""
typ, val, tb = sys.exc_info()
diff --git a/sphinx/util/compat.py b/sphinx/util/compat.py
index bdb00845a..916f6fa3b 100644
--- a/sphinx/util/compat.py
+++ b/sphinx/util/compat.py
@@ -5,7 +5,7 @@
Stuff for docutils compatibility.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/util/console.py b/sphinx/util/console.py
index ac85034d9..b3b27a489 100644
--- a/sphinx/util/console.py
+++ b/sphinx/util/console.py
@@ -5,7 +5,7 @@
Format colored console output.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/util/docfields.py b/sphinx/util/docfields.py
index 896319f56..961b09a78 100644
--- a/sphinx/util/docfields.py
+++ b/sphinx/util/docfields.py
@@ -6,7 +6,7 @@
"Doc fields" are reST field lists in object descriptions that will
be domain-specifically transformed to a more appealing presentation.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/util/docstrings.py b/sphinx/util/docstrings.py
index ba81bf00e..d45b938d2 100644
--- a/sphinx/util/docstrings.py
+++ b/sphinx/util/docstrings.py
@@ -5,7 +5,7 @@
Utilities for docstring processing.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/util/inspect.py b/sphinx/util/inspect.py
index b5c3db598..95b7cb1ee 100644
--- a/sphinx/util/inspect.py
+++ b/sphinx/util/inspect.py
@@ -5,7 +5,7 @@
Helpers for inspecting Python modules.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -27,13 +27,18 @@ if sys.version_info >= (2, 5):
func = func.im_func
parts = 0, ()
if type(func) is partial:
- parts = len(func.args), func.keywords.keys()
+ keywords = func.keywords
+ if keywords is None:
+ keywords = {}
+ parts = len(func.args), keywords.keys()
func = func.func
if not inspect.isfunction(func):
raise TypeError('%r is not a Python function' % func)
args, varargs, varkw = inspect.getargs(func.func_code)
func_defaults = func.func_defaults
- if func_defaults:
+ if func_defaults is None:
+ func_defaults = []
+ else:
func_defaults = list(func_defaults)
if parts[0]:
args = args[parts[0]:]
@@ -45,7 +50,10 @@ if sys.version_info >= (2, 5):
del func_defaults[i]
except IndexError:
pass
- return inspect.ArgSpec(args, varargs, varkw, func_defaults)
+ if sys.version_info >= (2, 6):
+ return inspect.ArgSpec(args, varargs, varkw, func_defaults)
+ else:
+ return (args, varargs, varkw, func_defaults)
else:
getargspec = inspect.getargspec
@@ -70,12 +78,12 @@ def safe_getattr(obj, name, *defargs):
raise AttributeError(name)
-def safe_getmembers(object, predicate=None):
+def safe_getmembers(object, predicate=None, attr_getter=safe_getattr):
"""A version of inspect.getmembers() that uses safe_getattr()."""
results = []
for key in dir(object):
try:
- value = safe_getattr(object, key, None)
+ value = attr_getter(object, key, None)
except AttributeError:
continue
if not predicate or predicate(value):
diff --git a/sphinx/util/jsdump.py b/sphinx/util/jsdump.py
index 191e24191..51fe73380 100644
--- a/sphinx/util/jsdump.py
+++ b/sphinx/util/jsdump.py
@@ -6,7 +6,7 @@
This module implements a simple JavaScript serializer.
Uses the basestring encode function from simplejson by Bob Ippolito.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/util/jsonimpl.py b/sphinx/util/jsonimpl.py
index cf5ddb424..aa0ea8251 100644
--- a/sphinx/util/jsonimpl.py
+++ b/sphinx/util/jsonimpl.py
@@ -5,7 +5,7 @@
JSON serializer implementation wrapper.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/util/matching.py b/sphinx/util/matching.py
index fa6cb7e0b..b4c710765 100644
--- a/sphinx/util/matching.py
+++ b/sphinx/util/matching.py
@@ -5,7 +5,7 @@
Pattern-matching utility functions for Sphinx.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/util/nodes.py b/sphinx/util/nodes.py
index dbedb7f2f..1d4d3ba53 100644
--- a/sphinx/util/nodes.py
+++ b/sphinx/util/nodes.py
@@ -5,7 +5,7 @@
Docutils node-related utility functions for Sphinx.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -43,6 +43,22 @@ IGNORED_NODES = (
def extract_messages(doctree):
"""Extract translatable messages from a document tree."""
for node in doctree.traverse(nodes.TextElement):
+ # workaround: nodes.term doesn't have source, line and rawsource
+ # (fixed in Docutils r7495)
+ if isinstance(node, nodes.term) and not node.source:
+ definition_list_item = node.parent
+ if definition_list_item.line is not None:
+ node.source = definition_list_item.source
+ node.line = definition_list_item.line - 1
+ node.rawsource = definition_list_item.\
+ rawsource.split("\n", 2)[0]
+ # workaround: nodes.caption doesn't have source, line.
+ # this issue was filed to Docutils tracker:
+ # sf.net/tracker/?func=detail&aid=3599485&group_id=38414&atid=422032
+ if isinstance(node, nodes.caption) and not node.source:
+ node.source = node.parent.source
+ node.line = 0 #need fix docutils to get `node.line`
+
if not node.source:
continue # built-in message
if isinstance(node, IGNORED_NODES):
@@ -58,6 +74,19 @@ def extract_messages(doctree):
yield node, msg
+def traverse_translatable_index(doctree):
+ """Traverse translatable index node from a document tree."""
+ def is_block_index(node):
+ return isinstance(node, addnodes.index) and \
+ node.get('inline') == False
+ for node in doctree.traverse(is_block_index):
+ if 'raw_entries' in node:
+ entries = node['raw_entries']
+ else:
+ entries = node['entries']
+ yield node, entries
+
+
def nested_parse_with_titles(state, content, node):
"""Version of state.nested_parse() that allows titles and does not require
titles to have the same decoration as the calling document.
@@ -204,3 +233,17 @@ def _new_copy(self):
return self.__class__(self.rawsource, **self.attributes)
nodes.Element.copy = _new_copy
+
+# monkey-patch Element.__repr__ to return str if include unicode.
+# sf.net/tracker/?func=detail&aid=3601607&group_id=38414&atid=422030
+import sys
+if sys.version_info < (3,):
+ _element_repr_orig = nodes.Element.__repr__
+
+ def _repr(self):
+ s = _element_repr_orig(self)
+ if isinstance(s, unicode):
+ return s.encode('utf-8')
+ return s
+
+ nodes.Element.__repr__ = _repr
diff --git a/sphinx/util/osutil.py b/sphinx/util/osutil.py
index 5becc37df..7322289ef 100644
--- a/sphinx/util/osutil.py
+++ b/sphinx/util/osutil.py
@@ -5,7 +5,7 @@
Operating system-related utility functions for Sphinx.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -40,12 +40,20 @@ def relative_uri(base, to):
return to
b2 = base.split(SEP)
t2 = to.split(SEP)
- # remove common segments
- for x, y in zip(b2, t2):
+ # remove common segments (except the last segment)
+ for x, y in zip(b2[:-1], t2[:-1]):
if x != y:
break
b2.pop(0)
t2.pop(0)
+ if b2 == t2:
+ # Special case: relative_uri('f/index.html','f/index.html')
+ # returns '', not 'index.html'
+ return ''
+ if len(b2) == 1 and t2 == ['']:
+ # Special case: relative_uri('f/index.html','f/') should
+ # return './', not ''
+ return '.' + SEP
return ('..' + SEP) * (len(b2)-1) + SEP.join(t2)
@@ -136,8 +144,9 @@ else:
def safe_relpath(path, start=None):
+ from sphinx.util.pycompat import relpath
try:
- return os.path.relpath(path, start)
+ return relpath(path, start)
except ValueError:
return path
@@ -148,3 +157,6 @@ def find_catalog(docname, compaction):
ret = docname
return ret
+
+fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding()
+
diff --git a/sphinx/util/png.py b/sphinx/util/png.py
index 50c72efdc..3adabadf9 100644
--- a/sphinx/util/png.py
+++ b/sphinx/util/png.py
@@ -5,7 +5,7 @@
PNG image manipulation helpers.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/util/pycompat.py b/sphinx/util/pycompat.py
index 9e081b02f..3d252c917 100644
--- a/sphinx/util/pycompat.py
+++ b/sphinx/util/pycompat.py
@@ -5,7 +5,7 @@
Stuff for Python version compatibility.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -64,6 +64,35 @@ else:
return s.encode('ascii', 'backslashreplace')
+def execfile_(filepath, _globals):
+ from sphinx.util.osutil import fs_encoding
+ # get config source -- 'b' is a no-op under 2.x, while 'U' is
+ # ignored under 3.x (but 3.x compile() accepts \r\n newlines)
+ f = open(filepath, 'rbU')
+ try:
+ source = f.read()
+ finally:
+ f.close()
+
+ # py25,py26,py31 accept only LF eol instead of CRLF
+ if sys.version_info[:2] in ((2, 5), (2, 6), (3, 1)):
+ source = source.replace(b('\r\n'), b('\n'))
+
+ # compile to a code object, handle syntax errors
+ filepath_enc = filepath.encode(fs_encoding)
+ try:
+ code = compile(source, filepath_enc, 'exec')
+ except SyntaxError:
+ if convert_with_2to3:
+ # maybe the file uses 2.x syntax; try to refactor to
+ # 3.x syntax using 2to3
+ source = convert_with_2to3(filepath)
+ code = compile(source, filepath_enc, 'exec')
+ else:
+ raise
+ exec code in _globals
+
+
try:
from html import escape as htmlescape
except ImportError:
@@ -82,6 +111,13 @@ if sys.version_info >= (2, 6):
except ImportError:
from itertools import izip_longest as zip_longest
+ import os
+ relpath = os.path.relpath
+ del os
+
+ import io
+ open = io.open
+
else:
# Python < 2.6
from itertools import izip, repeat, chain
@@ -114,6 +150,39 @@ else:
except IndexError:
pass
+ from os.path import curdir
+ def relpath(path, start=curdir):
+ """Return a relative version of a path"""
+ from os.path import sep, abspath, commonprefix, join, pardir
+
+ if not path:
+ raise ValueError("no path specified")
+
+ start_list = abspath(start).split(sep)
+ path_list = abspath(path).split(sep)
+
+ # Work out how much of the filepath is shared by start and path.
+ i = len(commonprefix([start_list, path_list]))
+
+ rel_list = [pardir] * (len(start_list)-i) + path_list[i:]
+ if not rel_list:
+ return start
+ return join(*rel_list)
+ del curdir
+
+ from types import MethodType
+ def open(filename, mode='r', *args, **kw):
+ newline = kw.pop('newline', None)
+ mode = mode.replace('t', '')
+ f = codecs.open(filename, mode, *args, **kw)
+ if newline is not None:
+ f._write = f.write
+ def write(self, text):
+ text = text.replace(u'\r\n', u'\n').replace(u'\n', newline)
+ self._write(text)
+ f.write = MethodType(write, f)
+ return f
+
# ------------------------------------------------------------------------------
# Missing builtins and codecs in Python < 2.5
diff --git a/sphinx/util/tags.py b/sphinx/util/tags.py
index 7499634da..a6e621407 100644
--- a/sphinx/util/tags.py
+++ b/sphinx/util/tags.py
@@ -3,7 +3,7 @@
sphinx.util.tags
~~~~~~~~~~~~~~~~
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/util/texescape.py b/sphinx/util/texescape.py
index 1f6f76e66..926208a09 100644
--- a/sphinx/util/texescape.py
+++ b/sphinx/util/texescape.py
@@ -5,7 +5,7 @@
TeX escaping helper.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/util/websupport.py b/sphinx/util/websupport.py
index d9b472135..a20947a8d 100644
--- a/sphinx/util/websupport.py
+++ b/sphinx/util/websupport.py
@@ -3,7 +3,7 @@
sphinx.util.websupport
~~~~~~~~~~~~~~~~~~~~~~
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/versioning.py b/sphinx/versioning.py
index d45ed1d3f..a16751bb5 100644
--- a/sphinx/versioning.py
+++ b/sphinx/versioning.py
@@ -6,7 +6,7 @@
Implements the low-level algorithms Sphinx uses for the versioning of
doctrees.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from uuid import uuid4
diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py
index 72864a17b..55d56fa03 100644
--- a/sphinx/websupport/__init__.py
+++ b/sphinx/websupport/__init__.py
@@ -5,7 +5,7 @@
Base Module for web support functions.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/websupport/errors.py b/sphinx/websupport/errors.py
index 225b10c86..a0bc07379 100644
--- a/sphinx/websupport/errors.py
+++ b/sphinx/websupport/errors.py
@@ -5,7 +5,7 @@
Contains Error classes for the web support package.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/websupport/search/__init__.py b/sphinx/websupport/search/__init__.py
index 385c3fa97..a5b40117b 100644
--- a/sphinx/websupport/search/__init__.py
+++ b/sphinx/websupport/search/__init__.py
@@ -5,7 +5,7 @@
Server side search support for the web support package.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -102,7 +102,7 @@ class BaseSearch(object):
res = self.context_re.search(text)
if res is None:
return ''
- context_start = max(res.start() - length/2, 0)
+ context_start = max(res.start() - int(length/2), 0)
context_end = context_start + length
context = ''.join([context_start > 0 and '...' or '',
text[context_start:context_end],
diff --git a/sphinx/websupport/search/nullsearch.py b/sphinx/websupport/search/nullsearch.py
index 61f2d2fba..bfa78ce7f 100644
--- a/sphinx/websupport/search/nullsearch.py
+++ b/sphinx/websupport/search/nullsearch.py
@@ -5,7 +5,7 @@
The default search adapter, does nothing.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/websupport/search/whooshsearch.py b/sphinx/websupport/search/whooshsearch.py
index 1ed3d714b..f6343e7e2 100644
--- a/sphinx/websupport/search/whooshsearch.py
+++ b/sphinx/websupport/search/whooshsearch.py
@@ -5,7 +5,7 @@
Whoosh search adapter.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/websupport/search/xapiansearch.py b/sphinx/websupport/search/xapiansearch.py
index 0615be842..6476a71df 100644
--- a/sphinx/websupport/search/xapiansearch.py
+++ b/sphinx/websupport/search/xapiansearch.py
@@ -5,7 +5,7 @@
Xapian search adapter.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/websupport/storage/__init__.py b/sphinx/websupport/storage/__init__.py
index 77292812b..871aa03bb 100644
--- a/sphinx/websupport/storage/__init__.py
+++ b/sphinx/websupport/storage/__init__.py
@@ -5,7 +5,7 @@
Storage for the websupport package.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/websupport/storage/differ.py b/sphinx/websupport/storage/differ.py
index fb3b8dc95..e088f9825 100644
--- a/sphinx/websupport/storage/differ.py
+++ b/sphinx/websupport/storage/differ.py
@@ -5,7 +5,7 @@
A differ for creating an HTML representations of proposal diffs
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/websupport/storage/sqlalchemy_db.py b/sphinx/websupport/storage/sqlalchemy_db.py
index 67136d1ae..587b27413 100644
--- a/sphinx/websupport/storage/sqlalchemy_db.py
+++ b/sphinx/websupport/storage/sqlalchemy_db.py
@@ -6,7 +6,7 @@
SQLAlchemy table and mapper definitions used by the
:class:`sphinx.websupport.storage.sqlalchemystorage.SQLAlchemyStorage`.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/websupport/storage/sqlalchemystorage.py b/sphinx/websupport/storage/sqlalchemystorage.py
index e6eccfe97..56528f4be 100644
--- a/sphinx/websupport/storage/sqlalchemystorage.py
+++ b/sphinx/websupport/storage/sqlalchemystorage.py
@@ -5,7 +5,7 @@
An SQLAlchemy storage backend.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -116,7 +116,7 @@ class SQLAlchemyStorage(StorageBackend):
def get_metadata(self, docname, moderator):
session = Session()
subquery = session.query(
- Comment.id, Comment.node_id,
+ Comment.node_id,
func.count('*').label('comment_count')).group_by(
Comment.node_id).subquery()
nodes = session.query(Node.id, subquery.c.comment_count).outerjoin(
diff --git a/sphinx/writers/__init__.py b/sphinx/writers/__init__.py
index 880e4fdbf..40db0d69c 100644
--- a/sphinx/writers/__init__.py
+++ b/sphinx/writers/__init__.py
@@ -5,6 +5,6 @@
Custom docutils writers.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py
index 2051e38ee..252cedc41 100644
--- a/sphinx/writers/html.py
+++ b/sphinx/writers/html.py
@@ -5,7 +5,7 @@
docutils writers handling Sphinx' custom nodes.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -23,7 +23,10 @@ from sphinx.util.smartypants import sphinx_smarty_pants
try:
from PIL import Image # check for the Python Imaging Library
except ImportError:
- Image = None
+ try:
+ import Image
+ except ImportError:
+ Image = None
class HTMLWriter(Writer):
@@ -200,7 +203,7 @@ class HTMLTranslator(BaseTranslator):
def visit_admonition(self, node, name=''):
self.body.append(self.starttag(
node, 'div', CLASS=('admonition ' + name)))
- if name and name != 'seealso':
+ if name:
node.insert(0, nodes.title(name, admonitionlabels[name]))
self.set_first_last(node)
diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py
index 4281b6545..6471a61a1 100644
--- a/sphinx/writers/latex.py
+++ b/sphinx/writers/latex.py
@@ -8,7 +8,7 @@
Much of this code is adapted from Dave Kuhlman's "docpy" writer from his
docutils sandbox.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -100,8 +100,9 @@ class LaTeXWriter(writers.Writer):
class ExtBabel(Babel):
def get_shorthandoff(self):
shortlang = self.language.split('_')[0]
- if shortlang in ('de', 'ngerman', 'sl', 'slovene', 'pt', 'portuges', 'es', 'spanish',
- 'nl', 'dutch', 'pl', 'polish', 'it', 'italian'):
+ if shortlang in ('de', 'ngerman', 'sl', 'slovene', 'pt', 'portuges',
+ 'es', 'spanish', 'nl', 'dutch', 'pl', 'polish', 'it',
+ 'italian'):
return '\\shorthandoff{"}'
return ''
@@ -206,20 +207,20 @@ class LaTeXTranslator(nodes.NodeVisitor):
# pTeX (Japanese TeX) for support
if builder.config.language == 'ja':
- self.elements['classoptions'] = ',dvipdfm'
- # found elements of babel, but this should be above sphinx.sty.
- # because pTeX (Japanese TeX) cannot handle this count.
- self.elements['babel'] = r'\newcount\pdfoutput\pdfoutput=0'
- # to make the pdf with correct encoded hyperref bookmarks
- self.elements['preamble'] += \
- r'\AtBeginDvi{\special{pdf:tounicode EUC-UCS2}}'
+ # use dvipdfmx as default class option in Japanese
+ self.elements['classoptions'] = ',dvipdfmx'
+ # disable babel which has not publishing quality in Japanese
+ self.elements['babel'] = ''
+ # disable fncychap in Japanese documents
+ self.elements['fncychap'] = ''
else:
self.elements['classoptions'] += ',english'
print self.elements
# allow the user to override them all
self.elements.update(builder.config.latex_elements)
if self.elements['extraclassoptions']:
- self.elements['classoptions'] += ',' + self.elements['extraclassoptions']
+ self.elements['classoptions'] += ',' + \
+ self.elements['extraclassoptions']
self.highlighter = highlighting.PygmentsBridge('latex',
builder.config.pygments_style, builder.config.trim_doctest_flags)
@@ -297,7 +298,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
if i > 0:
ret.append('\\indexspace\n')
ret.append('\\bigletter{%s}\n' %
- letter.translate(tex_escape_map))
+ unicode(letter).translate(tex_escape_map))
for entry in entries:
if not entry[3]:
continue
diff --git a/sphinx/writers/manpage.py b/sphinx/writers/manpage.py
index 81f2988b2..cca2d0740 100644
--- a/sphinx/writers/manpage.py
+++ b/sphinx/writers/manpage.py
@@ -5,7 +5,7 @@
Manual page writer, extended for Sphinx custom nodes.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -72,6 +72,11 @@ class ManualPageTranslator(BaseTranslator):
# since self.append_header() is never called, need to do this here
self.body.append(MACRO_DEF)
+ # Overwrite admonition label translations with our own
+ for label, translation in admonitionlabels.items():
+ self.language.labels[label] = self.deunicode(translation)
+
+
# overwritten -- added quotes around all .TH arguments
def header(self):
tmpl = (".TH \"%(title_upper)s\" \"%(manual_section)s\""
@@ -193,12 +198,6 @@ class ManualPageTranslator(BaseTranslator):
def depart_seealso(self, node):
self.depart_admonition(node)
- # overwritten -- use our own label translations
- def visit_admonition(self, node, name=None):
- if name:
- self.body.append('.IP %s\n' %
- self.deunicode(admonitionlabels.get(name, name)))
-
def visit_productionlist(self, node):
self.ensure_eol()
names = []
@@ -341,5 +340,13 @@ class ManualPageTranslator(BaseTranslator):
self.body.append(node.astext())
raise nodes.SkipNode
+ def visit_meta(self, node):
+ raise nodes.SkipNode
+
+ def visit_inline(self, node):
+ pass
+ def depart_inline(self, node):
+ pass
+
def unknown_visit(self, node):
raise NotImplementedError('Unknown node: ' + node.__class__.__name__)
diff --git a/sphinx/writers/texinfo.py b/sphinx/writers/texinfo.py
index 5799e0234..a8306c113 100644
--- a/sphinx/writers/texinfo.py
+++ b/sphinx/writers/texinfo.py
@@ -5,7 +5,7 @@
Custom docutils writer for Texinfo.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -233,30 +233,31 @@ class TexinfoTranslator(nodes.NodeVisitor):
"""Generates a unique id for each section.
Assigns the attribute ``node_name`` to each section."""
+
+ def add_node_name(name):
+ node_id = self.escape_id(name)
+ nth, suffix = 1, ''
+ while node_id + suffix in self.written_ids or \
+ node_id + suffix in self.node_names:
+ nth += 1
+ suffix = '<%s>' % nth
+ node_id += suffix
+ self.written_ids.add(node_id)
+ self.node_names[node_id] = name
+ return node_id
+
# must have a "Top" node
self.document['node_name'] = 'Top'
- self.node_names['Top'] = 'Top'
- self.written_ids.update(('Top', 'top'))
+ add_node_name('Top')
+ add_node_name('top')
# each index is a node
- for name, content in self.indices:
- self.node_names[name] = name
- self.written_ids.add(name)
+ self.indices = [(add_node_name(name), content)
+ for name, content in self.indices]
# each section is also a node
for section in self.document.traverse(nodes.section):
title = section.next_node(nodes.Titular)
name = (title and title.astext()) or ''
- node_id = self.escape_id(name) or ''
- assert node_id and name
- nth, suffix = 1, ''
- while node_id + suffix in self.written_ids:
- nth += 1
- suffix = '<%s>' % nth
- node_id += suffix
- assert node_id not in self.node_names
- assert node_id not in self.written_ids
- section['node_name'] = node_id
- self.node_names[node_id] = name
- self.written_ids.add(node_id)
+ section['node_name'] = add_node_name(name)
def collect_node_menus(self):
"""Collect the menu entries for each "node" section."""
@@ -371,7 +372,11 @@ class TexinfoTranslator(nodes.NodeVisitor):
for entry in entries:
name = self.node_names[entry]
# special formatting for entries that are divided by an em-dash
- parts = reg.split(name, 1)
+ try:
+ parts = reg.split(name, 1)
+ except TypeError:
+ # could be a gettext proxy
+ parts = [name]
if len(parts) == 2:
name, desc = parts
else:
@@ -450,8 +455,7 @@ class TexinfoTranslator(nodes.NodeVisitor):
self.builder.docnames)
if not content:
continue
- node_name = self.escape_id(indexcls.localname)
- self.indices.append((node_name,
+ self.indices.append((indexcls.localname,
generate(content, collapsed)))
self.indices.append((_('Index'), '\n@printindex ge\n'))
@@ -1175,7 +1179,7 @@ class TexinfoTranslator(nodes.NodeVisitor):
for id in production.get('ids'):
self.add_anchor(id, production)
s = production['tokenname'].ljust(maxlen) + ' ::='
- lastname = production['tokenname']
+ ##lastname = production['tokenname']
else:
s = '%s ' % (' '*maxlen)
self.body.append(self.escape(s))
diff --git a/sphinx/writers/text.py b/sphinx/writers/text.py
index e5ab070c7..1f90497e7 100644
--- a/sphinx/writers/text.py
+++ b/sphinx/writers/text.py
@@ -5,14 +5,16 @@
Custom docutils writer for plain text.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import os
import re
import textwrap
+from itertools import groupby
from docutils import nodes, writers
+from docutils.utils import column_width
from sphinx import addnodes
from sphinx.locale import admonitionlabels, versionlabels, _
@@ -27,6 +29,98 @@ class TextWrapper(textwrap.TextWrapper):
r'[^\s\w]*\w+[a-zA-Z]-(?=\w+[a-zA-Z])|' # hyphenated words
r'(?<=[\w\!\"\'\&\.\,\?])-{2,}(?=\w))') # em-dash
+ def _wrap_chunks(self, chunks):
+ """_wrap_chunks(chunks : [string]) -> [string]
+
+ Original _wrap_chunks use len() to calculate width.
+ This method respect to wide/fullwidth characters for width adjustment.
+ """
+ drop_whitespace = getattr(self, 'drop_whitespace', True) #py25 compat
+ lines = []
+ if self.width <= 0:
+ raise ValueError("invalid width %r (must be > 0)" % self.width)
+
+ chunks.reverse()
+
+ while chunks:
+ cur_line = []
+ cur_len = 0
+
+ if lines:
+ indent = self.subsequent_indent
+ else:
+ indent = self.initial_indent
+
+ width = self.width - column_width(indent)
+
+ if drop_whitespace and chunks[-1].strip() == '' and lines:
+ del chunks[-1]
+
+ while chunks:
+ l = column_width(chunks[-1])
+
+ if cur_len + l <= width:
+ cur_line.append(chunks.pop())
+ cur_len += l
+
+ else:
+ break
+
+ if chunks and column_width(chunks[-1]) > width:
+ self._handle_long_word(chunks, cur_line, cur_len, width)
+
+ if drop_whitespace and cur_line and cur_line[-1].strip() == '':
+ del cur_line[-1]
+
+ if cur_line:
+ lines.append(indent + ''.join(cur_line))
+
+ return lines
+
+ def _break_word(self, word, space_left):
+ """_break_word(word : string, space_left : int) -> (string, string)
+
+ Break line by unicode width instead of len(word).
+ """
+ total = 0
+ for i,c in enumerate(word):
+ total += column_width(c)
+ if total > space_left:
+ return word[:i-1], word[i-1:]
+ return word, ''
+
+ def _split(self, text):
+ """_split(text : string) -> [string]
+
+ Override original method that only split by 'wordsep_re'.
+ This '_split' split wide-characters into chunk by one character.
+ """
+ split = lambda t: textwrap.TextWrapper._split(self, t)
+ chunks = []
+ for chunk in split(text):
+ for w, g in groupby(chunk, column_width):
+ if w == 1:
+ chunks.extend(split(''.join(g)))
+ else:
+ chunks.extend(list(g))
+ return chunks
+
+ def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
+ """_handle_long_word(chunks : [string],
+ cur_line : [string],
+ cur_len : int, width : int)
+
+ Override original method for using self._break_word() instead of slice.
+ """
+ space_left = max(width - cur_len, 1)
+ if self.break_long_words:
+ l, r = self._break_word(reversed_chunks[-1], space_left)
+ cur_line.append(l)
+ reversed_chunks[-1] = r
+
+ elif not cur_line:
+ cur_line.append(reversed_chunks.pop())
+
MAXWIDTH = 70
STDINDENT = 3
@@ -71,6 +165,7 @@ class TextTranslator(nodes.NodeVisitor):
self.stateindent = [0]
self.list_counter = []
self.sectionlevel = 0
+ self.lineblocklevel = 0
self.table = None
def add_text(self, text):
@@ -164,7 +259,8 @@ class TextTranslator(nodes.NodeVisitor):
char = '^'
text = ''.join(x[1] for x in self.states.pop() if x[0] == -1)
self.stateindent.pop()
- self.states[-1].append((0, ['', text, '%s' % (char * len(text)), '']))
+ self.states[-1].append(
+ (0, ['', text, '%s' % (char * column_width(text)), '']))
def visit_subtitle(self, node):
pass
@@ -270,11 +366,6 @@ class TextTranslator(nodes.NodeVisitor):
self.end_state(wrap=False)
raise nodes.SkipNode
- def visit_seealso(self, node):
- self.new_state()
- def depart_seealso(self, node):
- self.end_state(first='')
-
def visit_footnote(self, node):
self._footnote = node.children[0].astext().strip()
self.new_state(len(self._footnote) + 3)
@@ -293,6 +384,11 @@ class TextTranslator(nodes.NodeVisitor):
def visit_label(self, node):
raise nodes.SkipNode
+ def visit_legend(self, node):
+ pass
+ def depart_legend(self, node):
+ pass
+
# XXX: option list could use some better styling
def visit_option_list(self, node):
@@ -390,7 +486,7 @@ class TextTranslator(nodes.NodeVisitor):
for i, cell in enumerate(line):
par = my_wrap(cell, width=colwidths[i])
if par:
- maxwidth = max(map(len, par))
+ maxwidth = max(map(column_width, par))
else:
maxwidth = 0
realwidths[i] = max(realwidths[i], maxwidth)
@@ -410,7 +506,9 @@ class TextTranslator(nodes.NodeVisitor):
out = ['|']
for i, cell in enumerate(line):
if cell:
- out.append(' ' + cell.ljust(realwidths[i]+1))
+ adjust_len = len(cell) - column_width(cell)
+ out.append(' ' + cell.ljust(
+ realwidths[i] + 1 + adjust_len))
else:
out.append(' ' * (realwidths[i] + 2))
out.append('|')
@@ -572,6 +670,8 @@ class TextTranslator(nodes.NodeVisitor):
depart_tip = _make_depart_admonition('tip')
visit_warning = _visit_admonition
depart_warning = _make_depart_admonition('warning')
+ visit_seealso = _visit_admonition
+ depart_seealso = _make_depart_admonition('seealso')
def visit_versionmodified(self, node):
self.new_state(0)
@@ -593,14 +693,18 @@ class TextTranslator(nodes.NodeVisitor):
self.end_state(wrap=False)
def visit_line_block(self, node):
- self.new_state(0)
+ self.new_state()
+ self.lineblocklevel += 1
def depart_line_block(self, node):
- self.end_state(wrap=False)
+ self.lineblocklevel -= 1
+ self.end_state(wrap=False, end=None)
+ if not self.lineblocklevel:
+ self.add_text('\n')
def visit_line(self, node):
pass
def depart_line(self, node):
- pass
+ self.add_text('\n')
def visit_block_quote(self, node):
self.new_state()
@@ -709,6 +813,11 @@ class TextTranslator(nodes.NodeVisitor):
def depart_inline(self, node):
pass
+ def visit_container(self, node):
+ pass
+ def depart_container(self, node):
+ pass
+
def visit_problematic(self, node):
self.add_text('>>')
def depart_problematic(self, node):
diff --git a/sphinx/writers/websupport.py b/sphinx/writers/websupport.py
index f75cd47b1..71a9ba00d 100644
--- a/sphinx/writers/websupport.py
+++ b/sphinx/writers/websupport.py
@@ -5,7 +5,7 @@
sphinx.websupport writer that adds comment-related annotations.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/writers/xml.py b/sphinx/writers/xml.py
new file mode 100644
index 000000000..9f1fc7626
--- /dev/null
+++ b/sphinx/writers/xml.py
@@ -0,0 +1,51 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.writers.xml
+ ~~~~~~~~~~~~~~~~~~
+
+ Docutils-native XML and pseudo-XML writers.
+
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+from docutils import writers
+from docutils.writers.docutils_xml import Writer as BaseXMLWriter
+
+
+class XMLWriter(BaseXMLWriter):
+
+ def __init__(self, builder):
+ BaseXMLWriter.__init__(self)
+ self.builder = builder
+
+ def translate(self, *args, **kwargs):
+ self.document.settings.newlines = \
+ self.document.settings.indents = \
+ self.builder.env.config.xml_pretty
+ self.document.settings.xml_declaration = True
+ self.document.settings.doctype_declaration = True
+ return BaseXMLWriter.translate(self)
+
+
+class PseudoXMLWriter(writers.Writer):
+
+ supported = ('pprint', 'pformat', 'pseudoxml')
+ """Formats this writer supports."""
+
+ config_section = 'pseudoxml writer'
+ config_section_dependencies = ('writers',)
+
+ output = None
+ """Final translated form of `document`."""
+
+ def __init__(self, builder):
+ writers.Writer.__init__(self)
+ self.builder = builder
+
+ def translate(self):
+ self.output = self.document.pformat()
+
+ def supports(self, format):
+ """This writer supports all format-specific elements."""
+ return True
diff --git a/tests/path.py b/tests/path.py
index 8e9afeaa8..bd0552d70 100644
--- a/tests/path.py
+++ b/tests/path.py
@@ -16,16 +16,16 @@ from codecs import open
FILESYSTEMENCODING = sys.getfilesystemencoding() or sys.getdefaultencoding()
-class path(str):
+class path(unicode):
"""
Represents a path which behaves like a string.
"""
if sys.version_info < (3, 0):
def __new__(cls, s, encoding=FILESYSTEMENCODING, errors='strict'):
- if isinstance(s, unicode):
- s = s.encode(encoding, errors=errors)
- return str.__new__(cls, s)
- return str.__new__(cls, s)
+ if isinstance(s, str):
+ s = s.decode(encoding, errors)
+ return unicode.__new__(cls, s)
+ return unicode.__new__(cls, s)
@property
def parent(self):
@@ -193,4 +193,4 @@ class path(str):
__div__ = __truediv__ = joinpath
def __repr__(self):
- return '%s(%s)' % (self.__class__.__name__, str.__repr__(self))
+ return '%s(%s)' % (self.__class__.__name__, unicode.__repr__(self))
diff --git a/tests/root/Makefile b/tests/root/Makefile
index e9c11d067..7954bc7cb 100644
--- a/tests/root/Makefile
+++ b/tests/root/Makefile
@@ -23,7 +23,7 @@ help:
@echo " linkcheck to check all external links for integrity"
clean:
- -rm -rf _build/*
+ rm -rf _build/*
html:
mkdir -p _build/html _build/doctrees
diff --git a/tests/root/autodoc.txt b/tests/root/autodoc.txt
index 5c03f947c..d4b3404c4 100644
--- a/tests/root/autodoc.txt
+++ b/tests/root/autodoc.txt
@@ -32,3 +32,16 @@ Just testing a few autodoc possibilities...
:noindex:
.. autoclass:: MarkupError
+
+
+.. currentmodule:: test_autodoc
+
+.. autoclass:: InstAttCls
+ :members:
+
+ All members (5 total)
+
+.. autoclass:: InstAttCls
+ :members: ca1, ia1
+
+ Specific members (2 total)
diff --git a/tests/root/conf.py b/tests/root/conf.py
index b97ddfcc1..37b9127e7 100644
--- a/tests/root/conf.py
+++ b/tests/root/conf.py
@@ -3,6 +3,7 @@
import sys, os
sys.path.append(os.path.abspath('.'))
+sys.path.append(os.path.abspath('..'))
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.jsmath', 'sphinx.ext.todo',
'sphinx.ext.coverage', 'sphinx.ext.autosummary',
@@ -54,6 +55,11 @@ texinfo_documents = [
'Georg Brandl \\and someone else', 'Sphinx Testing', 'Miscellaneous'),
]
+man_pages = [
+ ('contents', 'SphinxTests', 'Sphinx Tests Documentation',
+ 'Georg Brandl and someone else', 1),
+]
+
value_from_conf_py = 84
coverage_c_path = ['special/*.h']
@@ -67,6 +73,28 @@ extlinks = {'issue': ('http://bugs.python.org/issue%s', 'issue '),
# modify tags from conf.py
tags.add('confpytag')
+# -- linkcode
+
+if 'test_linkcode' in tags:
+ import glob
+
+ extensions.remove('sphinx.ext.viewcode')
+ extensions.append('sphinx.ext.linkcode')
+
+ exclude_patterns.extend(glob.glob('*.txt') + glob.glob('*/*.txt'))
+ exclude_patterns.remove('contents.txt')
+ exclude_patterns.remove('objects.txt')
+
+ def linkcode_resolve(domain, info):
+ if domain == 'py':
+ fn = info['module'].replace('.', '/')
+ return "http://foobar/source/%s.py" % fn
+ elif domain == "js":
+ return "http://foobar/js/" + info['fullname']
+ elif domain in ("c", "cpp"):
+ return "http://foobar/%s/%s" % (domain, "".join(info['names']))
+ else:
+ raise AssertionError()
# -- extension API
diff --git a/tests/root/contents.txt b/tests/root/contents.txt
index 280953b46..de60ec6dd 100644
--- a/tests/root/contents.txt
+++ b/tests/root/contents.txt
@@ -27,6 +27,7 @@ Contents:
doctest
extensions
versioning/index
+ footnote
Python
diff --git a/tests/root/footnote.txt b/tests/root/footnote.txt
new file mode 100644
index 000000000..c1359dfe4
--- /dev/null
+++ b/tests/root/footnote.txt
@@ -0,0 +1,40 @@
+:tocdepth: 2
+
+Testing footnote and citation
+================================
+.. #1058 footnote-backlinks-do-not-work
+
+numbered footnote
+--------------------
+
+[1]_
+
+auto-numbered footnote
+------------------------------
+
+[#]_
+
+named footnote
+--------------------
+
+[#foo]_
+
+citation
+--------------------
+
+[bar]_
+
+footenotes
+--------------------
+
+.. rubric:: Footnotes
+
+.. [1] numbered
+
+.. [#] auto numbered
+
+.. [#foo] named
+
+.. rubric:: Citations
+
+.. [bar] cite
diff --git a/tests/root/markup.txt b/tests/root/markup.txt
index dde13a6b5..cbfbbbe3d 100644
--- a/tests/root/markup.txt
+++ b/tests/root/markup.txt
@@ -191,6 +191,10 @@ Figures
My caption of the figure
+ My description paragraph of the figure.
+
+ Description paragraph is wraped with legend node.
+
Version markup
--------------
diff --git a/tests/roots/test-intl/bom.po b/tests/roots/test-intl/bom.po
new file mode 100644
index 000000000..c6025eb1e
--- /dev/null
+++ b/tests/roots/test-intl/bom.po
@@ -0,0 +1,12 @@
+#, fuzzy
+msgid ""
+msgstr ""
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "File with UTF-8 BOM"
+msgstr "Datei mit UTF-8"
+
+msgid "This file has a UTF-8 \"BOM\"."
+msgstr "This file has umlauts: äöü."
diff --git a/tests/roots/test-intl/bom.txt b/tests/roots/test-intl/bom.txt
new file mode 100644
index 000000000..3fea824f8
--- /dev/null
+++ b/tests/roots/test-intl/bom.txt
@@ -0,0 +1,5 @@
+File with UTF-8 BOM
+===================
+
+This file has a UTF-8 "BOM".
+
diff --git a/tests/roots/test-intl/conf.py b/tests/roots/test-intl/conf.py
new file mode 100644
index 000000000..457c5056f
--- /dev/null
+++ b/tests/roots/test-intl/conf.py
@@ -0,0 +1,7 @@
+# -*- coding: utf-8 -*-
+
+import sys, os
+
+project = 'Sphinx intl '
+source_suffix = '.txt'
+keep_warnings = True
diff --git a/tests/roots/test-intl/contents.txt b/tests/roots/test-intl/contents.txt
new file mode 100644
index 000000000..298beb6ce
--- /dev/null
+++ b/tests/roots/test-intl/contents.txt
@@ -0,0 +1,21 @@
+CONTENTS
+========
+
+.. toctree::
+ :maxdepth: 2
+ :numbered:
+
+ subdir/contents
+ bom
+ warnings
+ footnote
+ external_links
+ refs_inconsistency
+ literalblock
+ seealso
+ definition_terms
+ figure_caption
+ index_entries
+ role_xref
+ glossary_terms
+ glossary_terms_inconsistency
diff --git a/tests/roots/test-intl/definition_terms.po b/tests/roots/test-intl/definition_terms.po
new file mode 100644
index 000000000..2c3a3bca4
--- /dev/null
+++ b/tests/roots/test-intl/definition_terms.po
@@ -0,0 +1,32 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) 2012, foof
+# This file is distributed under the same license as the foo package.
+# FIRST AUTHOR , YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: sphinx 1.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-01-01 05:00\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: LANGUAGE \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "i18n with definition terms"
+msgstr "I18N WITH DEFINITION TERMS"
+
+msgid "Some term"
+msgstr "SOME TERM"
+
+msgid "The corresponding definition"
+msgstr "THE CORRESPONDING DEFINITION"
+
+msgid "Some other term"
+msgstr "SOME OTHER TERM"
+
+msgid "The corresponding definition #2"
+msgstr "THE CORRESPONDING DEFINITION #2"
diff --git a/tests/roots/test-intl/definition_terms.txt b/tests/roots/test-intl/definition_terms.txt
new file mode 100644
index 000000000..9891401d1
--- /dev/null
+++ b/tests/roots/test-intl/definition_terms.txt
@@ -0,0 +1,11 @@
+:tocdepth: 2
+
+i18n with definition terms
+==========================
+
+Some term
+ The corresponding definition
+
+Some other term
+ The corresponding definition #2
+
diff --git a/tests/roots/test-intl/external_links.po b/tests/roots/test-intl/external_links.po
new file mode 100644
index 000000000..4cd19ddab
--- /dev/null
+++ b/tests/roots/test-intl/external_links.po
@@ -0,0 +1,32 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) 2012, foof
+# This file is distributed under the same license as the foo package.
+# FIRST AUTHOR , YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: sphinx 1.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-11-22 08:28\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: LANGUAGE \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "i18n with external links"
+msgstr "EXTERNAL LINKS"
+
+msgid "External link to Python_."
+msgstr "EXTERNAL LINK TO Python_."
+
+msgid "Internal link to `i18n with external links`_."
+msgstr "`EXTERNAL LINKS`_ IS INTERNAL LINK."
+
+msgid "Inline link by `Sphinx `_."
+msgstr "INLINE LINK BY `SPHINX `_."
+
+msgid "Unnamed link__."
+msgstr "UNNAMED LINK__."
diff --git a/tests/roots/test-intl/external_links.txt b/tests/roots/test-intl/external_links.txt
new file mode 100644
index 000000000..7ac1db02e
--- /dev/null
+++ b/tests/roots/test-intl/external_links.txt
@@ -0,0 +1,13 @@
+:tocdepth: 2
+
+i18n with external links
+========================
+.. #1044 external-links-dont-work-in-localized-html
+
+* External link to Python_.
+* Internal link to `i18n with external links`_.
+* Inline link by `Sphinx `_.
+* Unnamed link__.
+
+.. _Python: http://python.org
+.. __: http://google.com
diff --git a/tests/roots/test-intl/figure_caption.po b/tests/roots/test-intl/figure_caption.po
new file mode 100644
index 000000000..01b4c041e
--- /dev/null
+++ b/tests/roots/test-intl/figure_caption.po
@@ -0,0 +1,29 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) 2012, foof
+# This file is distributed under the same license as the foo package.
+# FIRST AUTHOR , YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: sphinx 1.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-01-04 7:00\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: LANGUAGE \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "i18n with figure caption"
+msgstr "I18N WITH FIGURE CAPTION"
+
+msgid "My caption of the figure"
+msgstr "MY CAPTION OF THE FIGURE"
+
+msgid "My description paragraph1 of the figure."
+msgstr "MY DESCRIPTION PARAGRAPH1 OF THE FIGURE."
+
+msgid "My description paragraph2 of the figure."
+msgstr "MY DESCRIPTION PARAGRAPH2 OF THE FIGURE."
diff --git a/tests/roots/test-intl/figure_caption.txt b/tests/roots/test-intl/figure_caption.txt
new file mode 100644
index 000000000..75567b3a1
--- /dev/null
+++ b/tests/roots/test-intl/figure_caption.txt
@@ -0,0 +1,12 @@
+:tocdepth: 2
+
+i18n with figure caption
+========================
+
+.. figure:: i18n.png
+
+ My caption of the figure
+
+ My description paragraph1 of the figure.
+
+ My description paragraph2 of the figure.
diff --git a/tests/roots/test-intl/footnote.po b/tests/roots/test-intl/footnote.po
new file mode 100644
index 000000000..47f8d3db4
--- /dev/null
+++ b/tests/roots/test-intl/footnote.po
@@ -0,0 +1,33 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) 2012, foof
+# This file is distributed under the same license as the foo package.
+# FIRST AUTHOR , YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: sphinx 1.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-11-22 08:28\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: LANGUAGE \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "i18n with Footnote"
+msgstr "I18N WITH FOOTNOTE"
+
+msgid "[100]_ Contents [#]_ for `i18n with Footnote`_ [ref]_"
+msgstr "`I18N WITH FOOTNOTE`_ INCLUDE THIS CONTENTS [ref]_ [#]_ [100]_"
+
+msgid "This is a auto numbered footnote."
+msgstr "THIS IS A AUTO NUMBERED FOOTNOTE."
+
+msgid "This is a named footnote."
+msgstr "THIS IS A NAMED FOOTNOTE."
+
+msgid "This is a numbered footnote."
+msgstr "THIS IS A NUMBERED FOOTNOTE."
+
diff --git a/tests/roots/test-intl/footnote.txt b/tests/roots/test-intl/footnote.txt
new file mode 100644
index 000000000..3ef76bbaa
--- /dev/null
+++ b/tests/roots/test-intl/footnote.txt
@@ -0,0 +1,11 @@
+:tocdepth: 2
+
+i18n with Footnote
+==================
+.. #955 cant-build-html-with-footnotes-when-using
+
+[100]_ Contents [#]_ for `i18n with Footnote`_ [ref]_
+
+.. [#] This is a auto numbered footnote.
+.. [ref] This is a named footnote.
+.. [100] This is a numbered footnote.
diff --git a/tests/roots/test-intl/glossary_terms.po b/tests/roots/test-intl/glossary_terms.po
new file mode 100644
index 000000000..1ffcaeb20
--- /dev/null
+++ b/tests/roots/test-intl/glossary_terms.po
@@ -0,0 +1,35 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) 2012, foof
+# This file is distributed under the same license as the foo package.
+# FIRST AUTHOR , YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: sphinx 1.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-01-29 14:10\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: LANGUAGE \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "i18n with glossary terms"
+msgstr "I18N WITH GLOSSARY TERMS"
+
+msgid "Some term"
+msgstr "SOME NEW TERM"
+
+msgid "The corresponding glossary"
+msgstr "THE CORRESPONDING GLOSSARY"
+
+msgid "Some other term"
+msgstr "SOME OTHER NEW TERM"
+
+msgid "The corresponding glossary #2"
+msgstr "THE CORRESPONDING GLOSSARY #2"
+
+msgid "link to :term:`Some term`."
+msgstr "LINK TO :term:`SOME NEW TERM`."
diff --git a/tests/roots/test-intl/glossary_terms.txt b/tests/roots/test-intl/glossary_terms.txt
new file mode 100644
index 000000000..a6e16c948
--- /dev/null
+++ b/tests/roots/test-intl/glossary_terms.txt
@@ -0,0 +1,14 @@
+:tocdepth: 2
+
+i18n with glossary terms
+========================
+
+.. glossary::
+
+ Some term
+ The corresponding glossary
+
+ Some other term
+ The corresponding glossary #2
+
+link to :term:`Some term`.
diff --git a/tests/roots/test-intl/glossary_terms_inconsistency.po b/tests/roots/test-intl/glossary_terms_inconsistency.po
new file mode 100644
index 000000000..5e3016578
--- /dev/null
+++ b/tests/roots/test-intl/glossary_terms_inconsistency.po
@@ -0,0 +1,23 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) 2012, foof
+# This file is distributed under the same license as the foo package.
+# FIRST AUTHOR , YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: sphinx 1.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-01-29 14:10\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: LANGUAGE \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "i18n with glossary terms inconsistency"
+msgstr "I18N WITH GLOSSARY TERMS INCONSISTENCY"
+
+msgid "link to :term:`Some term` and :term:`Some other term`."
+msgstr "LINK TO :term:`SOME NEW TERM`."
diff --git a/tests/roots/test-intl/glossary_terms_inconsistency.txt b/tests/roots/test-intl/glossary_terms_inconsistency.txt
new file mode 100644
index 000000000..837411b6f
--- /dev/null
+++ b/tests/roots/test-intl/glossary_terms_inconsistency.txt
@@ -0,0 +1,6 @@
+:tocdepth: 2
+
+i18n with glossary terms inconsistency
+======================================
+
+1. link to :term:`Some term` and :term:`Some other term`.
diff --git a/tests/roots/test-intl/i18n.png b/tests/roots/test-intl/i18n.png
new file mode 100644
index 000000000..72c12d13f
Binary files /dev/null and b/tests/roots/test-intl/i18n.png differ
diff --git a/tests/roots/test-intl/index_entries.po b/tests/roots/test-intl/index_entries.po
new file mode 100644
index 000000000..6da9a813d
--- /dev/null
+++ b/tests/roots/test-intl/index_entries.po
@@ -0,0 +1,77 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) 2013, foo
+# This file is distributed under the same license as the foo package.
+# FIRST AUTHOR , YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: foo foo\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-01-05 18:10\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: LANGUAGE \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "i18n with index entries"
+msgstr ""
+
+msgid "index target section"
+msgstr ""
+
+msgid "this is :index:`Newsletter` target paragraph."
+msgstr "THIS IS :index:`NEWSLETTER` TARGET PARAGRAPH."
+
+msgid "various index entries"
+msgstr ""
+
+msgid "That's all."
+msgstr ""
+
+msgid "Mailing List"
+msgstr "MAILING LIST"
+
+msgid "Newsletter"
+msgstr "NEWSLETTER"
+
+msgid "Recipients List"
+msgstr "RECIPIENTS LIST"
+
+msgid "First"
+msgstr "FIRST"
+
+msgid "Second"
+msgstr "SECOND"
+
+msgid "Third"
+msgstr "THIRD"
+
+msgid "Entry"
+msgstr "ENTRY"
+
+msgid "See"
+msgstr "SEE"
+
+msgid "Module"
+msgstr "MODULE"
+
+msgid "Keyword"
+msgstr "KEYWORD"
+
+msgid "Operator"
+msgstr "OPERATOR"
+
+msgid "Object"
+msgstr "OBJECT"
+
+msgid "Exception"
+msgstr "EXCEPTION"
+
+msgid "Statement"
+msgstr "STATEMENT"
+
+msgid "Builtin"
+msgstr "BUILTIN"
diff --git a/tests/roots/test-intl/index_entries.txt b/tests/roots/test-intl/index_entries.txt
new file mode 100644
index 000000000..c914a4b43
--- /dev/null
+++ b/tests/roots/test-intl/index_entries.txt
@@ -0,0 +1,31 @@
+:tocdepth: 2
+
+i18n with index entries
+=======================
+
+.. index::
+ single: Mailing List
+ pair: Newsletter; Recipients List
+
+index target section
+--------------------
+
+this is :index:`Newsletter` target paragraph.
+
+
+various index entries
+---------------------
+
+.. index::
+ triple: First; Second; Third
+ see: Entry; Mailing List
+ seealso: See; Newsletter
+ module: Module
+ keyword: Keyword
+ operator: Operator
+ object: Object
+ exception: Exception
+ statement: Statement
+ builtin: Builtin
+
+That's all.
diff --git a/tests/roots/test-intl/literalblock.po b/tests/roots/test-intl/literalblock.po
new file mode 100644
index 000000000..8ea83b3bd
--- /dev/null
+++ b/tests/roots/test-intl/literalblock.po
@@ -0,0 +1,30 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) 2012, foof
+# This file is distributed under the same license as the foo package.
+# FIRST AUTHOR , YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: sphinx 1.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-11-22 08:28\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: LANGUAGE \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "i18n with literal block"
+msgstr "I18N WITH LITERAL BLOCK"
+
+msgid "Correct literal block::"
+msgstr "CORRECT LITERAL BLOCK::"
+
+msgid "Missing literal block::"
+msgstr "MISSING LITERAL BLOCK::"
+
+msgid "That's all."
+msgstr "THAT'S ALL."
+
diff --git a/tests/roots/test-intl/literalblock.txt b/tests/roots/test-intl/literalblock.txt
new file mode 100644
index 000000000..c9c710997
--- /dev/null
+++ b/tests/roots/test-intl/literalblock.txt
@@ -0,0 +1,13 @@
+:tocdepth: 2
+
+i18n with literal block
+=========================
+
+Correct literal block::
+
+ this is
+ literal block
+
+Missing literal block::
+
+That's all.
diff --git a/tests/roots/test-intl/refs_inconsistency.po b/tests/roots/test-intl/refs_inconsistency.po
new file mode 100644
index 000000000..9cab687fd
--- /dev/null
+++ b/tests/roots/test-intl/refs_inconsistency.po
@@ -0,0 +1,39 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) 2012, foof
+# This file is distributed under the same license as the foo package.
+# FIRST AUTHOR , YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: sphinx 1.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-12-05 08:28\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: LANGUAGE \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "i18n with refs inconsistency"
+msgstr "I18N WITH REFS INCONSISTENCY"
+
+msgid "[100]_ for [#]_ footnote [ref2]_."
+msgstr "FOR FOOTNOTE [ref2]_."
+
+msgid "for reference_."
+msgstr "reference_ FOR reference_."
+
+msgid "normal text."
+msgstr "ORPHAN REFERENCE: `I18N WITH REFS INCONSISTENCY`_."
+
+msgid "This is a auto numbered footnote."
+msgstr "THIS IS A AUTO NUMBERED FOOTNOTE."
+
+msgid "This is a named footnote."
+msgstr "THIS IS A NAMED FOOTNOTE."
+
+msgid "This is a numbered footnote."
+msgstr "THIS IS A NUMBERED FOOTNOTE."
+
diff --git a/tests/roots/test-intl/refs_inconsistency.txt b/tests/roots/test-intl/refs_inconsistency.txt
new file mode 100644
index 000000000..c65c5b458
--- /dev/null
+++ b/tests/roots/test-intl/refs_inconsistency.txt
@@ -0,0 +1,13 @@
+:tocdepth: 2
+
+i18n with refs inconsistency
+=============================
+
+* [100]_ for [#]_ footnote [ref2]_.
+* for reference_.
+* normal text.
+
+.. [#] This is a auto numbered footnote.
+.. [ref2] This is a named footnote.
+.. [100] This is a numbered footnote.
+.. _reference: http://www.example.com
diff --git a/tests/roots/test-intl/role_xref.po b/tests/roots/test-intl/role_xref.po
new file mode 100644
index 000000000..e7a348b36
--- /dev/null
+++ b/tests/roots/test-intl/role_xref.po
@@ -0,0 +1,23 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) 2012, foof
+# This file is distributed under the same license as the foo package.
+# FIRST AUTHOR , YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: sphinx 1.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-02-04 14:00\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: LANGUAGE \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "i18n role xref"
+msgstr "I18N ROCK'N ROLE XREF"
+
+msgid "link to :term:`Some term`, :ref:`i18n-role-xref`, :doc:`contents`."
+msgstr "LINK TO :ref:`i18n-role-xref`, :doc:`contents`, :term:`SOME NEW TERM`."
diff --git a/tests/roots/test-intl/role_xref.txt b/tests/roots/test-intl/role_xref.txt
new file mode 100644
index 000000000..382b740c9
--- /dev/null
+++ b/tests/roots/test-intl/role_xref.txt
@@ -0,0 +1,9 @@
+:tocdepth: 2
+
+.. _i18n-role-xref:
+
+i18n role xref
+==============
+
+link to :term:`Some term`, :ref:`i18n-role-xref`, :doc:`contents`.
+
diff --git a/tests/roots/test-intl/seealso.po b/tests/roots/test-intl/seealso.po
new file mode 100644
index 000000000..d3b27e51b
--- /dev/null
+++ b/tests/roots/test-intl/seealso.po
@@ -0,0 +1,33 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) 2010, Georg Brandl & Team
+# This file is distributed under the same license as the Sphinx package.
+# FIRST AUTHOR , YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: Sphinx 0.6\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-12-16 06:06\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: LANGUAGE \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "i18n with seealso"
+msgstr "I18N WITH SEEALSO"
+
+msgid "short text 1"
+msgstr "SHORT TEXT 1"
+
+msgid "long text 1"
+msgstr "LONG TEXT 1"
+
+msgid "short text 2"
+msgstr "SHORT TEXT 2"
+
+msgid "long text 2"
+msgstr "LONG TEXT 2"
+
diff --git a/tests/roots/test-intl/seealso.txt b/tests/roots/test-intl/seealso.txt
new file mode 100644
index 000000000..ed8859972
--- /dev/null
+++ b/tests/roots/test-intl/seealso.txt
@@ -0,0 +1,15 @@
+:tocdepth: 2
+
+i18n with seealso
+============================
+.. #960 directive-seelaso-ignored-in-the-gettext
+
+.. seealso:: short text 1
+
+.. seealso::
+
+ long text 1
+
+.. seealso:: short text 2
+
+ long text 2
diff --git a/tests/roots/test-intl/subdir/contents.txt b/tests/roots/test-intl/subdir/contents.txt
new file mode 100644
index 000000000..b6509bafb
--- /dev/null
+++ b/tests/roots/test-intl/subdir/contents.txt
@@ -0,0 +1,2 @@
+subdir contents
+===============
diff --git a/tests/roots/test-intl/warnings.po b/tests/roots/test-intl/warnings.po
new file mode 100644
index 000000000..bf82510ee
--- /dev/null
+++ b/tests/roots/test-intl/warnings.po
@@ -0,0 +1,23 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) 2010, Georg Brandl & Team
+# This file is distributed under the same license as the Sphinx package.
+# FIRST AUTHOR , YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: Sphinx 0.6\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-02-04 13:06\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: LANGUAGE \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "i18n with reST warnings"
+msgstr "I18N WITH REST WARNINGS"
+
+msgid "line of ``literal`` markup."
+msgstr "LINE OF ``BROKEN LITERAL MARKUP."
diff --git a/tests/roots/test-intl/warnings.txt b/tests/roots/test-intl/warnings.txt
new file mode 100644
index 000000000..a80fe183d
--- /dev/null
+++ b/tests/roots/test-intl/warnings.txt
@@ -0,0 +1,5 @@
+i18n with reST warnings
+========================
+
+line of ``literal`` markup.
+
diff --git a/tests/roots/test-only-directive/conf.py b/tests/roots/test-only-directive/conf.py
new file mode 100644
index 000000000..bcb4305da
--- /dev/null
+++ b/tests/roots/test-only-directive/conf.py
@@ -0,0 +1,2 @@
+
+project = 'test-only-directive'
diff --git a/tests/roots/test-only-directive/contents.rst b/tests/roots/test-only-directive/contents.rst
new file mode 100644
index 000000000..9a93be9e7
--- /dev/null
+++ b/tests/roots/test-only-directive/contents.rst
@@ -0,0 +1,6 @@
+test-only-directive
+===================
+
+.. toctree::
+
+ only
diff --git a/tests/roots/test-only-directive/only.rst b/tests/roots/test-only-directive/only.rst
new file mode 100644
index 000000000..4a3eb48a6
--- /dev/null
+++ b/tests/roots/test-only-directive/only.rst
@@ -0,0 +1,203 @@
+
+1. Sections in only directives
+==============================
+
+Testing sections in only directives.
+
+.. only:: nonexisting_tag
+
+ Skipped Section
+ ---------------
+ Should not be here.
+
+.. only:: not nonexisting_tag
+
+ 1.1. Section
+ ------------
+ Should be here.
+
+1.2. Section
+------------
+
+.. only:: not nonexisting_tag
+
+ 1.2.1. Subsection
+ ~~~~~~~~~~~~~~~~~
+ Should be here.
+
+.. only:: nonexisting_tag
+
+ Skipped Subsection
+ ~~~~~~~~~~~~~~~~~~
+ Should not be here.
+
+1.3. Section
+------------
+
+1.3.1. Subsection
+~~~~~~~~~~~~~~~~~
+Should be here.
+
+1.4. Section
+------------
+
+.. only:: not nonexisting_tag
+
+ 1.4.1. Subsection
+ ~~~~~~~~~~~~~~~~~
+ Should be here.
+
+1.5. Section
+------------
+
+.. only:: not nonexisting_tag
+
+ 1.5.1. Subsection
+ ~~~~~~~~~~~~~~~~~
+ Should be here.
+
+1.5.2. Subsection
+~~~~~~~~~~~~~~~~~
+Should be here.
+
+1.6. Section
+------------
+
+1.6.1. Subsection
+~~~~~~~~~~~~~~~~~
+Should be here.
+
+.. only:: not nonexisting_tag
+
+ 1.6.2. Subsection
+ ~~~~~~~~~~~~~~~~~
+ Should be here.
+
+1.6.3. Subsection
+~~~~~~~~~~~~~~~~~
+Should be here.
+
+1.7. Section
+------------
+
+1.7.1. Subsection
+~~~~~~~~~~~~~~~~~
+Should be here.
+
+.. only:: not nonexisting_tag
+
+ 1.7.1.1. Subsubsection
+ ......................
+ Should be here.
+
+1.8. Section
+------------
+
+1.8.1. Subsection
+~~~~~~~~~~~~~~~~~
+Should be here.
+
+1.8.1.1. Subsubsection
+......................
+Should be here.
+
+.. only:: not nonexisting_tag
+
+ 1.8.1.2. Subsubsection
+ ......................
+ Should be here.
+
+1.9. Section
+------------
+
+.. only:: nonexisting_tag
+
+ Skipped Subsection
+ ~~~~~~~~~~~~~~~~~~
+
+1.9.1. Subsection
+~~~~~~~~~~~~~~~~~
+Should be here.
+
+1.9.1.1. Subsubsection
+......................
+Should be here.
+
+.. only:: not nonexisting_tag
+
+ 1.10. Section
+ -------------
+ Should be here.
+
+1.11. Section
+-------------
+
+Text before subsection 11.1.
+
+.. only:: not nonexisting_tag
+
+ More text before subsection 11.1.
+
+ 1.11.1. Subsection
+ ~~~~~~~~~~~~~~~~~~
+ Should be here.
+
+Text after subsection 11.1.
+
+.. only:: not nonexisting_tag
+
+ 1.12. Section
+ -------------
+ Should be here.
+
+ 1.12.1. Subsection
+ ~~~~~~~~~~~~~~~~~~
+ Should be here.
+
+ 1.13. Section
+ -------------
+ Should be here.
+
+.. only:: not nonexisting_tag
+
+ 1.14. Section
+ -------------
+ Should be here.
+
+ .. only:: not nonexisting_tag
+
+ 1.14.1. Subsection
+ ~~~~~~~~~~~~~~~~~~
+ Should be here.
+
+ 1.15. Section
+ -------------
+ Should be here.
+
+.. only:: nonexisting_tag
+
+ Skipped document level heading
+ ==============================
+ Should not be here.
+
+.. only:: not nonexisting_tag
+
+ 2. Included document level heading
+ ==================================
+ Should be here.
+
+3. Document level heading
+=========================
+Should be here.
+
+.. only:: nonexisting_tag
+
+ Skipped document level heading
+ ==============================
+ Should not be here.
+
+.. only:: not nonexisting_tag
+
+ 4. Another included document level heading
+ ==========================================
+ Should be here.
diff --git a/tests/run.py b/tests/run.py
index 8b3bf844d..c0ae0d236 100755
--- a/tests/run.py
+++ b/tests/run.py
@@ -6,7 +6,7 @@
This script runs the Sphinx unit test suite.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -18,14 +18,14 @@ if sys.version_info >= (3, 0):
from distutils.util import copydir_run_2to3
testroot = path.dirname(__file__) or '.'
if 'BUILD_TEST_PATH' in environ:
- # for tox test.
+ # for tox testing
newroot = environ['BUILD_TEST_PATH']
- # tox install sphinx package, not need sys.path.insert.
+ # tox installs the sphinx package, no need for sys.path.insert
else:
newroot = path.join(testroot, path.pardir, 'build')
newroot = path.join(newroot, listdir(newroot)[0], 'tests')
# always test the sphinx package from build/lib/
- sys.path.insert(0, path.join(newroot, path.pardir))
+ sys.path.insert(0, path.abspath(path.join(newroot, path.pardir)))
copydir_run_2to3(testroot, newroot)
# switch to the converted dir so nose tests the right tests
chdir(newroot)
diff --git a/tests/test_application.py b/tests/test_application.py
index 4baabcec9..87ec42f01 100644
--- a/tests/test_application.py
+++ b/tests/test_application.py
@@ -5,13 +5,15 @@
Test the Sphinx class.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from StringIO import StringIO
+from docutils import nodes
from sphinx.application import ExtensionError
+from sphinx.domains import Domain
from util import *
@@ -40,6 +42,12 @@ def test_events(app):
"Callback called when disconnected"
+@with_app()
+def test_emit_with_multibyte_name_node(app):
+ node = nodes.section(names=[u'\u65e5\u672c\u8a9e'])
+ app.emit('my_event', node)
+
+
def test_output():
status, warnings = StringIO(), StringIO()
app = TestApp(status=status, warning=warnings)
@@ -69,3 +77,23 @@ def test_extensions():
assert warnings.getvalue().startswith("WARNING: extension 'shutil'")
finally:
app.cleanup()
+
+def test_domain_override():
+ class A(Domain):
+ name = 'foo'
+ class B(A):
+ name = 'foo'
+ class C(Domain):
+ name = 'foo'
+ status, warnings = StringIO(), StringIO()
+ app = TestApp(status=status, warning=warnings)
+ try:
+ # No domain know named foo.
+ raises_msg(ExtensionError, 'domain foo not yet registered',
+ app.override_domain, A)
+ assert app.add_domain(A) is None
+ assert app.override_domain(B) is None
+ raises_msg(ExtensionError, 'new domain not a subclass of registered '
+ 'foo domain', app.override_domain, C)
+ finally:
+ app.cleanup()
diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py
index 965064c37..fcd064929 100644
--- a/tests/test_autodoc.py
+++ b/tests/test_autodoc.py
@@ -6,7 +6,7 @@
Test the autodoc extension. This tests mainly the Documenters; the auto
directives are tested in a test source file translated by test_build.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -14,6 +14,7 @@ import sys
from StringIO import StringIO
from util import *
+from nose.tools import with_setup
from docutils.statemachine import ViewList
@@ -22,8 +23,7 @@ from sphinx.ext.autodoc import AutoDirective, add_documenter, \
def setup_module():
- global app, lid, options, directive
-
+ global app
app = TestApp()
app.builder.env.app = app
app.builder.env.temp_data['docname'] = 'dummy'
@@ -31,6 +31,15 @@ def setup_module():
app.connect('autodoc-process-signature', process_signature)
app.connect('autodoc-skip-member', skip_member)
+
+def teardown_module():
+ app.cleanup()
+
+
+def setup_test():
+ global options, directive
+ global processed_docstrings, processed_signatures, _warnings
+
options = Struct(
inherited_members = False,
undoc_members = False,
@@ -54,8 +63,9 @@ def setup_module():
filename_set = set(),
)
-def teardown_module():
- app.cleanup()
+ processed_docstrings = []
+ processed_signatures = []
+ _warnings = []
_warnings = []
@@ -80,12 +90,15 @@ def process_signature(app, what, name, obj, options, args, retann):
def skip_member(app, what, name, obj, skip, options):
+ if name in ('__special1__', '__special2__'):
+ return skip
if name.startswith('_'):
return True
if name == 'skipmeth':
return True
+@with_setup(setup_test)
def test_parse_name():
def verify(objtype, name, result):
inst = AutoDirective._registry[objtype](directive, name)
@@ -127,6 +140,7 @@ def test_parse_name():
del directive.env.temp_data['autodoc:class']
+@with_setup(setup_test)
def test_format_signature():
def formatsig(objtype, name, obj, args, retann):
inst = AutoDirective._registry[objtype](directive, name)
@@ -165,6 +179,20 @@ def test_format_signature():
assert formatsig('class', 'C', C, None, None) == '(a, b=None)'
assert formatsig('class', 'C', D, 'a, b', 'X') == '(a, b) -> X'
+ #__init__ have signature at first line of docstring
+ class F2:
+ '''some docstring for F2.'''
+ def __init__(self, *args, **kw):
+ '''
+ __init__(a1, a2, kw1=True, kw2=False)
+
+ some docstring for __init__.
+ '''
+ class G2(F2, object):
+ pass
+ for C in (F2, G2):
+ assert formatsig('class', 'C', C, None, None) == '(a1, a2, kw1=True, kw2=False)'
+
# test for methods
class H:
def foo1(self, b, *c):
@@ -182,7 +210,23 @@ def test_format_signature():
# test processing by event handler
assert formatsig('method', 'bar', H.foo1, None, None) == '42'
+ # test functions created via functools.partial
+ from functools import partial
+ curried1 = partial(lambda a, b, c: None, 'A')
+ assert formatsig('function', 'curried1', curried1, None, None) == \
+ '(b, c)'
+ curried2 = partial(lambda a, b, c=42: None, 'A')
+ assert formatsig('function', 'curried2', curried2, None, None) == \
+ '(b, c=42)'
+ curried3 = partial(lambda a, b, *c: None, 'A')
+ assert formatsig('function', 'curried3', curried3, None, None) == \
+ '(b, *c)'
+ curried4 = partial(lambda a, b, c=42, *d, **e: None, 'A')
+ assert formatsig('function', 'curried4', curried4, None, None) == \
+ '(b, c=42, *d, **e)'
+
+@with_setup(setup_test)
def test_get_doc():
def getdocl(objtype, obj, encoding=None):
inst = AutoDirective._registry[objtype](directive, 'tmp')
@@ -251,6 +295,7 @@ def test_get_doc():
'', 'Other', ' lines']
+@with_setup(setup_test)
def test_docstring_processing():
def process(objtype, name, obj):
inst = AutoDirective._registry[objtype](directive, name)
@@ -301,6 +346,8 @@ def test_docstring_processing():
assert process('function', 'h', h) == ['first line', 'third line', '']
app.disconnect(lid)
+
+@with_setup(setup_test)
def test_new_documenter():
class MyDocumenter(ModuleLevelDocumenter):
objtype = 'integer'
@@ -328,6 +375,40 @@ def test_new_documenter():
assert_result_contains('.. py:data:: integer', 'module', 'test_autodoc')
+@with_setup(setup_test, AutoDirective._special_attrgetters.clear)
+def test_attrgetter_using():
+ def assert_getter_works(objtype, name, obj, attrs=[], **kw):
+ getattr_spy = []
+ def special_getattr(obj, name, *defargs):
+ if name in attrs:
+ getattr_spy.append((obj, name))
+ return None
+ return getattr(obj, name, *defargs)
+ AutoDirective._special_attrgetters[type] = special_getattr
+
+ del getattr_spy[:]
+ inst = AutoDirective._registry[objtype](directive, name)
+ inst.generate(**kw)
+
+ hooked_members = [s[1] for s in getattr_spy]
+ documented_members = [s[1] for s in processed_signatures]
+ for attr in attrs:
+ fullname = '.'.join((name, attr))
+ assert attr in hooked_members
+ assert fullname not in documented_members, \
+ '%r was not hooked by special_attrgetter function' % fullname
+
+ options.members = ALL
+ options.inherited_members = False
+ assert_getter_works('class', 'test_autodoc.Class', Class,
+ ['meth'])
+
+ options.inherited_members = True
+ assert_getter_works('class', 'test_autodoc.Class', Class,
+ ['meth', 'inheritedmeth'])
+
+
+@with_setup(setup_test)
def test_generate():
def assert_warns(warn_str, objtype, name, **kw):
inst = AutoDirective._registry[objtype](directive, name)
@@ -386,10 +467,10 @@ def test_generate():
assert_warns("import for autodocumenting 'foobar'",
'function', 'foobar', more_content=None)
# importing
- assert_warns("import/find module 'test_foobar'",
+ assert_warns("failed to import module 'test_foobar'",
'module', 'test_foobar', more_content=None)
# attributes missing
- assert_warns("import/find function 'util.foobar'",
+ assert_warns("failed to import function 'foobar' from module 'util'",
'function', 'util.foobar', more_content=None)
# test auto and given content mixing
@@ -440,6 +521,15 @@ def test_generate():
should.append(('method', 'test_autodoc.Class.inheritedmeth'))
assert_processes(should, 'class', 'Class')
+ # test special members
+ options.special_members = ['__special1__']
+ should.append(('method', 'test_autodoc.Class.__special1__'))
+ assert_processes(should, 'class', 'Class')
+ options.special_members = ALL
+ should.append(('method', 'test_autodoc.Class.__special2__'))
+ assert_processes(should, 'class', 'Class')
+ options.special_members = False
+
options.members = []
# test module flags
assert_result_contains('.. py:module:: test_autodoc',
@@ -518,6 +608,12 @@ def test_generate():
'test_autodoc.DocstringSig.meth')
assert_result_contains(
' rest of docstring', 'method', 'test_autodoc.DocstringSig.meth')
+ assert_result_contains(
+ '.. py:method:: DocstringSig.meth2()', 'method',
+ 'test_autodoc.DocstringSig.meth2')
+ assert_result_contains(
+ ' indented line', 'method',
+ 'test_autodoc.DocstringSig.meth2')
assert_result_contains(
'.. py:classmethod:: Class.moore(a, e, f) -> happiness', 'method',
'test_autodoc.Class.moore')
@@ -534,6 +630,39 @@ def test_generate():
assert_result_contains(
' :annotation: = None', 'attribute', 'AttCls.a2')
+ # test explicit members with instance attributes
+ del directive.env.temp_data['autodoc:class']
+ del directive.env.temp_data['autodoc:module']
+ directive.env.temp_data['py:module'] = 'test_autodoc'
+ options.inherited_members = False
+ options.undoc_members = False
+ options.members = ALL
+ assert_processes([
+ ('class', 'test_autodoc.InstAttCls'),
+ ('attribute', 'test_autodoc.InstAttCls.ca1'),
+ ('attribute', 'test_autodoc.InstAttCls.ca2'),
+ ('attribute', 'test_autodoc.InstAttCls.ca3'),
+ ('attribute', 'test_autodoc.InstAttCls.ia1'),
+ ('attribute', 'test_autodoc.InstAttCls.ia2'),
+ ], 'class', 'InstAttCls')
+ del directive.env.temp_data['autodoc:class']
+ del directive.env.temp_data['autodoc:module']
+ options.members = ['ca1', 'ia1']
+ assert_processes([
+ ('class', 'test_autodoc.InstAttCls'),
+ ('attribute', 'test_autodoc.InstAttCls.ca1'),
+ ('attribute', 'test_autodoc.InstAttCls.ia1'),
+ ], 'class', 'InstAttCls')
+ del directive.env.temp_data['autodoc:class']
+ del directive.env.temp_data['autodoc:module']
+ del directive.env.temp_data['py:module']
+
+ # test descriptor class documentation
+ options.members = ['CustomDataDescriptor']
+ assert_result_contains('.. py:class:: CustomDataDescriptor(doc)',
+ 'module', 'test_autodoc')
+ assert_result_contains(' .. py:method:: CustomDataDescriptor.meth()',
+ 'module', 'test_autodoc')
# --- generate fodder ------------
@@ -559,6 +688,10 @@ class CustomDataDescriptor(object):
return self
return 42
+ def meth(self):
+ """Function."""
+ return "The Answer"
+
def _funky_classmethod(name, b, c, d, docstring=None):
"""Generates a classmethod for a class from a template by filling out
some arguments."""
@@ -628,6 +761,13 @@ class Class(Base):
self.inst_attr_string = None
"""a documented instance attribute"""
+ def __special1__(self):
+ """documented special method"""
+
+ def __special2__(self):
+ # undocumented special method
+ pass
+
class CustomDict(dict):
"""Docstring."""
@@ -660,6 +800,13 @@ First line of docstring
rest of docstring
"""
+ def meth2(self):
+ """First line, no signature
+ Second line followed by indentation::
+
+ indented line
+ """
+
class StrRepr(str):
def __repr__(self):
return self
@@ -667,3 +814,22 @@ class StrRepr(str):
class AttCls(object):
a1 = StrRepr('hello\nworld')
a2 = None
+
+class InstAttCls(object):
+ """Class with documented class and instance attributes."""
+
+ #: Doc comment for class attribute InstAttCls.ca1.
+ #: It can have multiple lines.
+ ca1 = 'a'
+
+ ca2 = 'b' #: Doc comment for InstAttCls.ca2. One line only.
+
+ ca3 = 'c'
+ """Docstring for class attribute InstAttCls.ca3."""
+
+ def __init__(self):
+ #: Doc comment for instance attribute InstAttCls.ia1
+ self.ia1 = 'd'
+
+ self.ia2 = 'e'
+ """Docstring for instance attribute InstAttCls.ia2."""
diff --git a/tests/test_autosummary.py b/tests/test_autosummary.py
index 048940d0b..dab72c1e5 100644
--- a/tests/test_autosummary.py
+++ b/tests/test_autosummary.py
@@ -5,7 +5,7 @@
Test the autosummary extension.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/tests/test_build.py b/tests/test_build.py
index 5f24f89ac..c2f0b38d1 100644
--- a/tests/test_build.py
+++ b/tests/test_build.py
@@ -5,11 +5,12 @@
Test all builders that have no special checks.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from util import *
+from textwrap import dedent
def teardown_module():
@@ -57,7 +58,35 @@ else:
@with_app(buildername='man')
def test_man(app):
app.builder.build_all()
+ assert (app.outdir / 'SphinxTests.1').exists()
@with_app(buildername='singlehtml', cleanenv=True)
def test_singlehtml(app):
app.builder.build_all()
+
+@with_app(buildername='xml')
+def test_xml(app):
+ app.builder.build_all()
+
+@with_app(buildername='pseudoxml')
+def test_pseudoxml(app):
+ app.builder.build_all()
+
+@with_app(buildername='html', srcdir='(temp)')
+def test_multibyte_path(app):
+ srcdir = path(app.srcdir)
+ mb_name = u'\u65e5\u672c\u8a9e'
+ (srcdir / mb_name).makedirs()
+ (srcdir / mb_name / (mb_name + '.txt')).write_text(dedent("""
+ multi byte file name page
+ ==========================
+ """))
+
+ master_doc = srcdir / 'contents.txt'
+ master_doc.write_bytes((master_doc.text() + dedent("""
+ .. toctree::
+
+ %(mb_name)s/%(mb_name)s
+ """ % locals())
+ ).encode('utf-8'))
+ app.builder.build_all()
diff --git a/tests/test_build_gettext.py b/tests/test_build_gettext.py
index ab68289e3..f6cb4b8e2 100644
--- a/tests/test_build_gettext.py
+++ b/tests/test_build_gettext.py
@@ -11,6 +11,7 @@
import gettext
import os
+import re
from subprocess import Popen, PIPE
from util import *
@@ -19,6 +20,7 @@ from util import SkipTest
def teardown_module():
(test_root / '_build').rmtree(True)
+ (test_roots / 'test-intl' / '_build').rmtree(True),
@with_app(buildername='gettext')
@@ -37,6 +39,14 @@ def test_build(app):
assert (app.outdir / 'subdir.pot').isfile()
+@with_app(buildername='gettext')
+def test_seealso(app):
+ # regression test for issue #960
+ app.builder.build(['markup'])
+ catalog = (app.outdir / 'markup.pot').text(encoding='utf-8')
+ assert 'msgid "something, something else, something more"' in catalog
+
+
@with_app(buildername='gettext')
def test_gettext(app):
app.builder.build(['markup'])
@@ -79,3 +89,51 @@ def test_gettext(app):
_ = gettext.translation('test_root', app.outdir, languages=['en']).gettext
assert _("Testing various markup") == u"Testing various markup"
+
+
+@with_app(buildername='gettext',
+ srcdir=(test_roots / 'test-intl'),
+ doctreedir=(test_roots / 'test-intl' / '_build' / 'doctree'),
+ confoverrides={'gettext_compact': False})
+def test_gettext_index_entries(app):
+ # regression test for #976
+ app.builder.build(['index_entries'])
+
+ _msgid_getter = re.compile(r'msgid "(.*)"').search
+ def msgid_getter(msgid):
+ m = _msgid_getter(msgid)
+ if m:
+ return m.groups()[0]
+ return None
+
+ pot = (app.outdir / 'index_entries.pot').text(encoding='utf-8')
+ msgids = filter(None, map(msgid_getter, pot.splitlines()))
+
+ expected_msgids = [
+ "i18n with index entries",
+ "index target section",
+ "this is :index:`Newsletter` target paragraph.",
+ "various index entries",
+ "That's all.",
+ "Mailing List",
+ "Newsletter",
+ "Recipients List",
+ "First",
+ "Second",
+ "Third",
+ "Entry",
+ "See",
+ "Module",
+ "Keyword",
+ "Operator",
+ "Object",
+ "Exception",
+ "Statement",
+ "Builtin",
+ ]
+ for expect in expected_msgids:
+ assert expect in msgids
+ msgids.remove(expect)
+
+ # unexpected msgid existent
+ assert msgids == []
diff --git a/tests/test_build_html.py b/tests/test_build_html.py
index a38806a86..abd60bbb5 100644
--- a/tests/test_build_html.py
+++ b/tests/test_build_html.py
@@ -5,7 +5,7 @@
Test the HTML builder and check output against XPath.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -44,7 +44,7 @@ reading included file u'.*?wrongenc.inc' seems to be wrong, try giving an \
%(root)s/includes.txt:4: WARNING: download file not readable: .*?nonexisting.png
%(root)s/objects.txt:\\d*: WARNING: using old C markup; please migrate to \
new-style markup \(e.g. c:function instead of cfunction\), see \
-http://sphinx.pocoo.org/domains.html
+http://sphinx-doc.org/domains.html
"""
HTML_WARNINGS = ENV_WARNINGS + """\
@@ -319,7 +319,8 @@ def check_static_entries(outdir):
def test_html(app):
app.builder.build_all()
html_warnings = html_warnfile.getvalue().replace(os.sep, '/')
- html_warnings_exp = HTML_WARNINGS % {'root': re.escape(app.srcdir)}
+ html_warnings_exp = HTML_WARNINGS % {
+ 'root': re.escape(app.srcdir.replace(os.sep, '/'))}
assert re.match(html_warnings_exp + '$', html_warnings), \
'Warnings don\'t match:\n' + \
'--- Expected (regex):\n' + html_warnings_exp + \
@@ -328,7 +329,7 @@ def test_html(app):
for fname, paths in HTML_XPATH.iteritems():
parser = NslessParser()
parser.entity.update(htmlentitydefs.entitydefs)
- fp = open(os.path.join(app.outdir, fname))
+ fp = open(os.path.join(app.outdir, fname), 'rb')
try:
etree = ET.parse(fp, parser)
finally:
diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py
index 4e1e9f70c..721b3a2dd 100644
--- a/tests/test_build_latex.py
+++ b/tests/test_build_latex.py
@@ -5,7 +5,7 @@
Test the build process with LaTeX builder with the test root.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -42,7 +42,8 @@ def test_latex(app):
LaTeXTranslator.ignore_missing_images = True
app.builder.build_all()
latex_warnings = latex_warnfile.getvalue().replace(os.sep, '/')
- latex_warnings_exp = LATEX_WARNINGS % {'root': re.escape(app.srcdir)}
+ latex_warnings_exp = LATEX_WARNINGS % {
+ 'root': re.escape(app.srcdir.replace(os.sep, '/'))}
assert re.match(latex_warnings_exp + '$', latex_warnings), \
'Warnings don\'t match:\n' + \
'--- Expected (regex):\n' + latex_warnings_exp + \
diff --git a/tests/test_build_texinfo.py b/tests/test_build_texinfo.py
index c626c976b..37ad0582a 100644
--- a/tests/test_build_texinfo.py
+++ b/tests/test_build_texinfo.py
@@ -5,7 +5,7 @@
Test the build process with Texinfo builder with the test root.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -41,7 +41,8 @@ def test_texinfo(app):
TexinfoTranslator.ignore_missing_images = True
app.builder.build_all()
texinfo_warnings = texinfo_warnfile.getvalue().replace(os.sep, '/')
- texinfo_warnings_exp = TEXINFO_WARNINGS % {'root': re.escape(app.srcdir)}
+ texinfo_warnings_exp = TEXINFO_WARNINGS % {
+ 'root': re.escape(app.srcdir.replace(os.sep, '/'))}
assert re.match(texinfo_warnings_exp + '$', texinfo_warnings), \
'Warnings don\'t match:\n' + \
'--- Expected (regex):\n' + texinfo_warnings_exp + \
diff --git a/tests/test_build_text.py b/tests/test_build_text.py
new file mode 100644
index 000000000..79edc6230
--- /dev/null
+++ b/tests/test_build_text.py
@@ -0,0 +1,86 @@
+# -*- coding: utf-8 -*-
+"""
+ test_build_text
+ ~~~~~~~~~~~~~~~
+
+ Test the build process with Text builder with the test root.
+
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+from textwrap import dedent
+
+from docutils.utils import column_width
+from sphinx.writers.text import MAXWIDTH
+
+from util import *
+
+
+def with_text_app(*args, **kw):
+ default_kw = {
+ 'buildername': 'text',
+ 'srcdir': '(empty)',
+ 'confoverrides': {
+ 'project': 'text',
+ 'master_doc': 'contents',
+ },
+ }
+ default_kw.update(kw)
+ return with_app(*args, **default_kw)
+
+
+@with_text_app()
+def test_multibyte_title_line(app):
+ title = u'\u65e5\u672c\u8a9e'
+ underline = u'=' * column_width(title)
+ content = u'\n'.join((title, underline, u''))
+
+ (app.srcdir / 'contents.rst').write_text(content, encoding='utf-8')
+ app.builder.build_all()
+ result = (app.outdir / 'contents.txt').text(encoding='utf-8')
+
+ expect_underline = underline.replace('=', '*')
+ result_underline = result.splitlines()[2].strip()
+ assert expect_underline == result_underline
+
+
+@with_text_app()
+def test_multibyte_table(app):
+ text = u'\u65e5\u672c\u8a9e'
+ contents = (u"\n.. list-table::"
+ "\n"
+ "\n - - spam"
+ "\n - egg"
+ "\n"
+ "\n - - %(text)s"
+ "\n - %(text)s"
+ "\n" % locals())
+
+ (app.srcdir / 'contents.rst').write_text(contents, encoding='utf-8')
+ app.builder.build_all()
+ result = (app.outdir / 'contents.txt').text(encoding='utf-8')
+
+ lines = [line.strip() for line in result.splitlines() if line.strip()]
+ line_widths = [column_width(line) for line in lines]
+ assert len(set(line_widths)) == 1 # same widths
+
+
+@with_text_app()
+def test_multibyte_maxwidth(app):
+ sb_text = u'abc' #length=3
+ mb_text = u'\u65e5\u672c\u8a9e' #length=3
+
+ sb_line = ' '.join([sb_text] * int(MAXWIDTH / 3))
+ mb_line = ' '.join([mb_text] * int(MAXWIDTH / 3))
+ mix_line = ' '.join([sb_text, mb_text] * int(MAXWIDTH / 6))
+
+ contents = u'\n\n'.join((sb_line, mb_line, mix_line))
+
+ (app.srcdir / 'contents.rst').write_text(contents, encoding='utf-8')
+ app.builder.build_all()
+ result = (app.outdir / 'contents.txt').text(encoding='utf-8')
+
+ lines = [line.strip() for line in result.splitlines() if line.strip()]
+ line_widths = [column_width(line) for line in lines]
+ assert max(line_widths) < MAXWIDTH
diff --git a/tests/test_config.py b/tests/test_config.py
index b07a71bf1..84a3b4b77 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -6,7 +6,7 @@
Test the sphinx.config.Config class and its handling in the
Application class.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import sys
@@ -16,6 +16,7 @@ from util import *
import sphinx
from sphinx.config import Config
from sphinx.errors import ExtensionError, ConfigError, VersionRequirementError
+from sphinx.util.pycompat import b
@with_app(confoverrides={'master_doc': 'master', 'nonexisting_value': 'True',
@@ -113,3 +114,14 @@ def test_errors_warnings(dir):
def test_needs_sphinx():
raises(VersionRequirementError, TestApp,
confoverrides={'needs_sphinx': '9.9'})
+
+
+@with_tempdir
+def test_config_eol(tmpdir):
+ # test config file's eol patterns: LF, CRLF
+ configfile = tmpdir / 'conf.py'
+ for eol in ('\n', '\r\n'):
+ configfile.write_bytes(b('project = "spam"' + eol))
+ cfg = Config(tmpdir, 'conf.py', {}, None)
+ cfg.init_values()
+ assert cfg.project == u'spam'
diff --git a/tests/test_coverage.py b/tests/test_coverage.py
index c554c52c7..88c66140f 100644
--- a/tests/test_coverage.py
+++ b/tests/test_coverage.py
@@ -5,7 +5,7 @@
Test the coverage builder.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/tests/test_cpp_domain.py b/tests/test_cpp_domain.py
index 3ab8ece4c..db2589d50 100644
--- a/tests/test_cpp_domain.py
+++ b/tests/test_cpp_domain.py
@@ -5,13 +5,13 @@
Tests the C++ Domain
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from util import *
-from sphinx.domains.cpp import DefinitionParser
+from sphinx.domains.cpp import DefinitionParser, DefinitionError
def parse(name, string):
@@ -43,6 +43,15 @@ def test_type_definitions():
x = 'int printf(const char* fmt, ...)'
assert unicode(parse('function', x)) == x
+ x = 'int foo(const unsigned int j)'
+ assert unicode(parse('function', x)) == x
+
+ x = 'int foo(const unsigned int const j)'
+ assert unicode(parse('function', x)) == x
+
+ x = 'int foo(const int* const ptr)'
+ assert unicode(parse('function', x)) == x
+
x = 'std::vector> module::blah'
assert unicode(parse('type_object', x)) == x
@@ -60,6 +69,9 @@ def test_type_definitions():
x = 'constexpr int get_value()'
assert unicode(parse('function', x)) == x
+ x = 'static constexpr int get_value()'
+ assert unicode(parse('function', x)) == x
+
x = 'int get_value() const noexcept'
assert unicode(parse('function', x)) == x
@@ -73,6 +85,21 @@ def test_type_definitions():
x = 'module::myclass foo[n]'
assert unicode(parse('member_object', x)) == x
+ x = 'int foo(Foo f=Foo(double(), std::make_pair(int(2), double(3.4))))'
+ assert unicode(parse('function', x)) == x
+
+ x = 'int foo(A a=x(a))'
+ assert unicode(parse('function', x)) == x
+
+ x = 'int foo(B b=x(a)'
+ raises(DefinitionError, parse, 'function', x)
+
+ x = 'int foo)C c=x(a))'
+ raises(DefinitionError, parse, 'function', x)
+
+ x = 'int foo(D d=x(a'
+ raises(DefinitionError, parse, 'function', x)
+
def test_bases():
x = 'A'
diff --git a/tests/test_doctest.py b/tests/test_doctest.py
index 577fcb00e..4934e716b 100644
--- a/tests/test_doctest.py
+++ b/tests/test_doctest.py
@@ -5,7 +5,7 @@
Test the doctest extension.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/tests/test_env.py b/tests/test_env.py
index e62db33bd..12e0719da 100644
--- a/tests/test_env.py
+++ b/tests/test_env.py
@@ -5,7 +5,7 @@
Test the BuildEnvironment class.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import sys
@@ -54,6 +54,7 @@ def test_images():
tree = env.get_doctree('images')
app._warning.reset()
htmlbuilder = StandaloneHTMLBuilder(app)
+ htmlbuilder.imgpath = 'dummy'
htmlbuilder.post_process_images(tree)
image_uri_message = "no matching candidate for image URI u'foo.*'"
if sys.version_info >= (3, 0):
diff --git a/tests/test_footnote.py b/tests/test_footnote.py
new file mode 100644
index 000000000..3a3d59670
--- /dev/null
+++ b/tests/test_footnote.py
@@ -0,0 +1,37 @@
+# -*- coding: utf-8 -*-
+"""
+ test_footnote
+ ~~~~~~~~~~~~~
+
+ Test for footnote and citation.
+
+ :copyright: Copyright 2010 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import re
+
+from util import *
+
+
+def teardown_module():
+ (test_root / '_build').rmtree(True)
+
+
+@with_app(buildername='html')
+def test_html(app):
+ app.builder.build(['footnote'])
+ result = (app.outdir / 'footnote.html').text(encoding='utf-8')
+ expects = [
+ '[1]',
+ '[2]',
+ '[3]',
+ '[bar]',
+ '[1]',
+ '[2]',
+ '[3]',
+ '[bar]',
+ ]
+ for expect in expects:
+ matches = re.findall(re.escape(expect), result)
+ assert len(matches) == 1
diff --git a/tests/test_highlighting.py b/tests/test_highlighting.py
index 949552c1e..b392193d6 100644
--- a/tests/test_highlighting.py
+++ b/tests/test_highlighting.py
@@ -5,7 +5,7 @@
Test the Pygments highlighting bridge.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/tests/test_i18n.py b/tests/test_i18n.py
index 930108e95..210243e79 100644
--- a/tests/test_i18n.py
+++ b/tests/test_i18n.py
@@ -5,7 +5,7 @@
Test locale features.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/tests/test_intersphinx.py b/tests/test_intersphinx.py
index 55320eaec..0171ae035 100644
--- a/tests/test_intersphinx.py
+++ b/tests/test_intersphinx.py
@@ -5,7 +5,7 @@
Test the intersphinx extension.
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/tests/test_intl.py b/tests/test_intl.py
index d1e28002f..4dec84646 100644
--- a/tests/test_intl.py
+++ b/tests/test_intl.py
@@ -11,43 +11,68 @@
"""
from subprocess import Popen, PIPE
+import re
+import os
+from StringIO import StringIO
+
+from sphinx.util.pycompat import relpath
from util import *
from util import SkipTest
+warnfile = StringIO()
+root = test_roots / 'test-intl'
+doctreedir = root / '_build' / 'doctree'
+
+
+def with_intl_app(*args, **kw):
+ default_kw = {
+ 'srcdir': root,
+ 'doctreedir': doctreedir,
+ 'confoverrides': {
+ 'language': 'xx', 'locale_dirs': ['.'],
+ 'gettext_compact': False,
+ },
+ }
+ default_kw.update(kw)
+ return with_app(*args, **default_kw)
+
+
def setup_module():
- (test_root / 'xx' / 'LC_MESSAGES').makedirs()
+ # Delete remnants left over after failed build
+ (root / 'xx').rmtree(True)
+ (root / 'xx' / 'LC_MESSAGES').makedirs()
# Compile all required catalogs into binary format (*.mo).
- for catalog in 'bom', 'subdir':
- try:
- p = Popen(['msgfmt', test_root / '%s.po' % catalog, '-o',
- test_root / 'xx' / 'LC_MESSAGES' / '%s.mo' % catalog],
- stdout=PIPE, stderr=PIPE)
- except OSError:
- # The test will fail the second time it's run if we don't
- # tear down here. Not sure if there's a more idiomatic way
- # of ensuring that teardown gets run in the event of an
- # exception from the setup function.
- teardown_module()
- raise SkipTest # most likely msgfmt was not found
- else:
- stdout, stderr = p.communicate()
- if p.returncode != 0:
- print stdout
- print stderr
- assert False, 'msgfmt exited with return code %s' % p.returncode
- assert (test_root / 'xx' / 'LC_MESSAGES' / ('%s.mo' % catalog)
- ).isfile(), 'msgfmt failed'
+ for dirpath, dirs, files in os.walk(root):
+ dirpath = path(dirpath)
+ for f in [f for f in files if f.endswith('.po')]:
+ po = dirpath / f
+ mo = root / 'xx' / 'LC_MESSAGES' / (
+ relpath(po[:-3], root) + '.mo')
+ if not mo.parent.exists():
+ mo.parent.makedirs()
+ try:
+ p = Popen(['msgfmt', po, '-o', mo],
+ stdout=PIPE, stderr=PIPE)
+ except OSError:
+ raise SkipTest # most likely msgfmt was not found
+ else:
+ stdout, stderr = p.communicate()
+ if p.returncode != 0:
+ print stdout
+ print stderr
+ assert False, \
+ 'msgfmt exited with return code %s' % p.returncode
+ assert mo.isfile(), 'msgfmt failed'
def teardown_module():
- (test_root / '_build').rmtree(True)
- (test_root / 'xx').rmtree(True)
+ (root / '_build').rmtree(True)
+ (root / 'xx').rmtree(True)
-@with_app(buildername='text',
- confoverrides={'language': 'xx', 'locale_dirs': ['.']})
+@with_intl_app(buildername='text')
def test_simple(app):
app.builder.build(['bom'])
result = (app.outdir / 'bom.txt').text(encoding='utf-8')
@@ -57,9 +82,308 @@ def test_simple(app):
assert result == expect
-@with_app(buildername='text',
- confoverrides={'language': 'xx', 'locale_dirs': ['.']})
+@with_intl_app(buildername='text')
def test_subdir(app):
- app.builder.build(['subdir/includes'])
- result = (app.outdir / 'subdir' / 'includes.txt').text(encoding='utf-8')
- assert result.startswith(u"\ntranslation\n***********\n\n")
+ app.builder.build(['subdir/contents'])
+ result = (app.outdir / 'subdir' / 'contents.txt').text(encoding='utf-8')
+ assert result.startswith(u"\nsubdir contents\n***************\n")
+
+
+@with_intl_app(buildername='text', warning=warnfile)
+def test_i18n_warnings_in_translation(app):
+ app.builddir.rmtree(True)
+ app.builder.build(['warnings'])
+ result = (app.outdir / 'warnings.txt').text(encoding='utf-8')
+ expect = (u"\nI18N WITH REST WARNINGS"
+ u"\n***********************\n"
+ u"\nLINE OF >>``<[100]',
+ '[1]',
+ '[ref]',
+ '[1]',
+ '[ref]',
+ '[100]',
+ ]
+ for expect in expects:
+ matches = re.findall(re.escape(expect), result)
+ assert len(matches) == 1
+
+
+@with_intl_app(buildername='text', warning=warnfile, cleanenv=True)
+def test_i18n_warn_for_number_of_references_inconsistency(app):
+ app.builddir.rmtree(True)
+ app.builder.build(['refs_inconsistency'])
+ result = (app.outdir / 'refs_inconsistency.txt').text(encoding='utf-8')
+ expect = (u"\nI18N WITH REFS INCONSISTENCY"
+ u"\n****************************\n"
+ u"\n* FOR FOOTNOTE [ref2].\n"
+ u"\n* reference FOR reference.\n"
+ u"\n* ORPHAN REFERENCE: I18N WITH REFS INCONSISTENCY.\n"
+ u"\n[1] THIS IS A AUTO NUMBERED FOOTNOTE.\n"
+ u"\n[ref2] THIS IS A NAMED FOOTNOTE.\n"
+ u"\n[100] THIS IS A NUMBERED FOOTNOTE.\n")
+ assert result == expect
+
+ warnings = warnfile.getvalue().replace(os.sep, '/')
+ warning_fmt = u'.*/refs_inconsistency.txt:\\d+: ' \
+ u'WARNING: inconsistent %s in translated message\n'
+ expected_warning_expr = (
+ warning_fmt % 'footnote references' +
+ warning_fmt % 'references' +
+ warning_fmt % 'references')
+ assert re.search(expected_warning_expr, warnings)
+
+
+@with_intl_app(buildername='html', cleanenv=True)
+def test_i18n_link_to_undefined_reference(app):
+ app.builder.build(['refs_inconsistency'])
+ result = (app.outdir / 'refs_inconsistency.html').text(encoding='utf-8')
+
+ expected_expr = ('reference')
+ assert len(re.findall(expected_expr, result)) == 2
+
+ expected_expr = ('reference')
+ assert len(re.findall(expected_expr, result)) == 0
+
+ expected_expr = ('I18N WITH '
+ 'REFS INCONSISTENCY')
+ assert len(re.findall(expected_expr, result)) == 1
+
+
+@with_intl_app(buildername='html', cleanenv=True)
+def test_i18n_keep_external_links(app):
+ """regression test for #1044"""
+ app.builder.build(['external_links'])
+ result = (app.outdir / 'external_links.html').text(encoding='utf-8')
+
+ # external link check
+ expect_line = (u'