summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorg Brandl <georg@python.org>2011-01-08 15:16:38 +0100
committerGeorg Brandl <georg@python.org>2011-01-08 15:16:38 +0100
commit4728d8379beb1fb91c096f0973864fc246a0dc29 (patch)
treea47673f069028a0e3b42dd697db46bfcda4e1fcb
parent7f1e43cbf9baf8ca1e88f8aac4c0d970946f682c (diff)
parent53e6c23f52e9c0f76a4eb04d883a051f5e2dd0e0 (diff)
downloadsphinx-4728d8379beb1fb91c096f0973864fc246a0dc29.tar.gz
merge with https://bitbucket.org/dhellmann/sphinx-graphviz-paragraphs/
-rw-r--r--.hgignore2
-rw-r--r--AUTHORS6
-rw-r--r--CHANGES159
-rw-r--r--EXAMPLES19
-rw-r--r--LICENSE2
-rw-r--r--doc/Makefile15
-rw-r--r--doc/_static/pocoo.pngbin0 -> 4211 bytes
-rw-r--r--doc/_templates/index.html2
-rw-r--r--doc/_templates/indexsidebar.html3
-rw-r--r--doc/builders.rst20
-rw-r--r--doc/conf.py22
-rw-r--r--doc/config.rst204
-rw-r--r--doc/domains.rst60
-rw-r--r--doc/ext/appapi.rst27
-rw-r--r--doc/ext/autodoc.rst61
-rw-r--r--doc/ext/coverage.rst14
-rw-r--r--doc/ext/doctest.rst16
-rw-r--r--doc/ext/inheritance.rst7
-rw-r--r--doc/ext/math.rst44
-rw-r--r--doc/extensions.rst15
-rw-r--r--doc/faq.rst115
-rw-r--r--doc/glossary.rst4
-rw-r--r--doc/intl.rst12
-rw-r--r--doc/invocation.rst4
-rw-r--r--doc/markup/inline.rst12
-rw-r--r--doc/markup/misc.rst47
-rw-r--r--doc/markup/para.rst23
-rw-r--r--doc/themes/fullsize/pyramid.pngbin0 -> 131965 bytes
-rw-r--r--doc/themes/pyramid.pngbin0 -> 49630 bytes
-rw-r--r--doc/theming.rst13
-rw-r--r--doc/web/quickstart.rst13
-rw-r--r--doc/web/storagebackends.rst2
-rw-r--r--setup.py1
-rwxr-xr-xsphinx-apidoc.py15
-rwxr-xr-xsphinx-autogen.py4
-rwxr-xr-xsphinx-build.py2
-rwxr-xr-xsphinx-quickstart.py2
-rw-r--r--sphinx/__init__.py4
-rw-r--r--sphinx/addnodes.py5
-rw-r--r--sphinx/apidoc.py263
-rw-r--r--sphinx/application.py23
-rw-r--r--sphinx/builders/__init__.py6
-rw-r--r--sphinx/builders/changes.py5
-rw-r--r--sphinx/builders/devhelp.py2
-rw-r--r--sphinx/builders/epub.py21
-rw-r--r--sphinx/builders/html.py84
-rw-r--r--sphinx/builders/htmlhelp.py6
-rw-r--r--sphinx/builders/intl.py12
-rw-r--r--sphinx/builders/latex.py2
-rw-r--r--sphinx/builders/linkcheck.py181
-rw-r--r--sphinx/builders/manpage.py2
-rw-r--r--sphinx/builders/qthelp.py6
-rw-r--r--sphinx/builders/texinfo.py239
-rw-r--r--sphinx/builders/text.py2
-rw-r--r--sphinx/builders/versioning.py72
-rw-r--r--sphinx/builders/websupport.py125
-rw-r--r--sphinx/cmdline.py21
-rw-r--r--sphinx/config.py26
-rw-r--r--sphinx/directives/__init__.py2
-rw-r--r--sphinx/directives/code.py4
-rw-r--r--sphinx/directives/other.py9
-rw-r--r--sphinx/domains/__init__.py2
-rw-r--r--sphinx/domains/c.py53
-rw-r--r--sphinx/domains/cpp.py69
-rw-r--r--sphinx/domains/javascript.py4
-rw-r--r--sphinx/domains/python.py68
-rw-r--r--sphinx/domains/rst.py4
-rw-r--r--sphinx/domains/std.py125
-rw-r--r--sphinx/environment.py218
-rw-r--r--sphinx/errors.py2
-rw-r--r--sphinx/ext/__init__.py2
-rw-r--r--sphinx/ext/autodoc.py118
-rw-r--r--sphinx/ext/autosummary/__init__.py8
-rw-r--r--sphinx/ext/autosummary/generate.py4
-rw-r--r--sphinx/ext/coverage.py28
-rw-r--r--sphinx/ext/doctest.py83
-rw-r--r--sphinx/ext/extlinks.py2
-rw-r--r--sphinx/ext/graphviz.py5
-rw-r--r--sphinx/ext/ifconfig.py2
-rw-r--r--sphinx/ext/inheritance_diagram.py23
-rw-r--r--sphinx/ext/intersphinx.py2
-rw-r--r--sphinx/ext/jsmath.py2
-rw-r--r--sphinx/ext/mathbase.py46
-rw-r--r--sphinx/ext/mathjax.py67
-rw-r--r--sphinx/ext/oldcmarkup.py2
-rw-r--r--sphinx/ext/pngmath.py2
-rw-r--r--sphinx/ext/refcounting.py2
-rw-r--r--sphinx/ext/todo.py5
-rw-r--r--sphinx/ext/viewcode.py4
-rw-r--r--sphinx/highlighting.py26
-rw-r--r--sphinx/jinja2glue.py2
-rw-r--r--sphinx/locale/__init__.py3
-rw-r--r--sphinx/locale/fa/LC_MESSAGES/sphinx.js1
-rw-r--r--sphinx/locale/fa/LC_MESSAGES/sphinx.mobin0 -> 9545 bytes
-rw-r--r--sphinx/locale/fa/LC_MESSAGES/sphinx.po596
-rw-r--r--sphinx/locale/ja/LC_MESSAGES/sphinx.js2
-rw-r--r--sphinx/locale/ja/LC_MESSAGES/sphinx.mobin9950 -> 10110 bytes
-rw-r--r--sphinx/locale/ja/LC_MESSAGES/sphinx.po46
-rw-r--r--sphinx/locale/pl/LC_MESSAGES/sphinx.mobin9370 -> 11024 bytes
-rw-r--r--sphinx/locale/pl/LC_MESSAGES/sphinx.po102
-rw-r--r--sphinx/pycode/__init__.py2
-rw-r--r--sphinx/pycode/nodes.py2
-rw-r--r--sphinx/pycode/pgen2/driver.py2
-rw-r--r--sphinx/pygments_styles.py96
-rw-r--r--sphinx/quickstart.py72
-rw-r--r--sphinx/roles.py17
-rw-r--r--sphinx/search/__init__.py (renamed from sphinx/search.py)156
-rw-r--r--sphinx/search/en.py242
-rw-r--r--sphinx/search/ja.py273
-rw-r--r--sphinx/setup_command.py3
-rw-r--r--sphinx/texinputs/Makefile12
-rw-r--r--sphinx/texinputs/sphinx.sty75
-rw-r--r--sphinx/themes/agogo/layout.html2
-rw-r--r--sphinx/themes/agogo/static/agogo.css_t33
-rw-r--r--sphinx/themes/basic/defindex.html2
-rw-r--r--sphinx/themes/basic/domainindex.html2
-rw-r--r--sphinx/themes/basic/genindex-single.html51
-rw-r--r--sphinx/themes/basic/genindex-split.html2
-rw-r--r--sphinx/themes/basic/genindex.html64
-rw-r--r--sphinx/themes/basic/globaltoc.html2
-rw-r--r--sphinx/themes/basic/layout.html47
-rw-r--r--sphinx/themes/basic/localtoc.html2
-rw-r--r--sphinx/themes/basic/page.html2
-rw-r--r--sphinx/themes/basic/relations.html2
-rw-r--r--sphinx/themes/basic/search.html2
-rw-r--r--sphinx/themes/basic/searchbox.html2
-rw-r--r--sphinx/themes/basic/searchresults.html22
-rw-r--r--sphinx/themes/basic/sourcelink.html2
-rw-r--r--sphinx/themes/basic/static/basic.css21
-rw-r--r--sphinx/themes/basic/static/doctools.js2
-rw-r--r--sphinx/themes/basic/static/searchtools.js_t (renamed from sphinx/themes/basic/static/searchtools.js)201
-rw-r--r--sphinx/themes/basic/static/websupport.js456
-rw-r--r--sphinx/themes/default/layout.html2
-rw-r--r--sphinx/themes/default/static/default.css_t2
-rw-r--r--sphinx/themes/default/static/sidebar.js5
-rw-r--r--sphinx/themes/epub/epub-cover.html2
-rw-r--r--sphinx/themes/epub/layout.html2
-rw-r--r--sphinx/themes/epub/static/epub.css3
-rw-r--r--sphinx/themes/haiku/layout.html2
-rw-r--r--sphinx/themes/haiku/static/haiku.css_t2
-rw-r--r--sphinx/themes/nature/static/nature.css_t2
-rw-r--r--sphinx/themes/pyramid/layout.html24
-rw-r--r--sphinx/themes/pyramid/static/dialog-note.pngbin0 -> 1582 bytes
-rw-r--r--sphinx/themes/pyramid/static/dialog-seealso.pngbin0 -> 1502 bytes
-rw-r--r--sphinx/themes/pyramid/static/dialog-topic.pngbin0 -> 1910 bytes
-rw-r--r--sphinx/themes/pyramid/static/dialog-warning.pngbin0 -> 1391 bytes
-rw-r--r--sphinx/themes/pyramid/static/epub.css310
-rw-r--r--sphinx/themes/pyramid/static/footerbg.pngbin0 -> 333 bytes
-rw-r--r--sphinx/themes/pyramid/static/headerbg.pngbin0 -> 203 bytes
-rw-r--r--sphinx/themes/pyramid/static/ie6.css7
-rw-r--r--sphinx/themes/pyramid/static/middlebg.pngbin0 -> 2797 bytes
-rw-r--r--sphinx/themes/pyramid/static/pyramid.css_t323
-rw-r--r--sphinx/themes/pyramid/static/transparent.gifbin0 -> 49 bytes
-rw-r--r--sphinx/themes/pyramid/theme.conf4
-rw-r--r--sphinx/themes/scrolls/layout.html2
-rw-r--r--sphinx/themes/scrolls/static/scrolls.css_t2
-rw-r--r--sphinx/themes/sphinxdoc/layout.html2
-rw-r--r--sphinx/themes/sphinxdoc/static/sphinxdoc.css2
-rw-r--r--sphinx/themes/traditional/static/traditional.css2
-rw-r--r--sphinx/theming.py13
-rw-r--r--sphinx/util/__init__.py21
-rw-r--r--sphinx/util/compat.py2
-rw-r--r--sphinx/util/console.py2
-rw-r--r--sphinx/util/docfields.py23
-rw-r--r--sphinx/util/docstrings.py27
-rw-r--r--sphinx/util/inspect.py2
-rw-r--r--sphinx/util/jsdump.py6
-rw-r--r--sphinx/util/jsonimpl.py2
-rw-r--r--sphinx/util/matching.py2
-rw-r--r--sphinx/util/nodes.py21
-rw-r--r--sphinx/util/osutil.py2
-rw-r--r--sphinx/util/png.py2
-rw-r--r--sphinx/util/pycompat.py5
-rw-r--r--sphinx/util/tags.py2
-rw-r--r--sphinx/util/texescape.py2
-rw-r--r--sphinx/util/websupport.py5
-rw-r--r--sphinx/versioning.py2
-rw-r--r--sphinx/websupport/__init__.py203
-rw-r--r--sphinx/websupport/errors.py9
-rw-r--r--sphinx/websupport/search/__init__.py7
-rw-r--r--sphinx/websupport/search/nullsearch.py2
-rw-r--r--sphinx/websupport/search/whooshsearch.py2
-rw-r--r--sphinx/websupport/search/xapiansearch.py2
-rw-r--r--sphinx/websupport/storage/__init__.py10
-rw-r--r--sphinx/websupport/storage/differ.py22
-rw-r--r--sphinx/websupport/storage/sqlalchemy_db.py43
-rw-r--r--sphinx/websupport/storage/sqlalchemystorage.py41
-rw-r--r--sphinx/writers/__init__.py2
-rw-r--r--sphinx/writers/html.py67
-rw-r--r--sphinx/writers/latex.py183
-rw-r--r--sphinx/writers/manpage.py19
-rw-r--r--sphinx/writers/texinfo.py1256
-rw-r--r--sphinx/writers/text.py36
-rw-r--r--sphinx/writers/websupport.py6
-rwxr-xr-xtests/coverage.py31
-rw-r--r--tests/root/autodoc.txt6
-rw-r--r--tests/root/autodoc_fodder.py7
-rw-r--r--tests/root/conf.py5
-rw-r--r--tests/root/doctest.txt6
-rw-r--r--tests/root/markup.txt31
-rw-r--r--tests/root/objects.txt24
-rw-r--r--tests/root/subdir.po9
-rwxr-xr-xtests/run.py2
-rw-r--r--tests/test_application.py2
-rw-r--r--tests/test_autodoc.py35
-rw-r--r--tests/test_autosummary.py2
-rw-r--r--tests/test_build.py2
-rw-r--r--tests/test_build_gettext.py55
-rw-r--r--tests/test_build_html.py23
-rw-r--r--tests/test_build_latex.py3
-rw-r--r--tests/test_build_texinfo.py66
-rw-r--r--tests/test_config.py4
-rw-r--r--tests/test_coverage.py4
-rw-r--r--tests/test_cpp_domain.py2
-rw-r--r--tests/test_doctest.py12
-rw-r--r--tests/test_env.py8
-rw-r--r--tests/test_highlighting.py2
-rw-r--r--tests/test_i18n.py2
-rw-r--r--tests/test_intersphinx.py2
-rw-r--r--tests/test_intl.py60
-rw-r--r--tests/test_markup.py2
-rw-r--r--tests/test_metadata.py2
-rw-r--r--tests/test_quickstart.py6
-rw-r--r--tests/test_rst_domain.py2
-rw-r--r--tests/test_search.py4
-rw-r--r--tests/test_searchadapters.py2
-rw-r--r--tests/test_theming.py5
-rw-r--r--tests/test_versioning.py2
-rw-r--r--tests/test_websupport.py8
-rw-r--r--tests/util.py2
-rwxr-xr-xutils/check_sources.py2
231 files changed, 7130 insertions, 1753 deletions
diff --git a/.hgignore b/.hgignore
index b68cccf9..45ecebc3 100644
--- a/.hgignore
+++ b/.hgignore
@@ -17,3 +17,5 @@
~$
^utils/.*3\.py$
^distribute-
+^tests/root/_build/*
+^tests/root/generated/*
diff --git a/AUTHORS b/AUTHORS
index 2ee77739..09574fbe 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -8,12 +8,15 @@ Other contributors, listed alphabetically, are:
* Andi Albrecht -- agogo theme
* Henrique Bastos -- SVG support for graphviz extension
* Daniel Bültmann -- todo extension
+* Etienne Desautels -- apidoc module
* Michael Droettboom -- inheritance_diagram extension
* Charles Duffy -- original graphviz extension
+* Kevin Dunn -- MathJax extension
* Josip Dzolonga -- coverage builder
* Horst Gutmann -- internationalization support
* Martin Hans -- autodoc improvements
* Dave Kuhlman -- original LaTeX writer
+* Blaise Laflamme -- pyramid theme
* Thomas Lamb -- linkcheck builder
* Robert Lehmann -- gettext builder (GSOC project)
* Dan MacKinlay -- metadata fixes
@@ -27,9 +30,12 @@ Other contributors, listed alphabetically, are:
* Benjamin Peterson -- unittests
* T. Powers -- HTML output improvements
* Stefan Seefeld -- toctree improvements
+* Shibukawa Yoshiki -- pluggable search API and Japanese search
* Antonio Valentino -- qthelp builder
* Pauli Virtanen -- autodoc improvements, autosummary extension
* Stefan van der Walt -- autosummary extension
+* Thomas Waldmann -- apidoc module fixes
+* John Waltman -- Texinfo builder
* Barry Warsaw -- setup command improvements
* Sebastian Wiesner -- image handling, distutils support
diff --git a/CHANGES b/CHANGES
index c7823aa2..26c63f8b 100644
--- a/CHANGES
+++ b/CHANGES
@@ -3,6 +3,8 @@ Release 1.1 (in development)
* Added Python 3.x support.
+* Added a Texinfo builder.
+
* Added i18n support for content, a ``gettext`` builder and
related utilities.
@@ -10,16 +12,171 @@ Release 1.1 (in development)
* #460: Allow limiting the depth of section numbers for HTML.
+* #564: Add :confval:`autodoc_docstring_signature` which retrieves
+ the signature from the first line of the docstring, if it is
+ found there.
+
+* #176: Provide ``private-members`` option for autodoc directives.
+
+* #520: Provide ``special-members`` option for autodoc directives.
+
* #138: Add an ``index`` role, to make inline index entries.
* #443: Allow referencing external graphviz files.
+* Added the :mod:`sphinx.ext.mathjax` extension.
+
+* Added ``pyramid`` theme.
+
+* #98: Added a ``sphinx-apidoc`` script that autogenerates a
+ hierarchy of source files containing autodoc directives to
+ document modules and packages.
+
+* #472: linkcheck builder: Check links in parallel, use HTTP HEAD
+ requests and allow configuring the timeout. New config values:
+ :confval:`linkcheck_timeout` and :confval:`linkcheck_workers`.
+
+* #273: Add an API for adding full-text search support for languages
+ other than English. Add support for Japanese.
+
* #221: Add Swedish locale.
+* Added ``inline`` option to graphviz directives, and fixed the
+ default (block-style) in LaTeX output.
+
+* #521: Added :confval:`linkcheck_ignore` config value.
+
+* #454: Add more index markup capabilities: marking see/seealso entries,
+ and main entries for a given key.
+
+* #516: Added new value of the :confval:`latex_show_urls` option to
+ show the URLs in footnotes.
+
+* #526: Added Iranian translation.
+
+* #586: Implemented improved glossary markup which allows multiple terms per
+ definition.
+
+* #559: :confval:`html_add_permalinks` is now a string giving the
+ text to display in permalinks.
+
+* #553: Added :rst:dir:`testcleanup` blocks in the doctest extension.
+
+* #209: Added :confval:`text_newlines` and :confval:`text_sectionchars`
+ config values.
-Release 1.0.4 (in development)
+* Added :confval:`man_show_urls` config value.
+
+* #259: HTML table rows now have even/odd CSS classes to enable
+ "Zebra styling".
+
+* #478: Added :rst:dir:`py:decorator` directive to describe decorators.
+
+* #367: Added automatic exclusion of hidden members in inheritance
+ diagrams, and an option to selectively enable it.
+
+* #306: Added :event:`env-get-outdated` event.
+
+* C++ domain now supports array definitions.
+
+
+Release 1.0.7 (in development)
==============================
+* Fix a bug in the inheritance diagram exception that caused base
+ classes to be skipped if one of them is a builtin.
+
+* Fix general index links for C++ domain objects.
+
+* #332: Make admonition boundaries in LaTeX output visible.
+
+* #573: Fix KeyErrors occurring on rebuild after removing a file.
+
+* Fix a traceback when removing files with globbed toctrees.
+
+* If an autodoc object cannot be imported, always re-read the
+ document containing the directive on next build.
+
+* If an autodoc object cannot be imported, show the full traceback
+ of the import error.
+
+* Fix a bug where the removal of download files and images wasn't
+ noticed.
+
+* #571: Implement ``~`` cross-reference prefix for the C domain.
+
+* Fix regression of LaTeX output with the fix of #556.
+
+* #568: Fix lookup of class attribute documentation on descriptors
+ so that comment documentation now works.
+
+* Fix traceback with ``only`` directives preceded by targets.
+
+* Fix tracebacks occurring for duplicate C++ domain objects.
+
+
+Release 1.0.6 (Jan 04, 2011)
+============================
+
+* #581: Fix traceback in Python domain for empty cross-reference
+ targets.
+
+* #283: Fix literal block display issues on Chrome browsers.
+
+* #383, #148: Support sorting a limited range of accented characters
+ in the general index and the glossary.
+
+* #570: Try decoding ``-D`` and ``-A`` command-line arguments with
+ the locale's preferred encoding.
+
+* #528: Observe :confval:`locale_dirs` when looking for the JS
+ translations file.
+
+* #574: Add special code for better support of Japanese documents
+ in the LaTeX builder.
+
+* Regression of #77: If there is only one parameter given with
+ ``:param:`` markup, the bullet list is now suppressed again.
+
+* #556: Fix missing paragraph breaks in LaTeX output in certain
+ situations.
+
+* #567: Emit the ``autodoc-process-docstring`` event even for objects
+ without a docstring so that it can add content.
+
+* #565: In the LaTeX builder, not only literal blocks require different
+ table handling, but also quite a few other list-like block elements.
+
+* #515: Fix tracebacks in the viewcode extension for Python objects
+ that do not have a valid signature.
+
+* Fix strange reportings of line numbers for warnings generated from
+ autodoc-included docstrings, due to different behavior depending
+ on docutils version.
+
+* Several fixes to the C++ domain.
+
+
+Release 1.0.5 (Nov 12, 2010)
+============================
+
+* #557: Add CSS styles required by docutils 0.7 for aligned images
+ and figures.
+
+* In the Makefile generated by LaTeX output, do not delete pdf files
+ on clean; they might be required images.
+
+* #535: Fix LaTeX output generated for line blocks.
+
+* #544: Allow ``.pyw`` as a source file extension.
+
+
+Release 1.0.4 (Sep 17, 2010)
+============================
+
+* #524: Open intersphinx inventories in binary mode on Windows,
+ since version 2 contains zlib-compressed data.
+
* #513: Allow giving non-local URIs for JavaScript files, e.g.
in the JSMath extension.
diff --git a/EXAMPLES b/EXAMPLES
index b5fd96af..2789571c 100644
--- a/EXAMPLES
+++ b/EXAMPLES
@@ -20,7 +20,8 @@ Documentation using the default theme
* Cython: http://docs.cython.org/
* C\\C++ Python language binding project: http://language-binding.net/index.html
* Director: http://packages.python.org/director/
-* F2py: http://www.f2py.org/html/
+* Dirigible: http://www.projectdirigible.com/documentation/
+* F2py: http://f2py.sourceforge.net/docs/
* GeoDjango: http://geodjango.org/docs/
* gevent: http://www.gevent.org/
* Google Wave API: http://wave-robot-python-client.googlecode.com/svn/trunk/pydocs/index.html
@@ -28,6 +29,8 @@ Documentation using the default theme
* Heapkeeper: http://heapkeeper.org/
* Hedge: http://documen.tician.de/hedge/
* Kaa: http://doc.freevo.org/api/kaa/
+* Leo: http://webpages.charter.net/edreamleo/front.html
+* Lino: http://lino.saffre-rumma.ee/
* MeshPy: http://documen.tician.de/meshpy/
* mpmath: http://mpmath.googlecode.com/svn/trunk/doc/build/index.html
* OpenEXR: http://excamera.com/articles/26/doc/index.html
@@ -70,7 +73,6 @@ Documentation using a customized version of the default theme
* NOC: http://trac.nocproject.org/trac/wiki/NocGuide
* NumPy: http://docs.scipy.org/doc/numpy/reference/
* Peach^3: http://peach3.nl/doc/latest/userdoc/
-* Py on Windows: http://timgolden.me.uk/python-on-windows/
* PyLit: http://pylit.berlios.de/
* Sage: http://sagemath.org/doc/
* SciPy: http://docs.scipy.org/doc/scipy/reference/
@@ -91,6 +93,7 @@ Documentation using the sphinxdoc theme
* MyHDL: http://www.myhdl.org/doc/0.6/
* NetworkX: http://networkx.lanl.gov/
* Pweave: http://mpastell.com/pweave/
+* Pyre: http://docs.danse.us/pyre/sphinx/
* Pysparse: http://pysparse.sourceforge.net/
* PyTango:
http://www.tango-controls.org/static/PyTango/latest/doc/html/index.html
@@ -108,10 +111,11 @@ Documentation using another builtin theme
* C/C++ Development with Eclipse: http://book.dehlia.in/c-cpp-eclipse/ (agogo)
* Distribute: http://packages.python.org/distribute/ (nature)
-* Jinja: http://jinja.pocoo.org/2/documentation/ (scrolls)
+* Jinja: http://jinja.pocoo.org/ (scrolls)
* pip: http://pip.openplans.org/ (nature)
* Programmieren mit PyGTK und Glade (German):
http://www.florian-diesch.de/doc/python-und-glade/online/ (agogo)
+* pypol: http://pypol.altervista.org/ (nature)
* Spring Python: http://springpython.webfactional.com/current/sphinx/index.html
(nature)
* sqlparse: http://python-sqlparse.googlecode.com/svn/docs/api/index.html
@@ -158,3 +162,12 @@ Homepages and other non-documentation sites
* lunarsite: http://lunaryorn.de/
* The Wine Cellar Book: http://www.thewinecellarbook.com/doc/en/
* VOR: http://www.vor-cycling.be/
+
+
+Books produced using Sphinx
+---------------------------
+
+* The ``repoze.bfg`` Web Application Framework:
+ http://www.amazon.com/repoze-bfg-Web-Application-Framework-Version/dp/0615345379
+* A Theoretical Physics Reference book:
+ http://ondrejcertik.blogspot.com/2010/07/theoretical-physics-reference-book.html
diff --git a/LICENSE b/LICENSE
index 46891493..fa1ef995 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,7 +1,7 @@
License for Sphinx
==================
-Copyright (c) 2007-2010 by the Sphinx team (see AUTHORS file).
+Copyright (c) 2007-2011 by the Sphinx team (see AUTHORS file).
All rights reserved.
Redistribution and use in source and binary forms, with or without
diff --git a/doc/Makefile b/doc/Makefile
index aa3ecb61..0199ba47 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -29,6 +29,8 @@ help:
@echo " epub to make an epub file"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run pdflatex"
+ @echo " texinfo to make Texinfo files"
+ @echo " info to make Texinfo files and run them through makeinfo"
@echo " gettext to make PO message catalogs"
@echo " changes to make an overview over all changed/added/deprecated items"
@echo " linkcheck to check all external links for integrity"
@@ -131,3 +133,16 @@ linkcheck:
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) _build/doctest
+
+texinfo:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) _build/texinfo
+ @echo
+ @echo "Build finished. The Texinfo files are in _build/texinfo."
+ @echo "Run \`make' in that directory to run these through makeinfo" \
+ "(use \`make info' here to do that automatically)."
+
+info:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) _build/texinfo
+ @echo "Running Texinfo files through makeinfo..."
+ make -C _build/texinfo info
+ @echo "makeinfo finished; the Info files are in _build/texinfo."
diff --git a/doc/_static/pocoo.png b/doc/_static/pocoo.png
new file mode 100644
index 00000000..297dcd5e
--- /dev/null
+++ b/doc/_static/pocoo.png
Binary files differ
diff --git a/doc/_templates/index.html b/doc/_templates/index.html
index 524b2ffb..34dead7e 100644
--- a/doc/_templates/index.html
+++ b/doc/_templates/index.html
@@ -12,7 +12,7 @@
<p>
Sphinx is a tool that makes it easy to create intelligent and beautiful
documentation, written by Georg Brandl and licensed under the BSD license.</p>
- <p>It was originally created for <a href="http://docs.python.org/dev/">the
+ <p>It was originally created for <a href="http://docs.python.org/">the
new Python documentation</a>, and it has excellent facilities for the
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
diff --git a/doc/_templates/indexsidebar.html b/doc/_templates/indexsidebar.html
index 85b8fba9..feafd904 100644
--- a/doc/_templates/indexsidebar.html
+++ b/doc/_templates/indexsidebar.html
@@ -1,3 +1,6 @@
+<p class="logo"><a href="http://pocoo.org/">
+ <img src="{{ pathto("_static/pocoo.png", 1) }}" /></a></p>
+
<h3>Download</h3>
{% if version.endswith('(hg)') %}
<p>This documentation is for version <b>{{ version }}</b>, which is
diff --git a/doc/builders.rst b/doc/builders.rst
index 8ea46ad6..4a95e120 100644
--- a/doc/builders.rst
+++ b/doc/builders.rst
@@ -144,6 +144,26 @@ Note that a direct PDF builder using ReportLab is available in `rst2pdf
.. versionadded:: 1.0
+
+.. module:: sphinx.builders.texinfo
+.. class:: TexinfoBuilder
+
+ This builder produces Texinfo files that can be processed into Info files by
+ the :program:`makeinfo` program. You have to specify which documents are to
+ be included in which Texinfo files via the :confval:`texinfo_documents`
+ configuration value.
+
+ The Info format is the basis of the on-line help system used by GNU Emacs and
+ the terminal-based program :program:`info`. See :ref:`texinfo-faq` for more
+ details. The Texinfo format is the official documentation system used by the
+ GNU project. More information on Texinfo can be found at
+ `<http://www.gnu.org/software/texinfo/>`_.
+
+ Its name is ``texinfo``.
+
+ .. versionadded:: 1.1
+
+
.. currentmodule:: sphinx.builders.html
.. class:: SerializingHTMLBuilder
diff --git a/doc/conf.py b/doc/conf.py
index b3a1cda7..a1d0e8ce 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -13,7 +13,7 @@ templates_path = ['_templates']
exclude_patterns = ['_build']
project = 'Sphinx'
-copyright = '2007-2010, Georg Brandl'
+copyright = '2007-2011, Georg Brandl'
version = sphinx.__released__
release = version
show_authors = True
@@ -21,7 +21,6 @@ show_authors = True
html_theme = 'sphinxdoc'
modindex_common_prefix = ['sphinx.']
html_static_path = ['_static']
-html_index = 'index.html'
html_sidebars = {'index': ['indexsidebar.html', 'searchbox.html']}
html_additional_pages = {'index': 'index.html'}
html_use_opensearch = 'http://sphinx.pocoo.org'
@@ -45,6 +44,7 @@ latex_logo = '_static/sphinx.png'
latex_elements = {
'fontpkg': '\\usepackage{palatino}',
}
+latex_show_urls = 'footnote'
autodoc_member_order = 'groupwise'
todo_include_todos = True
@@ -64,6 +64,12 @@ man_pages = [
'template generator', '', 1),
]
+texinfo_documents = [
+ ('contents', 'sphinx', 'Sphinx Documentation', 'Georg Brandl',
+ 'Sphinx', 'The Sphinx documentation builder.', 'Documentation tools',
+ 1),
+]
+
# We're not using intersphinx right now, but if we did, this would be part of
# the mapping:
intersphinx_mapping = {'python': ('http://docs.python.org/dev', None)}
@@ -93,8 +99,12 @@ def parse_event(env, sig, signode):
def setup(app):
from sphinx.ext.autodoc import cut_lines
+ from sphinx.util.docfields import GroupedField
app.connect('autodoc-process-docstring', cut_lines(4, what=['module']))
- app.add_description_unit('confval', 'confval',
- objname='configuration value',
- indextemplate='pair: %s; configuration value')
- app.add_description_unit('event', 'event', 'pair: %s; event', parse_event)
+ app.add_object_type('confval', 'confval',
+ objname='configuration value',
+ indextemplate='pair: %s; configuration value')
+ fdesc = GroupedField('parameter', label='Parameters',
+ names=['param'], can_collapse=True)
+ app.add_object_type('event', 'event', 'pair: %s; event', parse_event,
+ doc_field_types=[fdesc])
diff --git a/doc/config.rst b/doc/config.rst
index 906a9e19..a1f550dd 100644
--- a/doc/config.rst
+++ b/doc/config.rst
@@ -98,7 +98,7 @@ General configuration
Example patterns:
- ``'library/xml.rst'`` -- ignores the ``library/xml.rst`` file (replaces
- entry in :confval:`unused_docs`
+ entry in :confval:`unused_docs`)
- ``'library/xml'`` -- ignores the ``library/xml`` directory (replaces entry
in :confval:`exclude_trees`)
- ``'library/xml*'`` -- ignores all files and directories starting with
@@ -361,6 +361,7 @@ documentation on :ref:`intl` for details.
* ``de`` -- German
* ``en`` -- English
* ``es`` -- Spanish
+ * ``fa`` -- Iranian
* ``fi`` -- Finnish
* ``fr`` -- French
* ``hr`` -- Croatian
@@ -498,13 +499,19 @@ that use Sphinx' HTMLWriter class.
.. confval:: html_add_permalinks
- If true, Sphinx will add "permalinks" for each heading and description
- environment as paragraph signs that become visible when the mouse hovers over
- them. Default: ``True``.
+ Sphinx will add "permalinks" for each heading and description environment as
+ paragraph signs that become visible when the mouse hovers over them.
+
+ This value determines the text for the permalink; it defaults to ``"¶"``.
+ Set it to ``None`` or the empty string to disable permalinks.
.. versionadded:: 0.6
Previously, this was always activated.
+ .. versionchanged:: 1.1
+ This can now be a string to select the actual text of the link.
+ Previously, only boolean values were accepted.
+
.. confval:: html_sidebars
Custom sidebar templates, must be a dictionary that maps document names to
@@ -687,6 +694,38 @@ that use Sphinx' HTMLWriter class.
.. versionadded:: 1.0
+.. confval:: html_search_language
+
+ Language to be used for generating the HTML full-text search index. This
+ defaults to the global language selected with :confval:`language`. If there
+ is no support for this language, ``"en"`` is used which selects the English
+ language.
+
+ Support is present for these languages:
+
+ * ``en`` -- English
+ * ``ja`` -- Japanese
+
+ .. versionadded:: 1.1
+
+.. confval:: html_search_options
+
+ A dictionary with options for the search language support, empty by default.
+ The meaning of these options depends on the language selected.
+
+ The English support has no options.
+
+ The Japanese support has these options:
+
+ * ``type`` -- ``'mecab'`` or ``'default'`` (selects either MeCab or
+ TinySegmenter word splitter algorithm)
+ * ``dic_enc`` -- the encoding for the MeCab algorithm
+ * ``dict`` -- the dictionary to use for the MeCab algorithm
+ * ``lib`` -- the library name for finding the MeCab library via ctypes if the
+ Python binding is not installed
+
+ .. versionadded:: 1.1
+
.. confval:: htmlhelp_basename
Output file base name for HTML help builder. Default is ``'pydoc'``.
@@ -811,6 +850,7 @@ the `Dublin Core metadata <http://dublincore.org/>`_.
a chapter, but can be confusing because it mixes entries of differnet
depth in one list. The default value is ``True``.
+
.. _latex-options:
Options for LaTeX output
@@ -893,10 +933,18 @@ These options influence LaTeX output.
.. confval:: latex_show_urls
- If true, add URL addresses after links. This is very useful for printed
- copies of the manual. Default is ``False``.
+ Control whether to display URL addresses. This is very useful for printed
+ copies of the manual. The setting can have the following values:
+
+ * ``'no'`` -- do not display URLs (default)
+ * ``'footnote'`` -- display URLs in footnotes
+ * ``'inline'`` -- display URLs inline in parentheses
.. versionadded:: 1.0
+ .. versionchanged:: 1.1
+ This value is now a string; previously it was a boolean value, and a true
+ value selected the ``'inline'`` display. For backwards compatibility,
+ ``True`` is still accepted.
.. confval:: latex_elements
@@ -1009,6 +1057,37 @@ These options influence LaTeX output.
Use the ``'pointsize'`` key in the :confval:`latex_elements` value.
+.. _text-options:
+
+Options for text output
+-----------------------
+
+These options influence text output.
+
+.. confval:: text_newlines
+
+ Determines which end-of-line character(s) are used in text output.
+
+ * ``'unix'``: use Unix-style line endings (``\n``)
+ * ``'windows'``: use Windows-style line endings (``\r\n``)
+ * ``'native'``: use the line ending style of the platform the documentation
+ is built on
+
+ Default: ``'unix'``.
+
+ .. versionadded:: 1.1
+
+.. confval:: text_sectionchars
+
+ A string of 7 characters that should be used for underlining sections.
+ The first character is used for first-level headings, the second for
+ second-level headings and so on.
+
+ The default is ``'*=-~"+`'``.
+
+ .. versionadded:: 1.1
+
+
.. _man-options:
Options for manual page output
@@ -1031,14 +1110,121 @@ These options influence manual page output.
well as the name of the manual page (in the NAME section).
* *description*: description of the manual page. This is used in the NAME
section.
- * *authors*: A list of strings with authors, or a single string. Can be
- an empty string or list if you do not want to automatically generate
- an AUTHORS section in the manual page.
+ * *authors*: A list of strings with authors, or a single string. Can be an
+ empty string or list if you do not want to automatically generate an
+ AUTHORS section in the manual page.
* *section*: The manual page section. Used for the output file name as well
as in the manual page header.
.. versionadded:: 1.0
+.. confval:: man_show_urls
+
+ If true, add URL addresses after links. Default is ``False``.
+
+ .. versionadded:: 1.1
+
+
+.. _texinfo-options:
+
+Options for Texinfo output
+--------------------------
+
+These options influence Texinfo output.
+
+.. confval:: texinfo_documents
+
+ This value determines how to group the document tree into Texinfo source
+ files. It must be a list of tuples ``(startdocname, targetname, title,
+ author, dir_entry, description, category, toctree_only)``, where the items
+ are:
+
+ * *startdocname*: document name that is the "root" of the Texinfo file. All
+ documents referenced by it in TOC trees will be included in the Texinfo
+ file too. (If you want only one Texinfo file, use your
+ :confval:`master_doc` here.)
+ * *targetname*: file name (no extension) of the Texinfo file in the output
+ directory.
+ * *title*: Texinfo document title. Can be empty to use the title of the
+ *startdoc*.
+ * *author*: Author for the Texinfo document. Use ``\and`` to separate
+ multiple authors, as in: ``'John \and Sarah'``.
+ * *dir_entry*: The name that will appear in the top-level ``DIR`` menu file.
+ * *description*: Descriptive text to appear in the top-level ``DIR`` menu
+ file.
+ * *category*: Specifies the section which this entry will appear in the
+ top-level ``DIR`` menu file.
+ * *toctree_only*: Must be ``True`` or ``False``. If ``True``, the *startdoc*
+ document itself is not included in the output, only the documents
+ referenced by it via TOC trees. With this option, you can put extra stuff
+ in the master document that shows up in the HTML, but not the Texinfo
+ output.
+
+ .. versionadded:: 1.1
+
+
+.. confval:: texinfo_appendices
+
+ A list of document names to append as an appendix to all manuals.
+
+ .. versionadded:: 1.1
+
+
+.. confval:: texinfo_elements
+
+ A dictionary that contains Texinfo snippets that override those Sphinx
+ usually puts into the generated ``.texi`` files.
+
+ * Keys that you may want to override include:
+
+ ``'paragraphindent'``
+ Number of spaces to indent the first line of each paragraph, default
+ ``2``. Specify ``0`` for no indentation.
+
+ ``'exampleindent'``
+ Number of spaces to indent the lines for examples or literal blocks,
+ default ``4``. Specify ``0`` for no indentation.
+
+ ``'preamble'``
+ Text inserted as is near the beginning of the file.
+
+ * Keys that are set by other options and therefore should not be overridden
+ are:
+
+ ``'filename'``
+ ``'title'``
+ ``'direntry'``
+
+ .. versionadded:: 1.1
+
+
+Options for the linkcheck builder
+---------------------------------
+
+.. confval:: linkcheck_ignore
+
+ A list of regular expressions that match URIs that should not be checked
+ when doing a ``linkcheck`` build. Example::
+
+ linkcheck_ignore = [r'http://localhost:\d+/']
+
+ .. versionadded:: 1.1
+
+.. confval:: linkcheck_timeout
+
+ A timeout value, in seconds, for the linkcheck builder. **Only works in
+ Python 2.6 and higher.** The default is to use Python's global socket
+ timeout.
+
+ .. versionadded:: 1.1
+
+.. confval:: linkcheck_workers
+
+ The number of worker threads to use when checking links. Default is 5
+ threads.
+
+ .. versionadded:: 1.1
+
.. rubric:: Footnotes
diff --git a/doc/domains.rst b/doc/domains.rst
index 8cd7a0c7..4cd77b9e 100644
--- a/doc/domains.rst
+++ b/doc/domains.rst
@@ -231,6 +231,45 @@ The following directives are provided for module and class contents:
.. versionadded:: 0.6
+.. rst:directive:: .. py:decorator:: name
+ .. py:decorator:: name(signature)
+
+ Describes a decorator function. The signature should *not* represent the
+ signature of the actual function, but the usage as a decorator. For example,
+ given the functions
+
+ .. code-block:: python
+
+ def removename(func):
+ func.__name__ = ''
+ return func
+
+ def setnewname(name):
+ def decorator(func):
+ func.__name__ = name
+ return func
+ return decorator
+
+ the descriptions should look like this::
+
+ .. py:decorator:: removename
+
+ Remove name of the decorated function.
+
+ .. py:decorator:: setnewname(name)
+
+ Set name of the decorated function to *name*.
+
+ There is no ``py:deco`` role to link to a decorator that is marked up with
+ this directive; rather, use the :rst:role:`py:func` role.
+
+.. rst:directive:: .. py:decoratormethod:: name
+ .. py:decoratormethod:: name(signature)
+
+ Same as :rst:dir:`py:decorator`, but for decorators that are methods.
+
+ Refer to a decorator method using the :rst:role:`py:meth` role.
+
.. _signatures:
@@ -288,11 +327,6 @@ explained by an example::
:type limit: integer or None
:rtype: list of strings
-It is also possible to combine parameter type and description, if the type is a
-single word, like this::
-
- :param integer limit: maximum number of stack frames to show
-
This will render like this:
.. py:function:: format_exception(etype, value, tb[, limit=None])
@@ -307,6 +341,13 @@ This will render like this:
:type limit: integer or None
:rtype: list of strings
+It is also possible to combine parameter type and description, if the type is a
+single word, like this::
+
+ :param integer limit: maximum number of stack frames to show
+
+
+.. _python-roles:
Cross-referencing Python objects
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -438,6 +479,8 @@ The C domain (name **c**) is suited for documentation of C API.
.. c:var:: PyObject* PyClass_Type
+.. _c-roles:
+
Cross-referencing C constructs
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -522,6 +565,9 @@ The following directives are available:
Select the current C++ namespace for the following objects.
+
+.. _cpp-roles:
+
These roles link to the given object types:
.. rst:role:: cpp:class
@@ -689,6 +735,8 @@ The JavaScript domain (name **js**) provides the following directives:
Describes the attribute *name* of *object*.
+.. _js-roles:
+
These roles are provided to refer to the described objects:
.. rst:role:: js:func
@@ -740,6 +788,8 @@ The reStructuredText domain (name **rst**) provides the following directives:
Foo description.
+.. _rst-roles:
+
These roles are provided to refer to the described objects:
.. rst:role:: rst:dir
diff --git a/doc/ext/appapi.rst b/doc/ext/appapi.rst
index 8f2a779b..d43a90eb 100644
--- a/doc/ext/appapi.rst
+++ b/doc/ext/appapi.rst
@@ -76,9 +76,9 @@ the following public API:
Node visitor functions for the Sphinx HTML, LaTeX, text and manpage writers
can be given as keyword arguments: the keyword must be one or more of
- ``'html'``, ``'latex'``, ``'text'``, ``'man'``, the value a 2-tuple of
- ``(visit, depart)`` methods. ``depart`` can be ``None`` if the ``visit``
- function raises :exc:`docutils.nodes.SkipNode`. Example:
+ ``'html'``, ``'latex'``, ``'text'``, ``'man'``, ``'texinfo'``, the value a
+ 2-tuple of ``(visit, depart)`` methods. ``depart`` can be ``None`` if the
+ ``visit`` function raises :exc:`docutils.nodes.SkipNode`. Example:
.. code-block:: python
@@ -163,7 +163,8 @@ the following public API:
.. versionadded:: 0.6
-.. method:: Sphinx.add_object_type(directivename, rolename, indextemplate='', parse_node=None, ref_nodeclass=None, objname='')
+.. method:: Sphinx.add_object_type(directivename, rolename, indextemplate='', parse_node=None, \
+ ref_nodeclass=None, objname='', doc_field_types=[])
This method is a very convenient way to add a new :term:`object` type that
can be cross-referenced. It will do this:
@@ -286,6 +287,15 @@ the following public API:
.. versionadded:: 0.6
+.. method:: Sphinx.add_search_language(cls)
+
+ Add *cls*, which must be a subclass of :class:`sphinx.search.SearchLanguage`,
+ as a support language for building the HTML full-text search index. The
+ class must have a *lang* attribute that indicates the language it should be
+ used for. See :confval:`html_search_language`.
+
+ .. versionadded:: 1.1
+
.. method:: Sphinx.connect(event, callback)
Register *callback* to be called when *event* is emitted. For details on
@@ -343,6 +353,15 @@ registered event handlers.
Emitted when the builder object has been created. It is available as
``app.builder``.
+.. event:: env-get-outdated (app, env, added, changed, removed)
+
+ Emitted when the environment determines which source files have changed and
+ should be re-read. *added*, *changed* and *removed* are sets of docnames
+ that the environment has determined. You can return a list of docnames to
+ re-read in addition to these.
+
+ .. versionadded:: 1.1
+
.. event:: env-purge-doc (app, env, docname)
Emitted when all traces of a source file should be cleaned from the
diff --git a/doc/ext/autodoc.rst b/doc/ext/autodoc.rst
index a1d9d98f..6c4a147a 100644
--- a/doc/ext/autodoc.rst
+++ b/doc/ext/autodoc.rst
@@ -93,8 +93,8 @@ inserting them into the page source under a suitable :rst:dir:`py:module`,
.. autoclass:: Noodle
:members: eat, slurp
- * If you want to make the ``members`` option the default, see
- :confval:`autodoc_default_flags`.
+ * If you want to make the ``members`` option (or other flag options described
+ below) the default, see :confval:`autodoc_default_flags`.
* Members without docstrings will be left out, unless you give the
``undoc-members`` flag option::
@@ -103,6 +103,23 @@ inserting them into the page source under a suitable :rst:dir:`py:module`,
:members:
:undoc-members:
+ * "Private" members (that is, those named like ``_private`` or ``__private``)
+ will be included if the ``private-members`` flag option is given.
+
+ .. versionadded:: 1.1
+
+ * Python "special" members (that is, those named like ``__special__``) will
+ be included if the ``special-members`` flag option is given::
+
+ .. autoclass:: my.Class
+ :members:
+ :private-members:
+ :special-members:
+
+ would document both "private" and "special" members of the class.
+
+ .. versionadded:: 1.1
+
* For classes and exceptions, members inherited from base classes will be
left out, unless you give the ``inherited-members`` flag option, in
addition to ``members``::
@@ -152,8 +169,8 @@ inserting them into the page source under a suitable :rst:dir:`py:module`,
.. versionadded:: 0.5
- * :rst:dir:`automodule` and :rst:dir:`autoclass` also has an ``member-order`` option
- that can be used to override the global value of
+ * :rst:dir:`automodule` and :rst:dir:`autoclass` also has an ``member-order``
+ option that can be used to override the global value of
:confval:`autodoc_member_order` for one directive.
.. versionadded:: 0.6
@@ -173,9 +190,9 @@ inserting them into the page source under a suitable :rst:dir:`py:module`,
.. rst:directive:: autofunction
- autodata
- automethod
- autoattribute
+ autodata
+ automethod
+ autoattribute
These work exactly like :rst:dir:`autoclass` etc., but do not offer the options
used for automatic member documentation.
@@ -183,16 +200,23 @@ inserting them into the page source under a suitable :rst:dir:`py:module`,
For module data members and class attributes, documentation can either be put
into a special-formatted comment *before* the attribute definition, or in a
docstring *after* the definition. This means that in the following class
- definition, both attributes can be autodocumented::
+ definition, all attributes can be autodocumented::
class Foo:
"""Docstring for class Foo."""
- #: Doc comment for attribute Foo.bar.
+ #: Doc comment for class attribute Foo.bar.
bar = 1
baz = 2
- """Docstring for attribute Foo.baz."""
+ """Docstring for class attribute Foo.baz."""
+
+ def __init__(self):
+ #: Doc comment for instance attribute qux.
+ self.qux = 3
+
+ self.spam = 4
+ """Docstring for instance attribute spam."""
.. versionchanged:: 0.6
:rst:dir:`autodata` and :rst:dir:`autoattribute` can now extract docstrings.
@@ -246,7 +270,8 @@ There are also new config values that you can set:
This value is a list of autodoc directive flags that should be automatically
applied to all autodoc directives. The supported flags are ``'members'``,
- ``'undoc-members'``, ``'inherited-members'`` and ``'show-inheritance'``.
+ ``'undoc-members'``, ``'private-members'``, ``'special-members'``,
+ ``'inherited-members'`` and ``'show-inheritance'``.
If you set one of these flags in this config value, you can use a negated
form, :samp:`'no-{flag}'`, in an autodoc directive, to disable it once.
@@ -260,6 +285,20 @@ There are also new config values that you can set:
.. versionadded:: 1.0
+.. confval:: autodoc_docstring_signature
+
+ Functions imported from C modules cannot be introspected, and therefore the
+ signature for such functions cannot be automatically determined. However, it
+ is an often-used convention to put the signature into the first line of the
+ function's docstring.
+
+ If this boolean value is set to ``True`` (which is the default), autodoc will
+ look at the first line of the docstring for functions and methods, and if it
+ looks like a signature, use the line as the signature and remove it from the
+ docstring content.
+
+ .. versionadded:: 1.1
+
Docstring preprocessing
-----------------------
diff --git a/doc/ext/coverage.rst b/doc/ext/coverage.rst
index 13294f8b..839478fe 100644
--- a/doc/ext/coverage.rst
+++ b/doc/ext/coverage.rst
@@ -14,6 +14,7 @@ This extension features one additional builder, the :class:`CoverageBuilder`.
.. todo:: Write this section.
+
Several new configuration values can be used to specify what the builder
should check:
@@ -28,3 +29,16 @@ should check:
.. confval:: coverage_c_regexes
.. confval:: coverage_ignore_c_items
+
+.. confval:: coverage_write_headline
+
+ Set to ``False`` to not write headlines.
+
+ .. versionadded:: 1.1
+
+.. confval:: coverage_skip_undoc_in_source
+
+ Skip objects that are not documented in the source with a docstring.
+ ``False`` by default.
+
+ .. versionadded:: 1.1
diff --git a/doc/ext/doctest.rst b/doc/ext/doctest.rst
index 20e747de..c884a10d 100644
--- a/doc/ext/doctest.rst
+++ b/doc/ext/doctest.rst
@@ -45,6 +45,14 @@ names.
but executed before the doctests of the group(s) it belongs to.
+.. rst:directive:: .. testcleanup:: [group]
+
+ A cleanup code block. This code is not shown in the output for other
+ builders, but executed after the doctests of the group(s) it belongs to.
+
+ .. versionadded:: 1.1
+
+
.. rst:directive:: .. doctest:: [group]
A doctest-style code block. You can use standard :mod:`doctest` flags for
@@ -181,6 +189,14 @@ There are also these config values for customizing the doctest extension:
.. versionadded:: 0.6
+.. confval:: doctest_global_cleanup
+
+ Python code that is treated like it were put in a ``testcleanup`` directive
+ for *every* file that is tested, and for every group. You can use this to
+ e.g. remove any temporary files that the tests leave behind.
+
+ .. versionadded:: 1.1
+
.. confval:: doctest_test_doctest_blocks
If this is a nonempty string (the default is ``'default'``), standard reST
diff --git a/doc/ext/inheritance.rst b/doc/ext/inheritance.rst
index cdd01791..5e0a76fc 100644
--- a/doc/ext/inheritance.rst
+++ b/doc/ext/inheritance.rst
@@ -30,6 +30,13 @@ It adds this directive:
``lib.``, you can give ``:parts: 1`` to remove that prefix from the displayed
node names.)
+ It also supports a ``private-bases`` flag option; if given, private base
+ classes (those whose name starts with ``_``) will be included.
+
+ .. versionchanged:: 1.1
+ Added ``private-bases`` option; previously, all bases were always
+ included.
+
New config values are:
diff --git a/doc/ext/math.rst b/doc/ext/math.rst
index f2896c39..ae91875d 100644
--- a/doc/ext/math.rst
+++ b/doc/ext/math.rst
@@ -171,20 +171,51 @@ There are various config values you can set to influence how the images are buil
installed. Therefore, the default for this option is ``False``.
-:mod:`sphinx.ext.jsmath` -- Render math via JavaScript
-------------------------------------------------------
+:mod:`sphinx.ext.mathjax` -- Render math via JavaScript
+-------------------------------------------------------
-.. module:: sphinx.ext.jsmath
- :synopsis: Render math via JavaScript.
+.. module:: sphinx.ext.mathjax
+ :synopsis: Render math using JavaScript via MathJax.
+
+.. versionadded:: 1.1
This extension puts math as-is into the HTML files. The JavaScript package
-jsMath_ is then loaded and transforms the LaTeX markup to readable math live in
+MathJax_ is then loaded and transforms the LaTeX markup to readable math live in
the browser.
-Because jsMath (and the necessary fonts) is very large, it is not included in
+Because MathJax (and the necessary fonts) is very large, it is not included in
Sphinx. You must install it yourself, and give Sphinx its path in this config
value:
+.. confval:: mathjax_path
+
+ The path to the JavaScript file to include in the HTML files in order to load
+ JSMath. There is no default.
+
+ The path can be absolute or relative; if it is relative, it is relative to
+ the ``_static`` directory of the built docs.
+
+ For example, if you put JSMath into the static path of the Sphinx docs, this
+ value would be ``MathJax/MathJax.js``. If you host more than one Sphinx
+ documentation set on one server, it is advisable to install MathJax in a
+ shared location.
+
+ You can also give a full ``http://`` URL. Kevin Dunn maintains a MathJax
+ installation on a public server, which he offers for use by development and
+ production servers::
+
+ mathjax_path = 'http://mathjax.connectmv.com/MathJax.js'
+
+
+:mod:`sphinx.ext.jsmath` -- Render math via JavaScript
+------------------------------------------------------
+
+.. module:: sphinx.ext.jsmath
+ :synopsis: Render math using JavaScript via JSMath.
+
+This extension works just as the MathJax extension does, but uses the older
+package jsMath_. It provides this config value:
+
.. confval:: jsmath_path
The path to the JavaScript file to include in the HTML files in order to load
@@ -200,6 +231,7 @@ value:
.. _dvipng: http://savannah.nongnu.org/projects/dvipng/
+.. _MathJax: http://www.mathjax.org/
.. _jsMath: http://www.math.union.edu/~dpvc/jsmath/
.. _preview-latex package: http://www.gnu.org/software/auctex/preview-latex.html
.. _AmSMath LaTeX package: http://www.ams.org/tex/amslatex.html
diff --git a/doc/extensions.rst b/doc/extensions.rst
index 49224774..e05d4e9e 100644
--- a/doc/extensions.rst
+++ b/doc/extensions.rst
@@ -59,14 +59,19 @@ These extensions are built in and can be activated by respective entries in the
Third-party extensions
----------------------
-There are several extensions that are not (yet) maintained in the Sphinx
-distribution. The `Wiki at BitBucket`_ maintains a list of those.
+You can find several extensions contributed by users in the `Sphinx Contrib`_
+repository. It is open for anyone who wants to maintain an extension
+publicly; just send a short message asking for write permissions.
-If you write an extension that you think others will find useful, please write
-to the project mailing list (`join here <http://groups.google.com/group/sphinx-dev>`_)
-and we'll find the proper way of including or hosting it for the public.
+There are also several extensions hosted elsewhere. The `Wiki at BitBucket`_
+maintains a list of those.
+
+If you write an extension that you think others will find useful or you think
+should be included as a part of Sphinx, please write to the project mailing
+list (`join here <http://groups.google.com/group/sphinx-dev>`_).
.. _Wiki at BitBucket: http://www.bitbucket.org/birkenfeld/sphinx/wiki/Home
+.. _Sphinx Contrib: http://www.bitbucket.org/birkenfeld/sphinx-contrib
Where to put your own extensions?
diff --git a/doc/faq.rst b/doc/faq.rst
index 5869e3af..a0f00855 100644
--- a/doc/faq.rst
+++ b/doc/faq.rst
@@ -60,10 +60,11 @@ PyPI
Sphinx documentation to the PyPI package documentation area at
http://packages.python.org/.
-github pages
- You can use `Michael Jones' sphinx-to-github tool
- <http://github.com/michaeljones/sphinx-to-github/tree/master>`_ to prepare
- Sphinx HTML output.
+GitHub Pages
+ Directories starting with underscores are ignored by default which breaks
+ static files in Sphinx. GitHub's preprocessor can be `disabled
+ <https://github.com/blog/572-bypassing-jekyll-on-github-pages>`_ to support
+ Sphinx HTML output properly.
Google Analytics
You can use a custom ``layout.html`` template, like this:
@@ -148,3 +149,109 @@ some notes:
.. _Calibre: http://calibre-ebook.com/
.. _FBreader: http://www.fbreader.org/
.. _Bookworm: http://bookworm.oreilly.com/
+
+
+.. _texinfo-faq:
+
+Texinfo info
+------------
+
+The Texinfo builder is currently in an experimental stage but has successfully
+been used to build the documentation for both Sphinx and Python. The intended
+use of this builder is to generate Texinfo that is then processed into Info
+files.
+
+There are two main programs for reading Info files, ``info`` and GNU Emacs. The
+``info`` program has less features but is available in most Unix environments
+and can be quickly accessed from the terminal. Emacs provides better font and
+color display and supports extensive customization (of course).
+
+
+.. _texinfo-links:
+
+Displaying Links
+~~~~~~~~~~~~~~~~
+
+One noticeable problem you may encounter with the generated Info files is how
+references are displayed. If you read the source of an Info file, a reference
+to this section would look like::
+
+ * note Displaying Links: target-id
+
+In the stand-alone reader, ``info``, references are displayed just as they
+appear in the source. Emacs, on the other-hand, will by default replace
+``\*note:`` with ``see`` and hide the ``target-id``. For example:
+
+ :ref:`texinfo-links`
+
+The exact behavior of how Emacs displays references is dependent on the variable
+``Info-hide-note-references``. If set to the value of ``hide``, Emacs will hide
+both the ``\*note:`` part and the ``target-id``. This is generally the best way
+to view Sphinx-based documents since they often make frequent use of links and
+do not take this limitation into account. However, changing this variable
+affects how all Info documents are displayed and most due take this behavior
+into account.
+
+If you want Emacs to display Info files produced by Sphinx using the value
+``hide`` for ``Info-hide-note-references`` and the default value for all other
+Info files, try adding the following Emacs Lisp code to your start-up file,
+``~/.emacs.d/init.el``.
+
+::
+
+ (defadvice info-insert-file-contents (after
+ sphinx-info-insert-file-contents
+ activate)
+ "Hack to make `Info-hide-note-references' buffer-local and
+ automatically set to `hide' iff it can be determined that this file
+ was created from a Texinfo file generated by Docutils or Sphinx."
+ (set (make-local-variable 'Info-hide-note-references)
+ (default-value 'Info-hide-note-references))
+ (save-excursion
+ (save-restriction
+ (widen) (goto-char (point-min))
+ (when (re-search-forward
+ "^Generated by \\(Sphinx\\|Docutils\\)"
+ (save-excursion (search-forward "^_" nil t)) t)
+ (set (make-local-variable 'Info-hide-note-references)
+ 'hide)))))
+
+
+Notes
+~~~~~
+
+The following notes may be helpful if you want to create Texinfo files:
+
+- Each section corresponds to a different ``node`` in the Info file.
+
+- Some characters cannot be properly escaped in menu entries and xrefs. The
+ following characters are replaced by spaces in these contexts: ``@``, ``{``,
+ ``}``, ``.``, ``,``, and ``:``.
+
+- In the HTML and Tex output, the word ``see`` is automatically inserted before
+ all xrefs.
+
+- Links to external Info files can be created using the somewhat official URI
+ scheme ``info``. For example::
+
+ info:Texinfo#makeinfo_options
+
+ which produces:
+
+ info:Texinfo#makeinfo_options
+
+- Inline markup appears as follows in Info:
+
+ * strong -- \*strong\*
+ * emphasis -- _emphasis_
+ * literal -- \`literal'
+
+ It is possible to change this behavior using the Texinfo command
+ ``@definfoenclose``. For example, to make inline markup more closely resemble
+ reST, add the following to your :file:`conf.py`::
+
+ texinfo_elements = {'preamble': """\
+ @definfoenclose strong,**,**
+ @definfoenclose emph,*,*
+ @definfoenclose code,`@w{}`,`@w{}`
+ """}
diff --git a/doc/glossary.rst b/doc/glossary.rst
index 2a82e20f..8bc393eb 100644
--- a/doc/glossary.rst
+++ b/doc/glossary.rst
@@ -23,7 +23,9 @@ Glossary
A reStructuredText markup element that allows marking a block of content
with special meaning. Directives are supplied not only by docutils, but
Sphinx and custom extensions can add their own. The basic directive
- syntax looks like this::
+ syntax looks like this:
+
+ .. sourcecode:: rst
.. directivename:: argument ...
:option: value
diff --git a/doc/intl.rst b/doc/intl.rst
index 89cfcd2c..3a9e32f2 100644
--- a/doc/intl.rst
+++ b/doc/intl.rst
@@ -12,9 +12,10 @@ in itself. See the :ref:`intl-options` for details on configuration.
.. figure:: translation.png
:width: 100%
- Workflow visualization of translations in Sphinx. [1]_
+ Workflow visualization of translations in Sphinx. (The stick-figure is taken
+ from an `XKCD comic <http://xkcd.com/779/>`_.)
-**gettext** [2]_ is an established standard for internationalization and
+**gettext** [1]_ is an established standard for internationalization and
localization. It naïvely maps messages in a program to a translated string.
Sphinx uses these facilities to translate whole documents.
@@ -46,7 +47,7 @@ up automatically.
An example: you have a document ``usage.rst`` in your Sphinx project. The
gettext builder will put its messages into ``usage.pot``. Image you have
-Spanish translations [3]_ on your hands in ``usage.po`` --- for your builds to
+Spanish translations [2]_ on your hands in ``usage.po`` --- for your builds to
be translated you need to follow these instructions:
* Compile your message catalog to a locale directory, say ``translated``, so it
@@ -62,8 +63,7 @@ be translated you need to follow these instructions:
.. rubric:: Footnotes
-.. [1] The stick-figure is taken from an `XKCD comic <http://xkcd.com/779/>`_.
-.. [2] See the `GNU gettext utilites
+.. [1] See the `GNU gettext utilites
<http://www.gnu.org/software/gettext/manual/gettext.html#Introduction>`_
for details on that software suite.
-.. [3] Because nobody expects the Spanish Inquisition!
+.. [2] Because nobody expects the Spanish Inquisition!
diff --git a/doc/invocation.rst b/doc/invocation.rst
index e143a323..c8e9a61f 100644
--- a/doc/invocation.rst
+++ b/doc/invocation.rst
@@ -40,6 +40,10 @@ The :program:`sphinx-build` script has several options:
**man**
Build manual pages in groff format for UNIX systems.
+ **texinfo**
+ Build Texinfo files that can be processed into Info files using
+ :program:`makeinfo`.
+
**text**
Build plain text files.
diff --git a/doc/markup/inline.rst b/doc/markup/inline.rst
index 78aaea69..69dd832f 100644
--- a/doc/markup/inline.rst
+++ b/doc/markup/inline.rst
@@ -44,6 +44,18 @@ more versatile:
tool-tip on mouse-hover) will always be the full target name.
+Cross-referencing objects
+-------------------------
+
+These roles are described with their respective domains:
+
+* :ref:`Python <python-roles>`
+* :ref:`C <c-roles>`
+* :ref:`C++ <cpp-roles>`
+* :ref:`JavaScript <js-roles>`
+* :ref:`ReST <rst-roles>`
+
+
.. _ref-role:
Cross-referencing arbitrary locations
diff --git a/doc/markup/misc.rst b/doc/markup/misc.rst
index 44da3aac..95899ef3 100644
--- a/doc/markup/misc.rst
+++ b/doc/markup/misc.rst
@@ -13,10 +13,12 @@ like this::
:fieldname: Field content
-A field list at the very top of a file is parsed by docutils as the "docinfo",
+A field list near the top of a file is parsed by docutils as the "docinfo"
which is normally used to record the author, date of publication and other
-metadata. *In Sphinx*, the docinfo is used as metadata, too, but not displayed
-in the output.
+metadata. *In Sphinx*, a field list preceding any other markup is moved from
+the docinfo to the Sphinx environment as document metadata and is not displayed
+in the output; a field list appearing after the document title will be part of
+the docinfo as normal and will be displayed in the output.
At the moment, these metadata fields are recognized:
@@ -111,11 +113,28 @@ mainly contained in information units, such as the language reference.
Likewise, ``triple: module; search; path`` is a shortcut that creates
three index entries, which are ``module; search path``, ``search; path,
module`` and ``path; module search``.
+ see
+ ``see: entry; other`` creates an index entry that refers from ``entry`` to
+ ``other``.
+ seealso
+ Like ``see``, but inserts "see also" instead of "see".
module, keyword, operator, object, exception, statement, builtin
These all create two index entries. For example, ``module: hashlib``
creates the entries ``module; hashlib`` and ``hashlib; module``. (These
are Python-specific and therefore deprecated.)
+ You can mark up "main" index entries by prefixing them with an exclamation
+ mark. The references to "main" entries are emphasized in the generated
+ index. For example, if two pages contain ::
+
+ .. index:: Python
+
+ and one page contains ::
+
+ .. index:: ! Python
+
+ then the backlink to the latter page is emphasized among the three backlinks.
+
For index directives containing only "single" entries, there is a shorthand
notation::
@@ -123,6 +142,9 @@ mainly contained in information units, such as the language reference.
This creates four index entries.
+ .. versionchanged:: 1.1
+ Added ``see`` and ``seealso`` types, as well as marking main entries.
+
.. rst:role:: index
While the :rst:dir:`index` directive is a block-level markup and links to the
@@ -203,9 +225,16 @@ following directive exists:
.. warning::
- Tables that contain literal blocks cannot be set with ``tabulary``. They are
- therefore set with the standard LaTeX ``tabular`` environment. Also, the
- verbatim environment used for literal blocks only works in ``p{width}``
- columns, which means that by default, Sphinx generates such column specs for
- such tables. Use the :rst:dir:`tabularcolumns` directive to get finer control
- over such tables.
+ Tables that contain list-like elements such as object descriptions,
+ blockquotes or any kind of lists cannot be set out of the box with
+ ``tabulary``. They are therefore set with the standard LaTeX ``tabular``
+ environment if you don't give a ``tabularcolumns`` directive. If you do, the
+ table will be set with ``tabulary``, but you must use the ``p{width}``
+ construct for the columns that contain these elements.
+
+ Literal blocks do not work with ``tabulary`` at all, so tables containing a
+ literal block are always set with ``tabular``. Also, the verbatim
+ environment used for literal blocks only works in ``p{width}`` columns, which
+ means that by default, Sphinx generates such column specs for such tables.
+ Use the :rst:dir:`tabularcolumns` directive to get finer control over such
+ tables.
diff --git a/doc/markup/para.rst b/doc/markup/para.rst
index 42bcc33c..ced18d81 100644
--- a/doc/markup/para.rst
+++ b/doc/markup/para.rst
@@ -52,7 +52,7 @@ units as well as normal text:
Similar to :rst:dir:`versionadded`, but describes when and what changed in
the named feature in some way (new parameters, changed side effects, etc.).
-.. rst:directive:: .. deprecated:: vesion
+.. rst:directive:: .. deprecated:: version
Similar to :rst:dir:`versionchanged`, but describes when the feature was
deprecated. An explanation can also be given, for example to inform the
@@ -153,9 +153,9 @@ Glossary
.. rst:directive:: .. glossary::
- This directive must contain a reST definition list with terms and
- definitions. The definitions will then be referencable with the :rst:role:`term`
- role. Example::
+ This directive must contain a reST definition-list-like markup with terms and
+ definitions. The definitions will then be referencable with the
+ :rst:role:`term` role. Example::
.. glossary::
@@ -169,10 +169,25 @@ Glossary
The directory which, including its subdirectories, contains all
source files for one Sphinx project.
+ In contrast to regular definition lists, *multiple* terms per entry are
+ allowed, and inline markup is allowed in terms. You can link to all of the
+ terms. For example::
+
+ .. glossary::
+
+ term 1
+ term 2
+ Definition of both terms.
+
+ (When the glossary is sorted, the first term determines the sort order.)
+
.. versionadded:: 0.6
You can now give the glossary directive a ``:sorted:`` flag that will
automatically sort the entries alphabetically.
+ .. versionchanged:: 1.1
+ Now supports multiple terms and inline markup in terms.
+
Grammar production displays
---------------------------
diff --git a/doc/themes/fullsize/pyramid.png b/doc/themes/fullsize/pyramid.png
new file mode 100644
index 00000000..429a8b7e
--- /dev/null
+++ b/doc/themes/fullsize/pyramid.png
Binary files differ
diff --git a/doc/themes/pyramid.png b/doc/themes/pyramid.png
new file mode 100644
index 00000000..b16095c9
--- /dev/null
+++ b/doc/themes/pyramid.png
Binary files differ
diff --git a/doc/theming.rst b/doc/theming.rst
index 716eb50a..216118c1 100644
--- a/doc/theming.rst
+++ b/doc/theming.rst
@@ -69,9 +69,9 @@ Builtin themes
| | |
| *traditional* | *nature* |
+--------------------+--------------------+
-| |haiku| | |
+| |haiku| | |pyramid| |
| | |
-| *haiku* | |
+| *haiku* | *pyramid* |
+--------------------+--------------------+
.. |default| image:: themes/default.png
@@ -81,6 +81,7 @@ Builtin themes
.. |traditional| image:: themes/traditional.png
.. |nature| image:: themes/nature.png
.. |haiku| image:: themes/haiku.png
+.. |pyramid| image:: themes/pyramid.png
Sphinx comes with a selection of themes to choose from.
@@ -144,8 +145,7 @@ These themes are:
on the right side. There are currently no options beyond *nosidebar*.
* **scrolls** -- A more lightweight theme, based on `the Jinja documentation
- <http://jinja.pocoo.org/2/documentation/>`_. The following color options are
- available:
+ <http://jinja.pocoo.org/>`_. The following color options are available:
- **headerbordercolor**
- **subheadlinecolor**
@@ -178,6 +178,9 @@ These themes are:
* **nature** -- A greenish theme. There are currently no options beyond
*nosidebar*.
+* **pyramid** -- A theme from the Pyramid web framework project, designed by
+ Blais Laflamme. THere are currently no options beyond *nosidebar*.
+
* **haiku** -- A theme without sidebar inspired by the `Haiku OS user guide
<http://www.haiku-os.org/docs/userguide/en/contents.html>`_. The following
options are supported:
@@ -206,7 +209,7 @@ name), containing the following:
* A :file:`theme.conf` file, see below.
* HTML templates, if needed.
* A ``static/`` directory containing any static files that will be copied to the
- output statid directory on build. These can be images, styles, script files.
+ output static directory on build. These can be images, styles, script files.
The :file:`theme.conf` file is in INI format [1]_ (readable by the standard
Python :mod:`ConfigParser` module) and has the following structure:
diff --git a/doc/web/quickstart.rst b/doc/web/quickstart.rst
index 0627c9c3..bd0f71a7 100644
--- a/doc/web/quickstart.rst
+++ b/doc/web/quickstart.rst
@@ -228,8 +228,8 @@ pass the `displayed` keyword argument::
username=username, proposal=proposal,
displayed=False)
-You can then create two new views to handle the moderation of comments. The
-first will be called when a moderator decides a comment should be accepted and
+You can then create a new view to handle the moderation of comments. It
+will be called when a moderator decides a comment should be accepted and
displayed::
@app.route('/docs/accept_comment', methods=['POST'])
@@ -239,14 +239,7 @@ displayed::
support.accept_comment(comment_id, moderator=moderator)
return 'OK'
-The next is very similar, but used when rejecting a comment::
-
- @app.route('/docs/reject_comment', methods=['POST'])
- def reject_comment():
- moderator = g.user.moderator if g.user else False
- comment_id = request.form.get('id')
- support.reject_comment(comment_id, moderator=moderator)
- return 'OK'
+Rejecting comments happens via comment deletion.
To perform a custom action (such as emailing a moderator) when a new comment is
added but not displayed, you can pass callable to the :class:`~.WebSupport`
diff --git a/doc/web/storagebackends.rst b/doc/web/storagebackends.rst
index a46ea9e5..d191b43e 100644
--- a/doc/web/storagebackends.rst
+++ b/doc/web/storagebackends.rst
@@ -42,5 +42,3 @@ StorageBackend Methods
.. automethod:: StorageBackend.update_username
.. automethod:: StorageBackend.accept_comment
-
-.. automethod:: StorageBackend.reject_comment
diff --git a/setup.py b/setup.py
index 3b1d2d91..c73facaa 100644
--- a/setup.py
+++ b/setup.py
@@ -193,6 +193,7 @@ setup(
'console_scripts': [
'sphinx-build = sphinx:main',
'sphinx-quickstart = sphinx.quickstart:main',
+ 'sphinx-apidoc = sphinx.apidoc:main',
'sphinx-autogen = sphinx.ext.autosummary.generate:main',
],
'distutils.commands': [
diff --git a/sphinx-apidoc.py b/sphinx-apidoc.py
new file mode 100755
index 00000000..9cafb497
--- /dev/null
+++ b/sphinx-apidoc.py
@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+ Sphinx - Python documentation toolchain
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import sys
+
+if __name__ == '__main__':
+ from sphinx.apidoc import main
+ sys.exit(main(sys.argv))
diff --git a/sphinx-autogen.py b/sphinx-autogen.py
index 62178419..b1e467e7 100755
--- a/sphinx-autogen.py
+++ b/sphinx-autogen.py
@@ -4,8 +4,8 @@
Sphinx - Python documentation toolchain
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- :copyright: 2007-2010 by Georg Brandl.
- :license: BSD.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
"""
import sys
diff --git a/sphinx-build.py b/sphinx-build.py
index 30f3f6e6..70822d73 100755
--- a/sphinx-build.py
+++ b/sphinx-build.py
@@ -4,7 +4,7 @@
Sphinx - Python documentation toolchain
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx-quickstart.py b/sphinx-quickstart.py
index 99c0f0b3..5d7c1544 100755
--- a/sphinx-quickstart.py
+++ b/sphinx-quickstart.py
@@ -4,7 +4,7 @@
Sphinx - Python documentation toolchain
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/__init__.py b/sphinx/__init__.py
index 211e2413..468b3406 100644
--- a/sphinx/__init__.py
+++ b/sphinx/__init__.py
@@ -5,7 +5,7 @@
The Sphinx documentation toolchain.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -15,7 +15,7 @@
import sys
from os import path
-__version__ = '1.1pre'
+__version__ = '1.1pre'
__released__ = '1.1 (hg)' # used when Sphinx builds its own docs
package_dir = path.abspath(path.dirname(__file__))
diff --git a/sphinx/addnodes.py b/sphinx/addnodes.py
index 592bef5d..94f1d615 100644
--- a/sphinx/addnodes.py
+++ b/sphinx/addnodes.py
@@ -5,7 +5,7 @@
Additional docutils nodes.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -171,6 +171,9 @@ class literal_emphasis(nodes.emphasis):
class abbreviation(nodes.Inline, nodes.TextElement):
"""Node for abbreviations with explanations."""
+class termsep(nodes.Structural, nodes.Element):
+ """Separates two terms within a <term> node."""
+
# make the new nodes known to docutils; needed because the HTML writer will
# choke at some point if these are not added
diff --git a/sphinx/apidoc.py b/sphinx/apidoc.py
new file mode 100644
index 00000000..ca142098
--- /dev/null
+++ b/sphinx/apidoc.py
@@ -0,0 +1,263 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.apidoc
+ ~~~~~~~~~~~~~
+
+ Parses a directory tree looking for Python modules and packages and creates
+ ReST files appropriately to create code documentation with Sphinx. It also
+ creates a modules index (named modules.<suffix>).
+
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+import os
+import sys
+import optparse
+from os import path
+
+# automodule options
+OPTIONS = [
+ 'members',
+ 'undoc-members',
+ # 'inherited-members', # disabled because there's a bug in sphinx
+ 'show-inheritance',
+]
+
+INITPY = '__init__.py'
+
+def makename(package, module):
+ """Join package and module with a dot."""
+ # Both package and module can be None/empty.
+ if package:
+ name = package
+ if module:
+ name += '.' + module
+ else:
+ name = module
+ return name
+
+def write_file(name, text, opts):
+ """Write the output file for module/package <name>."""
+ fname = path.join(opts.destdir, "%s.%s" % (name, opts.suffix))
+ if opts.dryrun:
+ print 'Would create file %s.' % fname
+ return
+ if not opts.force and path.isfile(fname):
+ print 'File %s already exists, skipping.' % fname
+ else:
+ print 'Creating file %s.' % fname
+ f = open(fname, 'w')
+ try:
+ f.write(text)
+ finally:
+ f.close()
+
+def format_heading(level, text):
+ """Create a heading of <level> [1, 2 or 3 supported]."""
+ underlining = ['=', '-', '~', ][level-1] * len(text)
+ return '%s\n%s\n\n' % (text, underlining)
+
+def format_directive(module, package=None):
+ """Create the automodule directive and add the options."""
+ directive = '.. automodule:: %s\n' % makename(package, module)
+ for option in OPTIONS:
+ directive += ' :%s:\n' % option
+ return directive
+
+def create_module_file(package, module, opts):
+ """Build the text of the file and write the file."""
+ text = format_heading(1, '%s Module' % module)
+ #text += format_heading(2, ':mod:`%s` Module' % module)
+ text += format_directive(module, package)
+ write_file(makename(package, module), text, opts)
+
+def create_package_file(root, master_package, subroot, py_files, opts, subs):
+ """Build the text of the file and write the file."""
+ package = path.split(root)[-1]
+ text = format_heading(1, '%s Package' % package)
+ # add each module in the package
+ for py_file in py_files:
+ if shall_skip(path.join(root, py_file)):
+ continue
+ is_package = py_file == INITPY
+ py_file = path.splitext(py_file)[0]
+ py_path = makename(subroot, py_file)
+ if is_package:
+ heading = ':mod:`%s` Package' % package
+ else:
+ heading = ':mod:`%s` Module' % py_file
+ text += format_heading(2, heading)
+ text += format_directive(is_package and subroot or py_path,
+ master_package)
+ text += '\n'
+
+ # build a list of directories that are packages (they contain an INITPY file)
+ subs = [sub for sub in subs if path.isfile(path.join(root, sub, INITPY))]
+ # if there are some package directories, add a TOC for theses subpackages
+ if subs:
+ text += format_heading(2, 'Subpackages')
+ text += '.. toctree::\n\n'
+ for sub in subs:
+ text += ' %s.%s\n' % (makename(master_package, subroot), sub)
+ text += '\n'
+
+ write_file(makename(master_package, subroot), text, opts)
+
+def create_modules_toc_file(master_package, modules, opts, name='modules'):
+ """
+ Create the module's index.
+ """
+ text = format_heading(1, '%s Modules' % opts.header)
+ text += '.. toctree::\n'
+ text += ' :maxdepth: %s\n\n' % opts.maxdepth
+
+ modules.sort()
+ prev_module = ''
+ for module in modules:
+ # look if the module is a subpackage and, if yes, ignore it
+ if module.startswith(prev_module + '.'):
+ continue
+ prev_module = module
+ text += ' %s\n' % module
+
+ write_file(name, text, opts)
+
+def shall_skip(module):
+ """
+ Check if we want to skip this module.
+ """
+ # skip it, if there is nothing (or just \n or \r\n) in the file
+ return path.getsize(module) < 3
+
+def recurse_tree(rootpath, excludes, opts):
+ """
+ Look for every file in the directory tree and create the corresponding
+ ReST files.
+ """
+ # check if the base directory is a package and get is name
+ if INITPY in os.listdir(rootpath):
+ package_name = path.abspath(rootpath).split(path.sep)[-1]
+ else:
+ package_name = None
+
+ toc = []
+ tree = os.walk(rootpath, False)
+ for root, subs, files in tree:
+ # keep only the Python script files
+ py_files = sorted([f for f in files if path.splitext(f)[1] == '.py'])
+ if INITPY in py_files:
+ py_files.remove(INITPY)
+ py_files.insert(0, INITPY)
+ # remove hidden ('.') and private ('_') directories
+ subs = sorted([sub for sub in subs if sub[0] not in ['.', '_']])
+ # check if there are valid files to process
+ # TODO: could add check for windows hidden files
+ if "/." in root or "/_" in root \
+ or not py_files \
+ or is_excluded(root, excludes):
+ continue
+ if INITPY in py_files:
+ # we are in package ...
+ if (# ... with subpackage(s)
+ subs
+ or
+ # ... with some module(s)
+ len(py_files) > 1
+ or
+ # ... with a not-to-be-skipped INITPY file
+ not shall_skip(path.join(root, INITPY))
+ ):
+ subroot = root[len(rootpath):].lstrip(path.sep).\
+ replace(path.sep, '.')
+ create_package_file(root, package_name, subroot,
+ py_files, opts, subs)
+ toc.append(makename(package_name, subroot))
+ elif root == rootpath:
+ # if we are at the root level, we don't require it to be a package
+ for py_file in py_files:
+ if not shall_skip(path.join(rootpath, py_file)):
+ module = path.splitext(py_file)[0]
+ create_module_file(package_name, module, opts)
+ toc.append(makename(package_name, module))
+
+ # create the module's index
+ if not opts.notoc:
+ create_modules_toc_file(package_name, toc, opts)
+
+def normalize_excludes(rootpath, excludes):
+ """
+ Normalize the excluded directory list:
+ * must be either an absolute path or start with rootpath,
+ * otherwise it is joined with rootpath
+ * with trailing slash
+ """
+ sep = path.sep
+ f_excludes = []
+ for exclude in excludes:
+ if not path.isabs(exclude) and not exclude.startswith(rootpath):
+ exclude = path.join(rootpath, exclude)
+ if not exclude.endswith(sep):
+ exclude += sep
+ f_excludes.append(exclude)
+ return f_excludes
+
+def is_excluded(root, excludes):
+ """
+ Check if the directory is in the exclude list.
+
+ Note: by having trailing slashes, we avoid common prefix issues, like
+ e.g. an exlude "foo" also accidentally excluding "foobar".
+ """
+ sep = path.sep
+ if not root.endswith(sep):
+ root += sep
+ for exclude in excludes:
+ if root.startswith(exclude):
+ return True
+ return False
+
+def main(argv):
+ """
+ Parse and check the command line arguments.
+ """
+ parser = optparse.OptionParser(
+ usage="""\
+usage: %prog [options] -o <output_path> <module_path> [exclude_paths, ...]
+
+Look recursively in <module_path> for Python modules and packages and create
+a reST file with automodule directives per package in the <output_path>.
+
+Note: By default this script will not overwrite already created files.""")
+
+ parser.add_option('-o', '--output-dir', action='store', dest='destdir',
+ help='Directory to place all output', default='')
+ parser.add_option('-d', '--maxdepth', action='store', dest='maxdepth',
+ help='Maximum depth of submodules to show in the TOC '
+ '(default: 4)', type='int', default=4)
+ parser.add_option('-f', '--force', action='store_true', dest='force',
+ help='Overwrite all the files')
+ parser.add_option('-n', '--dry-run', action='store_true', dest='dryrun',
+ help='Run the script without creating the files')
+ parser.add_option('-T', '--no-toc', action='store_true', dest='notoc',
+ help='Don\'t create the table of contents file')
+ parser.add_option('-H', '--doc-header', action='store', dest='header',
+ help='Documentation Header (default: Project)',
+ default='Project')
+ parser.add_option('-s', '--suffix', action='store', dest='suffix',
+ help='file suffix (default: rst)', default='rst')
+
+ (opts, args) = parser.parse_args(argv[1:])
+
+ if not args:
+ parser.error('A package path is required.')
+ if not opts.destdir:
+ parser.error('An output directory is required.')
+ rootpath, excludes = args[0], args[1:]
+ if not path.isdir(rootpath):
+ print >>sys.stderr, '%s is not a directory.' % rootpath
+ sys.exit(1)
+ if not path.isdir(opts.destdir):
+ print '%s is not a valid output directory.' % opts.destdir
+ sys.exit(1)
+ excludes = normalize_excludes(rootpath, excludes)
+ recurse_tree(rootpath, excludes, opts)
diff --git a/sphinx/application.py b/sphinx/application.py
index 50f4102e..840fc46b 100644
--- a/sphinx/application.py
+++ b/sphinx/application.py
@@ -7,7 +7,7 @@
Gracefully adapted from the TextPress system by Armin.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -40,6 +40,7 @@ from sphinx.util.console import bold
# List of all known core events. Maps name to arguments description.
events = {
'builder-inited': '',
+ 'env-get-outdated': 'env, added, changed, removed',
'env-purge-doc': 'env, docname',
'source-read': 'docname, source text',
'doctree-read': 'the doctree before being pickled',
@@ -209,6 +210,12 @@ class Sphinx(object):
self.builder.cleanup()
def warn(self, message, location=None, prefix='WARNING: '):
+ if isinstance(location, tuple):
+ docname, lineno = location
+ if docname:
+ location = '%s:%s' % (self.env.doc2path(docname), lineno or '')
+ else:
+ location = None
warntext = location and '%s: %s%s\n' % (location, prefix, message) or \
'%s%s\n' % (prefix, message)
if self.warningiserror:
@@ -358,6 +365,9 @@ class Sphinx(object):
elif key == 'man':
from sphinx.writers.manpage import ManualPageTranslator \
as translator
+ elif key == 'texinfo':
+ from sphinx.writers.texinfo import TexinfoTranslator \
+ as translator
else:
# ignore invalid keys for compatibility
continue
@@ -422,13 +432,15 @@ class Sphinx(object):
setattr(self.domains[domain], 'get_%s_index' % name, func)
def add_object_type(self, directivename, rolename, indextemplate='',
- parse_node=None, ref_nodeclass=None, objname=''):
+ parse_node=None, ref_nodeclass=None, objname='',
+ doc_field_types=[]):
StandardDomain.object_types[directivename] = \
ObjType(objname or directivename, rolename)
# create a subclass of GenericObject as the new directive
new_directive = type(directivename, (GenericObject, object),
{'indextemplate': indextemplate,
- 'parse_node': staticmethod(parse_node)})
+ 'parse_node': staticmethod(parse_node),
+ 'doc_field_types': doc_field_types})
StandardDomain.directives[directivename] = new_directive
# XXX support more options?
StandardDomain.roles[rolename] = XRefRole(innernodeclass=ref_nodeclass)
@@ -478,6 +490,11 @@ class Sphinx(object):
from sphinx.ext import autodoc
autodoc.AutoDirective._special_attrgetters[type] = getter
+ def add_search_language(self, cls):
+ from sphinx.search import languages, SearchLanguage
+ assert isinstance(cls, SearchLanguage)
+ languages[cls.lang] = cls
+
class TemplateBridge(object):
"""
diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py
index ce04f769..33954033 100644
--- a/sphinx/builders/__init__.py
+++ b/sphinx/builders/__init__.py
@@ -5,7 +5,7 @@
Builder superclass for all builders.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -272,7 +272,8 @@ class Builder(object):
# add all toctree-containing files that may have changed
for docname in list(docnames):
for tocdocname in self.env.files_to_rebuild.get(docname, []):
- docnames.add(tocdocname)
+ if tocdocname in self.env.found_docs:
+ docnames.add(tocdocname)
docnames.add(self.config.master_doc)
self.info(bold('preparing documents... '), nonl=True)
@@ -325,6 +326,7 @@ BUILTIN_BUILDERS = {
'latex': ('latex', 'LaTeXBuilder'),
'text': ('text', 'TextBuilder'),
'man': ('manpage', 'ManualPageBuilder'),
+ 'texinfo': ('texinfo', 'TexinfoBuilder'),
'changes': ('changes', 'ChangesBuilder'),
'linkcheck': ('linkcheck', 'CheckExternalLinksBuilder'),
'websupport': ('websupport', 'WebSupportBuilder'),
diff --git a/sphinx/builders/changes.py b/sphinx/builders/changes.py
index 980ed760..3e351e6c 100644
--- a/sphinx/builders/changes.py
+++ b/sphinx/builders/changes.py
@@ -5,7 +5,7 @@
Changelog builder.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -30,7 +30,8 @@ class ChangesBuilder(Builder):
def init(self):
self.create_template_bridge()
- Theme.init_themes(self)
+ Theme.init_themes(self.confdir, self.config.html_theme_path,
+ warn=self.warn)
self.theme = Theme('default')
self.templates.init(self, self.theme)
diff --git a/sphinx/builders/devhelp.py b/sphinx/builders/devhelp.py
index d43cd624..a1b41945 100644
--- a/sphinx/builders/devhelp.py
+++ b/sphinx/builders/devhelp.py
@@ -7,7 +7,7 @@
.. _Devhelp: http://live.gnome.org/devhelp
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/builders/epub.py b/sphinx/builders/epub.py
index d75ec36a..ddc6fb5c 100644
--- a/sphinx/builders/epub.py
+++ b/sphinx/builders/epub.py
@@ -6,12 +6,13 @@
Build epub files.
Originally derived from qthelp.py.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import os
import re
+import sys
import time
import codecs
import zipfile
@@ -288,16 +289,17 @@ class EpubBuilder(StandaloneHTMLBuilder):
# Logic modeled from themes/basic/genindex.html
for key, columns in tree:
for entryname, (links, subitems) in columns:
- for (i, link) in enumerate(links):
+ for (i, (ismain, link)) in enumerate(links):
m = _refuri_re.match(link)
if m:
- links[i] = self.fix_fragment(m.group(1), m.group(2))
+ links[i] = (ismain,
+ self.fix_fragment(m.group(1), m.group(2)))
for subentryname, subentrylinks in subitems:
- for (i, link) in enumerate(subentrylinks):
+ for (i, (ismain, link)) in enumerate(subentrylinks):
m = _refuri_re.match(link)
if m:
- subentrylinks[i] = \
- self.fix_fragment(m.group(1), m.group(2))
+ subentrylinks[i] = (ismain,
+ self.fix_fragment(m.group(1), m.group(2)))
def handle_page(self, pagename, addctx, templatename='page.html',
outfilename=None, event_arg=None):
@@ -542,7 +544,8 @@ class EpubBuilder(StandaloneHTMLBuilder):
epub.write(path.join(outdir, 'mimetype'), 'mimetype', \
zipfile.ZIP_STORED)
for file in projectfiles:
- if isinstance(file, unicode):
- file = file.encode('utf-8')
- epub.write(path.join(outdir, file), file, zipfile.ZIP_DEFLATED)
+ fp = path.join(outdir, file)
+ if isinstance(fp, unicode):
+ fp = fp.encode(sys.getfilesystemencoding())
+ epub.write(fp, file, zipfile.ZIP_DEFLATED)
epub.close()
diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py
index 78bafda8..6aa4fda4 100644
--- a/sphinx/builders/html.py
+++ b/sphinx/builders/html.py
@@ -5,7 +5,7 @@
Several HTML builders.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -88,6 +88,8 @@ class StandaloneHTMLBuilder(Builder):
self.tags_hash = ''
# section numbers for headings in the currently visited document
self.secnumbers = {}
+ # currently written docname
+ self.current_docname = None
self.init_templates()
self.init_highlighter()
@@ -101,21 +103,28 @@ class StandaloneHTMLBuilder(Builder):
self.link_suffix = self.out_suffix
if self.config.language is not None:
- jsfile_list = [path.join(package_dir, 'locale',
- self.config.language, 'LC_MESSAGES', 'sphinx.js'),
- path.join(sys.prefix, 'share/sphinx/locale',
- self.config.language, 'sphinx.js')]
-
- for jsfile in jsfile_list:
- if path.isfile(jsfile):
- self.script_files.append('_static/translations.js')
- break
+ if self._get_translations_js():
+ self.script_files.append('_static/translations.js')
+
+ def _get_translations_js(self):
+ candidates = [path.join(package_dir, 'locale', self.config.language,
+ 'LC_MESSAGES', 'sphinx.js'),
+ path.join(sys.prefix, 'share/sphinx/locale',
+ self.config.language, 'sphinx.js')] + \
+ [path.join(dir, self.config.language,
+ 'LC_MESSAGES', 'sphinx.js')
+ for dir in self.config.locale_dirs]
+ for jsfile in candidates:
+ if path.isfile(jsfile):
+ return jsfile
+ return None
def get_theme_config(self):
return self.config.html_theme, self.config.html_theme_options
def init_templates(self):
- Theme.init_themes(self)
+ Theme.init_themes(self.confdir, self.config.html_theme_path,
+ warn=self.warn)
themename, themeoptions = self.get_theme_config()
self.theme = Theme(themename)
self.theme_options = themeoptions.copy()
@@ -223,10 +232,15 @@ class StandaloneHTMLBuilder(Builder):
return pub.writer.parts
def prepare_writing(self, docnames):
- from sphinx.search import IndexBuilder
-
- self.indexer = IndexBuilder(self.env)
+ # create the search indexer
+ from sphinx.search import IndexBuilder, languages
+ lang = self.config.html_search_language or self.config.language
+ if not lang or lang not in languages:
+ lang = 'en'
+ self.indexer = IndexBuilder(self.env, lang,
+ self.config.html_search_options)
self.load_indexer(docnames)
+
self.docwriter = HTMLWriter(self)
self.docsettings = OptionParser(
defaults=self.env.settings,
@@ -398,6 +412,7 @@ class StandaloneHTMLBuilder(Builder):
self.imgpath = relative_uri(self.get_target_uri(docname), '_images')
self.post_process_images(doctree)
self.dlpath = relative_uri(self.get_target_uri(docname), '_downloads')
+ self.current_docname = docname
self.docwriter.write(doctree, destination)
self.docwriter.assemble_parts()
body = self.docwriter.parts['fragment']
@@ -525,22 +540,22 @@ class StandaloneHTMLBuilder(Builder):
f.close()
# then, copy translations JavaScript file
if self.config.language is not None:
- jsfile_list = [path.join(package_dir, 'locale',
- self.config.language, 'LC_MESSAGES', 'sphinx.js'),
- path.join(sys.prefix, 'share/sphinx/locale',
- self.config.language, 'sphinx.js')]
- for jsfile in jsfile_list:
- if path.isfile(jsfile):
- copyfile(jsfile, path.join(self.outdir, '_static',
- 'translations.js'))
- break
+ jsfile = self._get_translations_js()
+ if jsfile:
+ copyfile(jsfile, path.join(self.outdir, '_static',
+ 'translations.js'))
+
+ # add context items for search function used in searchtools.js_t
+ ctx = self.globalcontext.copy()
+ ctx.update(self.indexer.context_for_searchtool())
+
# then, copy over theme-supplied static files
if self.theme:
themeentries = [path.join(themepath, 'static')
for themepath in self.theme.get_dirchain()[::-1]]
for entry in themeentries:
copy_static_entry(entry, path.join(self.outdir, '_static'),
- self, self.globalcontext)
+ self, ctx)
# then, copy over all user-supplied static files
staticentries = [path.join(self.confdir, spath)
for spath in self.config.html_static_path]
@@ -553,7 +568,7 @@ class StandaloneHTMLBuilder(Builder):
self.warn('html_static_path entry %r does not exist' % entry)
continue
copy_static_entry(entry, path.join(self.outdir, '_static'), self,
- self.globalcontext, exclude_matchers=matchers)
+ ctx, exclude_matchers=matchers)
# copy logo and favicon files if not already in static path
if self.config.html_logo:
logobase = path.basename(self.config.html_logo)
@@ -611,7 +626,11 @@ class StandaloneHTMLBuilder(Builder):
def load_indexer(self, docnames):
keep = set(self.env.all_docs) - set(docnames)
try:
- f = open(path.join(self.outdir, self.searchindex_filename), 'rb')
+ searchindexfn = path.join(self.outdir, self.searchindex_filename)
+ if self.indexer_dumps_unicode:
+ f = codecs.open(searchindexfn, 'r', encoding='utf-8')
+ else:
+ f = open(searchindexfn, 'rb')
try:
self.indexer.load(f, self.indexer_format)
finally:
@@ -688,7 +707,10 @@ class StandaloneHTMLBuilder(Builder):
return uri
ctx['pathto'] = pathto
ctx['hasdoc'] = lambda name: name in self.env.all_docs
- ctx['encoding'] = encoding = self.config.html_output_encoding
+ if self.name != 'htmlhelp':
+ ctx['encoding'] = encoding = self.config.html_output_encoding
+ else:
+ ctx['encoding'] = encoding = self.encoding
ctx['toctree'] = lambda **kw: self._get_local_toctree(pagename, **kw)
self.add_sidebars(pagename, ctx)
ctx.update(addctx)
@@ -709,7 +731,7 @@ class StandaloneHTMLBuilder(Builder):
# outfilename's path is in general different from self.outdir
ensuredir(path.dirname(outfilename))
try:
- f = codecs.open(outfilename, 'w', encoding)
+ f = codecs.open(outfilename, 'w', encoding, 'xmlcharrefreplace')
try:
f.write(output)
finally:
@@ -923,6 +945,8 @@ class SerializingHTMLBuilder(StandaloneHTMLBuilder):
#: (pickle, simplejson etc.)
implementation = None
implementation_dumps_unicode = False
+ #: additional arguments for dump()
+ additional_dump_args = ()
#: the filename for the global context file
globalcontext_filename = None
@@ -951,8 +975,7 @@ class SerializingHTMLBuilder(StandaloneHTMLBuilder):
else:
f = open(filename, 'wb')
try:
- # XXX: the third argument is pickle-specific!
- self.implementation.dump(context, f, 2)
+ self.implementation.dump(context, f, *self.additional_dump_args)
finally:
f.close()
@@ -1003,6 +1026,7 @@ class PickleHTMLBuilder(SerializingHTMLBuilder):
"""
implementation = pickle
implementation_dumps_unicode = False
+ additional_dump_args = (pickle.HIGHEST_PROTOCOL,)
indexer_format = pickle
indexer_dumps_unicode = False
name = 'pickle'
diff --git a/sphinx/builders/htmlhelp.py b/sphinx/builders/htmlhelp.py
index f5d716d9..9227a6e6 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-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -258,8 +258,8 @@ class HTMLHelpBuilder(StandaloneHTMLBuilder):
def write_index(title, refs, subitems):
def write_param(name, value):
item = ' <param name="%s" value="%s">\n' % (name, value)
- f.write(item.encode('ascii', 'xmlcharrefreplace')
- .decode('ascii'))
+ f.write(item.encode(self.encoding, 'xmlcharrefreplace')
+ .decode(self.encoding))
title = cgi.escape(title)
f.write('<LI> <OBJECT type="text/sitemap">\n')
write_param('Keyword', title)
diff --git a/sphinx/builders/intl.py b/sphinx/builders/intl.py
index 1572747e..74ba03b5 100644
--- a/sphinx/builders/intl.py
+++ b/sphinx/builders/intl.py
@@ -5,7 +5,7 @@
The MessageCatalogBuilder class.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -17,7 +17,6 @@ from collections import defaultdict
from docutils import nodes
from sphinx.builders import Builder
-from sphinx.builders.versioning import VersioningBuilderMixin
from sphinx.util.nodes import extract_messages
from sphinx.util.osutil import SEP, copyfile
from sphinx.util.console import darkgreen
@@ -44,7 +43,7 @@ msgstr ""
"""[1:]
-class I18nBuilder(Builder, VersioningBuilderMixin):
+class I18nBuilder(Builder):
"""
General i18n builder.
"""
@@ -52,7 +51,6 @@ class I18nBuilder(Builder, VersioningBuilderMixin):
def init(self):
Builder.init(self)
- VersioningBuilderMixin.init(self)
self.catalogs = defaultdict(dict)
def get_target_uri(self, docname, typ=None):
@@ -67,15 +65,9 @@ class I18nBuilder(Builder, VersioningBuilderMixin):
def write_doc(self, docname, doctree):
catalog = self.catalogs[docname.split(SEP, 1)[0]]
- self.handle_versioning(docname, doctree, nodes.TextElement)
-
for node, msg in extract_messages(doctree):
catalog.setdefault(node.uid, msg)
- def finish(self):
- Builder.finish(self)
- VersioningBuilderMixin.finish(self)
-
class MessageCatalogBuilder(I18nBuilder):
"""
diff --git a/sphinx/builders/latex.py b/sphinx/builders/latex.py
index b2a3c4e7..b00546a0 100644
--- a/sphinx/builders/latex.py
+++ b/sphinx/builders/latex.py
@@ -5,7 +5,7 @@
LaTeX builder.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 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 4a0bab63..ad15b55d 100644
--- a/sphinx/builders/linkcheck.py
+++ b/sphinx/builders/linkcheck.py
@@ -5,13 +5,17 @@
The CheckExternalLinksBuilder class.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
+import re
+import sys
+import Queue
import socket
+import threading
from os import path
-from urllib2 import build_opener, HTTPError
+from urllib2 import build_opener, Request
from docutils import nodes
@@ -23,6 +27,12 @@ opener = build_opener()
opener.addheaders = [('User-agent', 'Mozilla/5.0')]
+class HeadRequest(Request):
+ """Subclass of urllib2.Request that sends a HEAD request."""
+ def get_method(self):
+ return 'HEAD'
+
+
class CheckExternalLinksBuilder(Builder):
"""
Checks for broken external links.
@@ -30,6 +40,7 @@ class CheckExternalLinksBuilder(Builder):
name = 'linkcheck'
def init(self):
+ self.to_ignore = map(re.compile, self.app.config.linkcheck_ignore)
self.good = set()
self.broken = {}
self.redirected = {}
@@ -38,6 +49,83 @@ class CheckExternalLinksBuilder(Builder):
# create output file
open(path.join(self.outdir, 'output.txt'), 'w').close()
+ # create queues and worker threads
+ self.wqueue = Queue.Queue()
+ self.rqueue = Queue.Queue()
+ self.workers = []
+ for i in range(self.app.config.linkcheck_workers):
+ thread = threading.Thread(target=self.check_thread)
+ thread.setDaemon(True)
+ thread.start()
+ self.workers.append(thread)
+
+ def check_thread(self):
+ kwargs = {}
+ if sys.version_info > (2, 5) and self.app.config.linkcheck_timeout:
+ kwargs['timeout'] = self.app.config.linkcheck_timeout
+
+ def check():
+ # check for various conditions without bothering the network
+ if len(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', ''
+ elif uri in self.good:
+ return 'working', ''
+ elif uri in self.broken:
+ return 'broken', self.broken[uri]
+ elif uri in self.redirected:
+ return 'redirected', self.redirected[uri]
+ for rex in self.to_ignore:
+ if rex.match(uri):
+ return 'ignored', ''
+
+ # need to actually check the URI
+ try:
+ f = opener.open(HeadRequest(uri), **kwargs)
+ f.close()
+ except Exception, err:
+ self.broken[uri] = str(err)
+ return 'broken', str(err)
+ if f.url.rstrip('/') == uri.rstrip('/'):
+ self.good.add(uri)
+ return 'working', 'new'
+ else:
+ self.redirected[uri] = f.url
+ return 'redirected', f.url
+
+ while True:
+ uri, docname, lineno = self.wqueue.get()
+ if uri is None:
+ break
+ status, info = check()
+ self.rqueue.put((uri, docname, lineno, status, info))
+
+ def process_result(self, result):
+ uri, docname, lineno, status, info = result
+ if status == 'unchecked':
+ return
+ if status == 'working' and info != 'new':
+ return
+ if lineno:
+ self.info('(line %3d) ' % lineno, nonl=1)
+ if status == 'ignored':
+ self.info(uri + ' - ' + darkgray('ignored'))
+ elif status == 'local':
+ self.info(uri + ' - ' + darkgray('local'))
+ self.write_entry('local', docname, lineno, uri)
+ elif status == 'working':
+ self.info(uri + ' - ' + darkgreen('working'))
+ elif status == 'broken':
+ self.info(uri + ' - ' + red('broken: ') + info)
+ self.write_entry('broken', docname, lineno, uri + ': ' + info)
+ if self.app.quiet:
+ self.warn('broken link: %s' % uri,
+ '%s:%s' % (self.env.doc2path(docname), lineno))
+ elif status == 'redirected':
+ self.info(uri + ' - ' + purple('redirected') + ' to ' + info)
+ self.write_entry('redirected', docname, lineno, uri + ' to ' + info)
+
def get_target_uri(self, docname, typ=None):
return ''
@@ -49,61 +137,25 @@ class CheckExternalLinksBuilder(Builder):
def write_doc(self, docname, doctree):
self.info()
+ n = 0
for node in doctree.traverse(nodes.reference):
- try:
- self.check(node, docname)
- except KeyError:
+ if 'refuri' not in node:
continue
-
- def check(self, node, docname):
- uri = node['refuri']
-
- if '#' in uri:
- uri = uri.split('#')[0]
-
- if uri in self.good:
- return
-
- lineno = None
- while lineno is None:
- node = node.parent
- if node is None:
- break
- lineno = node.line
-
- if len(uri) == 0 or uri[0:7] == 'mailto:' or uri[0:4] == 'ftp:':
- return
-
- if lineno:
- self.info('(line %3d) ' % lineno, nonl=1)
- if uri[0:5] == 'http:' or uri[0:6] == 'https:':
- self.info(uri, nonl=1)
-
- if uri in self.broken:
- (r, s) = self.broken[uri]
- elif uri in self.redirected:
- (r, s) = self.redirected[uri]
- else:
- (r, s) = self.resolve(uri)
-
- if r == 0:
- self.info(' - ' + darkgreen('working'))
- self.good.add(uri)
- elif r == 2:
- self.info(' - ' + red('broken: ') + s)
- self.write_entry('broken', docname, lineno, uri + ': ' + s)
- self.broken[uri] = (r, s)
- if self.app.quiet:
- self.warn('broken link: %s' % uri,
- '%s:%s' % (self.env.doc2path(docname), lineno))
- else:
- self.info(' - ' + purple('redirected') + ' to ' + s)
- self.write_entry('redirected', docname,
- lineno, uri + ' to ' + s)
- self.redirected[uri] = (r, s)
- else:
- self.info(uri + ' - ' + darkgray('local'))
- self.write_entry('local', docname, lineno, uri)
+ uri = node['refuri']
+ if '#' in uri:
+ uri = uri.split('#')[0]
+ lineno = None
+ while lineno is None:
+ node = node.parent
+ if node is None:
+ break
+ lineno = node.line
+ self.wqueue.put((uri, docname, lineno), False)
+ n += 1
+ done = 0
+ while done < n:
+ self.process_result(self.rqueue.get())
+ done += 1
if self.broken:
self.app.statuscode = 1
@@ -114,21 +166,6 @@ class CheckExternalLinksBuilder(Builder):
line, what, uri))
output.close()
- def resolve(self, uri):
- try:
- f = opener.open(uri)
- f.close()
- except HTTPError, err:
- #if err.code == 403 and uri.startswith('http://en.wikipedia.org/'):
- # # Wikipedia blocks requests from urllib User-Agent
- # return (0, 0)
- return (2, str(err))
- except Exception, err:
- return (2, str(err))
- if f.url.rstrip('/') == uri.rstrip('/'):
- return (0, 0)
- else:
- return (1, f.url)
-
def finish(self):
- return
+ for worker in self.workers:
+ self.wqueue.put((None, None, None), False)
diff --git a/sphinx/builders/manpage.py b/sphinx/builders/manpage.py
index 756e4732..93b56a01 100644
--- a/sphinx/builders/manpage.py
+++ b/sphinx/builders/manpage.py
@@ -5,7 +5,7 @@
Manual pages builder.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 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 de002165..5598aad2 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-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -23,7 +23,7 @@ from sphinx.builders.html import StandaloneHTMLBuilder
_idpattern = re.compile(
- r'(?P<title>.+) (\((?P<id>[\w\.]+)( (?P<descr>\w+))?\))$')
+ r'(?P<title>.+) (\((class in )?(?P<id>[\w\.]+)( (?P<descr>\w+))?\))$')
# Qt Help Collection Project (.qhcp).
@@ -143,7 +143,7 @@ class QtHelpBuilder(StandaloneHTMLBuilder):
# keywords
keywords = []
- index = self.env.create_index(self)
+ index = self.env.create_index(self, group_entries=False)
for (key, group) in index:
for title, (refs, subitems) in group:
keywords.extend(self.build_keywords(title, refs, subitems))
diff --git a/sphinx/builders/texinfo.py b/sphinx/builders/texinfo.py
new file mode 100644
index 00000000..52e32362
--- /dev/null
+++ b/sphinx/builders/texinfo.py
@@ -0,0 +1,239 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.builders.texinfo
+ ~~~~~~~~~~~~~~~~~~~~~~~
+
+ Texinfo builder.
+
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+from os import path
+
+from docutils import nodes
+from docutils.io import FileOutput
+from docutils.utils import new_document
+from docutils.frontend import OptionParser
+
+from sphinx import addnodes
+from sphinx.locale import _
+from sphinx.builders import Builder
+from sphinx.environment import NoUri
+from sphinx.util.nodes import inline_all_toctrees
+from sphinx.util.osutil import SEP, copyfile
+from sphinx.util.console import bold, darkgreen
+from sphinx.writers.texinfo import TexinfoWriter
+
+
+TEXINFO_MAKEFILE = '''\
+# Makefile for Sphinx Texinfo output
+
+infodir ?= /usr/share/info
+
+MAKEINFO = makeinfo --no-split
+MAKEINFO_html = makeinfo --no-split --html
+MAKEINFO_plaintext = makeinfo --no-split --plaintext
+TEXI2PDF = texi2pdf --batch --expand
+INSTALL_INFO = install-info
+
+ALLDOCS = $(basename $(wildcard *.texi))
+
+all: info
+info: $(addsuffix .info,$(ALLDOCS))
+plaintext: $(addsuffix .txt,$(ALLDOCS))
+html: $(addsuffix .html,$(ALLDOCS))
+pdf: $(addsuffix .pdf,$(ALLDOCS))
+
+install-info: info
+\tfor f in *.info; do \\
+\t cp -t $(infodir) "$$f" && \\
+\t $(INSTALL_INFO) --info-dir=$(infodir) "$$f" ; \\
+\tdone
+
+uninstall-info: info
+\tfor f in *.info; do \\
+\t rm -f "$(infodir)/$$f" ; \\
+\t $(INSTALL_INFO) --delete --info-dir=$(infodir) "$$f" ; \\
+\tdone
+
+%.info: %.texi
+\t$(MAKEINFO) -o '$@' '$<'
+
+%.txt: %.texi
+\t$(MAKEINFO_plaintext) -o '$@' '$<'
+
+%.html: %.texi
+\t$(MAKEINFO_html) -o '$@' '$<'
+
+%.pdf: %.texi
+\t-$(TEXI2PDF) '$<'
+\t-$(TEXI2PDF) '$<'
+\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
+
+.PHONY: all info plaintext html pdf install-info uninstall-info clean
+'''
+
+
+class TexinfoBuilder(Builder):
+ """
+ Builds Texinfo output to create Info documentation.
+ """
+ name = 'texinfo'
+ format = 'texinfo'
+ supported_image_types = ['application/pdf', 'image/png',
+ 'image/gif', 'image/jpeg']
+
+ def init(self):
+ self.docnames = []
+ self.document_data = []
+
+ def get_outdated_docs(self):
+ return 'all documents' # for now
+
+ def get_target_uri(self, docname, typ=None):
+ if docname not in self.docnames:
+ raise NoUri
+ else:
+ return '%' + docname
+
+ def get_relative_uri(self, from_, to, typ=None):
+ # ignore source path
+ return self.get_target_uri(to, typ)
+
+ def init_document_data(self):
+ preliminary_document_data = map(list, self.config.texinfo_documents)
+ if not preliminary_document_data:
+ self.warn('no "texinfo_documents" config value found; no documents '
+ 'will be written')
+ return
+ # assign subdirs to titles
+ self.titles = []
+ for entry in preliminary_document_data:
+ docname = entry[0]
+ if docname not in self.env.all_docs:
+ self.warn('"texinfo_documents" config value references unknown '
+ 'document %s' % docname)
+ continue
+ self.document_data.append(entry)
+ if docname.endswith(SEP+'index'):
+ docname = docname[:-5]
+ self.titles.append((docname, entry[2]))
+
+ def write(self, *ignored):
+ self.init_document_data()
+ for entry in self.document_data:
+ docname, targetname, title, author = entry[:4]
+ targetname += '.texi'
+ direntry = description = category = ''
+ if len(entry) > 6:
+ direntry, description, category = entry[4:7]
+ toctree_only = False
+ if len(entry) > 7:
+ toctree_only = entry[7]
+ destination = FileOutput(
+ destination_path=path.join(self.outdir, targetname),
+ encoding='utf-8')
+ self.info("processing " + targetname + "... ", nonl=1)
+ doctree = self.assemble_doctree(docname, toctree_only,
+ appendices=(self.config.texinfo_appendices or []))
+ self.info("writing... ", nonl=1)
+
+ # Add an Index section
+ if self.config.texinfo_domain_indices:
+ doctree.append(
+ nodes.section('',
+ nodes.title(_("Index"),
+ nodes.Text(_('Index'),
+ _('Index'))),
+ nodes.raw('@printindex ge\n',
+ nodes.Text('@printindex ge\n',
+ '@printindex ge\n'),
+ format="texinfo")))
+ self.post_process_images(doctree)
+ docwriter = TexinfoWriter(self)
+ settings = OptionParser(
+ defaults=self.env.settings,
+ components=(docwriter,)).get_default_values()
+ settings.author = author
+ settings.title = title
+ settings.texinfo_filename = targetname[:-5] + '.info'
+ settings.texinfo_elements = self.config.texinfo_elements
+ settings.texinfo_dir_entry = direntry or ''
+ settings.texinfo_dir_category = category or ''
+ settings.texinfo_dir_description = description or ''
+ settings.docname = docname
+ doctree.settings = settings
+ docwriter.write(doctree, destination)
+ self.info("done")
+
+ def assemble_doctree(self, indexfile, toctree_only, appendices):
+ self.docnames = set([indexfile] + appendices)
+ self.info(darkgreen(indexfile) + " ", nonl=1)
+ tree = self.env.get_doctree(indexfile)
+ tree['docname'] = indexfile
+ if toctree_only:
+ # extract toctree nodes from the tree and put them in a
+ # fresh document
+ new_tree = new_document('<texinfo output>')
+ new_sect = nodes.section()
+ new_sect += nodes.title(u'<Set title in conf.py>',
+ u'<Set title in conf.py>')
+ new_tree += new_sect
+ for node in tree.traverse(addnodes.toctree):
+ new_sect += node
+ tree = new_tree
+ largetree = inline_all_toctrees(self, self.docnames, indexfile, tree,
+ darkgreen)
+ largetree['docname'] = indexfile
+ for docname in appendices:
+ appendix = self.env.get_doctree(docname)
+ appendix['docname'] = docname
+ largetree.append(appendix)
+ self.info()
+ self.info("resolving references...")
+ self.env.resolve_references(largetree, indexfile, self)
+ # TODO: add support for external :ref:s
+ for pendingnode in largetree.traverse(addnodes.pending_xref):
+ docname = pendingnode['refdocname']
+ sectname = pendingnode['refsectname']
+ newnodes = [nodes.emphasis(sectname, sectname)]
+ for subdir, title in self.titles:
+ if docname.startswith(subdir):
+ newnodes.append(nodes.Text(_(' (in '), _(' (in ')))
+ newnodes.append(nodes.emphasis(title, title))
+ newnodes.append(nodes.Text(')', ')'))
+ break
+ else:
+ pass
+ pendingnode.replace_self(newnodes)
+ return largetree
+
+ def finish(self):
+ # copy image files
+ if self.images:
+ self.info(bold('copying images...'), nonl=1)
+ for src, dest in self.images.iteritems():
+ self.info(' '+src, nonl=1)
+ copyfile(path.join(self.srcdir, src),
+ path.join(self.outdir, dest))
+ self.info()
+
+ self.info(bold('copying Texinfo support files... '), nonl=True)
+ # copy Makefile
+ fn = path.join(self.outdir, 'Makefile')
+ self.info(fn, nonl=1)
+ try:
+ mkfile = open(fn, 'w')
+ try:
+ mkfile.write(TEXINFO_MAKEFILE)
+ finally:
+ mkfile.close()
+ except (IOError, OSError), err:
+ self.warn("error writing file %s: %s" % (fn, err))
+ self.info(' done')
diff --git a/sphinx/builders/text.py b/sphinx/builders/text.py
index 092a1d97..40fd3d79 100644
--- a/sphinx/builders/text.py
+++ b/sphinx/builders/text.py
@@ -5,7 +5,7 @@
Plain-text Sphinx builder.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/builders/versioning.py b/sphinx/builders/versioning.py
deleted file mode 100644
index 6c2bccca..00000000
--- a/sphinx/builders/versioning.py
+++ /dev/null
@@ -1,72 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- sphinx.builders.versioning
- ~~~~~~~~~~~~~~~~~~~~~~~~~~
-
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
- :license: BSD, see LICENSE for details.
-"""
-import os
-import pickle
-
-from docutils.utils import Reporter
-
-from sphinx.util.osutil import copyfile
-from sphinx.environment import WarningStream
-from sphinx.versioning import add_uids, merge_doctrees
-
-
-class VersioningBuilderMixin(object):
- def walk_doctree_files(self):
- for root, dirs, files in os.walk(self.doctreedir):
- for fn in files:
- yield os.path.join(root, fn)
-
- def init(self):
- for fp in self.walk_doctree_files():
- if fp.endswith('.doctree'):
- copyfile(fp, fp + '.old')
-
- def get_old_doctree(self, docname):
- fp = self.env.doc2path(docname, self.doctreedir, '.doctree.old')
- try:
- f = open(fp, 'rb')
- try:
- doctree = pickle.load(f)
- finally:
- f.close()
- except IOError:
- return None
- doctree.settings.env = self.env
- doctree.reporter = Reporter(self.env.doc2path(docname), 2, 5,
- stream=WarningStream(self.env._warnfunc))
- return doctree
-
- def resave_doctree(self, docname, doctree):
- reporter = doctree.reporter
- doctree.reporter = None
- doctree.settings.warning_stream = None
- doctree.settings.env = None
- doctree.settings.record_dependencies = None
-
- fp = self.env.doc2path(docname, self.doctreedir, '.doctree')
- f = open(fp, 'wb')
- try:
- pickle.dump(doctree, f, pickle.HIGHEST_PROTOCOL)
- finally:
- f.close()
-
- doctree.reporter = reporter
-
- def handle_versioning(self, docname, doctree, condition):
- old_doctree = self.get_old_doctree(docname)
- if old_doctree:
- list(merge_doctrees(old_doctree, doctree, condition))
- else:
- list(add_uids(doctree, condition))
- self.resave_doctree(docname, doctree)
-
- def finish(self):
- for fp in self.walk_doctree_files():
- if fp.endswith('.doctree.old'):
- os.remove(fp)
diff --git a/sphinx/builders/websupport.py b/sphinx/builders/websupport.py
index 303adfe6..e8f6aef3 100644
--- a/sphinx/builders/websupport.py
+++ b/sphinx/builders/websupport.py
@@ -5,51 +5,61 @@
Builder for the web support package.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
-import cPickle as pickle
from os import path
-from cgi import escape
import posixpath
import shutil
from docutils.io import StringOutput
+from sphinx.jinja2glue import BuiltinTemplateLoader
from sphinx.util.osutil import os_path, relative_uri, ensuredir, copyfile
-from sphinx.util.jsonimpl import dumps as dump_json
-from sphinx.util.websupport import is_commentable
-from sphinx.builders.html import StandaloneHTMLBuilder
-from sphinx.builders.versioning import VersioningBuilderMixin
+from sphinx.builders.html import PickleHTMLBuilder
from sphinx.writers.websupport import WebSupportTranslator
-class WebSupportBuilder(StandaloneHTMLBuilder, VersioningBuilderMixin):
+class WebSupportBuilder(PickleHTMLBuilder):
"""
Builds documents for the web support package.
"""
name = 'websupport'
- out_suffix = '.fpickle'
def init(self):
- StandaloneHTMLBuilder.init(self)
- VersioningBuilderMixin.init(self)
+ PickleHTMLBuilder.init(self)
+ # templates are needed for this builder, but the serializing
+ # builder does not initialize them
+ self.init_templates()
+ if not isinstance(self.templates, BuiltinTemplateLoader):
+ raise RuntimeError('websupport builder must be used with '
+ 'the builtin templates')
+ # add our custom JS
+ self.script_files.append('_static/websupport.js')
+
+ def set_webinfo(self, staticdir, virtual_staticdir, search, storage):
+ self.staticdir = staticdir
+ self.virtual_staticdir = virtual_staticdir
+ self.search = search
+ self.storage = storage
def init_translator_class(self):
self.translator_class = WebSupportTranslator
+ def prepare_writing(self, docnames):
+ PickleHTMLBuilder.prepare_writing(self, docnames)
+ self.globalcontext['no_search_suffix'] = True
+
def write_doc(self, docname, doctree):
destination = StringOutput(encoding='utf-8')
doctree.settings = self.docsettings
- self.handle_versioning(docname, doctree, is_commentable)
-
self.cur_docname = docname
self.secnumbers = self.env.toc_secnumbers.get(docname, {})
- self.imgpath = '/' + posixpath.join(self.app.staticdir, '_images')
+ self.imgpath = '/' + posixpath.join(self.virtual_staticdir, '_images')
self.post_process_images(doctree)
- self.dlpath = '/' + posixpath.join(self.app.staticdir, '_downloads')
+ self.dlpath = '/' + posixpath.join(self.virtual_staticdir, '_downloads')
self.docwriter.write(doctree, destination)
self.docwriter.assemble_parts()
body = self.docwriter.parts['fragment']
@@ -59,15 +69,11 @@ class WebSupportBuilder(StandaloneHTMLBuilder, VersioningBuilderMixin):
self.index_page(docname, doctree, ctx.get('title', ''))
self.handle_page(docname, ctx, event_arg=doctree)
- def get_target_uri(self, docname, typ=None):
- return docname
-
def load_indexer(self, docnames):
- self.indexer = self.app.search
+ self.indexer = self.search
self.indexer.init_indexing(changed=docnames)
- def handle_page(self, pagename, addctx, templatename='page.html',
- outfilename=None, event_arg=None):
+ def _render_page(self, pagename, addctx, templatename, event_arg=None):
# This is mostly copied from StandaloneHTMLBuilder. However, instead
# of rendering the template and saving the html, create a context
# dict and pickle it.
@@ -76,14 +82,16 @@ class WebSupportBuilder(StandaloneHTMLBuilder, VersioningBuilderMixin):
def pathto(otheruri, resource=False,
baseuri=self.get_target_uri(pagename)):
- if not resource:
+ if resource and '://' in otheruri:
+ return otheruri
+ elif not resource:
otheruri = self.get_target_uri(otheruri)
return relative_uri(baseuri, otheruri) or '#'
else:
- return '/' + posixpath.join(self.app.staticdir, otheruri)
+ return '/' + posixpath.join(self.virtual_staticdir, otheruri)
ctx['pathto'] = pathto
ctx['hasdoc'] = lambda name: name in self.env.all_docs
- ctx['encoding'] = encoding = self.config.html_output_encoding
+ ctx['encoding'] = self.config.html_output_encoding
ctx['toctree'] = lambda **kw: self._get_local_toctree(pagename, **kw)
self.add_sidebars(pagename, ctx)
ctx.update(addctx)
@@ -91,47 +99,52 @@ class WebSupportBuilder(StandaloneHTMLBuilder, VersioningBuilderMixin):
self.app.emit('html-page-context', pagename, templatename,
ctx, event_arg)
- # Create a dict that will be pickled and used by webapps.
- css = '<link rel="stylesheet" href="%s" type=text/css />' % \
- pathto('_static/pygments.css', 1)
- doc_ctx = {'body': ctx.get('body', ''),
- 'title': ctx.get('title', ''),
- 'css': css,
- 'js': self._make_js(ctx)}
- # Partially render the html template to proved a more useful ctx.
+ # create a dict that will be pickled and used by webapps
+ doc_ctx = {
+ 'body': ctx.get('body', ''),
+ 'title': ctx.get('title', ''),
+ }
+ # partially render the html template to get at interesting macros
template = self.templates.environment.get_template(templatename)
template_module = template.make_module(ctx)
- if hasattr(template_module, 'sidebar'):
- doc_ctx['sidebar'] = template_module.sidebar()
- if hasattr(template_module, 'relbar'):
- doc_ctx['relbar'] = template_module.relbar()
+ for item in ['sidebar', 'relbar', 'script', 'css']:
+ if hasattr(template_module, item):
+ doc_ctx[item] = getattr(template_module, item)()
+
+ return ctx, doc_ctx
+
+ def handle_page(self, pagename, addctx, templatename='page.html',
+ outfilename=None, event_arg=None):
+ ctx, doc_ctx = self._render_page(pagename, addctx,
+ templatename, event_arg)
if not outfilename:
outfilename = path.join(self.outdir, 'pickles',
os_path(pagename) + self.out_suffix)
-
ensuredir(path.dirname(outfilename))
- f = open(outfilename, 'wb')
- try:
- pickle.dump(doc_ctx, f, pickle.HIGHEST_PROTOCOL)
- finally:
- f.close()
+ self.dump_context(doc_ctx, outfilename)
# if there is a source file, copy the source file for the
# "show source" link
if ctx.get('sourcename'):
- source_name = path.join(self.app.builddir, self.app.staticdir,
+ source_name = path.join(self.staticdir,
'_sources', os_path(ctx['sourcename']))
ensuredir(path.dirname(source_name))
copyfile(self.env.doc2path(pagename), source_name)
def handle_finish(self):
- StandaloneHTMLBuilder.handle_finish(self)
- VersioningBuilderMixin.finish(self)
+ # get global values for css and script files
+ _, doc_ctx = self._render_page('tmp', {}, 'page.html')
+ self.globalcontext['css'] = doc_ctx['css']
+ self.globalcontext['script'] = doc_ctx['script']
+
+ PickleHTMLBuilder.handle_finish(self)
+
+ # move static stuff over to separate directory
directories = ['_images', '_static']
for directory in directories:
src = path.join(self.outdir, directory)
- dst = path.join(self.app.builddir, self.app.staticdir, directory)
+ dst = path.join(self.staticdir, directory)
if path.isdir(src):
if path.isdir(dst):
shutil.rmtree(dst)
@@ -139,23 +152,3 @@ class WebSupportBuilder(StandaloneHTMLBuilder, VersioningBuilderMixin):
def dump_search_index(self):
self.indexer.finish_indexing()
-
- def _make_js(self, ctx):
- def make_script(file):
- path = ctx['pathto'](file, 1)
- return '<script type="text/javascript" src="%s"></script>' % path
-
- opts = {
- 'URL_ROOT': ctx.get('url_root', ''),
- 'VERSION': ctx['release'],
- 'COLLAPSE_INDEX': False,
- 'FILE_SUFFIX': '',
- 'HAS_SOURCE': ctx['has_source']
- }
- scripts = [make_script(file) for file in ctx['script_files']]
- scripts.append(make_script('_static/websupport.js'))
- return '\n'.join([
- '<script type="text/javascript">'
- 'var DOCUMENTATION_OPTIONS = %s;' % dump_json(opts),
- '</script>'
- ] + scripts)
diff --git a/sphinx/cmdline.py b/sphinx/cmdline.py
index e3e94465..af780167 100644
--- a/sphinx/cmdline.py
+++ b/sphinx/cmdline.py
@@ -5,7 +5,7 @@
sphinx-build command-line handling.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -90,6 +90,13 @@ def main(argv):
if err:
return 1
+ # likely encoding used for command-line arguments
+ try:
+ locale = __import__('locale') # due to submodule of the same name
+ likely_encoding = locale.getpreferredencoding()
+ except Exception:
+ likely_encoding = None
+
buildername = None
force_all = freshenv = warningiserror = use_pdb = False
status = sys.stdout
@@ -129,7 +136,11 @@ def main(argv):
try:
val = int(val)
except ValueError:
- pass
+ if likely_encoding:
+ try:
+ val = val.decode(likely_encoding)
+ except UnicodeError:
+ pass
confoverrides[key] = val
elif opt == '-A':
try:
@@ -141,7 +152,11 @@ def main(argv):
try:
val = int(val)
except ValueError:
- pass
+ if likely_encoding:
+ try:
+ val = val.decode(likely_encoding)
+ except UnicodeError:
+ pass
confoverrides['html_context.%s' % key] = val
elif opt == '-n':
confoverrides['nitpicky'] = True
diff --git a/sphinx/config.py b/sphinx/config.py
index 8ad260e9..90c4b562 100644
--- a/sphinx/config.py
+++ b/sphinx/config.py
@@ -5,7 +5,7 @@
Build configuration file handling.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -76,7 +76,7 @@ class Config(object):
html_theme = ('default', 'html'),
html_theme_path = ([], 'html'),
html_theme_options = ({}, 'html'),
- html_title = (lambda self: '%s v%s documentation' %
+ html_title = (lambda self: '%s %s documentation' %
(self.project, self.release),
'html'),
html_short_title = (lambda self: self.html_title, 'html'),
@@ -92,7 +92,7 @@ class Config(object):
html_additional_pages = ({}, 'html'),
html_use_modindex = (True, 'html'), # deprecated
html_domain_indices = (True, 'html'),
- html_add_permalinks = (True, 'html'),
+ html_add_permalinks = (u'\u00B6', 'html'),
html_use_index = (True, 'html'),
html_split_index = (False, 'html'),
html_copy_source = (True, 'html'),
@@ -106,6 +106,8 @@ class Config(object):
html_output_encoding = ('utf-8', 'html'),
html_compact_lists = (True, 'html'),
html_secnumber_suffix = ('. ', 'html'),
+ html_search_language = (None, 'html'),
+ html_search_options = ({}, 'html'),
# HTML help only options
htmlhelp_basename = (lambda self: make_filename(self.project), None),
@@ -141,7 +143,7 @@ class Config(object):
latex_use_parts = (False, None),
latex_use_modindex = (True, None), # deprecated
latex_domain_indices = (True, None),
- latex_show_urls = (False, None),
+ latex_show_urls = ('no', None),
latex_show_pagerefs = (False, None),
# paper_size and font_size are still separate values
# so that you can give them easily on the command line
@@ -154,11 +156,23 @@ class Config(object):
latex_preamble = ('', None),
# text options
- text_sectionchars = ('*=-~"+`', 'text'),
- text_windows_newlines = (False, 'text'),
+ text_sectionchars = ('*=-~"+`', 'env'),
+ text_newlines = ('unix', 'env'),
# manpage options
man_pages = ([], None),
+ man_show_urls = (False, None),
+
+ # Texinfo options
+ texinfo_documents = ([], None),
+ texinfo_appendices = ([], None),
+ texinfo_elements = ({}, None),
+ texinfo_domain_indices = (True, None),
+
+ # linkcheck options
+ linkcheck_ignore = ([], None),
+ linkcheck_timeout = (None, None),
+ linkcheck_workers = (5, None),
)
def __init__(self, dirname, filename, overrides, tags):
diff --git a/sphinx/directives/__init__.py b/sphinx/directives/__init__.py
index b7adbcaf..f11a4059 100644
--- a/sphinx/directives/__init__.py
+++ b/sphinx/directives/__init__.py
@@ -5,7 +5,7 @@
Handlers for additional ReST directives.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 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 d235c4ca..99fb3502 100644
--- a/sphinx/directives/code.py
+++ b/sphinx/directives/code.py
@@ -3,7 +3,7 @@
sphinx.directives.code
~~~~~~~~~~~~~~~~~~~~~~
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -62,6 +62,7 @@ class CodeBlock(Directive):
literal = nodes.literal_block(code, code)
literal['language'] = self.arguments[0]
literal['linenos'] = 'linenos' in self.options
+ literal.line = self.lineno
return [literal]
@@ -166,6 +167,7 @@ class LiteralInclude(Directive):
text = text.expandtabs(self.options['tab-width'])
retnode = nodes.literal_block(text, text, source=filename)
retnode.line = 1
+ retnode.attributes['line_number'] = self.lineno
if self.options.get('language', ''):
retnode['language'] = self.options['language']
if 'linenos' in self.options:
diff --git a/sphinx/directives/other.py b/sphinx/directives/other.py
index dbc7336c..246d6403 100644
--- a/sphinx/directives/other.py
+++ b/sphinx/directives/other.py
@@ -3,7 +3,7 @@
sphinx.directives.other
~~~~~~~~~~~~~~~~~~~~~~~
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -80,8 +80,9 @@ class TocTree(Directive):
entries.append((title, ref))
elif docname not in env.found_docs:
ret.append(self.state.document.reporter.warning(
- 'toctree references unknown document %r' % docname,
- line=self.lineno))
+ 'toctree contains reference to nonexisting '
+ 'document %r' % docname, line=self.lineno))
+ env.note_reread()
else:
entries.append((title, docname))
includefiles.append(docname)
@@ -164,6 +165,7 @@ class Index(Directive):
self.state.document.note_explicit_target(targetnode)
indexnode = addnodes.index()
indexnode['entries'] = ne = []
+ indexnode['inline'] = False
for entry in arguments:
ne.extend(process_index_entry(entry, targetid))
return [indexnode, targetnode]
@@ -236,6 +238,7 @@ class TabularColumns(Directive):
def run(self):
node = addnodes.tabular_col_spec()
node['spec'] = self.arguments[0]
+ node.line = self.lineno
return [node]
diff --git a/sphinx/domains/__init__.py b/sphinx/domains/__init__.py
index 484cd968..d6faa127 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-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 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 c49dc284..b0dd332b 100644
--- a/sphinx/domains/c.py
+++ b/sphinx/domains/c.py
@@ -5,7 +5,7 @@
The C language domain.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -99,13 +99,20 @@ class CObject(ObjectDescription):
m = c_funcptr_name_re.match(name)
if m:
name = m.group(1)
+
+ typename = self.env.temp_data.get('c:type')
+ if self.name == 'c:member' and typename:
+ fullname = typename + '.' + name
+ else:
+ fullname = name
+
if not arglist:
if self.objtype == 'function':
# for functions, add an empty parameter list
signode += addnodes.desc_parameterlist()
if const:
signode += addnodes.desc_addname(const, const)
- return name
+ return fullname
paramlist = addnodes.desc_parameterlist()
arglist = arglist.replace('`', '').replace('\\ ', '') # remove markup
@@ -121,12 +128,13 @@ class CObject(ObjectDescription):
self._parse_type(param, arg)
else:
self._parse_type(param, ctype)
- param += nodes.emphasis(' '+argname, ' '+argname)
+ # separate by non-breaking space in the output
+ param += nodes.emphasis(' '+argname, u'\xa0'+argname)
paramlist += param
signode += paramlist
if const:
signode += addnodes.desc_addname(const, const)
- return name
+ return fullname
def get_index_text(self, name):
if self.objtype == 'function':
@@ -160,7 +168,32 @@ class CObject(ObjectDescription):
indextext = self.get_index_text(name)
if indextext:
- self.indexnode['entries'].append(('single', indextext, name, name))
+ self.indexnode['entries'].append(('single', indextext, name, ''))
+
+ def before_content(self):
+ self.typename_set = False
+ if self.name == 'c:type':
+ if self.names:
+ self.env.temp_data['c:type'] = self.names[0]
+ self.typename_set = True
+
+ def after_content(self):
+ if self.typename_set:
+ self.env.temp_data['c:type'] = None
+
+
+class CXRefRole(XRefRole):
+ def process_link(self, env, refnode, has_explicit_title, title, target):
+ if not has_explicit_title:
+ target = target.lstrip('~') # only has a meaning for the title
+ # if the first character is a tilde, don't display the module/class
+ # parts of the contents
+ if title[0:1] == '~':
+ title = title[1:]
+ dot = title.rfind('.')
+ if dot != -1:
+ title = title[dot+1:]
+ return title, target
class CDomain(Domain):
@@ -183,11 +216,11 @@ class CDomain(Domain):
'var': CObject,
}
roles = {
- 'func' : XRefRole(fix_parens=True),
- 'member': XRefRole(),
- 'macro': XRefRole(),
- 'data': XRefRole(),
- 'type': XRefRole(),
+ 'func' : CXRefRole(fix_parens=True),
+ 'member': CXRefRole(),
+ 'macro': CXRefRole(),
+ 'data': CXRefRole(),
+ 'type': CXRefRole(),
}
initial_data = {
'objects': {}, # fullname -> docname, objtype
diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py
index d45fd572..4e40dde7 100644
--- a/sphinx/domains/cpp.py
+++ b/sphinx/domains/cpp.py
@@ -5,7 +5,7 @@
The C++ language domain.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -23,11 +23,12 @@ from sphinx.util.nodes import make_refnode
from sphinx.util.compat import Directive
-_identifier_re = re.compile(r'\b(~?[a-zA-Z_][a-zA-Z0-9_]*)\b')
+_identifier_re = re.compile(r'(~?\b[a-zA-Z_][a-zA-Z0-9_]*)\b')
_whitespace_re = re.compile(r'\s+(?u)')
_string_re = re.compile(r"[LuU8]?('([^'\\]*(?:\\.[^'\\]*)*)'"
r'|"([^"\\]*(?:\\.[^"\\]*)*)")', re.S)
_visibility_re = re.compile(r'\b(public|private|protected)\b')
+_array_def_re = re.compile(r'\[\s*(.+?)?\s*\]')
_operator_re = re.compile(r'''(?x)
\[\s*\]
| \(\s*\)
@@ -150,12 +151,12 @@ class DefExpr(object):
return None
def split_owner(self):
- """Nodes returned by :meth:`get_name` can split off their owning parent.
-
- This function returns the owner and the name as a tuple of two items.
- If a node does not support it, :exc:`NotImplementedError` is raised.
+ """Nodes returned by :meth:`get_name` can split off their
+ owning parent. This function returns the owner and the
+ name as a tuple of two items. If a node does not support
+ it, it returns None as owner and self as name.
"""
- raise NotImplementedError()
+ return None, self
def prefix(self, prefix):
"""Prefix a name node (a node returned by :meth:`get_name`)."""
@@ -165,7 +166,7 @@ class DefExpr(object):
return unicode(self).encode('utf-8')
def __repr__(self):
- return '<defexpr %s>' % self
+ return '<%s %s>' % (self.__class__.__name__, self)
class PrimaryDefExpr(DefExpr):
@@ -173,9 +174,6 @@ class PrimaryDefExpr(DefExpr):
def get_name(self):
return self
- def split_owner(self):
- return None, self
-
def prefix(self, prefix):
if isinstance(prefix, PathDefExpr):
prefix = prefix.clone()
@@ -276,6 +274,22 @@ class PtrDefExpr(WrappingDefExpr):
return u'%s*' % self.typename
+class ArrayDefExpr(WrappingDefExpr):
+
+ def __init__(self, typename, size_hint=None):
+ WrappingDefExpr.__init__(self, typename)
+ self.size_hint = size_hint
+
+ def get_id(self):
+ return self.typename.get_id() + u'A'
+
+ def __unicode__(self):
+ return u'%s[%s]' % (
+ self.typename,
+ self.size_hint is not None and unicode(self.size_hint) or u''
+ )
+
+
class RefDefExpr(WrappingDefExpr):
def get_id(self):
@@ -326,9 +340,8 @@ class ArgumentDefExpr(DefExpr):
return self.type.get_id()
def __unicode__(self):
- return (self.type is not None and u'%s %s' % (self.type, self.name)
- or unicode(self.name)) + (self.default is not None and
- u'=%s' % self.default or u'')
+ return (u'%s %s' % (self.type or u'', self.name or u'')).strip() + \
+ (self.default is not None and u'=%s' % self.default or u'')
class NamedDefExpr(DefExpr):
@@ -448,9 +461,9 @@ class DefinitionParser(object):
'mutable': None,
'const': None,
'typename': None,
- 'unsigned': set(('char', 'int', 'long')),
- 'signed': set(('char', 'int', 'long')),
- 'short': set(('int', 'short')),
+ 'unsigned': set(('char', 'short', 'int', 'long')),
+ 'signed': set(('char', 'short', 'int', 'long')),
+ 'short': set(('int',)),
'long': set(('int', 'long', 'double'))
}
@@ -566,6 +579,8 @@ class DefinitionParser(object):
expr = ConstDefExpr(expr)
elif self.skip_string('*'):
expr = PtrDefExpr(expr)
+ elif self.match(_array_def_re):
+ expr = ArrayDefExpr(expr, self.last_match.group(1))
elif self.skip_string('&'):
expr = RefDefExpr(expr)
else:
@@ -697,14 +712,13 @@ class DefinitionParser(object):
self.fail('expected comma between arguments')
self.skip_ws()
- argname = self._parse_type()
- argtype = default = None
+ argtype = self._parse_type()
+ argname = default = None
self.skip_ws()
if self.skip_string('='):
self.pos += 1
default = self._parse_default_expr()
elif self.current_char not in ',)':
- argtype = argname
argname = self._parse_name()
self.skip_ws()
if self.skip_string('='):
@@ -827,17 +841,18 @@ class CPPObject(ObjectDescription):
def add_target_and_index(self, sigobj, sig, signode):
theid = sigobj.get_id()
name = unicode(sigobj.name)
- signode['names'].append(theid)
- signode['ids'].append(theid)
- signode['first'] = (not self.names)
- self.state.document.note_explicit_target(signode)
+ if theid not in self.state.document.ids:
+ signode['names'].append(theid)
+ signode['ids'].append(theid)
+ signode['first'] = (not self.names)
+ self.state.document.note_explicit_target(signode)
- self.env.domaindata['cpp']['objects'].setdefault(name,
- (self.env.docname, self.objtype, theid))
+ self.env.domaindata['cpp']['objects'].setdefault(name,
+ (self.env.docname, self.objtype, theid))
indextext = self.get_index_text(name)
if indextext:
- self.indexnode['entries'].append(('single', indextext, name, name))
+ self.indexnode['entries'].append(('single', indextext, theid, ''))
def before_content(self):
lastname = self.names and self.names[-1]
diff --git a/sphinx/domains/javascript.py b/sphinx/domains/javascript.py
index 2b615847..57cee8f9 100644
--- a/sphinx/domains/javascript.py
+++ b/sphinx/domains/javascript.py
@@ -5,7 +5,7 @@
The JavaScript domain.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -96,7 +96,7 @@ class JSObject(ObjectDescription):
indextext = self.get_index_text(objectname, name_obj)
if indextext:
self.indexnode['entries'].append(('single', indextext,
- fullname, fullname))
+ fullname, ''))
def get_index_text(self, objectname, name_obj):
name, obj = name_obj
diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py
index e3090a29..0dbd883c 100644
--- a/sphinx/domains/python.py
+++ b/sphinx/domains/python.py
@@ -5,7 +5,7 @@
The Python domain.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -94,10 +94,12 @@ class PyObject(ObjectDescription):
TypedField('parameter', label=l_('Parameters'),
names=('param', 'parameter', 'arg', 'argument',
'keyword', 'kwarg', 'kwparam'),
- typerolename='obj', typenames=('paramtype', 'type')),
+ typerolename='obj', typenames=('paramtype', 'type'),
+ can_collapse=True),
TypedField('variable', label=l_('Variables'), rolename='obj',
names=('var', 'ivar', 'cvar'),
- typerolename='obj', typenames=('vartype',)),
+ typerolename='obj', typenames=('vartype',),
+ can_collapse=True),
GroupedField('exceptions', label=l_('Raises'), rolename='exc',
names=('raises', 'raise', 'exception', 'except'),
can_collapse=True),
@@ -219,7 +221,7 @@ class PyObject(ObjectDescription):
indextext = self.get_index_text(modname, name_cls)
if indextext:
self.indexnode['entries'].append(('single', indextext,
- fullname, fullname))
+ fullname, ''))
def before_content(self):
# needed for automatic qualification of members (reset in subclasses)
@@ -355,6 +357,38 @@ class PyClassmember(PyObject):
self.clsname_set = True
+class PyDecoratorMixin(object):
+ """
+ Mixin for decorator directives.
+ """
+ def handle_signature(self, sig, signode):
+ ret = super(PyDecoratorMixin, self).handle_signature(sig, signode)
+ signode.insert(0, addnodes.desc_addname('@', '@'))
+ return ret
+
+ def needs_arglist(self):
+ return False
+
+
+class PyDecoratorFunction(PyDecoratorMixin, PyModulelevel):
+ """
+ Directive to mark functions meant to be used as decorators.
+ """
+ def run(self):
+ # a decorator function is a function after all
+ self.name = 'py:function'
+ return PyModulelevel.run(self)
+
+
+class PyDecoratorMethod(PyDecoratorMixin, PyClassmember):
+ """
+ Directive to mark methods meant to be used as decorators.
+ """
+ def run(self):
+ self.name = 'py:method'
+ return PyClassmember.run(self)
+
+
class PyModule(Directive):
"""
Directive to mark description of a new module.
@@ -397,7 +431,7 @@ class PyModule(Directive):
if not noindex:
indextext = _('%s (module)') % modname
inode = addnodes.index(entries=[('single', indextext,
- 'module-' + modname, modname)])
+ 'module-' + modname, '')])
ret.append(inode)
return ret
@@ -532,16 +566,18 @@ class PythonDomain(Domain):
}
directives = {
- 'function': PyModulelevel,
- 'data': PyModulelevel,
- 'class': PyClasslike,
- 'exception': PyClasslike,
- 'method': PyClassmember,
- 'classmethod': PyClassmember,
- 'staticmethod': PyClassmember,
- 'attribute': PyClassmember,
- 'module': PyModule,
- 'currentmodule': PyCurrentModule,
+ 'function': PyModulelevel,
+ 'data': PyModulelevel,
+ 'class': PyClasslike,
+ 'exception': PyClasslike,
+ 'method': PyClassmember,
+ 'classmethod': PyClassmember,
+ 'staticmethod': PyClassmember,
+ 'attribute': PyClassmember,
+ 'module': PyModule,
+ 'currentmodule': PyCurrentModule,
+ 'decorator': PyDecoratorFunction,
+ 'decoratormethod': PyDecoratorMethod,
}
roles = {
'data': PyXRefRole(),
@@ -579,7 +615,7 @@ class PythonDomain(Domain):
name = name[:-2]
if not name:
- return None, None
+ return []
objects = self.data['objects']
matches = []
diff --git a/sphinx/domains/rst.py b/sphinx/domains/rst.py
index 30134d9e..e67f827e 100644
--- a/sphinx/domains/rst.py
+++ b/sphinx/domains/rst.py
@@ -5,7 +5,7 @@
The reStructuredText domain.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -48,7 +48,7 @@ class ReSTMarkup(ObjectDescription):
indextext = self.get_index_text(self.objtype, name)
if indextext:
self.indexnode['entries'].append(('single', indextext,
- targetname, targetname))
+ targetname, ''))
def get_index_text(self, objectname, name):
if self.objtype == 'directive':
diff --git a/sphinx/domains/std.py b/sphinx/domains/std.py
index 6194ace9..7dcec616 100644
--- a/sphinx/domains/std.py
+++ b/sphinx/domains/std.py
@@ -5,14 +5,16 @@
The standard domain.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import re
+import unicodedata
from docutils import nodes
from docutils.parsers.rst import directives
+from docutils.statemachine import ViewList
from sphinx import addnodes
from sphinx.roles import XRefRole
@@ -59,7 +61,7 @@ class GenericObject(ObjectDescription):
indextype = 'single'
indexentry = self.indextemplate % (name,)
self.indexnode['entries'].append((indextype, indexentry,
- targetname, targetname))
+ targetname, ''))
self.env.domaindata['std']['objects'][self.objtype, name] = \
self.env.docname, targetname
@@ -80,8 +82,8 @@ class EnvVarXRefRole(XRefRole):
tgtid = 'index-%s' % env.new_serialno('index')
indexnode = addnodes.index()
indexnode['entries'] = [
- ('single', varname, tgtid, varname),
- ('single', _('environment variable; %s') % varname, tgtid, varname)
+ ('single', varname, tgtid, ''),
+ ('single', _('environment variable; %s') % varname, tgtid, '')
]
targetnode = nodes.target('', '', ids=[tgtid])
document.note_explicit_target(targetnode)
@@ -116,7 +118,7 @@ class Target(Directive):
indextype = indexentry[:colon].strip()
indexentry = indexentry[colon+1:].strip()
inode = addnodes.index(entries=[(indextype, indexentry,
- targetname, targetname)])
+ targetname, '')])
ret.insert(0, inode)
name = self.name
if ':' in self.name:
@@ -159,7 +161,7 @@ class Cmdoption(ObjectDescription):
self.indexnode['entries'].append(
('pair', _('%scommand line option; %s') %
((currprogram and currprogram + ' ' or ''), sig),
- targetname, targetname))
+ targetname, ''))
self.env.domaindata['std']['progoptions'][currprogram, name] = \
self.env.docname, targetname
@@ -205,8 +207,8 @@ class OptionXRefRole(XRefRole):
class Glossary(Directive):
"""
- Directive to create a glossary with cross-reference targets
- for :term: roles.
+ Directive to create a glossary with cross-reference targets for :term:
+ roles.
"""
has_content = True
@@ -223,37 +225,100 @@ class Glossary(Directive):
gloss_entries = env.temp_data.setdefault('gloss_entries', set())
node = addnodes.glossary()
node.document = self.state.document
- self.state.nested_parse(self.content, self.content_offset, node)
-
- # the content should be definition lists
- dls = [child for child in node
- if isinstance(child, nodes.definition_list)]
- # now, extract definition terms to enable cross-reference creation
- new_dl = nodes.definition_list()
- new_dl['classes'].append('glossary')
+
+ # This directive implements a custom format of the reST definition list
+ # that allows multiple lines of terms before the definition. This is
+ # easy to parse since we know that the contents of the glossary *must
+ # be* a definition list.
+
+ # first, collect single entries
+ entries = []
+ in_definition = True
+ was_empty = True
+ messages = []
+ for (source, lineno, line) in self.content.xitems():
+ # empty line -> add to last definition
+ if not line:
+ if in_definition and entries:
+ entries[-1][1].append('', source, lineno)
+ was_empty = True
+ continue
+ # unindented line -> a term
+ if line and not line[0].isspace():
+ # first term of definition
+ if in_definition:
+ if not was_empty:
+ messages.append(self.state.reporter.system_message(
+ 2, 'glossary term must be preceded by empty line',
+ source=source, line=lineno))
+ entries.append(([(line, source, lineno)], ViewList()))
+ in_definition = False
+ # second term and following
+ else:
+ if was_empty:
+ messages.append(self.state.reporter.system_message(
+ 2, 'glossary terms must not be separated by empty '
+ 'lines', source=source, line=lineno))
+ entries[-1][0].append((line, source, lineno))
+ else:
+ if not in_definition:
+ # first line of definition, determines indentation
+ in_definition = True
+ indent_len = len(line) - len(line.lstrip())
+ entries[-1][1].append(line[indent_len:], source, lineno)
+ was_empty = False
+
+ # now, parse all the entries into a big definition list
items = []
- for dl in dls:
- for li in dl.children:
- if not li.children or not isinstance(li[0], nodes.term):
- continue
- termtext = li.children[0].astext()
+ for terms, definition in entries:
+ termtexts = []
+ termnodes = []
+ system_messages = []
+ ids = []
+ for line, source, lineno in terms:
+ # parse the term with inline markup
+ res = self.state.inline_text(line, lineno)
+ system_messages.extend(res[1])
+
+ # get a text-only representation of the term and register it
+ # as a cross-reference target
+ tmp = nodes.paragraph('', '', *res[0])
+ termtext = tmp.astext()
new_id = 'term-' + nodes.make_id(termtext)
if new_id in gloss_entries:
new_id = 'term-' + str(len(gloss_entries))
gloss_entries.add(new_id)
- li[0]['names'].append(new_id)
- li[0]['ids'].append(new_id)
+ ids.append(new_id)
objects['term', termtext.lower()] = env.docname, new_id
+ termtexts.append(termtext)
# add an index entry too
indexnode = addnodes.index()
- indexnode['entries'] = [('single', termtext, new_id, termtext)]
- li.insert(0, indexnode)
- items.append((termtext, li))
+ indexnode['entries'] = [('single', termtext, new_id, 'main')]
+ termnodes.append(indexnode)
+ termnodes.extend(res[0])
+ termnodes.append(addnodes.termsep())
+ # make a single "term" node with all the terms, separated by termsep
+ # nodes (remove the dangling trailing separator)
+ term = nodes.term('', '', *termnodes[:-1])
+ term['ids'].extend(ids)
+ term['names'].extend(ids)
+ term += system_messages
+
+ defnode = nodes.definition()
+ self.state.nested_parse(definition, definition.items[0][1], defnode)
+
+ items.append((termtexts,
+ nodes.definition_list_item('', term, defnode)))
+
if 'sorted' in self.options:
- items.sort(key=lambda x: x[0].lower())
- new_dl.extend(item[1] for item in items)
- node.children = [new_dl]
- return [node]
+ items.sort(key=lambda x:
+ unicodedata.normalize('NFD', x[0][0].lower()))
+
+ dlist = nodes.definition_list()
+ dlist['classes'].append('glossary')
+ dlist.extend(item[1] for item in items)
+ node += dlist
+ return messages + [node]
token_re = re.compile('`([a-z_][a-z0-9_]*)`')
diff --git a/sphinx/environment.py b/sphinx/environment.py
index 883c91e9..59debcd8 100644
--- a/sphinx/environment.py
+++ b/sphinx/environment.py
@@ -5,7 +5,7 @@
Global creation environment.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -18,6 +18,7 @@ import codecs
import imghdr
import string
import posixpath
+import unicodedata
import cPickle as pickle
from os import path
from glob import glob
@@ -36,7 +37,7 @@ 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, \
+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
from sphinx.util.osutil import movefile, SEP, ustrftime
@@ -44,6 +45,7 @@ from sphinx.util.matching import compile_matchers
from sphinx.util.pycompat import all, class_types
from sphinx.errors import SphinxError, ExtensionError
from sphinx.locale import _, init as init_locale
+from sphinx.versioning import add_uids, merge_doctrees
fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding()
@@ -66,7 +68,7 @@ default_settings = {
# This is increased every time an environment attribute is added
# or changed to properly invalidate pickle files.
-ENV_VERSION = 38
+ENV_VERSION = 39
default_substitutions = set([
@@ -201,15 +203,13 @@ class Locale(Transform):
# fetch translations
dirs = [path.join(env.srcdir, x)
for x in env.config.locale_dirs]
- catalog, empty = init_locale(dirs, env.config.language, section)
- if not empty:
+ catalog, has_catalog = init_locale(dirs, env.config.language, section)
+ if not has_catalog:
return
parser = RSTParser()
for node, msg in extract_messages(self.document):
- # XXX ctx not used
- #ctx = node.parent
patch = new_document(source, settings)
msgstr = catalog.gettext(msg)
# XXX add marker to untranslated parts
@@ -217,7 +217,9 @@ class Locale(Transform):
continue
parser.parse(msgstr, patch)
patch = patch[0]
- assert isinstance(patch, nodes.paragraph)
+ # 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
@@ -335,6 +337,8 @@ class BuildEnvironment:
# contains all built docnames
self.dependencies = {} # docname -> set of dependent file
# names, relative to documentation root
+ self.reread_always = set() # docnames to re-read unconditionally on
+ # next build
# File metadata
self.metadata = {} # docname -> dict of metadata items
@@ -377,17 +381,14 @@ class BuildEnvironment:
self.settings['warning_stream'] = WarningStream(func)
def warn(self, docname, msg, lineno=None):
- if docname:
- if lineno is None:
- lineno = ''
- self._warnfunc(msg, '%s:%s' % (self.doc2path(docname), lineno))
- else:
- self._warnfunc(msg)
+ # strange argument order is due to backwards compatibility
+ self._warnfunc(msg, (docname, lineno))
def clear_doc(self, docname):
"""Remove all traces of a source file in the inventory."""
if docname in self.all_docs:
self.all_docs.pop(docname, None)
+ self.reread_always.discard(docname)
self.metadata.pop(docname, None)
self.dependencies.pop(docname, None)
self.titles.pop(docname, None)
@@ -489,6 +490,10 @@ class BuildEnvironment:
'.doctree')):
changed.add(docname)
continue
+ # check the "reread always" list
+ if docname in self.reread_always:
+ changed.add(docname)
+ continue
# check the mtime of the document
mtime = self.all_docs[docname]
newmtime = path.getmtime(self.doc2path(docname))
@@ -551,10 +556,15 @@ class BuildEnvironment:
added, changed, removed = self.get_outdated_files(config_changed)
+ # allow user intervention as well
+ for docs in app.emit('env-get-outdated', self, added, changed, removed):
+ changed.update(set(docs) & self.found_docs)
+
# if files were added or removed, all documents with globbed toctrees
# must be reread
if added or removed:
- changed.update(self.glob_toctrees)
+ # ... but not those that already were removed
+ changed.update(self.glob_toctrees & self.found_docs)
msg += '%s added, %s changed, %s removed' % (len(added), len(changed),
len(removed))
@@ -569,8 +579,7 @@ class BuildEnvironment:
self.clear_doc(docname)
# read all new and changed files
- to_read = added | changed
- for docname in sorted(to_read):
+ for docname in sorted(added | changed):
yield docname
self.read_doc(docname, app=app)
@@ -687,6 +696,11 @@ class BuildEnvironment:
codecs.register_error('sphinx', self.warn_and_replace)
class SphinxSourceClass(FileInput):
+ def __init__(self_, *args, **kwds):
+ # don't call sys.exit() on IOErrors
+ kwds['handle_io_errors'] = False
+ FileInput.__init__(self_, *args, **kwds)
+
def decode(self_, data):
if isinstance(data, unicode):
return data
@@ -740,6 +754,26 @@ class BuildEnvironment:
# store time of build, for outdated files detection
self.all_docs[docname] = time.time()
+ # get old doctree
+ old_doctree_path = self.doc2path(docname, self.doctreedir, '.doctree')
+ try:
+ f = open(old_doctree_path, 'rb')
+ try:
+ old_doctree = pickle.load(f)
+ finally:
+ f.close()
+ old_doctree.settings.env = self
+ old_doctree.reporter = Reporter(self.doc2path(docname), 2, 5,
+ stream=WarningStream(self._warnfunc))
+ except EnvironmentError:
+ old_doctree = None
+
+ # add uids for versioning
+ if old_doctree is None:
+ list(add_uids(doctree, nodes.TextElement))
+ else:
+ list(merge_doctrees(old_doctree, doctree, nodes.TextElement))
+
# make it picklable
doctree.reporter = None
doctree.transformer = None
@@ -795,6 +829,9 @@ class BuildEnvironment:
def note_dependency(self, filename):
self.dependencies.setdefault(self.docname, set()).add(filename)
+ def note_reread(self):
+ self.reread_always.add(self.docname)
+
def note_versionchange(self, type, version, node, lineno):
self.versionchanges.setdefault(version, []).append(
(type, self.temp_data['docname'], lineno,
@@ -1280,11 +1317,12 @@ class BuildEnvironment:
self.warn(docname,
'toctree contains reference to document '
'%r that doesn\'t have a title: no link '
- 'will be generated' % ref)
+ 'will be generated' % ref, toctreenode.line)
except KeyError:
# this is raised if the included file does not exist
self.warn(docname, 'toctree contains reference to '
- 'nonexisting document %r' % ref)
+ 'nonexisting document %r' % ref,
+ toctreenode.line)
else:
# if titles_only is given, only keep the main title and
# sub-toctrees
@@ -1413,7 +1451,9 @@ class BuildEnvironment:
if ret:
node.replace_self(node.children)
else:
- node.replace_self([])
+ # replacing by [] would result in an "Losing ids" exception
+ # if there is a target node before the only node
+ node.replace_self(nodes.comment())
# allow custom references to be resolved
builder.app.emit('doctree-resolved', doctree, fromdocname)
@@ -1477,99 +1517,95 @@ class BuildEnvironment:
return rewrite_needed
- def create_index(self, builder, _fixre=re.compile(r'(.*) ([(][^()]*[)])')):
+ def create_index(self, builder, group_entries=True,
+ _fixre=re.compile(r'(.*) ([(][^()]*[)])')):
"""Create the real index from the collected index entries."""
new = {}
- def add_entry(word, subword, dic=new):
+ def add_entry(word, subword, link=True, dic=new):
entry = dic.get(word)
if not entry:
dic[word] = entry = [[], {}]
if subword:
- add_entry(subword, '', dic=entry[1])
- else:
+ add_entry(subword, '', link=link, dic=entry[1])
+ elif link:
try:
- entry[0].append(builder.get_relative_uri('genindex', fn)
- + '#' + tid)
+ uri = builder.get_relative_uri('genindex', fn) + '#' + tid
except NoUri:
pass
+ else:
+ entry[0].append((main, uri))
for fn, entries in self.indexentries.iteritems():
# new entry types must be listed in directives/other.py!
- for type, value, tid, alias in entries:
- if type == 'single':
- try:
- entry, subentry = value.split(';', 1)
- except ValueError:
- entry, subentry = value, ''
- if not entry:
- self.warn(fn, 'invalid index entry %r' % value)
- continue
- add_entry(entry.strip(), subentry.strip())
- elif type == 'pair':
- try:
- first, second = map(lambda x: x.strip(),
- value.split(';', 1))
- if not first or not second:
- raise ValueError
- except ValueError:
- self.warn(fn, 'invalid pair index entry %r' % value)
- continue
- add_entry(first, second)
- add_entry(second, first)
- elif type == 'triple':
- try:
- first, second, third = map(lambda x: x.strip(),
- value.split(';', 2))
- if not first or not second or not third:
- raise ValueError
- except ValueError:
- self.warn(fn, 'invalid triple index entry %r' % value)
- continue
- add_entry(first, second+' '+third)
- add_entry(second, third+', '+first)
- add_entry(third, first+' '+second)
- else:
- self.warn(fn, 'unknown index entry type %r' % type)
+ for type, value, tid, main in entries:
+ try:
+ if type == 'single':
+ try:
+ entry, subentry = split_into(2, 'single', value)
+ except ValueError:
+ entry, = split_into(1, 'single', value)
+ subentry = ''
+ add_entry(entry, subentry)
+ elif type == 'pair':
+ first, second = split_into(2, 'pair', value)
+ add_entry(first, second)
+ add_entry(second, first)
+ elif type == 'triple':
+ first, second, third = split_into(3, 'triple', value)
+ add_entry(first, second+' '+third)
+ add_entry(second, third+', '+first)
+ add_entry(third, first+' '+second)
+ elif type == 'see':
+ first, second = split_into(2, 'see', value)
+ add_entry(first, _('see %s') % second, link=False)
+ elif type == 'seealso':
+ first, second = split_into(2, 'see', value)
+ add_entry(first, _('see also %s') % second, link=False)
+ else:
+ self.warn(fn, 'unknown index entry type %r' % type)
+ except ValueError, err:
+ self.warn(fn, str(err))
# sort the index entries; put all symbols at the front, even those
# following the letters in ASCII, this is where the chr(127) comes from
def keyfunc(entry, lcletters=string.ascii_lowercase + '_'):
- lckey = entry[0].lower()
+ lckey = unicodedata.normalize('NFD', entry[0].lower())
if lckey[0:1] in lcletters:
return chr(127) + lckey
return lckey
newlist = new.items()
newlist.sort(key=keyfunc)
- # fixup entries: transform
- # func() (in module foo)
- # func() (in module bar)
- # into
- # func()
- # (in module foo)
- # (in module bar)
- oldkey = ''
- oldsubitems = None
- i = 0
- while i < len(newlist):
- key, (targets, subitems) = newlist[i]
- # cannot move if it has subitems; structure gets too complex
- if not subitems:
- m = _fixre.match(key)
- if m:
- if oldkey == m.group(1):
- # prefixes match: add entry as subitem of the
- # previous entry
- oldsubitems.setdefault(m.group(2), [[], {}])[0].\
- extend(targets)
- del newlist[i]
- continue
- oldkey = m.group(1)
- else:
- oldkey = key
- oldsubitems = subitems
- i += 1
+ if group_entries:
+ # fixup entries: transform
+ # func() (in module foo)
+ # func() (in module bar)
+ # into
+ # func()
+ # (in module foo)
+ # (in module bar)
+ oldkey = ''
+ oldsubitems = None
+ i = 0
+ while i < len(newlist):
+ key, (targets, subitems) = newlist[i]
+ # cannot move if it has subitems; structure gets too complex
+ if not subitems:
+ m = _fixre.match(key)
+ if m:
+ if oldkey == m.group(1):
+ # prefixes match: add entry as subitem of the
+ # previous entry
+ oldsubitems.setdefault(m.group(2), [[], {}])[0].\
+ extend(targets)
+ del newlist[i]
+ continue
+ oldkey = m.group(1)
+ else:
+ oldkey = key
+ oldsubitems = subitems
+ i += 1
# group the entries by letter
def keyfunc2(item, letters=string.ascii_uppercase + '_'):
@@ -1577,7 +1613,7 @@ class BuildEnvironment:
k, v = item
v[1] = sorted((si, se) for (si, (se, void)) in v[1].iteritems())
# now calculate the key
- letter = k[0].upper()
+ letter = unicodedata.normalize('NFD', k[0])[0].upper()
if letter in letters:
return letter
else:
diff --git a/sphinx/errors.py b/sphinx/errors.py
index b614d9ab..bfc2be37 100644
--- a/sphinx/errors.py
+++ b/sphinx/errors.py
@@ -6,7 +6,7 @@
Contains SphinxError and a few subclasses (in an extra module to avoid
circular import problems).
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/ext/__init__.py b/sphinx/ext/__init__.py
index 31b6f2ae..07269dc0 100644
--- a/sphinx/ext/__init__.py
+++ b/sphinx/ext/__init__.py
@@ -5,6 +5,6 @@
Contains Sphinx features not activated by default.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/ext/autodoc.py b/sphinx/ext/autodoc.py
index 3a2476a6..044a181f 100644
--- a/sphinx/ext/autodoc.py
+++ b/sphinx/ext/autodoc.py
@@ -7,13 +7,14 @@
the doctree, thus avoiding duplication between docstrings and documentation
for those who like elaborate docstrings.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import re
import sys
import inspect
+import traceback
from types import FunctionType, BuiltinFunctionType, MethodType
from docutils import nodes
@@ -103,7 +104,7 @@ class AutodocReporter(object):
return getattr(self.reporter, name)
def system_message(self, level, message, *children, **kwargs):
- if 'line' in kwargs:
+ if 'line' in kwargs and 'source' not in kwargs:
try:
source, line = self.viewlist.items[kwargs['line']]
except IndexError:
@@ -216,6 +217,8 @@ class Documenter(object):
priority = 0
#: order if autodoc_member_order is set to 'groupwise'
member_order = 0
+ #: true if the generated content may contain titles
+ titles_allowed = False
option_spec = {'noindex': bool_option}
@@ -327,10 +330,13 @@ class Documenter(object):
# this used to only catch SyntaxError, ImportError and AttributeError,
# but importing modules with side effects can raise all kinds of errors
except Exception, err:
+ if self.env.app and not self.env.app.quiet:
+ self.env.app.info(traceback.format_exc().rstrip())
self.directive.warn(
'autodoc can\'t import/find %s %r, it reported error: '
'"%s", please check your spelling and sys.path' %
(self.objtype, str(self.fullname), err))
+ self.env.note_reread()
return False
def get_real_modname(self):
@@ -412,15 +418,15 @@ class Documenter(object):
# etc. don't support a prepended module name
self.add_line(u' :module: %s' % self.modname, '<autodoc>')
- def get_doc(self, encoding=None):
+ def get_doc(self, encoding=None, ignore=1):
"""Decode and return lines of the docstring(s) for the object."""
docstring = self.get_attr(self.object, '__doc__', None)
# make sure we have Unicode docstrings, then sanitize and split
# into lines
if isinstance(docstring, unicode):
- return [prepare_docstring(docstring)]
+ return [prepare_docstring(docstring, ignore)]
elif docstring:
- return [prepare_docstring(force_decode(docstring, encoding))]
+ return [prepare_docstring(force_decode(docstring, encoding), ignore)]
return []
def process_doc(self, docstrings):
@@ -461,6 +467,11 @@ class Documenter(object):
if not no_docstring:
encoding = self.analyzer and self.analyzer.encoding
docstrings = self.get_doc(encoding)
+ if not docstrings:
+ # append at least a dummy docstring, so that the event
+ # autodoc-process-docstring is fired and can add some
+ # content if desired
+ docstrings.append([])
for i, line in enumerate(self.process_doc(docstrings)):
self.add_line(line, sourcename, i)
@@ -518,8 +529,11 @@ class Documenter(object):
Members are skipped if
- - they are private (except if given explicitly)
- - they are undocumented (except if undoc-members is given)
+ - they are private (except if given explicitly or the private-members
+ option is set)
+ - they are special methods (except if given explicitly or the
+ special-members option is set)
+ - they are undocumented (except if the undoc-members option is set)
The user can override the skipping decision by connecting to the
``autodoc-skip-member`` event.
@@ -539,9 +553,13 @@ class Documenter(object):
# if isattr is True, the member is documented as an attribute
isattr = False
- if want_all and membername.startswith('_'):
+ if want_all and membername.startswith('__') and \
+ membername.endswith('__') and len(membername) > 4:
+ # special __methods__
+ skip = not self.options.special_members
+ elif want_all and membername.startswith('_'):
# ignore members whose name starts with _ by default
- skip = True
+ skip = not self.options.private_members
elif (namespace, membername) in attr_docs:
# keep documented attributes
skip = False
@@ -664,7 +682,7 @@ class Documenter(object):
# parse right now, to get PycodeErrors on parsing (results will
# be cached anyway)
self.analyzer.find_attr_docs()
- except PycodeError, err:
+ except PycodeError:
# no source file -- e.g. for builtin and C modules
self.analyzer = None
# at least add the module.__file__ as a dependency
@@ -681,7 +699,7 @@ class Documenter(object):
# make sure that the result starts with an empty line. This is
# necessary for some situations where another directive preprocesses
# reST and no starting newline is present
- self.add_line(u'', '')
+ self.add_line(u'', '<autodoc>')
# format the object's signature, if any
sig = self.format_signature()
@@ -706,6 +724,7 @@ class ModuleDocumenter(Documenter):
"""
objtype = 'module'
content_indent = u''
+ titles_allowed = True
option_spec = {
'members': members_option, 'undoc-members': bool_option,
@@ -713,6 +732,7 @@ class ModuleDocumenter(Documenter):
'show-inheritance': bool_option, 'synopsis': identity,
'platform': identity, 'deprecated': bool_option,
'member-order': identity, 'exclude-members': members_set_option,
+ 'private-members': bool_option, 'special-members': bool_option,
}
@classmethod
@@ -819,7 +839,53 @@ class ClassLevelDocumenter(Documenter):
return modname, parents + [base]
-class FunctionDocumenter(ModuleLevelDocumenter):
+class DocstringSignatureMixin(object):
+ """
+ Mixin for FunctionDocumenter and MethodDocumenter to provide the
+ feature of reading the signature from the docstring.
+ """
+
+ def _find_signature(self, encoding=None):
+ docstrings = Documenter.get_doc(self, encoding, 2)
+ if len(docstrings) != 1:
+ return
+ doclines = docstrings[0]
+ setattr(self, '__new_doclines', doclines)
+ if not doclines:
+ return
+ # match first line of docstring against signature RE
+ match = py_ext_sig_re.match(doclines[0])
+ if not match:
+ return
+ exmod, path, base, args, retann = match.groups()
+ # the base name must match ours
+ if not self.objpath or base != self.objpath[-1]:
+ return
+ # ok, now jump over remaining empty lines and set the remaining
+ # lines as the new doclines
+ i = 1
+ while i < len(doclines) and not doclines[i].strip():
+ i += 1
+ setattr(self, '__new_doclines', doclines[i:])
+ return args, retann
+
+ def get_doc(self, encoding=None, ignore=1):
+ lines = getattr(self, '__new_doclines', None)
+ if lines is not None:
+ return [lines]
+ return Documenter.get_doc(self, encoding, ignore)
+
+ def format_signature(self):
+ if self.args is None and self.env.config.autodoc_docstring_signature:
+ # only act if a signature is not explicitly given already, and if
+ # the feature is enabled
+ result = self._find_signature()
+ if result is not None:
+ self.args, self.retann = result
+ return Documenter.format_signature(self)
+
+
+class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter):
"""
Specialized Documenter subclass for functions.
"""
@@ -833,8 +899,8 @@ class FunctionDocumenter(ModuleLevelDocumenter):
def format_args(self):
if inspect.isbuiltin(self.object) or \
inspect.ismethoddescriptor(self.object):
- # can never get arguments of a C function or method
- return None
+ # cannot introspect arguments of a C function or method
+ pass
try:
argspec = inspect.getargspec(self.object)
except TypeError:
@@ -867,6 +933,7 @@ class ClassDocumenter(ModuleLevelDocumenter):
'noindex': bool_option, 'inherited-members': bool_option,
'show-inheritance': bool_option, 'member-order': identity,
'exclude-members': members_set_option,
+ 'private-members': bool_option, 'special-members': bool_option,
}
@classmethod
@@ -923,13 +990,13 @@ class ClassDocumenter(ModuleLevelDocumenter):
self.add_line(_(u' Bases: %s') % ', '.join(bases),
'<autodoc>')
- def get_doc(self, encoding=None):
+ def get_doc(self, encoding=None, ignore=1):
content = self.env.config.autoclass_content
docstrings = []
- docstring = self.get_attr(self.object, '__doc__', None)
- if docstring:
- docstrings.append(docstring)
+ attrdocstring = self.get_attr(self.object, '__doc__', None)
+ if attrdocstring:
+ docstrings.append(attrdocstring)
# for classes, what the "docstring" is can be controlled via a
# config value; the default is only the class docstring
@@ -999,7 +1066,7 @@ class DataDocumenter(ModuleLevelDocumenter):
pass
-class MethodDocumenter(ClassLevelDocumenter):
+class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter):
"""
Specialized Documenter subclass for methods (normal, static and class).
"""
@@ -1082,6 +1149,10 @@ class AttributeDocumenter(ClassLevelDocumenter):
def document_members(self, all_members=False):
pass
+ def get_real_modname(self):
+ return self.get_attr(self.parent or self.object, '__module__', None) \
+ or self.modname
+
class InstanceAttributeDocumenter(AttributeDocumenter):
"""
@@ -1133,8 +1204,10 @@ class AutoDirective(Directive):
_special_attrgetters = {}
# flags that can be given in autodoc_default_flags
- _default_flags = set(['members', 'undoc-members', 'inherited-members',
- 'show-inheritance'])
+ _default_flags = set([
+ 'members', 'undoc-members', 'inherited-members', 'show-inheritance',
+ 'private-members', 'special-members',
+ ])
# standard docutils directive settings
has_content = True
@@ -1186,7 +1259,7 @@ class AutoDirective(Directive):
self.state.memo.reporter = AutodocReporter(self.result,
self.state.memo.reporter)
- if self.name == 'automodule':
+ if documenter.titles_allowed:
node = nodes.section()
# necessary so that the child nodes get the right source/line set
node.document = self.state.document
@@ -1224,6 +1297,7 @@ def setup(app):
app.add_config_value('autoclass_content', 'class', True)
app.add_config_value('autodoc_member_order', 'alphabetic', True)
app.add_config_value('autodoc_default_flags', [], True)
+ app.add_config_value('autodoc_docstring_signature', True, True)
app.add_event('autodoc-process-docstring')
app.add_event('autodoc-process-signature')
app.add_event('autodoc-skip-member')
diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py
index 8186a2e5..e0271697 100644
--- a/sphinx/ext/autosummary/__init__.py
+++ b/sphinx/ext/autosummary/__init__.py
@@ -49,7 +49,7 @@
resolved to a Python object, and otherwise it becomes simple emphasis.
This can be used as the default role to make links 'smart'.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -481,12 +481,14 @@ def setup(app):
html=(autosummary_toc_visit_html, autosummary_noop),
latex=(autosummary_noop, autosummary_noop),
text=(autosummary_noop, autosummary_noop),
- man=(autosummary_noop, autosummary_noop))
+ man=(autosummary_noop, autosummary_noop),
+ texinfo=(autosummary_noop, autosummary_noop))
app.add_node(autosummary_table,
html=(autosummary_table_visit_html, autosummary_noop),
latex=(autosummary_noop, autosummary_noop),
text=(autosummary_noop, autosummary_noop),
- man=(autosummary_noop, autosummary_noop))
+ man=(autosummary_noop, autosummary_noop),
+ texinfo=(autosummary_noop, autosummary_noop))
app.add_directive('autosummary', Autosummary)
app.add_role('autolink', autolink_role)
app.connect('doctree-read', process_autosummary_toc)
diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py
index 4b6348b5..389d55a5 100644
--- a/sphinx/ext/autosummary/generate.py
+++ b/sphinx/ext/autosummary/generate.py
@@ -12,9 +12,9 @@
Example Makefile rule::
generate:
- sphinx-autogen source/*.rst source/generated
+ sphinx-autogen -o source/generated source/*.rst
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/ext/coverage.py b/sphinx/ext/coverage.py
index f41820e2..e3e3a65e 100644
--- a/sphinx/ext/coverage.py
+++ b/sphinx/ext/coverage.py
@@ -6,7 +6,7 @@
Check Python modules and C API for coverage. Mostly written by Josip
Dzolonga for the Google Highly Open Participation contest.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -105,7 +105,8 @@ class CoverageBuilder(Builder):
output_file = path.join(self.outdir, 'c.txt')
op = open(output_file, 'w')
try:
- write_header(op, 'Undocumented C API elements', '=')
+ if self.config.coverage_write_headline:
+ write_header(op, 'Undocumented C API elements', '=')
op.write('\n')
for filename, undoc in self.c_undoc.iteritems():
@@ -120,6 +121,8 @@ class CoverageBuilder(Builder):
objects = self.env.domaindata['py']['objects']
modules = self.env.domaindata['py']['modules']
+ skip_undoc = self.config.coverage_skip_undoc_in_source
+
for mod_name in modules:
ignore = False
for exp in self.mod_ignorexps:
@@ -160,6 +163,8 @@ class CoverageBuilder(Builder):
if exp.match(name):
break
else:
+ if skip_undoc and not obj.__doc__:
+ continue
funcs.append(name)
elif inspect.isclass(obj):
for exp in self.cls_ignorexps:
@@ -167,6 +172,8 @@ class CoverageBuilder(Builder):
break
else:
if full_name not in objects:
+ if skip_undoc and not obj.__doc__:
+ continue
# not documented at all
classes[name] = []
continue
@@ -174,13 +181,18 @@ class CoverageBuilder(Builder):
attrs = []
for attr_name in dir(obj):
+ if attr_name not in obj.__dict__:
+ continue
attr = getattr(obj, attr_name)
- for attr_name, attr in inspect.getmembers(
- obj, lambda x: inspect.ismethod(x) or \
- inspect.isfunction(x)):
+ if not (inspect.ismethod(attr) or
+ inspect.isfunction(attr)):
+ continue
if attr_name[0] == '_':
# starts with an underscore, ignore it
continue
+ if skip_undoc and not attr.__doc__:
+ # skip methods without docstring if wished
+ continue
full_attr_name = '%s.%s' % (full_name, attr_name)
if full_attr_name not in objects:
@@ -197,8 +209,8 @@ class CoverageBuilder(Builder):
op = open(output_file, 'w')
failed = []
try:
- write_header(op, 'Undocumented Python objects', '=')
-
+ if self.config.coverage_write_headline:
+ write_header(op, 'Undocumented Python objects', '=')
keys = self.py_undoc.keys()
keys.sort()
for name in keys:
@@ -248,3 +260,5 @@ def setup(app):
app.add_config_value('coverage_c_path', [], False)
app.add_config_value('coverage_c_regexes', {}, False)
app.add_config_value('coverage_ignore_c_items', {}, False)
+ app.add_config_value('coverage_write_headline', True, False)
+ app.add_config_value('coverage_skip_undoc_in_source', False, False)
diff --git a/sphinx/ext/doctest.py b/sphinx/ext/doctest.py
index 62fbfdff..5fd8d114 100644
--- a/sphinx/ext/doctest.py
+++ b/sphinx/ext/doctest.py
@@ -6,7 +6,7 @@
Mimic doctest by automatically executing code snippets and checking
their results.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -56,7 +56,7 @@ class TestDirective(Directive):
test = code
code = doctestopt_re.sub('', code)
nodetype = nodes.literal_block
- if self.name == 'testsetup' or 'hide' in self.options:
+ if self.name in ('testsetup', 'testcleanup') or 'hide' in self.options:
nodetype = nodes.comment
if self.arguments:
groups = [x.strip() for x in self.arguments[0].split(',')]
@@ -86,6 +86,9 @@ class TestDirective(Directive):
class TestsetupDirective(TestDirective):
option_spec = {}
+class TestcleanupDirective(TestDirective):
+ option_spec = {}
+
class DoctestDirective(TestDirective):
option_spec = {
'hide': directives.flag,
@@ -113,6 +116,7 @@ class TestGroup(object):
self.name = name
self.setup = []
self.tests = []
+ self.cleanup = []
def add_code(self, code, prepend=False):
if code.type == 'testsetup':
@@ -120,6 +124,8 @@ class TestGroup(object):
self.setup.insert(0, code)
else:
self.setup.append(code)
+ elif code.type == 'testcleanup':
+ self.cleanup.append(code)
elif code.type == 'doctest':
self.tests.append([code])
elif code.type == 'testcode':
@@ -131,8 +137,8 @@ class TestGroup(object):
raise RuntimeError('invalid TestCode type')
def __repr__(self):
- return 'TestGroup(name=%r, setup=%r, tests=%r)' % (
- self.name, self.setup, self.tests)
+ return 'TestGroup(name=%r, setup=%r, cleanup=%r, tests=%r)' % (
+ self.name, self.setup, self.cleanup, self.tests)
class TestCode(object):
@@ -204,6 +210,8 @@ class DocTestBuilder(Builder):
self.total_tries = 0
self.setup_failures = 0
self.setup_tries = 0
+ self.cleanup_failures = 0
+ self.cleanup_tries = 0
date = time.strftime('%Y-%m-%d %H:%M:%S')
@@ -240,12 +248,14 @@ Doctest summary
%5d test%s
%5d failure%s in tests
%5d failure%s in setup code
+%5d failure%s in cleanup code
''' % (self.total_tries, s(self.total_tries),
self.total_failures, s(self.total_failures),
- self.setup_failures, s(self.setup_failures)))
+ self.setup_failures, s(self.setup_failures),
+ self.cleanup_failures, s(self.cleanup_failures)))
self.outfile.close()
- if self.total_failures or self.setup_failures:
+ if self.total_failures or self.setup_failures or self.cleanup_failures:
self.app.statuscode = 1
def write(self, build_docnames, updated_docnames, method='update'):
@@ -265,6 +275,12 @@ Doctest summary
optionflags=self.opt)
self.test_runner = SphinxDocTestRunner(verbose=False,
optionflags=self.opt)
+ self.cleanup_runner = SphinxDocTestRunner(verbose=False,
+ optionflags=self.opt)
+
+ self.test_runner._fakeout = self.setup_runner._fakeout
+ self.cleanup_runner._fakeout = self.setup_runner._fakeout
+
if self.config.doctest_test_doctest_blocks:
def condition(node):
return (isinstance(node, (nodes.literal_block, nodes.comment))
@@ -298,6 +314,11 @@ Doctest summary
'testsetup', lineno=0)
for group in groups.itervalues():
group.add_code(code, prepend=True)
+ if self.config.doctest_global_cleanup:
+ code = TestCode(self.config.doctest_global_cleanup,
+ 'testcleanup', lineno=0)
+ for group in groups.itervalues():
+ group.add_code(code)
if not groups:
return
@@ -313,29 +334,42 @@ Doctest summary
res_f, res_t = self.test_runner.summarize(self._out, verbose=True)
self.total_failures += res_f
self.total_tries += res_t
+ if self.cleanup_runner.tries:
+ res_f, res_t = self.cleanup_runner.summarize(self._out, verbose=True)
+ self.cleanup_failures += res_f
+ self.cleanup_tries += res_t
def compile(self, code, name, type, flags, dont_inherit):
return compile(code, name, self.type, flags, dont_inherit)
def test_group(self, group, filename):
ns = {}
- setup_examples = []
- for setup in group.setup:
- setup_examples.append(doctest.Example(setup.code, '',
- lineno=setup.lineno))
- if setup_examples:
- # simulate a doctest with the setup code
- setup_doctest = doctest.DocTest(setup_examples, {},
- '%s (setup code)' % group.name,
- filename, 0, None)
- setup_doctest.globs = ns
- old_f = self.setup_runner.failures
- self.type = 'exec' # the snippet may contain multiple statements
- self.setup_runner.run(setup_doctest, out=self._warn_out,
- clear_globs=False)
- if self.setup_runner.failures > old_f:
- # don't run the group
+
+ def run_setup_cleanup(runner, testcodes, what):
+ examples = []
+ for testcode in testcodes:
+ examples.append(doctest.Example(testcode.code, '',
+ lineno=testcode.lineno))
+ if not examples:
return
+ # simulate a doctest with the code
+ sim_doctest = doctest.DocTest(examples, {},
+ '%s (%s code)' % (group.name, what),
+ filename, 0, None)
+ sim_doctest.globs = ns
+ old_f = runner.failures
+ self.type = 'exec' # the snippet may contain multiple statements
+ runner.run(sim_doctest, out=self._warn_out, clear_globs=False)
+ if runner.failures > old_f:
+ return False
+ return True
+
+ # run the setup code
+ if not run_setup_cleanup(self.setup_runner, group.setup, 'setup'):
+ # if setup failed, don't run the group
+ return
+
+ # run the tests
for code in group.tests:
if len(code) == 1:
# ordinary doctests (code/output interleaved)
@@ -373,9 +407,13 @@ Doctest summary
# also don't clear the globs namespace after running the doctest
self.test_runner.run(test, out=self._warn_out, clear_globs=False)
+ # run the cleanup
+ run_setup_cleanup(self.cleanup_runner, group.cleanup, 'cleanup')
+
def setup(app):
app.add_directive('testsetup', TestsetupDirective)
+ app.add_directive('testcleanup', TestcleanupDirective)
app.add_directive('doctest', DoctestDirective)
app.add_directive('testcode', TestcodeDirective)
app.add_directive('testoutput', TestoutputDirective)
@@ -384,3 +422,4 @@ def setup(app):
app.add_config_value('doctest_path', [], False)
app.add_config_value('doctest_test_doctest_blocks', 'default', False)
app.add_config_value('doctest_global_setup', '', False)
+ app.add_config_value('doctest_global_cleanup', '', False)
diff --git a/sphinx/ext/extlinks.py b/sphinx/ext/extlinks.py
index 9a29918a..910354a2 100644
--- a/sphinx/ext/extlinks.py
+++ b/sphinx/ext/extlinks.py
@@ -20,7 +20,7 @@
You can also give an explicit caption, e.g. :exmpl:`Foo <foo>`.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/ext/graphviz.py b/sphinx/ext/graphviz.py
index f44e5136..8f7744b2 100644
--- a/sphinx/ext/graphviz.py
+++ b/sphinx/ext/graphviz.py
@@ -6,7 +6,7 @@
Allow graphviz-formatted graphs to be included in Sphinx-generated
documents inline.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -287,7 +287,8 @@ def render_dot_latex(self, node, code, options, prefix='graphviz'):
self.body.append('\n\\end{center}')
self.body.append('\n\\end{figure}\n')
else:
- self.body.append('%s\\includegraphics{%s}' % (para_separator, fname, para_separator))
+ self.body.append('%s\\includegraphics{%s}' %
+ (para_separator, fname, para_separator))
raise nodes.SkipNode
diff --git a/sphinx/ext/ifconfig.py b/sphinx/ext/ifconfig.py
index cdb6e2c3..5698ca28 100644
--- a/sphinx/ext/ifconfig.py
+++ b/sphinx/ext/ifconfig.py
@@ -16,7 +16,7 @@
namespace of the project configuration (that is, all variables from
``conf.py`` are available.)
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/ext/inheritance_diagram.py b/sphinx/ext/inheritance_diagram.py
index 3f6f0b4d..29439f7c 100644
--- a/sphinx/ext/inheritance_diagram.py
+++ b/sphinx/ext/inheritance_diagram.py
@@ -32,7 +32,7 @@ r"""
The graph is inserted as a PNG+image map into HTML and a PDF in
LaTeX.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -66,7 +66,8 @@ class InheritanceGraph(object):
from all the way to the root "object", and then is able to generate a
graphviz dot graph from them.
"""
- def __init__(self, class_names, currmodule, show_builtins=False, parts=0):
+ def __init__(self, class_names, currmodule, show_builtins=False,
+ private_bases=False, parts=0):
"""*class_names* is a list of child classes to show bases from.
If *show_builtins* is True, then Python builtins will be shown
@@ -74,7 +75,8 @@ class InheritanceGraph(object):
"""
self.class_names = class_names
classes = self._import_classes(class_names, currmodule)
- self.class_info = self._class_info(classes, show_builtins, parts)
+ self.class_info = self._class_info(classes, show_builtins,
+ private_bases, parts)
if not self.class_info:
raise InheritanceException('No classes found for '
'inheritance diagram')
@@ -131,7 +133,7 @@ class InheritanceGraph(object):
classes.extend(self._import_class_or_module(name, currmodule))
return classes
- def _class_info(self, classes, show_builtins, parts):
+ def _class_info(self, classes, show_builtins, private_bases, parts):
"""Return name and bases for all classes that are ancestors of
*classes*.
@@ -144,6 +146,8 @@ class InheritanceGraph(object):
def recurse(cls):
if not show_builtins and cls in builtins:
return
+ if not private_bases and cls.__name__.startswith('_'):
+ return
nodename = self.class_name(cls, parts)
fullname = self.class_name(cls, 0)
@@ -152,7 +156,9 @@ class InheritanceGraph(object):
all_classes[cls] = (nodename, fullname, baselist)
for base in cls.__bases__:
if not show_builtins and base in builtins:
- return
+ continue
+ if not private_bases and base.__name__.startswith('_'):
+ continue
baselist.append(self.class_name(base, parts))
if base not in all_classes:
recurse(base)
@@ -268,6 +274,7 @@ class InheritanceDiagram(Directive):
final_argument_whitespace = True
option_spec = {
'parts': directives.nonnegative_int,
+ 'private-bases': directives.flag,
}
def run(self):
@@ -284,7 +291,8 @@ class InheritanceDiagram(Directive):
try:
graph = InheritanceGraph(
class_names, env.temp_data.get('py:module'),
- parts=node['parts'])
+ parts=node['parts'],
+ private_bases='private-bases' in self.options)
except InheritanceException, err:
return [node.document.reporter.warning(err.args[0],
line=self.lineno)]
@@ -357,7 +365,8 @@ def setup(app):
latex=(latex_visit_inheritance_diagram, None),
html=(html_visit_inheritance_diagram, None),
text=(skip, None),
- man=(skip, None))
+ man=(skip, None),
+ texinfo=(skip, None))
app.add_directive('inheritance-diagram', InheritanceDiagram)
app.add_config_value('inheritance_graph_attrs', {}, False),
app.add_config_value('inheritance_node_attrs', {}, False),
diff --git a/sphinx/ext/intersphinx.py b/sphinx/ext/intersphinx.py
index 442617e1..709428a3 100644
--- a/sphinx/ext/intersphinx.py
+++ b/sphinx/ext/intersphinx.py
@@ -20,7 +20,7 @@
also be specified individually, e.g. if the docs should be buildable
without Internet access.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/ext/jsmath.py b/sphinx/ext/jsmath.py
index 8277caa5..bad8225b 100644
--- a/sphinx/ext/jsmath.py
+++ b/sphinx/ext/jsmath.py
@@ -6,7 +6,7 @@
Set up everything for use of JSMath to display math in HTML
via JavaScript.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/ext/mathbase.py b/sphinx/ext/mathbase.py
index 8a5b75d3..c9f993ae 100644
--- a/sphinx/ext/mathbase.py
+++ b/sphinx/ext/mathbase.py
@@ -5,13 +5,14 @@
Set up math support in source files and LaTeX/text output.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from docutils import nodes, utils
from docutils.parsers.rst import directives
+from sphinx.writers import texinfo
from sphinx.util.compat import Directive
@@ -69,6 +70,8 @@ class MathDirective(Directive):
node['nowrap'] = 'nowrap' in self.options
node['docname'] = self.state.document.settings.env.docname
ret = [node]
+ node.line = self.lineno
+ node.source = self.src
if node['label']:
tnode = nodes.target('', '', ids=['equation-' + node['label']])
self.state.document.note_explicit_target(tnode)
@@ -122,6 +125,20 @@ def man_visit_eqref(self, node):
raise nodes.SkipNode
+def texinfo_visit_math(self, node):
+ self.body.append('@math{' + texinfo.escape_arg(node['latex']) + '}')
+ raise nodes.SkipNode
+
+def texinfo_visit_displaymath(self, node):
+ self.visit_paragraph(node)
+def texinfo_depart_displaymath(self, node):
+ self.depart_paragraph(node)
+
+def texinfo_visit_eqref(self, node):
+ self.body.append(node['target'])
+ raise nodes.SkipNode
+
+
def html_visit_eqref(self, node):
self.body.append('<a href="#equation-%s">' % node['target'])
@@ -148,20 +165,23 @@ def number_equations(app, doctree, docname):
def setup_math(app, htmlinlinevisitors, htmldisplayvisitors):
app.add_node(math,
- latex=(latex_visit_math, None),
- text=(text_visit_math, None),
- man=(man_visit_math, None),
- html=htmlinlinevisitors)
+ latex=(latex_visit_math, None),
+ text=(text_visit_math, None),
+ man=(man_visit_math, None),
+ texinfo=(texinfo_visit_math, None),
+ html=htmlinlinevisitors)
app.add_node(displaymath,
- latex=(latex_visit_displaymath, None),
- text=(text_visit_displaymath, None),
- man=(man_visit_displaymath, man_depart_displaymath),
- html=htmldisplayvisitors)
+ latex=(latex_visit_displaymath, None),
+ text=(text_visit_displaymath, None),
+ man=(man_visit_displaymath, man_depart_displaymath),
+ texinfo=(texinfo_visit_displaymath, texinfo_depart_displaymath),
+ html=htmldisplayvisitors)
app.add_node(eqref,
- latex=(latex_visit_eqref, None),
- text=(text_visit_eqref, None),
- man=(man_visit_eqref, None),
- html=(html_visit_eqref, html_depart_eqref))
+ latex=(latex_visit_eqref, None),
+ text=(text_visit_eqref, None),
+ man=(man_visit_eqref, None),
+ texinfo=(texinfo_visit_eqref, None),
+ html=(html_visit_eqref, html_depart_eqref))
app.add_role('math', math_role)
app.add_role('eq', eq_role)
app.add_directive('math', MathDirective)
diff --git a/sphinx/ext/mathjax.py b/sphinx/ext/mathjax.py
new file mode 100644
index 00000000..e8d2d485
--- /dev/null
+++ b/sphinx/ext/mathjax.py
@@ -0,0 +1,67 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.ext.mathjax
+ ~~~~~~~~~~~~~~~~~~
+
+ Allow `MathJax <http://mathjax.org/>`_ to be used to display math in
+ Sphinx's HTML writer -- requires the MathJax JavaScript library on your
+ webserver/computer.
+
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+from docutils import nodes
+
+from sphinx.application import ExtensionError
+from sphinx.ext.mathbase import setup_math as mathbase_setup
+
+
+def html_visit_math(self, node):
+ self.body.append(self.starttag(node, 'span', '', CLASS='math'))
+ self.body.append(self.builder.config.mathjax_inline[0] +
+ self.encode(node['latex']) +
+ self.builder.config.mathjax_inline[1] + '</span>')
+ raise nodes.SkipNode
+
+def html_visit_displaymath(self, node):
+ self.body.append(self.starttag(node, 'div', CLASS='math'))
+ if node['nowrap']:
+ self.body.append(self.builder.config.mathjax_display[0] +
+ node['latex'] +
+ self.builder.config.mathjax_display[1])
+ self.body.append('</div>')
+ raise nodes.SkipNode
+
+ parts = [prt for prt in node['latex'].split('\n\n') if prt.strip()]
+ for i, part in enumerate(parts):
+ part = self.encode(part)
+ if i == 0:
+ # necessary to e.g. set the id property correctly
+ if node['number']:
+ self.body.append('<span class="eqno">(%s)</span>' %
+ node['number'])
+ if '&' in part or '\\\\' in part:
+ self.body.append(self.builder.config.mathjax_display[0] +
+ '\\begin{split}' + part + '\\end{split}' +
+ self.builder.config.mathjax_display[1])
+ else:
+ self.body.append(self.builder.config.mathjax_display[0] + part +
+ self.builder.config.mathjax_display[1])
+ self.body.append('</div>\n')
+ raise nodes.SkipNode
+
+def builder_inited(app):
+ if not app.config.mathjax_path:
+ raise ExtensionError('mathjax_path config value must be set for the '
+ 'mathjax extension to work')
+ app.add_javascript(app.config.mathjax_path)
+
+
+def setup(app):
+ mathbase_setup(app, (html_visit_math, None), (html_visit_displaymath, None))
+ app.add_config_value('mathjax_path', '', False)
+ app.add_config_value('mathjax_inline', [r'\(', r'\)'], 'html')
+ app.add_config_value('mathjax_display', [r'\[', r'\]'], 'html')
+ app.connect('builder-inited', builder_inited)
+
diff --git a/sphinx/ext/oldcmarkup.py b/sphinx/ext/oldcmarkup.py
index bc921a23..41969c36 100644
--- a/sphinx/ext/oldcmarkup.py
+++ b/sphinx/ext/oldcmarkup.py
@@ -5,7 +5,7 @@
Extension for compatibility with old C markup (directives and roles).
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/ext/pngmath.py b/sphinx/ext/pngmath.py
index e4e7c2d0..015a4904 100644
--- a/sphinx/ext/pngmath.py
+++ b/sphinx/ext/pngmath.py
@@ -5,7 +5,7 @@
Render math in HTML via dvipng.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/ext/refcounting.py b/sphinx/ext/refcounting.py
index 6ab9fe4b..a8dc721d 100644
--- a/sphinx/ext/refcounting.py
+++ b/sphinx/ext/refcounting.py
@@ -9,7 +9,7 @@
Usage: Set the `refcount_file` config value to the path to the reference
count data file.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/ext/todo.py b/sphinx/ext/todo.py
index a6c33202..6a44a9b4 100644
--- a/sphinx/ext/todo.py
+++ b/sphinx/ext/todo.py
@@ -8,7 +8,7 @@
all todos of your project and lists them along with a backlink to the
original location.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -159,7 +159,8 @@ def setup(app):
html=(visit_todo_node, depart_todo_node),
latex=(visit_todo_node, depart_todo_node),
text=(visit_todo_node, depart_todo_node),
- man=(visit_todo_node, depart_todo_node))
+ man=(visit_todo_node, depart_todo_node),
+ texinfo=(visit_todo_node, depart_todo_node))
app.add_directive('todo', Todo)
app.add_directive('todolist', TodoList)
diff --git a/sphinx/ext/viewcode.py b/sphinx/ext/viewcode.py
index b9bb9d77..020db697 100644
--- a/sphinx/ext/viewcode.py
+++ b/sphinx/ext/viewcode.py
@@ -5,7 +5,7 @@
Add links to module code in Python object descriptions.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -54,7 +54,7 @@ def doctree_read(app, doctree):
modname = signode.get('module')
if not modname:
continue
- fullname = signode['fullname']
+ fullname = signode.get('fullname')
if not has_tag(modname, fullname, env.docname):
continue
if fullname in names:
diff --git a/sphinx/highlighting.py b/sphinx/highlighting.py
index 6d710919..e84238e8 100644
--- a/sphinx/highlighting.py
+++ b/sphinx/highlighting.py
@@ -5,7 +5,7 @@
Highlight code blocks using Pygments.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -30,34 +30,14 @@ try:
from pygments.lexers import get_lexer_by_name, guess_lexer
from pygments.formatters import HtmlFormatter, LatexFormatter
from pygments.filters import ErrorToken
- from pygments.style import Style
from pygments.styles import get_style_by_name
- from pygments.styles.friendly import FriendlyStyle
- from pygments.token import Generic, Comment, Number
from pygments.util import ClassNotFound
+ from sphinx.pygments_styles import SphinxStyle, NoneStyle
except ImportError:
pygments = None
lexers = None
HtmlFormatter = LatexFormatter = None
else:
- class SphinxStyle(Style):
- """
- Like friendly, but a bit darker to enhance contrast on the green
- background.
- """
-
- background_color = '#eeffcc'
- default_style = ''
-
- styles = FriendlyStyle.styles
- styles.update({
- Generic.Output: '#333',
- Comment: 'italic #408090',
- Number: '#208050',
- })
-
- class NoneStyle(Style):
- """Style without any styling."""
lexers = dict(
none = TextLexer(),
@@ -207,7 +187,7 @@ class PygmentsBridge(object):
lexer = lexers[lang] = get_lexer_by_name(lang)
except ClassNotFound:
if warn:
- warn('Pygments lexer name %s is not known' % lang)
+ warn('Pygments lexer name %r is not known' % lang)
return self.unhighlighted(source)
else:
raise
diff --git a/sphinx/jinja2glue.py b/sphinx/jinja2glue.py
index 29ee334e..1c5390dc 100644
--- a/sphinx/jinja2glue.py
+++ b/sphinx/jinja2glue.py
@@ -5,7 +5,7 @@
Glue code for the jinja2 templating engine.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/locale/__init__.py b/sphinx/locale/__init__.py
index 126a37b5..3aca780c 100644
--- a/sphinx/locale/__init__.py
+++ b/sphinx/locale/__init__.py
@@ -5,7 +5,7 @@
Locale utilities.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -170,6 +170,7 @@ versionlabels = {
'deprecated': l_('Deprecated since version %s'),
}
+# XXX Python specific
pairindextypes = {
'module': l_('module'),
'keyword': l_('keyword'),
diff --git a/sphinx/locale/fa/LC_MESSAGES/sphinx.js b/sphinx/locale/fa/LC_MESSAGES/sphinx.js
new file mode 100644
index 00000000..c268479a
--- /dev/null
+++ b/sphinx/locale/fa/LC_MESSAGES/sphinx.js
@@ -0,0 +1 @@
+Documentation.addTranslations({"locale": "fa", "plural_expr": "(n > 1)", "messages": {"module, in ": "\u0645\u0627\u0698\u0648\u0644, \u062f\u0631", "Preparing search...": "...\u0622\u0645\u0627\u062f\u0647 \u062c\u0633\u062a\u062c\u0648", "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories.": ". \u0647\u06cc\u0686 \u0633\u0646\u062f\u06cc \u0628\u0631\u0627\u06cc \u062c\u0633\u062a\u062c\u0648\u06cc \u0634\u0645\u0627 \u06cc\u0627\u0641\u062a \u0646\u0634\u062f\u060c \u0644\u0637\u0641\u0627 \u0627\u0637\u0645\u06cc\u0646\u0627\u0646 \u062d\u0627\u0635\u0644 \u0646\u0645\u0627\u0626\u06cc\u062f \u06a9\u0647 \u0627\u0645\u0644\u0627\u06cc \u062a\u0645\u0627\u0645\u06cc \u06a9\u0644\u0645\u0627\u062a \u0635\u062d\u06cc\u062d \u0645\u06cc \u0628\u0627\u0634\u062f \u0648 \u0647\u0645\u0686\u0646\u06cc\u0646 \u0645\u0648\u0636\u0648\u0639\u0627\u062a \u06a9\u0627\u0641\u06cc \u0631\u0627 \u0628\u0631\u0627\u06cc \u0627\u06cc\u0646 \u062c\u0633\u062a\u062c\u0648 \u0627\u0646\u062a\u062e\u0627\u0628 \u0646\u0645\u0648\u062f\u0647 \u0627\u06cc\u062f", "Search finished, found %s page(s) matching the search query.": "\u062c\u0633\u062a\u062c\u0648 \u0627\u0646\u062c\u0627\u0645 \u0634\u062f \u060c %s \u0635\u0641\u062d\u0647 \u0645\u0637\u0627\u0628\u0642 \u0628\u0627 \u067e\u0631\u0633 \u0648 \u062c\u0648 \u067e\u06cc\u062f\u0627 \u0634\u062f", ", in ": ", \u062f\u0631", "Permalink to this headline": "\u0644\u06cc\u0646\u06a9 \u062b\u0627\u0628\u062a \u0628\u0647 \u0627\u06cc\u0646 \u0633\u0631 \u0645\u0642\u0627\u0644\u0647", "Searching": "\u062f\u0631 \u062d\u0627\u0644 \u062c\u0633\u062a\u062c\u0648", "Permalink to this definition": "\u0644\u06cc\u0646\u06a9 \u062b\u0627\u0628\u062a \u0628\u0647 \u0627\u06cc\u0646 \u062a\u0639\u0631\u06cc\u0641", "Hide Search Matches": "\u0639\u062f\u0645 \u0646\u0645\u0627\u06cc\u0634 \u0646\u062a\u0627\u06cc\u062c \u06cc\u0627\u0641\u062a \u0634\u062f\u0647", "Search Results": "\u0646\u062a\u0627\u06cc\u062c \u062c\u0633\u062a\u062c\u0648"}}); \ No newline at end of file
diff --git a/sphinx/locale/fa/LC_MESSAGES/sphinx.mo b/sphinx/locale/fa/LC_MESSAGES/sphinx.mo
new file mode 100644
index 00000000..c988b75c
--- /dev/null
+++ b/sphinx/locale/fa/LC_MESSAGES/sphinx.mo
Binary files differ
diff --git a/sphinx/locale/fa/LC_MESSAGES/sphinx.po b/sphinx/locale/fa/LC_MESSAGES/sphinx.po
new file mode 100644
index 00000000..fe75951e
--- /dev/null
+++ b/sphinx/locale/fa/LC_MESSAGES/sphinx.po
@@ -0,0 +1,596 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: Sphinx 1.0.3\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2008-11-09 19:46+0100\n"
+"PO-Revision-Date: \n"
+"Last-Translator: Omid Raha <omidraha.com@gmail.com>\n"
+"Language-Team: Omid Raha <omidraha.com@gmail.com>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1)\n"
+"X-Poedit-Language: Persian\n"
+"X-Poedit-Country: IRAN, ISLAMIC REPUBLIC OF\n"
+
+#: sphinx/builder.py:408
+#, python-format
+msgid "%b %d, %Y"
+msgstr ""
+
+#: sphinx/builder.py:427
+#: sphinx/templates/defindex.html:21
+msgid "General Index"
+msgstr "فهرست کلی"
+
+#: sphinx/builder.py:427
+msgid "index"
+msgstr "فهرست"
+
+#: sphinx/builder.py:429
+#: sphinx/htmlhelp.py:155
+#: sphinx/templates/defindex.html:19
+#: sphinx/templates/modindex.html:2
+#: sphinx/templates/modindex.html:13
+msgid "Global Module Index"
+msgstr "فهرست کلی ماژول ها"
+
+#: sphinx/builder.py:429
+msgid "modules"
+msgstr "ماژول ها"
+
+#: sphinx/builder.py:466
+msgid "next"
+msgstr "بعدی"
+
+#: sphinx/builder.py:473
+msgid "previous"
+msgstr "قبلی"
+
+#: sphinx/builder.py:1054
+msgid " (in "
+msgstr ""
+
+#: sphinx/builder.py:1129
+msgid "Builtins"
+msgstr "درونی سازی"
+
+#: sphinx/builder.py:1131
+msgid "Module level"
+msgstr "در سطح ماژول"
+
+#: sphinx/environment.py:102
+#: sphinx/latexwriter.py:164
+#, python-format
+msgid "%B %d, %Y"
+msgstr ""
+
+#: sphinx/environment.py:290
+#: sphinx/latexwriter.py:170
+#: sphinx/templates/genindex-single.html:2
+#: sphinx/templates/genindex-split.html:2
+#: sphinx/templates/genindex-split.html:5
+#: sphinx/templates/genindex.html:2
+#: sphinx/templates/genindex.html:5
+#: sphinx/templates/genindex.html:48
+#: sphinx/templates/layout.html:130
+msgid "Index"
+msgstr "فهرست"
+
+#: sphinx/environment.py:291
+#: sphinx/latexwriter.py:169
+msgid "Module Index"
+msgstr "فهرست ماژول ها"
+
+#: sphinx/environment.py:292
+#: sphinx/templates/defindex.html:16
+msgid "Search Page"
+msgstr "صفحه جستجو"
+
+#: sphinx/htmlwriter.py:79
+#: sphinx/static/doctools.js:145
+msgid "Permalink to this definition"
+msgstr "لینک ثابت به این تعریف"
+
+#: sphinx/htmlwriter.py:402
+#: sphinx/static/doctools.js:139
+msgid "Permalink to this headline"
+msgstr "لینک ثابت به این سر مقاله"
+
+#: sphinx/latexwriter.py:167
+msgid "Release"
+msgstr "انتشار"
+
+#: sphinx/roles.py:53
+#: sphinx/directives/desc.py:525
+#, python-format
+msgid "environment variable; %s"
+msgstr "%s متغیرهای عمومی؛"
+
+#: sphinx/roles.py:60
+#, python-format
+msgid "Python Enhancement Proposals!PEP %s"
+msgstr ""
+
+#: sphinx/textwriter.py:166
+#, python-format
+msgid "Platform: %s"
+msgstr "%s:پلتفرم"
+
+#: sphinx/textwriter.py:422
+msgid "[image]"
+msgstr ""
+
+#: sphinx/directives/desc.py:25
+#, python-format
+msgid "%s() (built-in function)"
+msgstr "%s() (توابع درونی)"
+
+#: sphinx/directives/desc.py:26
+#: sphinx/directives/desc.py:42
+#: sphinx/directives/desc.py:54
+#, python-format
+msgid "%s() (in module %s)"
+msgstr "%s() (در ماژول %s)"
+
+#: sphinx/directives/desc.py:29
+#, python-format
+msgid "%s (built-in variable)"
+msgstr "%s (متغیر درونی)"
+
+#: sphinx/directives/desc.py:30
+#: sphinx/directives/desc.py:66
+#, python-format
+msgid "%s (in module %s)"
+msgstr "%s (در ماژول %s)"
+
+#: sphinx/directives/desc.py:33
+#, python-format
+msgid "%s (built-in class)"
+msgstr "%s (کلاس درونی)"
+
+#: sphinx/directives/desc.py:34
+#, python-format
+msgid "%s (class in %s)"
+msgstr "%s (کلاس در %s)"
+
+#: sphinx/directives/desc.py:46
+#, python-format
+msgid "%s() (%s.%s method)"
+msgstr "%s() (%s.%s متد)"
+
+#: sphinx/directives/desc.py:48
+#, python-format
+msgid "%s() (%s method)"
+msgstr "%s() (%s متد)"
+
+#: sphinx/directives/desc.py:58
+#, python-format
+msgid "%s() (%s.%s static method)"
+msgstr "%s() (%s.%s متد استاتیک)"
+
+#: sphinx/directives/desc.py:60
+#, python-format
+msgid "%s() (%s static method)"
+msgstr "%s() (%s متد استاتیک)"
+
+#: sphinx/directives/desc.py:70
+#, python-format
+msgid "%s (%s.%s attribute)"
+msgstr "%s (%s.%s مشخصه)"
+
+#: sphinx/directives/desc.py:72
+#, python-format
+msgid "%s (%s attribute)"
+msgstr "%s (%s مشخصه)"
+
+#: sphinx/directives/desc.py:74
+#, python-format
+msgid "%s (C function)"
+msgstr "%s (C تابع)"
+
+#: sphinx/directives/desc.py:76
+#, python-format
+msgid "%s (C member)"
+msgstr "%s (C عضو)"
+
+#: sphinx/directives/desc.py:78
+#, python-format
+msgid "%s (C macro)"
+msgstr "%s (C ماکرو)"
+
+#: sphinx/directives/desc.py:80
+#, python-format
+msgid "%s (C type)"
+msgstr "%s (C نوع)"
+
+#: sphinx/directives/desc.py:82
+#, python-format
+msgid "%s (C variable)"
+msgstr "%s (C متغیر)"
+
+#: sphinx/directives/desc.py:100
+msgid "Raises"
+msgstr "برانگیختن"
+
+#: sphinx/directives/desc.py:104
+msgid "Variable"
+msgstr "متغیر"
+
+#: sphinx/directives/desc.py:107
+msgid "Returns"
+msgstr ""
+
+#: sphinx/directives/desc.py:114
+msgid "Return type"
+msgstr "نوع برگشتی"
+
+#: sphinx/directives/desc.py:141
+msgid "Parameters"
+msgstr "پارامترها"
+
+#: sphinx/directives/desc.py:411
+#, python-format
+msgid "%scommand line option; %s"
+msgstr "%sگزینه خط فرمان; %s"
+
+#: sphinx/directives/other.py:101
+msgid "Platforms: "
+msgstr ":پلتفرم ها"
+
+#: sphinx/directives/other.py:106
+#, python-format
+msgid "%s (module)"
+msgstr "%s (ماژول)"
+
+#: sphinx/directives/other.py:146
+msgid "Section author: "
+msgstr ":نویسنده این بخش"
+
+#: sphinx/directives/other.py:148
+msgid "Module author: "
+msgstr "نویسنده این ماژول:"
+
+#: sphinx/directives/other.py:150
+msgid "Author: "
+msgstr ":نویسنده"
+
+#: sphinx/directives/other.py:246
+msgid "See also"
+msgstr "همچنین ملاحظه نمائید"
+
+#: sphinx/ext/todo.py:32
+msgid "Todo"
+msgstr "در دست انجام"
+
+#: sphinx/ext/todo.py:78
+#, python-format
+msgid "(The original entry is located in %s, line %d and can be found "
+msgstr "( ورودی اصلی در %s ، در خط %d واقع شده است و می تواند یافت بشود"
+
+#: sphinx/ext/todo.py:84
+msgid "here"
+msgstr "اینجا"
+
+#: sphinx/locale/__init__.py:15
+msgid "Attention"
+msgstr "دقت"
+
+#: sphinx/locale/__init__.py:16
+msgid "Caution"
+msgstr "ملاحظه"
+
+#: sphinx/locale/__init__.py:17
+msgid "Danger"
+msgstr "خطر"
+
+#: sphinx/locale/__init__.py:18
+msgid "Error"
+msgstr "خطا"
+
+#: sphinx/locale/__init__.py:19
+msgid "Hint"
+msgstr "تذکر"
+
+#: sphinx/locale/__init__.py:20
+msgid "Important"
+msgstr "مهم"
+
+#: sphinx/locale/__init__.py:21
+msgid "Note"
+msgstr "توجه"
+
+#: sphinx/locale/__init__.py:22
+msgid "See Also"
+msgstr "همچنین ملاحظه نمائید"
+
+#: sphinx/locale/__init__.py:23
+msgid "Tip"
+msgstr "نکته"
+
+#: sphinx/locale/__init__.py:24
+msgid "Warning"
+msgstr "هشدار"
+
+#: sphinx/locale/__init__.py:28
+#, python-format
+msgid "New in version %s"
+msgstr "جدید در نسخه %s"
+
+#: sphinx/locale/__init__.py:29
+#, python-format
+msgid "Changed in version %s"
+msgstr "تغییر داده شده در نسخه %s"
+
+#: sphinx/locale/__init__.py:30
+#, python-format
+msgid "Deprecated since version %s"
+msgstr "منسوخ شده از نسخه %s"
+
+#: sphinx/locale/__init__.py:34
+msgid "module"
+msgstr "ماژول"
+
+#: sphinx/locale/__init__.py:35
+msgid "keyword"
+msgstr "کلمه کلیدی"
+
+#: sphinx/locale/__init__.py:36
+msgid "operator"
+msgstr "عملگر"
+
+#: sphinx/locale/__init__.py:37
+msgid "object"
+msgstr "شیء"
+
+#: sphinx/locale/__init__.py:38
+msgid "exception"
+msgstr "استثناء"
+
+#: sphinx/locale/__init__.py:39
+msgid "statement"
+msgstr "گذاره"
+
+#: sphinx/locale/__init__.py:40
+msgid "built-in function"
+msgstr "توابع درونی"
+
+#: sphinx/static/doctools.js:174
+msgid "Hide Search Matches"
+msgstr "عدم نمایش نتایج یافت شده"
+
+#: sphinx/static/searchtools.js:274
+msgid "Searching"
+msgstr "در حال جستجو"
+
+#: sphinx/static/searchtools.js:279
+msgid "Preparing search..."
+msgstr "...آماده جستجو"
+
+#: sphinx/static/searchtools.js:338
+msgid "module, in "
+msgstr "ماژول, در"
+
+#: sphinx/static/searchtools.js:347
+msgid ", in "
+msgstr ", در"
+
+#: sphinx/static/searchtools.js:447
+#: sphinx/templates/search.html:18
+msgid "Search Results"
+msgstr "نتایج جستجو"
+
+#: sphinx/static/searchtools.js:449
+msgid "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories."
+msgstr ". هیچ سندی برای جستجوی شما یافت نشد، لطفا اطمینان حاصل نمائید که املای تمامی کلمات صحیح می باشد و همچنین موضوعات کافی را برای این جستجو انتخاب نموده اید"
+
+#: sphinx/static/searchtools.js:451
+#, python-format
+msgid "Search finished, found %s page(s) matching the search query."
+msgstr "جستجو انجام شد ، %s صفحه مطابق با پرس و جو پیدا شد"
+
+#: sphinx/templates/defindex.html:2
+msgid "Overview"
+msgstr "بررسی اجمالی"
+
+#: sphinx/templates/defindex.html:11
+msgid "Indices and tables:"
+msgstr "ایندکس ها و جداول:"
+
+#: sphinx/templates/defindex.html:14
+msgid "Complete Table of Contents"
+msgstr "فهرست کامل مطالب"
+
+#: sphinx/templates/defindex.html:15
+msgid "lists all sections and subsections"
+msgstr "فهرست تمامی بخش ها و زیر مجموعه ها"
+
+#: sphinx/templates/defindex.html:17
+msgid "search this documentation"
+msgstr "جستجو در این اسناد"
+
+#: sphinx/templates/defindex.html:20
+msgid "quick access to all modules"
+msgstr "دسترسی سریع به تمامی متدها"
+
+#: sphinx/templates/defindex.html:22
+msgid "all functions, classes, terms"
+msgstr "تمامی توابع ، کلاس ها ، اصطلاحات"
+
+#: sphinx/templates/genindex-single.html:5
+#, python-format
+msgid "Index &ndash; %(key)s"
+msgstr "فهرست &ndash; %(key)s"
+
+#: sphinx/templates/genindex-single.html:44
+#: sphinx/templates/genindex-split.html:14
+#: sphinx/templates/genindex-split.html:27
+#: sphinx/templates/genindex.html:54
+msgid "Full index on one page"
+msgstr "فهرست کامل در یک صفحه"
+
+#: sphinx/templates/genindex-split.html:7
+msgid "Index pages by letter"
+msgstr "فهرست صفحات بر اساس حروف"
+
+#: sphinx/templates/genindex-split.html:15
+msgid "can be huge"
+msgstr ""
+
+#: sphinx/templates/layout.html:9
+msgid "Navigation"
+msgstr "ناوبری"
+
+#: sphinx/templates/layout.html:40
+msgid "Table Of Contents"
+msgstr "فهرست عناوین"
+
+#: sphinx/templates/layout.html:46
+msgid "Previous topic"
+msgstr "موضوع قبلی"
+
+#: sphinx/templates/layout.html:47
+msgid "previous chapter"
+msgstr "فصل قبلی"
+
+#: sphinx/templates/layout.html:50
+msgid "Next topic"
+msgstr "موضوع بعدی"
+
+#: sphinx/templates/layout.html:51
+msgid "next chapter"
+msgstr "فصل بعدی"
+
+#: sphinx/templates/layout.html:55
+msgid "This Page"
+msgstr "صفحه فعلی"
+
+#: sphinx/templates/layout.html:59
+msgid "Suggest Change"
+msgstr ""
+
+#: sphinx/templates/layout.html:60
+#: sphinx/templates/layout.html:62
+msgid "Show Source"
+msgstr "نمایش سورس"
+
+#: sphinx/templates/layout.html:71
+msgid "Quick search"
+msgstr "جستجو سریع"
+
+#: sphinx/templates/layout.html:71
+msgid "Keyword search"
+msgstr "جستجو کلید واژه"
+
+#: sphinx/templates/layout.html:73
+msgid "Go"
+msgstr "برو"
+
+#: sphinx/templates/layout.html:78
+msgid "Enter a module, class or function name."
+msgstr "نام یک ماژول ، کلاس و یا تابع را وارد نمائید"
+
+#: sphinx/templates/layout.html:119
+#, python-format
+msgid "Search within %(docstitle)s"
+msgstr "جستجو در %(docstitle)s"
+
+#: sphinx/templates/layout.html:128
+msgid "About these documents"
+msgstr "درباره این مستندات"
+
+#: sphinx/templates/layout.html:131
+#: sphinx/templates/search.html:2
+#: sphinx/templates/search.html:5
+msgid "Search"
+msgstr "جستجو"
+
+#: sphinx/templates/layout.html:133
+msgid "Copyright"
+msgstr "کپی رایت"
+
+#: sphinx/templates/layout.html:178
+#, python-format
+msgid "&copy; <a href=\"%(path)s\">Copyright</a> %(copyright)s."
+msgstr ""
+
+#: sphinx/templates/layout.html:180
+#, python-format
+msgid "&copy; Copyright %(copyright)s."
+msgstr ""
+
+#: sphinx/templates/layout.html:183
+#, python-format
+msgid "Last updated on %(last_updated)s."
+msgstr ". %(last_updated)s آخرین بروز رسانی در"
+
+#: sphinx/templates/layout.html:186
+#, python-format
+msgid "Created using <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> %(sphinx_version)s."
+msgstr ". <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> %(sphinx_version)s ایجاد شده با"
+
+#: sphinx/templates/modindex.html:15
+msgid "Most popular modules:"
+msgstr "متداول ترین ماژول ها:"
+
+#: sphinx/templates/modindex.html:24
+msgid "Show modules only available on these platforms"
+msgstr "تنها ماژول هایی که در این پلتفرم در دسترس هستند را نشان بده"
+
+#: sphinx/templates/modindex.html:56
+msgid "Deprecated"
+msgstr "منسوخ شده"
+
+#: sphinx/templates/opensearch.xml:4
+#, python-format
+msgid "Search %(docstitle)s"
+msgstr "جستجو %(docstitle)s"
+
+#: sphinx/templates/page.html:8
+msgid "<strong>Note:</strong> You requested an out-of-date URL from this server. We've tried to redirect you to the new location of this page, but it may not be the right one."
+msgstr ""
+
+#: sphinx/templates/search.html:7
+msgid ""
+"From here you can search these documents. Enter your search\n"
+" words into the box below and click \"search\". Note that the search\n"
+" function will automatically search for all of the words. Pages\n"
+" containing less words won't appear in the result list."
+msgstr "در اینجا شما می توانید مستندات را جستجو نمائید ، کلماتی را در کادر جستجو وارد کنید و سپس بر روی دکمه جستجو کلیک نمائید ، توجه کنید که تابع جستجو گر امر جستجو را بطور خودکار برای تمامی کلمات دنبال خواهد کرد .صفحاتی که شامل کلمات کمتری هستند ، در لیست جستجو نمایش داده نخواهند شد."
+
+#: sphinx/templates/search.html:14
+msgid "search"
+msgstr "جستجو"
+
+#: sphinx/templates/search.html:20
+msgid "Your search did not match any results."
+msgstr ".جستجوی شما نتیجه ایی در بر نداشت"
+
+#: sphinx/templates/changes/frameset.html:5
+#: sphinx/templates/changes/versionchanges.html:12
+#, python-format
+msgid "Changes in Version %(version)s &mdash; %(docstitle)s"
+msgstr "تغییرات در نسخه %(version)s &mdash; %(docstitle)s"
+
+#: sphinx/templates/changes/rstsource.html:5
+#, python-format
+msgid "%(filename)s &mdash; %(docstitle)s"
+msgstr ""
+
+#: sphinx/templates/changes/versionchanges.html:17
+#, python-format
+msgid "Automatically generated list of changes in version %(version)s"
+msgstr "لیست تولید شده خودکار از تغییرات در نسخه %(version)s"
+
+#: sphinx/templates/changes/versionchanges.html:18
+msgid "Library changes"
+msgstr "تغییرات کتابخانه ایی"
+
+#: sphinx/templates/changes/versionchanges.html:23
+msgid "C API changes"
+msgstr "C API تغییرات"
+
+#: sphinx/templates/changes/versionchanges.html:25
+msgid "Other changes"
+msgstr "دگر تغییرات"
+
diff --git a/sphinx/locale/ja/LC_MESSAGES/sphinx.js b/sphinx/locale/ja/LC_MESSAGES/sphinx.js
index 6b63245e..0a135540 100644
--- a/sphinx/locale/ja/LC_MESSAGES/sphinx.js
+++ b/sphinx/locale/ja/LC_MESSAGES/sphinx.js
@@ -1 +1 @@
-Documentation.addTranslations({"locale": "ja", "plural_expr": "0", "messages": {"Search Results": "\u691c\u7d22\u7d50\u679c", "Preparing search...": "\u691c\u7d22\u306e\u6e96\u5099\u4e2d...", "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories.": "\u691c\u7d22\u6761\u4ef6\u306b\u4e00\u81f4\u3059\u308b\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u306f\u3042\u308a\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u691c\u7d22\u3057\u305f\u3044\u8a00\u8449\u3092\u6b63\u3057\u3044\u3064\u3065\u308a\u3067\u5165\u529b\u3057\u3066\u3044\u308b\u304b\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u307e\u305f\u3001\u6b63\u3057\u3044\u30ab\u30c6\u30b4\u30ea\u306e\u691c\u7d22\u3092\u884c\u3063\u3066\u3044\u308b\u304b\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002", "Search finished, found %s page(s) matching the search query.": "\u691c\u7d22\u304c\u7d42\u4e86\u3057\u3001\u6761\u4ef6\u306b\u4e00\u81f4\u3059\u308b\u30da\u30fc\u30b8\u304c %s \u500b\u307f\u3064\u304b\u308a\u307e\u3057\u305f\u3002", ", in ": "", "Expand sidebar": "", "Permalink to this headline": "\u3053\u306e\u30d8\u30c3\u30c9\u30e9\u30a4\u30f3\u3078\u306e\u30d1\u30fc\u30de\u30ea\u30f3\u30af", "Searching": "\u691c\u7d22\u4e2d", "Collapse sidebar": "", "Permalink to this definition": "\u3053\u306e\u5b9a\u7fa9\u3078\u306e\u30d1\u30fc\u30de\u30ea\u30f3\u30af", "Hide Search Matches": "\u691c\u7d22\u7d50\u679c\u3092\u96a0\u3059"}}); \ No newline at end of file
+Documentation.addTranslations({"locale": "ja", "plural_expr": "0", "messages": {"Search Results": "\u691c\u7d22\u7d50\u679c", "Preparing search...": "\u691c\u7d22\u306e\u6e96\u5099\u4e2d...", "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories.": "\u691c\u7d22\u6761\u4ef6\u306b\u4e00\u81f4\u3059\u308b\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u306f\u3042\u308a\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u691c\u7d22\u3057\u305f\u3044\u8a00\u8449\u3092\u6b63\u3057\u3044\u3064\u3065\u308a\u3067\u5165\u529b\u3057\u3066\u3044\u308b\u304b\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u307e\u305f\u3001\u6b63\u3057\u3044\u30ab\u30c6\u30b4\u30ea\u306e\u691c\u7d22\u3092\u884c\u3063\u3066\u3044\u308b\u304b\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002", "Search finished, found %s page(s) matching the search query.": "\u691c\u7d22\u304c\u7d42\u4e86\u3057\u3001\u6761\u4ef6\u306b\u4e00\u81f4\u3059\u308b\u30da\u30fc\u30b8\u304c %s \u500b\u307f\u3064\u304b\u308a\u307e\u3057\u305f\u3002", ", in ": "", "Expand sidebar": "\u30b5\u30a4\u30c9\u30d0\u30fc\u3092\u5c55\u958b", "Permalink to this headline": "\u3053\u306e\u30d8\u30c3\u30c9\u30e9\u30a4\u30f3\u3078\u306e\u30d1\u30fc\u30de\u30ea\u30f3\u30af", "Searching": "\u691c\u7d22\u4e2d", "Collapse sidebar": "\u30b5\u30a4\u30c9\u30d0\u30fc\u3092\u305f\u305f\u3080", "Permalink to this definition": "\u3053\u306e\u5b9a\u7fa9\u3078\u306e\u30d1\u30fc\u30de\u30ea\u30f3\u30af", "Hide Search Matches": "\u691c\u7d22\u7d50\u679c\u3092\u96a0\u3059"}}); \ No newline at end of file
diff --git a/sphinx/locale/ja/LC_MESSAGES/sphinx.mo b/sphinx/locale/ja/LC_MESSAGES/sphinx.mo
index 5a8ada49..d77e83a4 100644
--- a/sphinx/locale/ja/LC_MESSAGES/sphinx.mo
+++ b/sphinx/locale/ja/LC_MESSAGES/sphinx.mo
Binary files differ
diff --git a/sphinx/locale/ja/LC_MESSAGES/sphinx.po b/sphinx/locale/ja/LC_MESSAGES/sphinx.po
index dd5a892e..03e7123d 100644
--- a/sphinx/locale/ja/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/ja/LC_MESSAGES/sphinx.po
@@ -170,7 +170,7 @@ msgstr "%s (C++ の関数)"
#: sphinx/domains/cpp.py:1038 sphinx/domains/javascript.py:162
#: sphinx/domains/python.py:525
msgid "class"
-msgstr ""
+msgstr "クラス"
#: sphinx/domains/javascript.py:105 sphinx/domains/python.py:244
#, python-format
@@ -190,7 +190,7 @@ msgstr "%s (のクラス)"
#: sphinx/domains/javascript.py:110
#, python-format
msgid "%s (global variable or constant)"
-msgstr ""
+msgstr "%s (グローバル変数または定数)"
#: sphinx/domains/javascript.py:112 sphinx/domains/python.py:346
#, python-format
@@ -204,11 +204,11 @@ msgstr "パラメタ"
#: sphinx/domains/javascript.py:124
msgid "Throws"
-msgstr ""
+msgstr "例外"
#: sphinx/domains/javascript.py:163 sphinx/domains/python.py:524
msgid "data"
-msgstr ""
+msgstr "データ"
#: sphinx/domains/javascript.py:164 sphinx/domains/python.py:530
msgid "attribute"
@@ -307,7 +307,7 @@ msgstr "例外"
#: sphinx/domains/python.py:527
msgid "method"
-msgstr ""
+msgstr "メソッド"
#: sphinx/domains/python.py:528
#, fuzzy
@@ -330,7 +330,7 @@ msgstr "撤廃"
#: sphinx/domains/rst.py:55
#, python-format
msgid "%s (directive)"
-msgstr ""
+msgstr "%s (ディレクティブ)"
#: sphinx/domains/rst.py:57
#, fuzzy, python-format
@@ -339,12 +339,12 @@ msgstr "%s (モジュール)"
#: sphinx/domains/rst.py:106
msgid "directive"
-msgstr ""
+msgstr "ディレクティブ"
#: sphinx/domains/rst.py:107
#, fuzzy
msgid "role"
-msgstr "モジュール"
+msgstr "ロール"
#: sphinx/domains/std.py:68 sphinx/domains/std.py:84
#, python-format
@@ -358,15 +358,15 @@ msgstr "%sコマンドラインオプション; %s"
#: sphinx/domains/std.py:328
msgid "glossary term"
-msgstr ""
+msgstr "用語集の項目"
#: sphinx/domains/std.py:329
msgid "grammar token"
-msgstr ""
+msgstr "文法トークン"
#: sphinx/domains/std.py:330
msgid "reference label"
-msgstr ""
+msgstr "参照ラベル"
#: sphinx/domains/std.py:331
msgid "environment variable"
@@ -374,7 +374,7 @@ msgstr "環境変数"
#: sphinx/domains/std.py:332
msgid "program option"
-msgstr ""
+msgstr "プログラムオプション"
#: sphinx/domains/std.py:360 sphinx/themes/basic/genindex-single.html:11
#: sphinx/themes/basic/genindex-split.html:11
@@ -410,37 +410,37 @@ msgstr "課題"
#: sphinx/ext/todo.py:109
#, python-format
msgid "(The <<original entry>> is located in %s, line %d.)"
-msgstr ""
+msgstr "(<<元のエントリ>> は、 %s の %d 行目です)"
#: sphinx/ext/todo.py:117
msgid "original entry"
-msgstr ""
+msgstr "元のエントリ"
#: sphinx/ext/viewcode.py:70
msgid "[source]"
-msgstr ""
+msgstr "[ソース]"
#: sphinx/ext/viewcode.py:117
msgid "[docs]"
-msgstr ""
+msgstr "[ドキュメント]"
#: sphinx/ext/viewcode.py:131
#, fuzzy
msgid "Module code"
-msgstr "モジュール"
+msgstr "モジュールコード"
#: sphinx/ext/viewcode.py:137
#, python-format
msgid "<h1>Source code for %s</h1>"
-msgstr ""
+msgstr "<h1>%s のソースコード</h1>"
#: sphinx/ext/viewcode.py:164
msgid "Overview: module code"
-msgstr ""
+msgstr "概要: モジュールコード"
#: sphinx/ext/viewcode.py:165
msgid "<h1>All modules for which code is available</h1>"
-msgstr ""
+msgstr "<h1>全モジュールのうち、コードを読めるもの</h1>"
#: sphinx/locale/__init__.py:155
msgid "Attention"
@@ -754,16 +754,16 @@ msgstr "検索が終了し、条件に一致するページが %s 個みつか
#: sphinx/themes/default/static/sidebar.js:66
msgid "Expand sidebar"
-msgstr ""
+msgstr "サイドバーを展開"
#: sphinx/themes/default/static/sidebar.js:79
#: sphinx/themes/default/static/sidebar.js:107
msgid "Collapse sidebar"
-msgstr ""
+msgstr "サイドバーをたたむ"
#: sphinx/themes/haiku/layout.html:26
msgid "Contents"
-msgstr ""
+msgstr "コンテンツ"
#: sphinx/writers/latex.py:172
msgid "Release"
diff --git a/sphinx/locale/pl/LC_MESSAGES/sphinx.mo b/sphinx/locale/pl/LC_MESSAGES/sphinx.mo
index 949564e0..132e7da3 100644
--- a/sphinx/locale/pl/LC_MESSAGES/sphinx.mo
+++ b/sphinx/locale/pl/LC_MESSAGES/sphinx.mo
Binary files differ
diff --git a/sphinx/locale/pl/LC_MESSAGES/sphinx.po b/sphinx/locale/pl/LC_MESSAGES/sphinx.po
index e2588b97..f5260f9e 100644
--- a/sphinx/locale/pl/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/pl/LC_MESSAGES/sphinx.po
@@ -4,8 +4,8 @@ msgstr ""
"Project-Id-Version: Sphinx 0.5\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2008-08-10 11:43+0000\n"
-"PO-Revision-Date: 2010-08-26 11:45+0000\n"
-"Last-Translator: Michał Kandulski <Michal.Kandulski@poczta.onet.pl>\n"
+"PO-Revision-Date: 2010-12-17 13:36+0100\n"
+"Last-Translator: Michał Kandulski <michal.kandulski@gmail.com>\n"
"Language-Team: \n"
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && "
"(n%100<10 || n%100>=20) ? 1 : 2)\n"
@@ -69,7 +69,7 @@ msgstr "Autor modułu: "
#: sphinx/directives/other.py:139
#, fuzzy
msgid "Code author: "
-msgstr "Autor modułu: "
+msgstr "Autor kodu: "
#: sphinx/directives/other.py:141
msgid "Author: "
@@ -82,7 +82,7 @@ msgstr "Zobacz także"
#: sphinx/domains/__init__.py:242
#, python-format
msgid "%s %s"
-msgstr ""
+msgstr "%s %s"
#: sphinx/domains/c.py:51 sphinx/domains/python.py:94
msgid "Parameters"
@@ -133,7 +133,7 @@ msgstr "pole"
#: sphinx/domains/c.py:173
msgid "macro"
-msgstr ""
+msgstr "makro"
#: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1041
msgid "type"
@@ -142,12 +142,12 @@ msgstr "typ"
#: sphinx/domains/c.py:175
#, fuzzy
msgid "variable"
-msgstr "Zmienna"
+msgstr "zmienna"
#: sphinx/domains/cpp.py:883
#, python-format
msgid "%s (C++ class)"
-msgstr "%s (klasie C++)"
+msgstr "%s (klasa C++)"
#: sphinx/domains/cpp.py:898
#, python-format
@@ -167,7 +167,7 @@ msgstr "%s (funkcja C++)"
#: sphinx/domains/cpp.py:1038 sphinx/domains/javascript.py:162
#: sphinx/domains/python.py:525
msgid "class"
-msgstr "klasie"
+msgstr "klasa"
#: sphinx/domains/javascript.py:105 sphinx/domains/python.py:244
#, python-format
@@ -182,12 +182,12 @@ msgstr "%s() (%s metoda)"
#: sphinx/domains/javascript.py:108
#, fuzzy, python-format
msgid "%s() (class)"
-msgstr "%s (klasie C++)"
+msgstr "%s() (klasa)"
#: sphinx/domains/javascript.py:110
#, python-format
msgid "%s (global variable or constant)"
-msgstr ""
+msgstr "%s (zmienna lub stała globalna)"
#: sphinx/domains/javascript.py:112 sphinx/domains/python.py:346
#, python-format
@@ -197,15 +197,15 @@ msgstr "%s (%s atrybut)"
#: sphinx/domains/javascript.py:121
#, fuzzy
msgid "Arguments"
-msgstr "Parametry"
+msgstr "Argumenty"
#: sphinx/domains/javascript.py:124
msgid "Throws"
-msgstr ""
+msgstr "Wyrzuca"
#: sphinx/domains/javascript.py:163 sphinx/domains/python.py:524
msgid "data"
-msgstr ""
+msgstr "dane"
#: sphinx/domains/javascript.py:164 sphinx/domains/python.py:530
msgid "attribute"
@@ -214,7 +214,7 @@ msgstr "atrybut"
#: sphinx/domains/python.py:98
#, fuzzy
msgid "Variables"
-msgstr "Zmienna"
+msgstr "Zmienne"
#: sphinx/domains/python.py:101
msgid "Raises"
@@ -244,7 +244,7 @@ msgstr "%s (klasa wbudowana)"
#: sphinx/domains/python.py:266
#, python-format
msgid "%s (class in %s)"
-msgstr "%s (w klasie %s)"
+msgstr "%s (klasa w module %s)"
#: sphinx/domains/python.py:306
#, python-format
@@ -254,27 +254,27 @@ msgstr "%s() (%s.%s metoda)"
#: sphinx/domains/python.py:318
#, python-format
msgid "%s() (%s.%s static method)"
-msgstr "%s() (%s.%s statyczna metoda)"
+msgstr "%s() (%s.%s metoda statyczna)"
#: sphinx/domains/python.py:321
#, python-format
msgid "%s() (%s static method)"
-msgstr "%s() (%s statyczna metoda)"
+msgstr "%s() (%s metoda statyczna)"
#: sphinx/domains/python.py:331
#, fuzzy, python-format
msgid "%s() (%s.%s class method)"
-msgstr "%s() (%s.%s metoda)"
+msgstr "%s() (%s.%s metoda klasy)"
#: sphinx/domains/python.py:334
#, fuzzy, python-format
msgid "%s() (%s class method)"
-msgstr "%s() (%s metoda)"
+msgstr "%s() (%s metoda klasy)"
#: sphinx/domains/python.py:344
#, python-format
msgid "%s (%s.%s attribute)"
-msgstr "%s (%s.%s atrybut)"
+msgstr "%s (atrybut %s.%s)"
#: sphinx/domains/python.py:392
msgid "Platforms: "
@@ -288,7 +288,7 @@ msgstr "%s (moduł)"
#: sphinx/domains/python.py:455
#, fuzzy
msgid "Python Module Index"
-msgstr "Indeks modułów"
+msgstr "Indeks modułów pythona"
#: sphinx/domains/python.py:456
msgid "modules"
@@ -304,12 +304,12 @@ msgstr "wyjątek"
#: sphinx/domains/python.py:527
msgid "method"
-msgstr ""
+msgstr "metoda"
#: sphinx/domains/python.py:528
#, fuzzy
msgid "class method"
-msgstr "%s() (%s metoda)"
+msgstr "metoda klasy"
#: sphinx/domains/python.py:529
msgid "static method"
@@ -322,26 +322,26 @@ msgstr "moduł"
#: sphinx/domains/python.py:657
#, fuzzy
msgid " (deprecated)"
-msgstr "Niezalecane"
+msgstr " (niezalecane)"
#: sphinx/domains/rst.py:55
#, python-format
msgid "%s (directive)"
-msgstr ""
+msgstr "%s (dyrektywa)"
#: sphinx/domains/rst.py:57
#, fuzzy, python-format
msgid "%s (role)"
-msgstr "%s (moduł)"
+msgstr "%s (rola)"
#: sphinx/domains/rst.py:106
msgid "directive"
-msgstr ""
+msgstr "dyrektywa"
#: sphinx/domains/rst.py:107
#, fuzzy
msgid "role"
-msgstr "moduł"
+msgstr "rola"
#: sphinx/domains/std.py:68 sphinx/domains/std.py:84
#, python-format
@@ -355,15 +355,15 @@ msgstr "%sopcja linii komend; %s"
#: sphinx/domains/std.py:328
msgid "glossary term"
-msgstr ""
+msgstr "termin glosariusza"
#: sphinx/domains/std.py:329
msgid "grammar token"
-msgstr ""
+msgstr "symbol gramatyki"
#: sphinx/domains/std.py:330
msgid "reference label"
-msgstr ""
+msgstr "etykieta odsyłacza"
#: sphinx/domains/std.py:331
msgid "environment variable"
@@ -371,7 +371,7 @@ msgstr "zmienna środowiskowa"
#: sphinx/domains/std.py:332
msgid "program option"
-msgstr ""
+msgstr "opcja programu"
#: sphinx/domains/std.py:360 sphinx/themes/basic/genindex-single.html:11
#: sphinx/themes/basic/genindex-split.html:11
@@ -402,44 +402,42 @@ msgstr "alias klasy :class:`%s`"
#: sphinx/ext/todo.py:41
msgid "Todo"
-msgstr "Do zrobienia"
+msgstr "Todo"
#: sphinx/ext/todo.py:109
#, fuzzy, python-format
msgid "(The <<original entry>> is located in %s, line %d.)"
-msgstr ""
-"(Oryginalny wpis znajduje się w pliku %s, w linii %d i może być "
-"odnaleziony "
+msgstr "(<<Oryginalny wpis>> znajduje się w pliku %s, w linii %d.)"
#: sphinx/ext/todo.py:117
msgid "original entry"
-msgstr ""
+msgstr "oryginalny wpis"
#: sphinx/ext/viewcode.py:70
msgid "[source]"
-msgstr ""
+msgstr "[źródło]"
#: sphinx/ext/viewcode.py:117
msgid "[docs]"
-msgstr ""
+msgstr "[dokumenty]"
#: sphinx/ext/viewcode.py:131
#, fuzzy
msgid "Module code"
-msgstr "moduł"
+msgstr "Kod modułu"
#: sphinx/ext/viewcode.py:137
#, python-format
msgid "<h1>Source code for %s</h1>"
-msgstr ""
+msgstr "<h1>Kod źródłowy modułu %s</h1>"
#: sphinx/ext/viewcode.py:164
msgid "Overview: module code"
-msgstr ""
+msgstr "Przeglądanie: kod modułu"
#: sphinx/ext/viewcode.py:165
msgid "<h1>All modules for which code is available</h1>"
-msgstr ""
+msgstr "<h1>Wszystkie moduły, dla których jest dostępny kod</h1>"
#: sphinx/locale/__init__.py:155
msgid "Attention"
@@ -552,15 +550,15 @@ msgstr "Kompletny spis treści"
#: sphinx/themes/basic/defindex.html:24
msgid "lists all sections and subsections"
-msgstr "wymień wszystkie rozdziały i podrozdziały"
+msgstr "wszystkie rozdziały i podrozdziały"
#: sphinx/themes/basic/defindex.html:26
msgid "search this documentation"
-msgstr "wyszukaj w dokumentacji"
+msgstr "przyszukaj tę dokumentację"
#: sphinx/themes/basic/defindex.html:28
msgid "Global Module Index"
-msgstr "Indeks modułów"
+msgstr "Globalny indeks modułów"
#: sphinx/themes/basic/defindex.html:29
msgid "quick access to all modules"
@@ -674,7 +672,7 @@ msgstr ""
#: sphinx/themes/basic/search.html:36
msgid "search"
-msgstr "Szukaj"
+msgstr "szukaj"
#: sphinx/themes/basic/search.html:40
#: sphinx/themes/basic/static/searchtools.js:504
@@ -752,7 +750,7 @@ msgid ""
"are spelled correctly and that you've selected enough categories."
msgstr ""
"Nie znaleziono żadnych pasujących dokumentów. Upewnij się, że wszystkie "
-"słowa są poprawnie wpisane i że wybrałeś wystarczającąliczbę kategorii."
+"słowa są poprawnie wpisane i że wybrałeś wystarczającą liczbę kategorii."
#: sphinx/themes/basic/static/searchtools.js:508
#, python-format
@@ -761,16 +759,16 @@ msgstr "Przeszukiwanie zakończone, znaleziono %s pasujących stron."
#: sphinx/themes/default/static/sidebar.js:66
msgid "Expand sidebar"
-msgstr ""
+msgstr "Rozwiń pasek boczny"
#: sphinx/themes/default/static/sidebar.js:79
#: sphinx/themes/default/static/sidebar.js:107
msgid "Collapse sidebar"
-msgstr ""
+msgstr "Zwiń pasek boczny"
#: sphinx/themes/haiku/layout.html:26
msgid "Contents"
-msgstr ""
+msgstr "Treść"
#: sphinx/writers/latex.py:172
msgid "Release"
@@ -790,5 +788,5 @@ msgstr "Kontynuacja na następnej stronie"
#: sphinx/writers/text.py:422
msgid "[image]"
-msgstr "[obrazek]"
+msgstr "[obraz]"
diff --git a/sphinx/pycode/__init__.py b/sphinx/pycode/__init__.py
index ef92297c..e4758835 100644
--- a/sphinx/pycode/__init__.py
+++ b/sphinx/pycode/__init__.py
@@ -5,7 +5,7 @@
Utilities parsing and analyzing Python code.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/pycode/nodes.py b/sphinx/pycode/nodes.py
index fc6eb93a..7adacc1d 100644
--- a/sphinx/pycode/nodes.py
+++ b/sphinx/pycode/nodes.py
@@ -5,7 +5,7 @@
Parse tree node implementations.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/pycode/pgen2/driver.py b/sphinx/pycode/pgen2/driver.py
index 39e347b7..5e6cf9a5 100644
--- a/sphinx/pycode/pgen2/driver.py
+++ b/sphinx/pycode/pgen2/driver.py
@@ -120,7 +120,7 @@ def load_grammar(gt="Grammar.txt", gp=None,
head, tail = os.path.splitext(gt)
if tail == ".txt":
tail = ""
- gp = head + tail + ".".join(map(str, sys.version_info)) + ".pickle"
+ gp = head + tail + ".".join(map(str, sys.version_info[:2])) + ".pickle"
if force or not _newer(gp, gt):
logger.info("Generating grammar tables from %s", gt)
g = pgen.generate_grammar(gt)
diff --git a/sphinx/pygments_styles.py b/sphinx/pygments_styles.py
new file mode 100644
index 00000000..44740b31
--- /dev/null
+++ b/sphinx/pygments_styles.py
@@ -0,0 +1,96 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.pygments_styles
+ ~~~~~~~~~~~~~~~~~~~~~~
+
+ Sphinx theme specific highlighting styles.
+
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+from pygments.style import Style
+from pygments.styles.friendly import FriendlyStyle
+from pygments.token import Generic, Comment, Number, Whitespace, Keyword, \
+ Operator, Name, String, Error
+
+
+class NoneStyle(Style):
+ """Style without any styling."""
+
+
+class SphinxStyle(Style):
+ """
+ Like friendly, but a bit darker to enhance contrast on the green
+ background.
+ """
+
+ background_color = '#eeffcc'
+ default_style = ''
+
+ styles = FriendlyStyle.styles
+ styles.update({
+ Generic.Output: '#333',
+ Comment: 'italic #408090',
+ Number: '#208050',
+ })
+
+
+class PyramidStyle(Style):
+ """
+ Pylons/pyramid pygments style based on friendly style, by Blaise Laflamme.
+ """
+
+ # work in progress...
+
+ background_color = "#f8f8f8"
+ default_style = ""
+
+ styles = {
+ Whitespace: "#bbbbbb",
+ Comment: "italic #60a0b0",
+ Comment.Preproc: "noitalic #007020",
+ Comment.Special: "noitalic bg:#fff0f0",
+
+ Keyword: "bold #007020",
+ Keyword.Pseudo: "nobold",
+ Keyword.Type: "nobold #902000",
+
+ Operator: "#666666",
+ Operator.Word: "bold #007020",
+
+ Name.Builtin: "#007020",
+ Name.Function: "#06287e",
+ Name.Class: "bold #0e84b5",
+ Name.Namespace: "bold #0e84b5",
+ Name.Exception: "#007020",
+ Name.Variable: "#bb60d5",
+ Name.Constant: "#60add5",
+ Name.Label: "bold #002070",
+ Name.Entity: "bold #d55537",
+ Name.Attribute: "#0e84b5",
+ Name.Tag: "bold #062873",
+ Name.Decorator: "bold #555555",
+
+ String: "#4070a0",
+ String.Doc: "italic",
+ String.Interpol: "italic #70a0d0",
+ String.Escape: "bold #4070a0",
+ String.Regex: "#235388",
+ String.Symbol: "#517918",
+ String.Other: "#c65d09",
+ Number: "#40a070",
+
+ Generic.Heading: "bold #000080",
+ Generic.Subheading: "bold #800080",
+ Generic.Deleted: "#A00000",
+ Generic.Inserted: "#00A000",
+ Generic.Error: "#FF0000",
+ Generic.Emph: "italic",
+ Generic.Strong: "bold",
+ Generic.Prompt: "bold #c65d09",
+ Generic.Output: "#888",
+ Generic.Traceback: "#04D",
+
+ Error: "#a40000 bg:#fbe3e4"
+ }
diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py
index 864cca95..4d7e2db3 100644
--- a/sphinx/quickstart.py
+++ b/sphinx/quickstart.py
@@ -5,7 +5,7 @@
Quickly setup documentation source to work with Sphinx.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -254,6 +254,22 @@ man_pages = [
('%(master_str)s', '%(project_manpage)s', u'%(project_doc)s',
[u'%(author_str)s'], 1)
]
+
+# If true, show URL addresses after external links.
+#man_show_urls = False
+
+# -- Options for Texinfo output ------------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ ('%(master_str)s', '%(project_fn)s', u'%(project_doc)s', u'%(author_str)s',
+ '%(project_fn)s', 'One line description of project.', 'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+texinfo_appendices = []
'''
EPUB_CONFIG = '''
@@ -364,6 +380,8 @@ help:
\t@echo " latexpdf to make LaTeX files and run them through pdflatex"
\t@echo " text to make text files"
\t@echo " man to make manual pages"
+\t@echo " texinfo to make Texinfo files"
+\t@echo " info to make Texinfo files and run them through makeinfo"
\t@echo " gettext to make PO message catalogs"
\t@echo " changes to make an overview of all changed/added/deprecated items"
\t@echo " linkcheck to check all external links for integrity"
@@ -451,6 +469,19 @@ man:
\t@echo
\t@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+texinfo:
+\t$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+\t@echo
+\t@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+\t@echo "Run \\`make' in that directory to run these through makeinfo" \\
+\t "(use \\`make info' here to do that automatically)."
+
+info:
+\t$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+\t@echo "Running Texinfo files through makeinfo..."
+\tmake -C $(BUILDDIR)/texinfo info
+\t@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
gettext:
\t$(SPHINXBUILD) -b gettext $(ALLSPHINXOPTS) $(BUILDDIR)/locale
\t@echo
@@ -504,6 +535,7 @@ if "%%1" == "help" (
\techo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
\techo. text to make text files
\techo. man to make manual pages
+\techo. texinfo to make Texinfo files
\techo. gettext to make PO message catalogs
\techo. changes to make an overview over all changed/added/deprecated items
\techo. linkcheck to check all external links for integrity
@@ -519,6 +551,7 @@ if "%%1" == "clean" (
if "%%1" == "html" (
\t%%SPHINXBUILD%% -b html %%ALLSPHINXOPTS%% %%BUILDDIR%%/html
+\tif errorlevel 1 exit /b 1
\techo.
\techo.Build finished. The HTML pages are in %%BUILDDIR%%/html.
\tgoto end
@@ -526,6 +559,7 @@ if "%%1" == "html" (
if "%%1" == "dirhtml" (
\t%%SPHINXBUILD%% -b dirhtml %%ALLSPHINXOPTS%% %%BUILDDIR%%/dirhtml
+\tif errorlevel 1 exit /b 1
\techo.
\techo.Build finished. The HTML pages are in %%BUILDDIR%%/dirhtml.
\tgoto end
@@ -533,6 +567,7 @@ if "%%1" == "dirhtml" (
if "%%1" == "singlehtml" (
\t%%SPHINXBUILD%% -b singlehtml %%ALLSPHINXOPTS%% %%BUILDDIR%%/singlehtml
+\tif errorlevel 1 exit /b 1
\techo.
\techo.Build finished. The HTML pages are in %%BUILDDIR%%/singlehtml.
\tgoto end
@@ -540,6 +575,7 @@ if "%%1" == "singlehtml" (
if "%%1" == "pickle" (
\t%%SPHINXBUILD%% -b pickle %%ALLSPHINXOPTS%% %%BUILDDIR%%/pickle
+\tif errorlevel 1 exit /b 1
\techo.
\techo.Build finished; now you can process the pickle files.
\tgoto end
@@ -547,6 +583,7 @@ if "%%1" == "pickle" (
if "%%1" == "json" (
\t%%SPHINXBUILD%% -b json %%ALLSPHINXOPTS%% %%BUILDDIR%%/json
+\tif errorlevel 1 exit /b 1
\techo.
\techo.Build finished; now you can process the JSON files.
\tgoto end
@@ -554,6 +591,7 @@ if "%%1" == "json" (
if "%%1" == "htmlhelp" (
\t%%SPHINXBUILD%% -b htmlhelp %%ALLSPHINXOPTS%% %%BUILDDIR%%/htmlhelp
+\tif errorlevel 1 exit /b 1
\techo.
\techo.Build finished; now you can run HTML Help Workshop with the ^
.hhp project file in %%BUILDDIR%%/htmlhelp.
@@ -562,6 +600,7 @@ if "%%1" == "htmlhelp" (
if "%%1" == "qthelp" (
\t%%SPHINXBUILD%% -b qthelp %%ALLSPHINXOPTS%% %%BUILDDIR%%/qthelp
+\tif errorlevel 1 exit /b 1
\techo.
\techo.Build finished; now you can run "qcollectiongenerator" with the ^
.qhcp project file in %%BUILDDIR%%/qthelp, like this:
@@ -573,6 +612,7 @@ if "%%1" == "qthelp" (
if "%%1" == "devhelp" (
\t%%SPHINXBUILD%% -b devhelp %%ALLSPHINXOPTS%% %%BUILDDIR%%/devhelp
+\tif errorlevel 1 exit /b 1
\techo.
\techo.Build finished.
\tgoto end
@@ -580,6 +620,7 @@ if "%%1" == "devhelp" (
if "%%1" == "epub" (
\t%%SPHINXBUILD%% -b epub %%ALLSPHINXOPTS%% %%BUILDDIR%%/epub
+\tif errorlevel 1 exit /b 1
\techo.
\techo.Build finished. The epub file is in %%BUILDDIR%%/epub.
\tgoto end
@@ -587,6 +628,7 @@ if "%%1" == "epub" (
if "%%1" == "latex" (
\t%%SPHINXBUILD%% -b latex %%ALLSPHINXOPTS%% %%BUILDDIR%%/latex
+\tif errorlevel 1 exit /b 1
\techo.
\techo.Build finished; the LaTeX files are in %%BUILDDIR%%/latex.
\tgoto end
@@ -594,6 +636,7 @@ if "%%1" == "latex" (
if "%%1" == "text" (
\t%%SPHINXBUILD%% -b text %%ALLSPHINXOPTS%% %%BUILDDIR%%/text
+\tif errorlevel 1 exit /b 1
\techo.
\techo.Build finished. The text files are in %%BUILDDIR%%/text.
\tgoto end
@@ -601,13 +644,23 @@ if "%%1" == "text" (
if "%%1" == "man" (
\t%%SPHINXBUILD%% -b man %%ALLSPHINXOPTS%% %%BUILDDIR%%/man
+\tif errorlevel 1 exit /b 1
\techo.
\techo.Build finished. The manual pages are in %%BUILDDIR%%/man.
\tgoto end
)
+if "%%1" == "texinfo" (
+\t%%SPHINXBUILD%% -b texinfo %%ALLSPHINXOPTS%% %%BUILDDIR%%/texinfo
+\tif errorlevel 1 exit /b 1
+\techo.
+\techo.Build finished. The Texinfo files are in %%BUILDDIR%%/texinfo.
+\tgoto end
+)
+
if "%%1" == "gettext" (
\t%%SPHINXBUILD%% -b gettext %%ALLSPHINXOPTS%% %%BUILDDIR%%/locale
+\tif errorlevel 1 exit /b 1
\techo.
\techo.Build finished. The message catalogs are in %%BUILDDIR%%/locale.
\tgoto end
@@ -615,6 +668,7 @@ if "%%1" == "gettext" (
if "%%1" == "changes" (
\t%%SPHINXBUILD%% -b changes %%ALLSPHINXOPTS%% %%BUILDDIR%%/changes
+\tif errorlevel 1 exit /b 1
\techo.
\techo.The overview file is in %%BUILDDIR%%/changes.
\tgoto end
@@ -622,6 +676,7 @@ if "%%1" == "changes" (
if "%%1" == "linkcheck" (
\t%%SPHINXBUILD%% -b linkcheck %%ALLSPHINXOPTS%% %%BUILDDIR%%/linkcheck
+\tif errorlevel 1 exit /b 1
\techo.
\techo.Link check complete; look for any errors in the above output ^
or in %%BUILDDIR%%/linkcheck/output.txt.
@@ -630,6 +685,7 @@ or in %%BUILDDIR%%/linkcheck/output.txt.
if "%%1" == "doctest" (
\t%%SPHINXBUILD%% -b doctest %%ALLSPHINXOPTS%% %%BUILDDIR%%/doctest
+\tif errorlevel 1 exit /b 1
\techo.
\techo.Testing of doctests in the sources finished, look at the ^
results in %%BUILDDIR%%/doctest/output.txt.
@@ -731,14 +787,24 @@ def inner_main(args):
if not color_terminal():
nocolor()
+ if len(args) > 3:
+ print 'Usage: sphinx-quickstart [root]'
+ sys.exit(1)
+ elif len(args) == 2:
+ d['path'] = args[1]
+
print bold('Welcome to the Sphinx %s quickstart utility.') % __version__
print '''
Please enter values for the following settings (just press Enter to
accept a default value, if one is given in brackets).'''
- print '''
+ if 'path' in d:
+ print bold('''
+Selected root path: %s''' % d['path'])
+ else:
+ print '''
Enter the root path for documentation.'''
- do_prompt(d, 'path', 'Root path for the documentation', '.', is_path)
+ do_prompt(d, 'path', 'Root path for the documentation', '.', is_path)
while path.isfile(path.join(d['path'], 'conf.py')) or \
path.isfile(path.join(d['path'], 'source', 'conf.py')):
diff --git a/sphinx/roles.py b/sphinx/roles.py
index b44868e6..1d791f6d 100644
--- a/sphinx/roles.py
+++ b/sphinx/roles.py
@@ -5,7 +5,7 @@
Handlers for additional ReST roles.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -170,8 +170,8 @@ def indexmarkup_role(typ, rawtext, etext, lineno, inliner,
inliner.document.note_explicit_target(targetnode)
if typ == 'pep':
indexnode['entries'] = [
- ('single', _('Python Enhancement Proposals!PEP %s') % text,
- targetid, 'PEP %s' % text)]
+ ('single', _('Python Enhancement Proposals; PEP %s') % text,
+ targetid, '')]
anchor = ''
anchorindex = text.find('#')
if anchorindex > 0:
@@ -190,8 +190,7 @@ def indexmarkup_role(typ, rawtext, etext, lineno, inliner,
rn += sn
return [indexnode, targetnode, rn], []
elif typ == 'rfc':
- indexnode['entries'] = [('single', 'RFC; RFC %s' % text,
- targetid, 'RFC %s' % text)]
+ indexnode['entries'] = [('single', 'RFC; RFC %s' % text, targetid, '')]
anchor = ''
anchorindex = text.find('#')
if anchorindex > 0:
@@ -282,7 +281,13 @@ def index_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
entries = process_index_entry(target, targetid)
# otherwise we just create a "single" entry
else:
- entries = [('single', target, targetid, target)]
+ # but allow giving main entry
+ main = ''
+ if target.startswith('!'):
+ target = target[1:]
+ title = title[1:]
+ main = 'main'
+ entries = [('single', target, targetid, main)]
indexnode = addnodes.index()
indexnode['entries'] = entries
textnode = nodes.Text(title, title)
diff --git a/sphinx/search.py b/sphinx/search/__init__.py
index 729b63b2..645ebfc8 100644
--- a/sphinx/search.py
+++ b/sphinx/search/__init__.py
@@ -3,9 +3,9 @@
sphinx.search
~~~~~~~~~~~~~
- Create a search index for offline search.
+ Create a full-text search index for offline search.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import re
@@ -14,28 +14,90 @@ import cPickle as pickle
from docutils.nodes import comment, Text, NodeVisitor, SkipNode
from sphinx.util import jsdump, rpartition
-try:
- # http://bitbucket.org/methane/porterstemmer/
- from porterstemmer import Stemmer as CStemmer
- CSTEMMER = True
-except ImportError:
- from sphinx.util.stemmer import PorterStemmer
- CSTEMMER = False
-
-
-word_re = re.compile(r'\w+(?u)')
-
-stopwords = set("""
-a and are as at
-be but by
-for
-if in into is it
-near no not
-of on or
-such
-that the their then there these they this to
-was will with
-""".split())
+
+
+class SearchLanguage(object):
+ """
+ This class is the base class for search natural language preprocessors. If
+ you want to add support for a new language, you should override the methods
+ of this class.
+
+ You should override `lang` class property too (e.g. 'en', 'fr' and so on).
+
+ .. attribute:: stopwords
+
+ This is a set of stop words of the target language. Default `stopwords`
+ is empty. This word is used for building index and embedded in JS.
+
+ .. attribute:: js_stemmer_code
+
+ Return stemmer class of JavaScript version. This class' name should be
+ ``Stemmer`` and this class must have ``stemWord`` method. This string is
+ embedded as-is in searchtools.js.
+
+ This class is used to preprocess search word which Sphinx HTML readers
+ type, before searching index. Default implementation does nothing.
+ """
+ lang = None
+ stopwords = set()
+ js_stemmer_code = """
+/**
+ * Dummy stemmer for languages without stemming rules.
+ */
+var Stemmer = function() {
+ this.stemWord = function(w) {
+ return w;
+ }
+}
+"""
+
+ _word_re = re.compile(r'\w+(?u)')
+
+ def __init__(self, options):
+ self.options = options
+ self.init(options)
+
+ def init(self, options):
+ """
+ Initialize the class with the options the user has given.
+ """
+
+ def split(self, input):
+ """
+ This method splits a sentence into words. Default splitter splits input
+ at white spaces, which should be enough for most languages except CJK
+ languages.
+ """
+ return self._word_re.findall(input)
+
+ def stem(self, word):
+ """
+ This method implements stemming algorithm of the Python version.
+
+ Default implementation does nothing. You should implement this if the
+ language has any stemming rules.
+
+ This class is used to preprocess search words before registering them in
+ the search index. The stemming of the Python version and the JS version
+ (given in the js_stemmer_code attribute) must be compatible.
+ """
+ return word
+
+ def word_filter(self, word):
+ """
+ Return true if the target word should be registered in the search index.
+ This method is called after stemming.
+ """
+ return not (((len(word) < 3) and (12353 < ord(word[0]) < 12436)) or
+ (ord(word[0]) < 256 and (len(word) < 3 or word in self.stopwords or
+ word.isdigit())))
+
+from sphinx.search import en, ja
+
+languages = {
+ 'en': en.SearchEnglish,
+ 'ja': ja.SearchJapanese,
+}
class _JavaScriptIndex(object):
@@ -67,39 +129,21 @@ class _JavaScriptIndex(object):
js_index = _JavaScriptIndex()
-if CSTEMMER:
- class Stemmer(CStemmer):
-
- def stem(self, word):
- return self(word.lower())
-
-else:
- class Stemmer(PorterStemmer):
- """
- All those porter stemmer implementations look hideous.
- make at least the stem method nicer.
- """
-
- def stem(self, word):
- word = word.lower()
- return PorterStemmer.stem(self, word, 0, len(word) - 1)
-
-
-
class WordCollector(NodeVisitor):
"""
A special visitor that collects words for the `IndexBuilder`.
"""
- def __init__(self, document):
+ def __init__(self, document, lang):
NodeVisitor.__init__(self, document)
self.found_words = []
+ self.lang = lang
def dispatch_visit(self, node):
if node.__class__ is comment:
raise SkipNode
if node.__class__ is Text:
- self.found_words.extend(word_re.findall(node.astext()))
+ self.found_words.extend(self.lang.split(node.astext()))
class IndexBuilder(object):
@@ -112,9 +156,8 @@ class IndexBuilder(object):
'pickle': pickle
}
- def __init__(self, env):
+ def __init__(self, env, lang, options):
self.env = env
- self._stemmer = Stemmer()
# filename -> title
self._titles = {}
# stemmed word -> set(filenames)
@@ -123,6 +166,8 @@ class IndexBuilder(object):
self._objtypes = {}
# objtype index -> objname (localized)
self._objnames = {}
+ # add language-specific SearchLanguage instance
+ self.lang = languages[lang](options)
def load(self, stream, format):
"""Reconstruct from frozen data."""
@@ -215,17 +260,22 @@ class IndexBuilder(object):
"""Feed a doctree to the index."""
self._titles[filename] = title
- visitor = WordCollector(doctree)
+ visitor = WordCollector(doctree, self.lang)
doctree.walk(visitor)
- def add_term(word, stem=self._stemmer.stem):
+ def add_term(word, stem=self.lang.stem):
word = stem(word)
- if len(word) < 3 or word in stopwords or word.isdigit():
- return
- self._mapping.setdefault(word, set()).add(filename)
+ if self.lang.word_filter(word):
+ self._mapping.setdefault(word, set()).add(filename)
- for word in word_re.findall(title):
+ for word in self.lang.split(title):
add_term(word)
for word in visitor.found_words:
add_term(word)
+
+ def context_for_searchtool(self):
+ return dict(
+ search_language_stemming_code = self.lang.js_stemmer_code,
+ search_language_stop_words = jsdump.dumps(self.lang.stopwords),
+ )
diff --git a/sphinx/search/en.py b/sphinx/search/en.py
new file mode 100644
index 00000000..16e93e89
--- /dev/null
+++ b/sphinx/search/en.py
@@ -0,0 +1,242 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.search_languages.en
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ English search language: includes the JS porter stemmer.
+
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+from sphinx.search import SearchLanguage
+
+try:
+ # http://bitbucket.org/methane/porterstemmer/
+ from porterstemmer import Stemmer as CStemmer
+ CSTEMMER = True
+except ImportError:
+ from sphinx.util.stemmer import PorterStemmer
+ CSTEMMER = False
+
+
+english_stopwords = set("""
+a and are as at
+be but by
+for
+if in into is it
+near no not
+of on or
+such
+that the their then there these they this to
+was will with
+""".split())
+
+js_porter_stemmer = """
+/**
+ * Porter Stemmer
+ */
+var Stemmer = function() {
+
+ var step2list = {
+ ational: 'ate',
+ tional: 'tion',
+ enci: 'ence',
+ anci: 'ance',
+ izer: 'ize',
+ bli: 'ble',
+ alli: 'al',
+ entli: 'ent',
+ eli: 'e',
+ ousli: 'ous',
+ ization: 'ize',
+ ation: 'ate',
+ ator: 'ate',
+ alism: 'al',
+ iveness: 'ive',
+ fulness: 'ful',
+ ousness: 'ous',
+ aliti: 'al',
+ iviti: 'ive',
+ biliti: 'ble',
+ logi: 'log'
+ };
+
+ var step3list = {
+ icate: 'ic',
+ ative: '',
+ alize: 'al',
+ iciti: 'ic',
+ ical: 'ic',
+ ful: '',
+ ness: ''
+ };
+
+ var c = "[^aeiou]"; // consonant
+ var v = "[aeiouy]"; // vowel
+ var C = c + "[^aeiouy]*"; // consonant sequence
+ var V = v + "[aeiou]*"; // vowel sequence
+
+ var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0
+ var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1
+ var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1
+ var s_v = "^(" + C + ")?" + v; // vowel in stem
+
+ this.stemWord = function (w) {
+ var stem;
+ var suffix;
+ var firstch;
+ var origword = w;
+
+ if (w.length < 3)
+ return w;
+
+ var re;
+ var re2;
+ var re3;
+ var re4;
+
+ firstch = w.substr(0,1);
+ if (firstch == "y")
+ w = firstch.toUpperCase() + w.substr(1);
+
+ // Step 1a
+ re = /^(.+?)(ss|i)es$/;
+ re2 = /^(.+?)([^s])s$/;
+
+ if (re.test(w))
+ w = w.replace(re,"$1$2");
+ else if (re2.test(w))
+ w = w.replace(re2,"$1$2");
+
+ // Step 1b
+ re = /^(.+?)eed$/;
+ re2 = /^(.+?)(ed|ing)$/;
+ if (re.test(w)) {
+ var fp = re.exec(w);
+ re = new RegExp(mgr0);
+ if (re.test(fp[1])) {
+ re = /.$/;
+ w = w.replace(re,"");
+ }
+ }
+ else if (re2.test(w)) {
+ var fp = re2.exec(w);
+ stem = fp[1];
+ re2 = new RegExp(s_v);
+ if (re2.test(stem)) {
+ w = stem;
+ re2 = /(at|bl|iz)$/;
+ re3 = new RegExp("([^aeiouylsz])\\1$");
+ re4 = new RegExp("^" + C + v + "[^aeiouwxy]$");
+ if (re2.test(w))
+ w = w + "e";
+ else if (re3.test(w)) {
+ re = /.$/;
+ w = w.replace(re,"");
+ }
+ else if (re4.test(w))
+ w = w + "e";
+ }
+ }
+
+ // Step 1c
+ re = /^(.+?)y$/;
+ if (re.test(w)) {
+ var fp = re.exec(w);
+ stem = fp[1];
+ re = new RegExp(s_v);
+ if (re.test(stem))
+ w = stem + "i";
+ }
+
+ // Step 2
+ re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;
+ if (re.test(w)) {
+ var fp = re.exec(w);
+ stem = fp[1];
+ suffix = fp[2];
+ re = new RegExp(mgr0);
+ if (re.test(stem))
+ w = stem + step2list[suffix];
+ }
+
+ // Step 3
+ re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;
+ if (re.test(w)) {
+ var fp = re.exec(w);
+ stem = fp[1];
+ suffix = fp[2];
+ re = new RegExp(mgr0);
+ if (re.test(stem))
+ w = stem + step3list[suffix];
+ }
+
+ // Step 4
+ re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;
+ re2 = /^(.+?)(s|t)(ion)$/;
+ if (re.test(w)) {
+ var fp = re.exec(w);
+ stem = fp[1];
+ re = new RegExp(mgr1);
+ if (re.test(stem))
+ w = stem;
+ }
+ else if (re2.test(w)) {
+ var fp = re2.exec(w);
+ stem = fp[1] + fp[2];
+ re2 = new RegExp(mgr1);
+ if (re2.test(stem))
+ w = stem;
+ }
+
+ // Step 5
+ re = /^(.+?)e$/;
+ if (re.test(w)) {
+ var fp = re.exec(w);
+ stem = fp[1];
+ re = new RegExp(mgr1);
+ re2 = new RegExp(meq1);
+ re3 = new RegExp("^" + C + v + "[^aeiouwxy]$");
+ if (re.test(stem) || (re2.test(stem) && !(re3.test(stem))))
+ w = stem;
+ }
+ re = /ll$/;
+ re2 = new RegExp(mgr1);
+ if (re.test(w) && re2.test(w)) {
+ re = /.$/;
+ w = w.replace(re,"");
+ }
+
+ // and turn initial Y back to y
+ if (firstch == "y")
+ w = firstch.toLowerCase() + w.substr(1);
+ return w;
+ }
+}
+"""
+
+
+class SearchEnglish(SearchLanguage):
+ lang = 'en'
+ js_stemmer_code = js_porter_stemmer
+ stopwords = english_stopwords
+
+ def init(self, options):
+ if CSTEMMER:
+ class Stemmer(CStemmer):
+ def stem(self, word):
+ return self(word.lower())
+ else:
+ class Stemmer(PorterStemmer):
+ """All those porter stemmer implementations look hideous;
+ make at least the stem method nicer.
+ """
+ def stem(self, word):
+ word = word.lower()
+ return PorterStemmer.stem(self, word, 0, len(word) - 1)
+
+ self.stemmer = Stemmer()
+
+ def stem(self, word):
+ return self.stemmer.stem(word)
diff --git a/sphinx/search/ja.py b/sphinx/search/ja.py
new file mode 100644
index 00000000..18d21bf0
--- /dev/null
+++ b/sphinx/search/ja.py
@@ -0,0 +1,273 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.search_languages.ja
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Japanese search language: includes routine to split words.
+
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+# Python Version of TinySegmenter
+# (http://chasen.org/~taku/software/TinySegmenter/)
+# TinySegmenter is super compact Japanese tokenizer.
+#
+# TinySegmenter was originally developed by Taku Kudo <taku(at)chasen.org>.
+# Python Version was developed by xnights <programming.magic(at)gmail.com>.
+# For details, see http://programming-magic.com/?id=170
+
+import os
+import re
+import sys
+
+try:
+ import MeCab
+ native_module = True
+except ImportError:
+ native_module = False
+
+from sphinx.search import SearchLanguage
+
+
+class MecabBinder(object):
+ def __init__(self, options):
+ self.ctypes_libmecab = None
+ self.ctypes_mecab = None
+ if not native_module:
+ self.init_ctypes(options)
+ else:
+ self.init_native(options)
+ self.dict_encode = options.get('dic_enc', 'utf-8')
+
+ def split(self, input):
+ input2 = input.encode(self.dict_encode)
+ if native_module:
+ result = self.native.parse(input2)
+ else:
+ result = self.ctypes_libmecab.mecab_sparse_tostr(
+ self.ctypes_mecab, input)
+ return result.decode(self.dict_encode).split(' ')
+
+ def init_native(self, options):
+ param = '-Owakati'
+ dict = options.get('dict')
+ if dict:
+ param += ' -d %s' % dict
+ self.native = MeCab.Tagger(param)
+
+ def init_ctypes(self, options):
+ import ctypes.util
+
+ lib = options.get('lib')
+
+ if lib is None:
+ if sys.platform.startswith('win'):
+ libname = 'libmecab.dll'
+ else:
+ libname = 'mecab'
+ libpath = ctypes.util.find_library(libname)
+ elif os.path.basename(lib) == lib:
+ libpath = ctypes.util.find_library(lib)
+ else:
+ libpath = None
+ if os.path.exists(lib):
+ libpath = lib
+ if libpath is None:
+ raise RuntimeError('MeCab dynamic library is not available')
+
+ param = 'mecab -Owakati'
+ dict = options.get('dict')
+ if dict:
+ param += ' -d %s' % dict
+
+ self.ctypes_libmecab = ctypes.CDLL(libpath)
+ self.ctypes_libmecab.mecab_sparse_tostr.restype = ctypes.c_char_p
+ self.ctypes_mecab = self.libmecab.mecab_new2(param)
+
+ def __del__(self):
+ if self.ctypes_libmecab:
+ self.ctypes_libmecab.mecab_destroy(self.ctypes_mecab)
+
+
+class TinySegmenter(object):
+ patterns_ = dict([(re.compile(pattern), value) for pattern, value in {
+ u'[一二三四五六七八九十百千万億兆]': u'M',
+ u'[一-龠々〆ヵヶ]': u'H',
+ u'[ぁ-ん]': u'I',
+ u'[ァ-ヴーア-ン゙ー]': u'K',
+ u'[a-zA-Za-zA-Z]': u'A',
+ u'[0-90-9]': u'N',
+ }.iteritems()])
+ BIAS__ = -332
+ BC1__ = {u'HH':6,u'II':2461,u'KH':406,u'OH':-1378}
+ BC2__ = {u'AA':-3267,u'AI':2744,u'AN':-878,u'HH':-4070,u'HM':-1711,u'HN':4012,u'HO':3761,u'IA':1327,u'IH':-1184,u'II':-1332,u'IK':1721,u'IO':5492,u'KI':3831,u'KK':-8741,u'MH':-3132,u'MK':3334,u'OO':-2920}
+ BC3__ = {u'HH':996,u'HI':626,u'HK':-721,u'HN':-1307,u'HO':-836,u'IH':-301,u'KK':2762,u'MK':1079,u'MM':4034,u'OA':-1652,u'OH':266}
+ BP1__ = {u'BB':295,u'OB':304,u'OO':-125,u'UB':352}
+ BP2__ = {u'BO':60,u'OO':-1762}
+ BQ1__ = {u'BHH':1150,u'BHM':1521,u'BII':-1158,u'BIM':886,u'BMH':1208,u'BNH':449,u'BOH':-91,u'BOO':-2597,u'OHI':451,u'OIH':-296,u'OKA':1851,u'OKH':-1020,u'OKK':904,u'OOO':2965}
+ BQ2__ = {u'BHH':118,u'BHI':-1159,u'BHM':466,u'BIH':-919,u'BKK':-1720,u'BKO':864,u'OHH':-1139,u'OHM':-181,u'OIH':153,u'UHI':-1146}
+ BQ3__ = {u'BHH':-792,u'BHI':2664,u'BII':-299,u'BKI':419,u'BMH':937,u'BMM':8335,u'BNN':998,u'BOH':775,u'OHH':2174,u'OHM':439,u'OII':280,u'OKH':1798,u'OKI':-793,u'OKO':-2242,u'OMH':-2402,u'OOO':11699}
+ BQ4__ = {u'BHH':-3895,u'BIH':3761,u'BII':-4654,u'BIK':1348,u'BKK':-1806,u'BMI':-3385,u'BOO':-12396,u'OAH':926,u'OHH':266,u'OHK':-2036,u'ONN':-973}
+ BW1__ = {u',と':660,u',同':727,u'B1あ':1404,u'B1同':542,u'、と':660,u'、同':727,u'」と':1682,u'あっ':1505,u'いう':1743,u'いっ':-2055,u'いる':672,u'うし':-4817,u'うん':665,u'から':3472,u'がら':600,u'こう':-790,u'こと':2083,u'こん':-1262,u'さら':-4143,u'さん':4573,u'した':2641,u'して':1104,u'すで':-3399,u'そこ':1977,u'それ':-871,u'たち':1122,u'ため':601,u'った':3463,u'つい':-802,u'てい':805,u'てき':1249,u'でき':1127,u'です':3445,u'では':844,u'とい':-4915,u'とみ':1922,u'どこ':3887,u'ない':5713,u'なっ':3015,u'など':7379,u'なん':-1113,u'にし':2468,u'には':1498,u'にも':1671,u'に対':-912,u'の一':-501,u'の中':741,u'ませ':2448,u'まで':1711,u'まま':2600,u'まる':-2155,u'やむ':-1947,u'よっ':-2565,u'れた':2369,u'れで':-913,u'をし':1860,u'を見':731,u'亡く':-1886,u'京都':2558,u'取り':-2784,u'大き':-2604,u'大阪':1497,u'平方':-2314,u'引き':-1336,u'日本':-195,u'本当':-2423,u'毎日':-2113,u'目指':-724,u'B1あ':1404,u'B1同':542,u'」と':1682}
+ BW2__ = {u'..':-11822,u'11':-669,u'――':-5730,u'−−':-13175,u'いう':-1609,u'うか':2490,u'かし':-1350,u'かも':-602,u'から':-7194,u'かれ':4612,u'がい':853,u'がら':-3198,u'きた':1941,u'くな':-1597,u'こと':-8392,u'この':-4193,u'させ':4533,u'され':13168,u'さん':-3977,u'しい':-1819,u'しか':-545,u'した':5078,u'して':972,u'しな':939,u'その':-3744,u'たい':-1253,u'たた':-662,u'ただ':-3857,u'たち':-786,u'たと':1224,u'たは':-939,u'った':4589,u'って':1647,u'っと':-2094,u'てい':6144,u'てき':3640,u'てく':2551,u'ては':-3110,u'ても':-3065,u'でい':2666,u'でき':-1528,u'でし':-3828,u'です':-4761,u'でも':-4203,u'とい':1890,u'とこ':-1746,u'とと':-2279,u'との':720,u'とみ':5168,u'とも':-3941,u'ない':-2488,u'なが':-1313,u'など':-6509,u'なの':2614,u'なん':3099,u'にお':-1615,u'にし':2748,u'にな':2454,u'によ':-7236,u'に対':-14943,u'に従':-4688,u'に関':-11388,u'のか':2093,u'ので':-7059,u'のに':-6041,u'のの':-6125,u'はい':1073,u'はが':-1033,u'はず':-2532,u'ばれ':1813,u'まし':-1316,u'まで':-6621,u'まれ':5409,u'めて':-3153,u'もい':2230,u'もの':-10713,u'らか':-944,u'らし':-1611,u'らに':-1897,u'りし':651,u'りま':1620,u'れた':4270,u'れて':849,u'れば':4114,u'ろう':6067,u'われ':7901,u'を通':-11877,u'んだ':728,u'んな':-4115,u'一人':602,u'一方':-1375,u'一日':970,u'一部':-1051,u'上が':-4479,u'会社':-1116,u'出て':2163,u'分の':-7758,u'同党':970,u'同日':-913,u'大阪':-2471,u'委員':-1250,u'少な':-1050,u'年度':-8669,u'年間':-1626,u'府県':-2363,u'手権':-1982,u'新聞':-4066,u'日新':-722,u'日本':-7068,u'日米':3372,u'曜日':-601,u'朝鮮':-2355,u'本人':-2697,u'東京':-1543,u'然と':-1384,u'社会':-1276,u'立て':-990,u'第に':-1612,u'米国':-4268,u'11':-669}
+ BW3__ = {u'あた':-2194,u'あり':719,u'ある':3846,u'い.':-1185,u'い。':-1185,u'いい':5308,u'いえ':2079,u'いく':3029,u'いた':2056,u'いっ':1883,u'いる':5600,u'いわ':1527,u'うち':1117,u'うと':4798,u'えと':1454,u'か.':2857,u'か。':2857,u'かけ':-743,u'かっ':-4098,u'かに':-669,u'から':6520,u'かり':-2670,u'が,':1816,u'が、':1816,u'がき':-4855,u'がけ':-1127,u'がっ':-913,u'がら':-4977,u'がり':-2064,u'きた':1645,u'けど':1374,u'こと':7397,u'この':1542,u'ころ':-2757,u'さい':-714,u'さを':976,u'し,':1557,u'し、':1557,u'しい':-3714,u'した':3562,u'して':1449,u'しな':2608,u'しま':1200,u'す.':-1310,u'す。':-1310,u'する':6521,u'ず,':3426,u'ず、':3426,u'ずに':841,u'そう':428,u'た.':8875,u'た。':8875,u'たい':-594,u'たの':812,u'たり':-1183,u'たる':-853,u'だ.':4098,u'だ。':4098,u'だっ':1004,u'った':-4748,u'って':300,u'てい':6240,u'てお':855,u'ても':302,u'です':1437,u'でに':-1482,u'では':2295,u'とう':-1387,u'とし':2266,u'との':541,u'とも':-3543,u'どう':4664,u'ない':1796,u'なく':-903,u'など':2135,u'に,':-1021,u'に、':-1021,u'にし':1771,u'にな':1906,u'には':2644,u'の,':-724,u'の、':-724,u'の子':-1000,u'は,':1337,u'は、':1337,u'べき':2181,u'まし':1113,u'ます':6943,u'まっ':-1549,u'まで':6154,u'まれ':-793,u'らし':1479,u'られ':6820,u'るる':3818,u'れ,':854,u'れ、':854,u'れた':1850,u'れて':1375,u'れば':-3246,u'れる':1091,u'われ':-605,u'んだ':606,u'んで':798,u'カ月':990,u'会議':860,u'入り':1232,u'大会':2217,u'始め':1681,u'市':965,u'新聞':-5055,u'日,':974,u'日、':974,u'社会':2024,u'カ月':990}
+ TC1__ = {u'AAA':1093,u'HHH':1029,u'HHM':580,u'HII':998,u'HOH':-390,u'HOM':-331,u'IHI':1169,u'IOH':-142,u'IOI':-1015,u'IOM':467,u'MMH':187,u'OOI':-1832}
+ TC2__ = {u'HHO':2088,u'HII':-1023,u'HMM':-1154,u'IHI':-1965,u'KKH':703,u'OII':-2649}
+ TC3__ = {u'AAA':-294,u'HHH':346,u'HHI':-341,u'HII':-1088,u'HIK':731,u'HOH':-1486,u'IHH':128,u'IHI':-3041,u'IHO':-1935,u'IIH':-825,u'IIM':-1035,u'IOI':-542,u'KHH':-1216,u'KKA':491,u'KKH':-1217,u'KOK':-1009,u'MHH':-2694,u'MHM':-457,u'MHO':123,u'MMH':-471,u'NNH':-1689,u'NNO':662,u'OHO':-3393}
+ TC4__ = {u'HHH':-203,u'HHI':1344,u'HHK':365,u'HHM':-122,u'HHN':182,u'HHO':669,u'HIH':804,u'HII':679,u'HOH':446,u'IHH':695,u'IHO':-2324,u'IIH':321,u'III':1497,u'IIO':656,u'IOO':54,u'KAK':4845,u'KKA':3386,u'KKK':3065,u'MHH':-405,u'MHI':201,u'MMH':-241,u'MMM':661,u'MOM':841}
+ TQ1__ = {u'BHHH':-227,u'BHHI':316,u'BHIH':-132,u'BIHH':60,u'BIII':1595,u'BNHH':-744,u'BOHH':225,u'BOOO':-908,u'OAKK':482,u'OHHH':281,u'OHIH':249,u'OIHI':200,u'OIIH':-68}
+ TQ2__ = {u'BIHH':-1401,u'BIII':-1033,u'BKAK':-543,u'BOOO':-5591}
+ TQ3__ = {u'BHHH':478,u'BHHM':-1073,u'BHIH':222,u'BHII':-504,u'BIIH':-116,u'BIII':-105,u'BMHI':-863,u'BMHM':-464,u'BOMH':620,u'OHHH':346,u'OHHI':1729,u'OHII':997,u'OHMH':481,u'OIHH':623,u'OIIH':1344,u'OKAK':2792,u'OKHH':587,u'OKKA':679,u'OOHH':110,u'OOII':-685}
+ TQ4__ = {u'BHHH':-721,u'BHHM':-3604,u'BHII':-966,u'BIIH':-607,u'BIII':-2181,u'OAAA':-2763,u'OAKK':180,u'OHHH':-294,u'OHHI':2446,u'OHHO':480,u'OHIH':-1573,u'OIHH':1935,u'OIHI':-493,u'OIIH':626,u'OIII':-4007,u'OKAK':-8156}
+ TW1__ = {u'につい':-4681,u'東京都':2026}
+ TW2__ = {u'ある程':-2049,u'いった':-1256,u'ころが':-2434,u'しょう':3873,u'その後':-4430,u'だって':-1049,u'ていた':1833,u'として':-4657,u'ともに':-4517,u'もので':1882,u'一気に':-792,u'初めて':-1512,u'同時に':-8097,u'大きな':-1255,u'対して':-2721,u'社会党':-3216}
+ TW3__ = {u'いただ':-1734,u'してい':1314,u'として':-4314,u'につい':-5483,u'にとっ':-5989,u'に当た':-6247,u'ので,':-727,u'ので、':-727,u'のもの':-600,u'れから':-3752,u'十二月':-2287}
+ TW4__ = {u'いう.':8576,u'いう。':8576,u'からな':-2348,u'してい':2958,u'たが,':1516,u'たが、':1516,u'ている':1538,u'という':1349,u'ました':5543,u'ません':1097,u'ようと':-4258,u'よると':5865}
+ UC1__ = {u'A':484,u'K':93,u'M':645,u'O':-505}
+ UC2__ = {u'A':819,u'H':1059,u'I':409,u'M':3987,u'N':5775,u'O':646}
+ UC3__ = {u'A':-1370,u'I':2311}
+ UC4__ = {u'A':-2643,u'H':1809,u'I':-1032,u'K':-3450,u'M':3565,u'N':3876,u'O':6646}
+ UC5__ = {u'H':313,u'I':-1238,u'K':-799,u'M':539,u'O':-831}
+ UC6__ = {u'H':-506,u'I':-253,u'K':87,u'M':247,u'O':-387}
+ UP1__ = {u'O':-214}
+ UP2__ = {u'B':69,u'O':935}
+ UP3__ = {u'B':189}
+ UQ1__ = {u'BH':21,u'BI':-12,u'BK':-99,u'BN':142,u'BO':-56,u'OH':-95,u'OI':477,u'OK':410,u'OO':-2422}
+ UQ2__ = {u'BH':216,u'BI':113,u'OK':1759}
+ UQ3__ = {u'BA':-479,u'BH':42,u'BI':1913,u'BK':-7198,u'BM':3160,u'BN':6427,u'BO':14761,u'OI':-827,u'ON':-3212}
+ UW1__ = {u',':156,u'、':156,u'「':-463,u'あ':-941,u'う':-127,u'が':-553,u'き':121,u'こ':505,u'で':-201,u'と':-547,u'ど':-123,u'に':-789,u'の':-185,u'は':-847,u'も':-466,u'や':-470,u'よ':182,u'ら':-292,u'り':208,u'れ':169,u'を':-446,u'ん':-137,u'・':-135,u'主':-402,u'京':-268,u'区':-912,u'午':871,u'国':-460,u'大':561,u'委':729,u'市':-411,u'日':-141,u'理':361,u'生':-408,u'県':-386,u'都':-718,u'「':-463,u'・':-135}
+ UW2__ = {u',':-829,u'、':-829,u'〇':892,u'「':-645,u'」':3145,u'あ':-538,u'い':505,u'う':134,u'お':-502,u'か':1454,u'が':-856,u'く':-412,u'こ':1141,u'さ':878,u'ざ':540,u'し':1529,u'す':-675,u'せ':300,u'そ':-1011,u'た':188,u'だ':1837,u'つ':-949,u'て':-291,u'で':-268,u'と':-981,u'ど':1273,u'な':1063,u'に':-1764,u'の':130,u'は':-409,u'ひ':-1273,u'べ':1261,u'ま':600,u'も':-1263,u'や':-402,u'よ':1639,u'り':-579,u'る':-694,u'れ':571,u'を':-2516,u'ん':2095,u'ア':-587,u'カ':306,u'キ':568,u'ッ':831,u'三':-758,u'不':-2150,u'世':-302,u'中':-968,u'主':-861,u'事':492,u'人':-123,u'会':978,u'保':362,u'入':548,u'初':-3025,u'副':-1566,u'北':-3414,u'区':-422,u'大':-1769,u'天':-865,u'太':-483,u'子':-1519,u'学':760,u'実':1023,u'小':-2009,u'市':-813,u'年':-1060,u'強':1067,u'手':-1519,u'揺':-1033,u'政':1522,u'文':-1355,u'新':-1682,u'日':-1815,u'明':-1462,u'最':-630,u'朝':-1843,u'本':-1650,u'東':-931,u'果':-665,u'次':-2378,u'民':-180,u'気':-1740,u'理':752,u'発':529,u'目':-1584,u'相':-242,u'県':-1165,u'立':-763,u'第':810,u'米':509,u'自':-1353,u'行':838,u'西':-744,u'見':-3874,u'調':1010,u'議':1198,u'込':3041,u'開':1758,u'間':-1257,u'「':-645,u'」':3145,u'ッ':831,u'ア':-587,u'カ':306,u'キ':568}
+ UW3__ = {u',':4889,u'1':-800,u'−':-1723,u'、':4889,u'々':-2311,u'〇':5827,u'」':2670,u'〓':-3573,u'あ':-2696,u'い':1006,u'う':2342,u'え':1983,u'お':-4864,u'か':-1163,u'が':3271,u'く':1004,u'け':388,u'げ':401,u'こ':-3552,u'ご':-3116,u'さ':-1058,u'し':-395,u'す':584,u'せ':3685,u'そ':-5228,u'た':842,u'ち':-521,u'っ':-1444,u'つ':-1081,u'て':6167,u'で':2318,u'と':1691,u'ど':-899,u'な':-2788,u'に':2745,u'の':4056,u'は':4555,u'ひ':-2171,u'ふ':-1798,u'へ':1199,u'ほ':-5516,u'ま':-4384,u'み':-120,u'め':1205,u'も':2323,u'や':-788,u'よ':-202,u'ら':727,u'り':649,u'る':5905,u'れ':2773,u'わ':-1207,u'を':6620,u'ん':-518,u'ア':551,u'グ':1319,u'ス':874,u'ッ':-1350,u'ト':521,u'ム':1109,u'ル':1591,u'ロ':2201,u'ン':278,u'・':-3794,u'一':-1619,u'下':-1759,u'世':-2087,u'両':3815,u'中':653,u'主':-758,u'予':-1193,u'二':974,u'人':2742,u'今':792,u'他':1889,u'以':-1368,u'低':811,u'何':4265,u'作':-361,u'保':-2439,u'元':4858,u'党':3593,u'全':1574,u'公':-3030,u'六':755,u'共':-1880,u'円':5807,u'再':3095,u'分':457,u'初':2475,u'別':1129,u'前':2286,u'副':4437,u'力':365,u'動':-949,u'務':-1872,u'化':1327,u'北':-1038,u'区':4646,u'千':-2309,u'午':-783,u'協':-1006,u'口':483,u'右':1233,u'各':3588,u'合':-241,u'同':3906,u'和':-837,u'員':4513,u'国':642,u'型':1389,u'場':1219,u'外':-241,u'妻':2016,u'学':-1356,u'安':-423,u'実':-1008,u'家':1078,u'小':-513,u'少':-3102,u'州':1155,u'市':3197,u'平':-1804,u'年':2416,u'広':-1030,u'府':1605,u'度':1452,u'建':-2352,u'当':-3885,u'得':1905,u'思':-1291,u'性':1822,u'戸':-488,u'指':-3973,u'政':-2013,u'教':-1479,u'数':3222,u'文':-1489,u'新':1764,u'日':2099,u'旧':5792,u'昨':-661,u'時':-1248,u'曜':-951,u'最':-937,u'月':4125,u'期':360,u'李':3094,u'村':364,u'東':-805,u'核':5156,u'森':2438,u'業':484,u'氏':2613,u'民':-1694,u'決':-1073,u'法':1868,u'海':-495,u'無':979,u'物':461,u'特':-3850,u'生':-273,u'用':914,u'町':1215,u'的':7313,u'直':-1835,u'省':792,u'県':6293,u'知':-1528,u'私':4231,u'税':401,u'立':-960,u'第':1201,u'米':7767,u'系':3066,u'約':3663,u'級':1384,u'統':-4229,u'総':1163,u'線':1255,u'者':6457,u'能':725,u'自':-2869,u'英':785,u'見':1044,u'調':-562,u'財':-733,u'費':1777,u'車':1835,u'軍':1375,u'込':-1504,u'通':-1136,u'選':-681,u'郎':1026,u'郡':4404,u'部':1200,u'金':2163,u'長':421,u'開':-1432,u'間':1302,u'関':-1282,u'雨':2009,u'電':-1045,u'非':2066,u'駅':1620,u'1':-800,u'」':2670,u'・':-3794,u'ッ':-1350,u'ア':551,u'グ':1319,u'ス':874,u'ト':521,u'ム':1109,u'ル':1591,u'ロ':2201,u'ン':278}
+ UW4__ = {u',':3930,u'.':3508,u'―':-4841,u'、':3930,u'。':3508,u'〇':4999,u'「':1895,u'」':3798,u'〓':-5156,u'あ':4752,u'い':-3435,u'う':-640,u'え':-2514,u'お':2405,u'か':530,u'が':6006,u'き':-4482,u'ぎ':-3821,u'く':-3788,u'け':-4376,u'げ':-4734,u'こ':2255,u'ご':1979,u'さ':2864,u'し':-843,u'じ':-2506,u'す':-731,u'ず':1251,u'せ':181,u'そ':4091,u'た':5034,u'だ':5408,u'ち':-3654,u'っ':-5882,u'つ':-1659,u'て':3994,u'で':7410,u'と':4547,u'な':5433,u'に':6499,u'ぬ':1853,u'ね':1413,u'の':7396,u'は':8578,u'ば':1940,u'ひ':4249,u'び':-4134,u'ふ':1345,u'へ':6665,u'べ':-744,u'ほ':1464,u'ま':1051,u'み':-2082,u'む':-882,u'め':-5046,u'も':4169,u'ゃ':-2666,u'や':2795,u'ょ':-1544,u'よ':3351,u'ら':-2922,u'り':-9726,u'る':-14896,u'れ':-2613,u'ろ':-4570,u'わ':-1783,u'を':13150,u'ん':-2352,u'カ':2145,u'コ':1789,u'セ':1287,u'ッ':-724,u'ト':-403,u'メ':-1635,u'ラ':-881,u'リ':-541,u'ル':-856,u'ン':-3637,u'・':-4371,u'ー':-11870,u'一':-2069,u'中':2210,u'予':782,u'事':-190,u'井':-1768,u'人':1036,u'以':544,u'会':950,u'体':-1286,u'作':530,u'側':4292,u'先':601,u'党':-2006,u'共':-1212,u'内':584,u'円':788,u'初':1347,u'前':1623,u'副':3879,u'力':-302,u'動':-740,u'務':-2715,u'化':776,u'区':4517,u'協':1013,u'参':1555,u'合':-1834,u'和':-681,u'員':-910,u'器':-851,u'回':1500,u'国':-619,u'園':-1200,u'地':866,u'場':-1410,u'塁':-2094,u'士':-1413,u'多':1067,u'大':571,u'子':-4802,u'学':-1397,u'定':-1057,u'寺':-809,u'小':1910,u'屋':-1328,u'山':-1500,u'島':-2056,u'川':-2667,u'市':2771,u'年':374,u'庁':-4556,u'後':456,u'性':553,u'感':916,u'所':-1566,u'支':856,u'改':787,u'政':2182,u'教':704,u'文':522,u'方':-856,u'日':1798,u'時':1829,u'最':845,u'月':-9066,u'木':-485,u'来':-442,u'校':-360,u'業':-1043,u'氏':5388,u'民':-2716,u'気':-910,u'沢':-939,u'済':-543,u'物':-735,u'率':672,u'球':-1267,u'生':-1286,u'産':-1101,u'田':-2900,u'町':1826,u'的':2586,u'目':922,u'省':-3485,u'県':2997,u'空':-867,u'立':-2112,u'第':788,u'米':2937,u'系':786,u'約':2171,u'経':1146,u'統':-1169,u'総':940,u'線':-994,u'署':749,u'者':2145,u'能':-730,u'般':-852,u'行':-792,u'規':792,u'警':-1184,u'議':-244,u'谷':-1000,u'賞':730,u'車':-1481,u'軍':1158,u'輪':-1433,u'込':-3370,u'近':929,u'道':-1291,u'選':2596,u'郎':-4866,u'都':1192,u'野':-1100,u'銀':-2213,u'長':357,u'間':-2344,u'院':-2297,u'際':-2604,u'電':-878,u'領':-1659,u'題':-792,u'館':-1984,u'首':1749,u'高':2120,u'「':1895,u'」':3798,u'・':-4371,u'ッ':-724,u'ー':-11870,u'カ':2145,u'コ':1789,u'セ':1287,u'ト':-403,u'メ':-1635,u'ラ':-881,u'リ':-541,u'ル':-856,u'ン':-3637}
+ UW5__ = {u',':465,u'.':-299,u'1':-514,u'E2':-32768,u']':-2762,u'、':465,u'。':-299,u'「':363,u'あ':1655,u'い':331,u'う':-503,u'え':1199,u'お':527,u'か':647,u'が':-421,u'き':1624,u'ぎ':1971,u'く':312,u'げ':-983,u'さ':-1537,u'し':-1371,u'す':-852,u'だ':-1186,u'ち':1093,u'っ':52,u'つ':921,u'て':-18,u'で':-850,u'と':-127,u'ど':1682,u'な':-787,u'に':-1224,u'の':-635,u'は':-578,u'べ':1001,u'み':502,u'め':865,u'ゃ':3350,u'ょ':854,u'り':-208,u'る':429,u'れ':504,u'わ':419,u'を':-1264,u'ん':327,u'イ':241,u'ル':451,u'ン':-343,u'中':-871,u'京':722,u'会':-1153,u'党':-654,u'務':3519,u'区':-901,u'告':848,u'員':2104,u'大':-1296,u'学':-548,u'定':1785,u'嵐':-1304,u'市':-2991,u'席':921,u'年':1763,u'思':872,u'所':-814,u'挙':1618,u'新':-1682,u'日':218,u'月':-4353,u'査':932,u'格':1356,u'機':-1508,u'氏':-1347,u'田':240,u'町':-3912,u'的':-3149,u'相':1319,u'省':-1052,u'県':-4003,u'研':-997,u'社':-278,u'空':-813,u'統':1955,u'者':-2233,u'表':663,u'語':-1073,u'議':1219,u'選':-1018,u'郎':-368,u'長':786,u'間':1191,u'題':2368,u'館':-689,u'1':-514,u'E2':-32768,u'「':363,u'イ':241,u'ル':451,u'ン':-343}
+ UW6__ = {u',':227,u'.':808,u'1':-270,u'E1':306,u'、':227,u'。':808,u'あ':-307,u'う':189,u'か':241,u'が':-73,u'く':-121,u'こ':-200,u'じ':1782,u'す':383,u'た':-428,u'っ':573,u'て':-1014,u'で':101,u'と':-105,u'な':-253,u'に':-149,u'の':-417,u'は':-236,u'も':-206,u'り':187,u'る':-135,u'を':195,u'ル':-673,u'ン':-496,u'一':-277,u'中':201,u'件':-800,u'会':624,u'前':302,u'区':1792,u'員':-1212,u'委':798,u'学':-960,u'市':887,u'広':-695,u'後':535,u'業':-697,u'相':753,u'社':-507,u'福':974,u'空':-822,u'者':1811,u'連':463,u'郎':1082,u'1':-270,u'E1':306,u'ル':-673,u'ン':-496}
+
+ # ctype_
+ def ctype_(self, char):
+ for pattern, value in self.patterns_.iteritems():
+ if pattern.match(char):
+ return value
+ return u'O'
+ # ts_
+ def ts_(self, dict, key):
+ if key in dict:
+ return dict[key]
+ return 0
+
+ # segment
+ def split(self, input):
+ if not input:
+ return []
+
+ result = []
+ seg = [u'B3',u'B2',u'B1']
+ ctype = [u'O',u'O',u'O']
+ for t in input:
+ seg.append(t)
+ ctype.append(self.ctype_(t))
+ seg.append(u'E1')
+ seg.append(u'E2')
+ seg.append(u'E3')
+ ctype.append(u'O')
+ ctype.append(u'O')
+ ctype.append(u'O')
+ word = seg[3]
+ p1 = u'U'
+ p2 = u'U'
+ p3 = u'U'
+
+ for i in range(4, len(seg) - 3):
+ score = self.BIAS__
+ w1 = seg[i-3]
+ w2 = seg[i-2]
+ w3 = seg[i-1]
+ w4 = seg[i]
+ w5 = seg[i+1]
+ w6 = seg[i+2]
+ c1 = ctype[i-3]
+ c2 = ctype[i-2]
+ c3 = ctype[i-1]
+ c4 = ctype[i]
+ c5 = ctype[i+1]
+ c6 = ctype[i+2]
+ score += self.ts_(self.UP1__, p1)
+ score += self.ts_(self.UP2__, p2)
+ score += self.ts_(self.UP3__, p3)
+ score += self.ts_(self.BP1__, p1 + p2)
+ score += self.ts_(self.BP2__, p2 + p3)
+ score += self.ts_(self.UW1__, w1)
+ score += self.ts_(self.UW2__, w2)
+ score += self.ts_(self.UW3__, w3)
+ score += self.ts_(self.UW4__, w4)
+ score += self.ts_(self.UW5__, w5)
+ score += self.ts_(self.UW6__, w6)
+ score += self.ts_(self.BW1__, w2 + w3)
+ score += self.ts_(self.BW2__, w3 + w4)
+ score += self.ts_(self.BW3__, w4 + w5)
+ score += self.ts_(self.TW1__, w1 + w2 + w3)
+ score += self.ts_(self.TW2__, w2 + w3 + w4)
+ score += self.ts_(self.TW3__, w3 + w4 + w5)
+ score += self.ts_(self.TW4__, w4 + w5 + w6)
+ score += self.ts_(self.UC1__, c1)
+ score += self.ts_(self.UC2__, c2)
+ score += self.ts_(self.UC3__, c3)
+ score += self.ts_(self.UC4__, c4)
+ score += self.ts_(self.UC5__, c5)
+ score += self.ts_(self.UC6__, c6)
+ score += self.ts_(self.BC1__, c2 + c3)
+ score += self.ts_(self.BC2__, c3 + c4)
+ score += self.ts_(self.BC3__, c4 + c5)
+ score += self.ts_(self.TC1__, c1 + c2 + c3)
+ score += self.ts_(self.TC2__, c2 + c3 + c4)
+ score += self.ts_(self.TC3__, c3 + c4 + c5)
+ score += self.ts_(self.TC4__, c4 + c5 + c6)
+# score += self.ts_(self.TC5__, c4 + c5 + c6)
+ score += self.ts_(self.UQ1__, p1 + c1)
+ score += self.ts_(self.UQ2__, p2 + c2)
+ score += self.ts_(self.UQ1__, p3 + c3)
+ score += self.ts_(self.BQ1__, p2 + c2 + c3)
+ score += self.ts_(self.BQ2__, p2 + c3 + c4)
+ score += self.ts_(self.BQ3__, p3 + c2 + c3)
+ score += self.ts_(self.BQ4__, p3 + c3 + c4)
+ score += self.ts_(self.TQ1__, p2 + c1 + c2 + c3)
+ score += self.ts_(self.TQ2__, p2 + c2 + c3 + c4)
+ score += self.ts_(self.TQ3__, p3 + c1 + c2 + c3)
+ score += self.ts_(self.TQ4__, p3 + c2 + c3 + c4)
+ p = u'O'
+ if score > 0:
+ result.append(word)
+ word = u''
+ p = u'B'
+ p1 = p2
+ p2 = p3
+ p3 = p
+ word += seg[i]
+
+ result.append(word)
+ return result
+
+
+class SearchJapanese(SearchLanguage):
+ """
+ Japanese search implementation: uses no stemmer, but word splitting is quite
+ complicated.
+ """
+ lang = 'ja'
+
+ def init(self, options):
+ type = options.get('type', 'default')
+ if type not in ('mecab', 'default'):
+ raise ValueError(("Japanese tokenizer's type should be 'mecab'"
+ " or 'default'"))
+ self.libmecab = None
+ if type == 'mecab':
+ self.splitter = MecabBinder(options)
+ else:
+ self.splitter = TinySegmenter()
+
+ def split(self, input):
+ return self.splitter.split(input)
+
+ def word_filter(self, stemmed_word):
+ return len(stemmed_word) > 1
diff --git a/sphinx/setup_command.py b/sphinx/setup_command.py
index 5fe34f18..4cae738d 100644
--- a/sphinx/setup_command.py
+++ b/sphinx/setup_command.py
@@ -8,7 +8,7 @@
:author: Sebastian Wiesner
:contact: basti.wiesner@gmx.net
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -111,7 +111,6 @@ class BuildDoc(Command):
build = self.get_finalized_command('build')
self.build_dir = os.path.join(build.build_base, 'sphinx')
self.mkpath(self.build_dir)
- self.ensure_dirname('build_dir')
self.doctree_dir = os.path.join(self.build_dir, 'doctrees')
self.mkpath(self.doctree_dir)
self.builder_target_dir = os.path.join(self.build_dir, self.builder)
diff --git a/sphinx/texinputs/Makefile b/sphinx/texinputs/Makefile
index d5a73f5b..f219a2fe 100644
--- a/sphinx/texinputs/Makefile
+++ b/sphinx/texinputs/Makefile
@@ -14,6 +14,15 @@ all-pdf: $(ALLPDF)
all-dvi: $(ALLDVI)
all-ps: all-dvi
for f in *.dvi; do dvips $$f; done
+all-pdf-ja: $(wildcard *.tex)
+ ebb $(wildcard *.pdf *.png *.gif *.jpeg)
+ platex -kanji=utf8 $(LATEXOPTS) '$<'
+ platex -kanji=utf8 $(LATEXOPTS) '$<'
+ platex -kanji=utf8 $(LATEXOPTS) '$<'
+ -mendex -U -f -d '$(basename $<).dic' -s python.ist '$(basename $<).idx'
+ platex -kanji=utf8 $(LATEXOPTS) '$<'
+ platex -kanji=utf8 $(LATEXOPTS) '$<'
+ dvipdfmx '$(basename $<).dvi'
zip: all-$(FMT)
mkdir $(ARCHIVEPREFIX)docs-$(FMT)
@@ -49,8 +58,7 @@ bz2: tar
pdflatex $(LATEXOPTS) '$<'
clean:
- rm -f *.pdf *.dvi *.ps
- rm -f *.log *.ind *.aux *.toc *.syn *.idx *.out *.ilg *.pla
+ rm -f *.dvi *.log *.ind *.aux *.toc *.syn *.idx *.out *.ilg *.pla
.PHONY: all all-pdf all-dvi all-ps clean
diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty
index cee4dc56..be0a0284 100644
--- a/sphinx/texinputs/sphinx.sty
+++ b/sphinx/texinputs/sphinx.sty
@@ -16,6 +16,7 @@
\RequirePackage{amsmath} % for \text
\RequirePackage{makeidx}
\RequirePackage{framed}
+\RequirePackage{ifthen}
\RequirePackage{color}
% For highlighted code.
\RequirePackage{fancyvrb}
@@ -137,15 +138,11 @@
% Play with vspace to be able to keep the indentation.
\newlength\distancetoright
-\newlength\leftsidespace
\def\mycolorbox#1{%
- \setlength\leftsidespace{\@totalleftmargin}%
\setlength\distancetoright{\linewidth}%
\advance\distancetoright -\@totalleftmargin %
- \noindent\hspace*{\@totalleftmargin}%
\fcolorbox{VerbatimBorderColor}{VerbatimColor}{%
\begin{minipage}{\distancetoright}%
- \noindent\hspace*{-\leftsidespace}%
#1
\end{minipage}%
}%
@@ -178,23 +175,6 @@
}
-% Index-entry generation support.
-%
-
-% Command to generate two index entries (using subentries)
-\newcommand{\indexii}[2]{\index{#1!#2}\index{#2!#1}}
-
-% And three entries (using only one level of subentries)
-\newcommand{\indexiii}[3]{\index{#1!#2 #3}\index{#2!#3, #1}\index{#3!#1 #2}}
-
-% And four (again, using only one level of subentries)
-\newcommand{\indexiv}[4]{
-\index{#1!#2 #3 #4}
-\index{#2!#3 #4, #1}
-\index{#3!#4, #1 #2}
-\index{#4!#1 #2 #3}
-}
-
% \moduleauthor{name}{email}
\newcommand{\moduleauthor}[2]{}
@@ -209,7 +189,7 @@
{\py@TitleColor\thesubsection}{0.5em}{\py@TitleColor}{\py@NormalColor}
\titleformat{\subsubsection}{\py@HeaderFamily}%
{\py@TitleColor\thesubsubsection}{0.5em}{\py@TitleColor}{\py@NormalColor}
-\titleformat{\paragraph}{\large\py@HeaderFamily}%
+\titleformat{\paragraph}{\small\py@HeaderFamily}%
{\py@TitleColor}{0em}{\py@TitleColor}{\py@NormalColor}
% {fulllineitems} is the main environment for object descriptions.
@@ -268,11 +248,11 @@
\newcommand{\py@heavybox}{
\setlength{\fboxrule}{1pt}
- \setlength{\fboxsep}{7pt}
+ \setlength{\fboxsep}{6pt}
\setlength{\py@noticelength}{\linewidth}
\addtolength{\py@noticelength}{-2\fboxsep}
\addtolength{\py@noticelength}{-2\fboxrule}
- \setlength{\shadowsize}{3pt}
+ %\setlength{\shadowsize}{3pt}
\Sbox
\minipage{\py@noticelength}
}
@@ -282,15 +262,26 @@
\fbox{\TheSbox}
}
+\newcommand{\py@lightbox}{{%
+ \setlength\parskip{0pt}\par
+ \rule[0ex]{\linewidth}{0.5pt}%
+ \par\vspace{-0.5ex}%
+ }}
+\newcommand{\py@endlightbox}{{%
+ \setlength{\parskip}{0pt}%
+ \par\rule[0.5ex]{\linewidth}{0.5pt}%
+ \par\vspace{-0.5ex}%
+ }}
+
% Some are quite plain:
-\newcommand{\py@noticestart@note}{}
-\newcommand{\py@noticeend@note}{}
-\newcommand{\py@noticestart@hint}{}
-\newcommand{\py@noticeend@hint}{}
-\newcommand{\py@noticestart@important}{}
-\newcommand{\py@noticeend@important}{}
-\newcommand{\py@noticestart@tip}{}
-\newcommand{\py@noticeend@tip}{}
+\newcommand{\py@noticestart@note}{\py@lightbox}
+\newcommand{\py@noticeend@note}{\py@endlightbox}
+\newcommand{\py@noticestart@hint}{\py@lightbox}
+\newcommand{\py@noticeend@hint}{\py@endlightbox}
+\newcommand{\py@noticestart@important}{\py@lightbox}
+\newcommand{\py@noticeend@important}{\py@endlightbox}
+\newcommand{\py@noticestart@tip}{\py@lightbox}
+\newcommand{\py@noticeend@tip}{\py@endlightbox}
% Others gets more visible distinction:
\newcommand{\py@noticestart@warning}{\py@heavybox}
@@ -307,7 +298,7 @@
\newenvironment{notice}[2]{
\def\py@noticetype{#1}
\csname py@noticestart@#1\endcsname
- \par\strong{#2}
+ \strong{#2}
}{\csname py@noticeend@\py@noticetype\endcsname}
% Allow the release number to be specified independently of the
@@ -462,3 +453,21 @@
{#2}% node content
}% close "span"
}
+
+\providecommand*{\DUprovidelength}[2]{
+ \ifthenelse{\isundefined{#1}}{\newlength{#1}\setlength{#1}{#2}}{}
+}
+
+\DUprovidelength{\DUlineblockindent}{2.5em}
+\ifthenelse{\isundefined{\DUlineblock}}{
+ \newenvironment{DUlineblock}[1]{%
+ \list{}{\setlength{\partopsep}{\parskip}
+ \addtolength{\partopsep}{\baselineskip}
+ \setlength{\topsep}{0pt}
+ \setlength{\itemsep}{0.15\baselineskip}
+ \setlength{\parsep}{0pt}
+ \setlength{\leftmargin}{#1}}
+ \raggedright
+ }
+ {\endlist}
+}{}
diff --git a/sphinx/themes/agogo/layout.html b/sphinx/themes/agogo/layout.html
index 719d2d5a..6668a7f6 100644
--- a/sphinx/themes/agogo/layout.html
+++ b/sphinx/themes/agogo/layout.html
@@ -5,7 +5,7 @@
Sphinx layout template for the agogo theme, originally written
by Andi Albrecht.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{% extends "basic/layout.html" %}
diff --git a/sphinx/themes/agogo/static/agogo.css_t b/sphinx/themes/agogo/static/agogo.css_t
index 10f03c47..904fd7be 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-2010 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
@@ -326,6 +326,37 @@ div.footer .left {
/* Styles copied from basic theme */
+img.align-left, .figure.align-left, object.align-left {
+ clear: left;
+ float: left;
+ margin-right: 1em;
+}
+
+img.align-right, .figure.align-right, object.align-right {
+ clear: right;
+ float: right;
+ margin-left: 1em;
+}
+
+img.align-center, .figure.align-center, object.align-center {
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.align-left {
+ text-align: left;
+}
+
+.align-center {
+ clear: both;
+ text-align: center;
+}
+
+.align-right {
+ text-align: right;
+}
+
/* -- search page ----------------------------------------------------------- */
ul.search {
diff --git a/sphinx/themes/basic/defindex.html b/sphinx/themes/basic/defindex.html
index f337faec..ce8d3af6 100644
--- a/sphinx/themes/basic/defindex.html
+++ b/sphinx/themes/basic/defindex.html
@@ -4,7 +4,7 @@
Default template for the "index" page.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{% extends "layout.html" %}
diff --git a/sphinx/themes/basic/domainindex.html b/sphinx/themes/basic/domainindex.html
index 0aca7e69..8d98c429 100644
--- a/sphinx/themes/basic/domainindex.html
+++ b/sphinx/themes/basic/domainindex.html
@@ -4,7 +4,7 @@
Template for domain indices (module index, ...).
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{% extends "layout.html" %}
diff --git a/sphinx/themes/basic/genindex-single.html b/sphinx/themes/basic/genindex-single.html
index 1e98ba9c..b6fa6a85 100644
--- a/sphinx/themes/basic/genindex-single.html
+++ b/sphinx/themes/basic/genindex-single.html
@@ -4,34 +4,51 @@
Template for a "single" page of a split index.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
+{% macro indexentries(firstname, links) %}
+ <dt>
+ {%- if links -%}
+ <a href="{{ links[0][1] }}">
+ {%- if links[0][0] %}<strong>{% endif -%}
+ {{ firstname|e }}
+ {%- if links[0][0] %}</strong>{% endif -%}
+ </a>
+
+ {%- for ismain, link in links[1:] -%}
+ , <a href="{{ link }}">{% if ismain %}<strong>{% endif -%}
+ [{{ loop.index }}]
+ {%- if ismain %}</strong>{% endif -%}
+ </a>
+ {%- endfor %}
+ {%- else %}
+ {{ firstname|e }}
+ {%- endif %}
+ </dt>
+{% endmacro %}
+
{% extends "layout.html" %}
{% set title = _('Index') %}
{% block body %}
- <h1 id="index">{% trans key=key %}Index &ndash; {{ key }}{% endtrans %}</h1>
+<h1 id="index">{% trans key=key %}Index &ndash; {{ key }}{% endtrans %}</h1>
<table width="100%" class="indextable"><tr>
{%- for column in entries|slice(2) if column %}
<td width="33%" valign="top"><dl>
- {%- for entryname, (links, subitems) in column %}
- <dt>{% if links %}<a href="{{ links[0] }}">{{ entryname|e }}</a>
- {%- for link in links[1:] %}, <a href="{{ link }}">[{{ loop.index }}]</a>{% endfor %}
- {%- else %}{{ entryname|e }}{% endif %}</dt>
- {%- if subitems %}
- <dd><dl>
- {%- for subentryname, subentrylinks in subitems %}
- <dt><a href="{{ subentrylinks[0] }}">{{ subentryname|e }}</a>
- {%- for link in subentrylinks[1:] %}, <a href="{{ link }}">[{{ loop.index }}]</a>{% endfor -%}
- </dt>
+ {%- for entryname, (links, subitems) in column %}
+ {{ indexentries(entryname, links) }}
+ {%- if subitems %}
+ <dd><dl>
+ {%- for subentryname, subentrylinks in subitems %}
+ {{ indexentries(subentryname, subentrylinks) }}
+ {%- endfor %}
+ </dl></dd>
+ {%- endif -%}
{%- endfor %}
- </dl></dd>
- {%- endif -%}
-{%- endfor %}
-</dl></td>
-{%- endfor %}
+ </dl></td>
+ {%- endfor %}
</tr></table>
{% endblock %}
diff --git a/sphinx/themes/basic/genindex-split.html b/sphinx/themes/basic/genindex-split.html
index d068a96a..6da88a17 100644
--- a/sphinx/themes/basic/genindex-split.html
+++ b/sphinx/themes/basic/genindex-split.html
@@ -4,7 +4,7 @@
Template for a "split" index overview page.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{% extends "layout.html" %}
diff --git a/sphinx/themes/basic/genindex.html b/sphinx/themes/basic/genindex.html
index 4d46380f..536f0963 100644
--- a/sphinx/themes/basic/genindex.html
+++ b/sphinx/themes/basic/genindex.html
@@ -4,42 +4,60 @@
Template for an "all-in-one" index.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
+{% macro indexentries(firstname, links) %}
+ <dt>
+ {%- if links -%}
+ <a href="{{ links[0][1] }}">
+ {%- if links[0][0] %}<strong>{% endif -%}
+ {{ firstname|e }}
+ {%- if links[0][0] %}</strong>{% endif -%}
+ </a>
+
+ {%- for ismain, link in links[1:] -%}
+ , <a href="{{ link }}">{% if ismain %}<strong>{% endif -%}
+ [{{ loop.index }}]
+ {%- if ismain %}</strong>{% endif -%}
+ </a>
+ {%- endfor %}
+ {%- else %}
+ {{ firstname|e }}
+ {%- endif %}
+ </dt>
+{% endmacro %}
+
{% extends "layout.html" %}
{% set title = _('Index') %}
{% block body %}
- <h1 id="index">{{ _('Index') }}</h1>
+<h1 id="index">{{ _('Index') }}</h1>
- <div class="genindex-jumpbox">
- {% for key, dummy in genindexentries -%}
- <a href="#{{ key }}"><strong>{{ key }}</strong></a> {% if not loop.last %}| {% endif %}
- {%- endfor %}
- </div>
+<div class="genindex-jumpbox">
+ {% for key, dummy in genindexentries -%}
+ <a href="#{{ key }}"><strong>{{ key }}</strong></a>
+ {% if not loop.last %}| {% endif %}
+ {%- endfor %}
+</div>
- {%- for key, entries in genindexentries %}
+{%- for key, entries in genindexentries %}
<h2 id="{{ key }}">{{ key }}</h2>
<table width="100%" class="indextable genindextable"><tr>
{%- for column in entries|slice(2) if column %}
<td width="33%" valign="top"><dl>
- {%- for entryname, (links, subitems) in column %}
- <dt>{% if links %}<a href="{{ links[0] }}">{{ entryname|e }}</a>
- {%- for link in links[1:] %}, <a href="{{ link }}">[{{ loop.index }}]</a>{% endfor %}
- {%- else %}{{ entryname|e }}{% endif %}</dt>
- {%- if subitems %}
- <dd><dl>
- {%- for subentryname, subentrylinks in subitems %}
- <dt><a href="{{ subentrylinks[0] }}">{{ subentryname|e }}</a>
- {%- for link in subentrylinks[1:] %}, <a href="{{ link }}">[{{ loop.index }}]</a>{% endfor -%}
- </dt>
+ {%- for entryname, (links, subitems) in column %}
+ {{ indexentries(entryname, links) }}
+ {%- if subitems %}
+ <dd><dl>
+ {%- for subentryname, subentrylinks in subitems %}
+ {{ indexentries(subentryname, subentrylinks) }}
+ {%- endfor %}
+ </dl></dd>
+ {%- endif -%}
{%- endfor %}
- </dl></dd>
- {%- endif -%}
-{%- endfor %}
-</dl></td>
-{%- endfor %}
+ </dl></td>
+ {%- endfor %}
</tr></table>
{% endfor %}
diff --git a/sphinx/themes/basic/globaltoc.html b/sphinx/themes/basic/globaltoc.html
index 7422888c..ee191faf 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-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
<h3><a href="{{ pathto(master_doc) }}">{{ _('Table Of Contents') }}</a></h3>
diff --git a/sphinx/themes/basic/layout.html b/sphinx/themes/basic/layout.html
index e31e8544..9fb989cb 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-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{%- block doctype -%}
@@ -16,7 +16,13 @@
{%- set render_sidebar = (not embedded) and (not theme_nosidebar|tobool) and
(sidebars != []) %}
{%- set url_root = pathto('', 1) %}
+{# XXX necessary? #}
{%- if url_root == '#' %}{% set url_root = '' %}{% endif %}
+{%- if not embedded and docstitle %}
+ {%- set titlesuffix = " &mdash; "|safe + docstitle|e %}
+{%- else %}
+ {%- set titlesuffix = "" %}
+{%- endif %}
{%- macro relbar() %}
<div class="related">
@@ -78,24 +84,7 @@
{%- endif %}
{%- endmacro %}
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset={{ encoding }}" />
- {{ metatags }}
- {%- if not embedded and docstitle %}
- {%- set titlesuffix = " &mdash; "|safe + docstitle|e %}
- {%- else %}
- {%- set titlesuffix = "" %}
- {%- endif %}
- {%- block htmltitle %}
- <title>{{ title|striptags|e }}{{ titlesuffix }}</title>
- {%- endblock %}
- <link rel="stylesheet" href="{{ pathto('_static/' + style, 1) }}" type="text/css" />
- <link rel="stylesheet" href="{{ pathto('_static/pygments.css', 1) }}" type="text/css" />
- {%- for cssfile in css_files %}
- <link rel="stylesheet" href="{{ pathto(cssfile, 1) }}" type="text/css" />
- {%- endfor %}
- {%- if not embedded %}
+{%- macro script() %}
<script type="text/javascript">
var DOCUMENTATION_OPTIONS = {
URL_ROOT: '{{ url_root }}',
@@ -108,6 +97,26 @@
{%- for scriptfile in script_files %}
<script type="text/javascript" src="{{ pathto(scriptfile, 1) }}"></script>
{%- endfor %}
+{%- endmacro %}
+
+{%- macro css() %}
+ <link rel="stylesheet" href="{{ pathto('_static/' + style, 1) }}" type="text/css" />
+ <link rel="stylesheet" href="{{ pathto('_static/pygments.css', 1) }}" type="text/css" />
+ {%- for cssfile in css_files %}
+ <link rel="stylesheet" href="{{ pathto(cssfile, 1) }}" type="text/css" />
+ {%- endfor %}
+{%- endmacro %}
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset={{ encoding }}" />
+ {{ metatags }}
+ {%- block htmltitle %}
+ <title>{{ title|striptags|e }}{{ titlesuffix }}</title>
+ {%- endblock %}
+ {{ css() }}
+ {%- if not embedded %}
+ {{ script() }}
{%- if use_opensearch %}
<link rel="search" type="application/opensearchdescription+xml"
title="{% trans docstitle=docstitle|e %}Search within {{ docstitle }}{% endtrans %}"
diff --git a/sphinx/themes/basic/localtoc.html b/sphinx/themes/basic/localtoc.html
index 896eda9c..aa612ebf 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-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 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 c7188fa5..f6e7a688 100644
--- a/sphinx/themes/basic/page.html
+++ b/sphinx/themes/basic/page.html
@@ -4,7 +4,7 @@
Master template for simple pages.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{% extends "layout.html" %}
diff --git a/sphinx/themes/basic/relations.html b/sphinx/themes/basic/relations.html
index b693daf8..82abbeaa 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-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 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 39193549..4cdc6935 100644
--- a/sphinx/themes/basic/search.html
+++ b/sphinx/themes/basic/search.html
@@ -4,7 +4,7 @@
Template for the search page.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{% extends "layout.html" %}
diff --git a/sphinx/themes/basic/searchbox.html b/sphinx/themes/basic/searchbox.html
index 56d6617c..88edac5a 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-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 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 4b5da1a3..667abffd 100644
--- a/sphinx/themes/basic/searchresults.html
+++ b/sphinx/themes/basic/searchresults.html
@@ -1,10 +1,10 @@
{#
basic/searchresults.html
- ~~~~~~~~~~~~~~~~~
+ ~~~~~~~~~~~~~~~~~~~~~~~~
Template for the body of the search results page.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
<h1 id="search-documentation">Search</h1>
@@ -17,20 +17,20 @@
<input type="submit" value="search" />
<span id="search-progress" style="padding-left: 10px"></span>
</form>
-{% if search_performed %}
-<h2>Search Results</h2>
-{% if not search_results %}
-<p>Your search did not match any results.</p>
-{% endif %}
-{% endif %}
+{%- if search_performed %}
+ <h2>Search Results</h2>
+ {%- if not search_results %}
+ <p>Your search did not match any results.</p>
+ {%- endif %}
+{%- endif %}
<div id="search-results">
- {% if search_results %}
+ {%- if search_results %}
<ul class="search">
{% for href, caption, context in search_results %}
- <li><a href="{{ href }}?highlight={{ q }}">{{ caption }}</a>
+ <li><a href="{{ docroot }}{{ href }}/?highlight={{ q }}">{{ caption }}</a>
<div class="context">{{ context|e }}</div>
</li>
{% endfor %}
</ul>
- {% endif %}
+ {%- endif %}
</div>
diff --git a/sphinx/themes/basic/sourcelink.html b/sphinx/themes/basic/sourcelink.html
index 8fa7563b..53f2f6b1 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-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 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 b/sphinx/themes/basic/static/basic.css
index 69f30d4f..6882a583 100644
--- a/sphinx/themes/basic/static/basic.css
+++ b/sphinx/themes/basic/static/basic.css
@@ -4,7 +4,7 @@
*
* Sphinx stylesheet -- basic theme.
*
- * :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
@@ -213,6 +213,24 @@ p.rubric {
font-weight: bold;
}
+img.align-left, .figure.align-left, object.align-left {
+ clear: left;
+ float: left;
+ margin-right: 1em;
+}
+
+img.align-right, .figure.align-right, object.align-right {
+ clear: right;
+ float: right;
+ margin-left: 1em;
+}
+
+img.align-center, .figure.align-center, object.align-center {
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+}
+
.align-left {
text-align: left;
}
@@ -426,6 +444,7 @@ dl.glossary dt {
pre {
overflow: auto;
+ overflow-y: hidden; /* fixes display issues on Chrome browsers */
}
td.linenos pre {
diff --git a/sphinx/themes/basic/static/doctools.js b/sphinx/themes/basic/static/doctools.js
index eeea95ea..8b9bd2c0 100644
--- a/sphinx/themes/basic/static/doctools.js
+++ b/sphinx/themes/basic/static/doctools.js
@@ -4,7 +4,7 @@
*
* Sphinx JavaScript utilties for all documentation.
*
- * :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
diff --git a/sphinx/themes/basic/static/searchtools.js b/sphinx/themes/basic/static/searchtools.js_t
index 5cbfe004..6be7489f 100644
--- a/sphinx/themes/basic/static/searchtools.js
+++ b/sphinx/themes/basic/static/searchtools.js_t
@@ -1,10 +1,10 @@
/*
- * searchtools.js
- * ~~~~~~~~~~~~~~
+ * searchtools.js_t
+ * ~~~~~~~~~~~~~~~~
*
* Sphinx JavaScript utilties for the full-text search.
*
- * :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
@@ -36,188 +36,7 @@ jQuery.makeSearchSummary = function(text, keywords, hlwords) {
return rv;
}
-/**
- * Porter Stemmer
- */
-var PorterStemmer = function() {
-
- var step2list = {
- ational: 'ate',
- tional: 'tion',
- enci: 'ence',
- anci: 'ance',
- izer: 'ize',
- bli: 'ble',
- alli: 'al',
- entli: 'ent',
- eli: 'e',
- ousli: 'ous',
- ization: 'ize',
- ation: 'ate',
- ator: 'ate',
- alism: 'al',
- iveness: 'ive',
- fulness: 'ful',
- ousness: 'ous',
- aliti: 'al',
- iviti: 'ive',
- biliti: 'ble',
- logi: 'log'
- };
-
- var step3list = {
- icate: 'ic',
- ative: '',
- alize: 'al',
- iciti: 'ic',
- ical: 'ic',
- ful: '',
- ness: ''
- };
-
- var c = "[^aeiou]"; // consonant
- var v = "[aeiouy]"; // vowel
- var C = c + "[^aeiouy]*"; // consonant sequence
- var V = v + "[aeiou]*"; // vowel sequence
-
- var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0
- var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1
- var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1
- var s_v = "^(" + C + ")?" + v; // vowel in stem
-
- this.stemWord = function (w) {
- var stem;
- var suffix;
- var firstch;
- var origword = w;
-
- if (w.length < 3)
- return w;
-
- var re;
- var re2;
- var re3;
- var re4;
-
- firstch = w.substr(0,1);
- if (firstch == "y")
- w = firstch.toUpperCase() + w.substr(1);
-
- // Step 1a
- re = /^(.+?)(ss|i)es$/;
- re2 = /^(.+?)([^s])s$/;
-
- if (re.test(w))
- w = w.replace(re,"$1$2");
- else if (re2.test(w))
- w = w.replace(re2,"$1$2");
-
- // Step 1b
- re = /^(.+?)eed$/;
- re2 = /^(.+?)(ed|ing)$/;
- if (re.test(w)) {
- var fp = re.exec(w);
- re = new RegExp(mgr0);
- if (re.test(fp[1])) {
- re = /.$/;
- w = w.replace(re,"");
- }
- }
- else if (re2.test(w)) {
- var fp = re2.exec(w);
- stem = fp[1];
- re2 = new RegExp(s_v);
- if (re2.test(stem)) {
- w = stem;
- re2 = /(at|bl|iz)$/;
- re3 = new RegExp("([^aeiouylsz])\\1$");
- re4 = new RegExp("^" + C + v + "[^aeiouwxy]$");
- if (re2.test(w))
- w = w + "e";
- else if (re3.test(w)) {
- re = /.$/;
- w = w.replace(re,"");
- }
- else if (re4.test(w))
- w = w + "e";
- }
- }
-
- // Step 1c
- re = /^(.+?)y$/;
- if (re.test(w)) {
- var fp = re.exec(w);
- stem = fp[1];
- re = new RegExp(s_v);
- if (re.test(stem))
- w = stem + "i";
- }
-
- // Step 2
- re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;
- if (re.test(w)) {
- var fp = re.exec(w);
- stem = fp[1];
- suffix = fp[2];
- re = new RegExp(mgr0);
- if (re.test(stem))
- w = stem + step2list[suffix];
- }
-
- // Step 3
- re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;
- if (re.test(w)) {
- var fp = re.exec(w);
- stem = fp[1];
- suffix = fp[2];
- re = new RegExp(mgr0);
- if (re.test(stem))
- w = stem + step3list[suffix];
- }
-
- // Step 4
- re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;
- re2 = /^(.+?)(s|t)(ion)$/;
- if (re.test(w)) {
- var fp = re.exec(w);
- stem = fp[1];
- re = new RegExp(mgr1);
- if (re.test(stem))
- w = stem;
- }
- else if (re2.test(w)) {
- var fp = re2.exec(w);
- stem = fp[1] + fp[2];
- re2 = new RegExp(mgr1);
- if (re2.test(stem))
- w = stem;
- }
-
- // Step 5
- re = /^(.+?)e$/;
- if (re.test(w)) {
- var fp = re.exec(w);
- stem = fp[1];
- re = new RegExp(mgr1);
- re2 = new RegExp(meq1);
- re3 = new RegExp("^" + C + v + "[^aeiouwxy]$");
- if (re.test(stem) || (re2.test(stem) && !(re3.test(stem))))
- w = stem;
- }
- re = /ll$/;
- re2 = new RegExp(mgr1);
- if (re.test(w) && re2.test(w)) {
- re = /.$/;
- w = w.replace(re,"");
- }
-
- // and turn initial Y back to y
- if (firstch == "y")
- w = firstch.toLowerCase() + w.substr(1);
- return w;
- }
-}
-
+{{ search_language_stemming_code|safe }}
/**
* Search Module
@@ -300,14 +119,10 @@ var Search = {
},
query : function(query) {
- var stopwords = ['and', 'then', 'into', 'it', 'as', 'are', 'in',
- 'if', 'for', 'no', 'there', 'their', 'was', 'is',
- 'be', 'to', 'that', 'but', 'they', 'not', 'such',
- 'with', 'by', 'a', 'on', 'these', 'of', 'will',
- 'this', 'near', 'the', 'or', 'at'];
-
- // stem the searchterms and add them to the correct list
- var stemmer = new PorterStemmer();
+ var stopwords = {{ search_language_stop_words }};
+
+ // Stem the searchterms and add them to the correct list
+ var stemmer = new Stemmer();
var searchterms = [];
var excluded = [];
var hlterms = [];
diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js
index 870b0cdc..cbb60923 100644
--- a/sphinx/themes/basic/static/websupport.js
+++ b/sphinx/themes/basic/static/websupport.js
@@ -1,6 +1,17 @@
+/*
+ * websupport.js
+ * ~~~~~~~~~~~~~
+ *
+ * sphinx.websupport utilties for all documentation.
+ *
+ * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ * :license: BSD, see LICENSE for details.
+ *
+ */
+
(function($) {
- $.fn.autogrow = function(){
- return this.each(function(){
+ $.fn.autogrow = function() {
+ return this.each(function() {
var textarea = this;
$.fn.autogrow.resize(textarea);
@@ -39,60 +50,60 @@
}
function initEvents() {
- $('a.comment_close').live("click", function(event) {
+ $('a.comment-close').live("click", function(event) {
+ event.preventDefault();
hide($(this).attr('id').substring(2));
- return false;
});
- $('.vote').live("click", function() {
+ $('a.vote').live("click", function(event) {
+ event.preventDefault();
handleVote($(this));
- return false;
});
- $('a.reply').live("click", function() {
+ $('a.reply').live("click", function(event) {
+ event.preventDefault();
openReply($(this).attr('id').substring(2));
- return false;
});
- $('a.close_reply').live("click", function() {
+ $('a.close-reply').live("click", function(event) {
+ event.preventDefault();
closeReply($(this).attr('id').substring(2));
- return false;
});
- $('a.sort_option').live("click", function(event) {
+ $('a.sort-option').live("click", function(event) {
+ event.preventDefault();
handleReSort($(this));
- return false;
});
- $('a.show_proposal').live("click", function() {
+ $('a.show-proposal').live("click", function(event) {
+ event.preventDefault();
showProposal($(this).attr('id').substring(2));
- return false;
});
- $('a.hide_proposal').live("click", function() {
+ $('a.hide-proposal').live("click", function(event) {
+ event.preventDefault();
hideProposal($(this).attr('id').substring(2));
- return false;
});
- $('a.show_propose_change').live("click", function() {
+ $('a.show-propose-change').live("click", function(event) {
+ event.preventDefault();
showProposeChange($(this).attr('id').substring(2));
- return false;
});
- $('a.hide_propose_change').live("click", function() {
+ $('a.hide-propose-change').live("click", function(event) {
+ event.preventDefault();
hideProposeChange($(this).attr('id').substring(2));
- return false;
});
- $('a.accept_comment').live("click", function() {
+ $('a.accept-comment').live("click", function(event) {
+ event.preventDefault();
acceptComment($(this).attr('id').substring(2));
- return false;
- });
- $('a.reject_comment').live("click", function() {
- rejectComment($(this).attr('id').substring(2));
- return false;
});
- $('a.delete_comment').live("click", function() {
+ $('a.delete-comment').live("click", function(event) {
+ event.preventDefault();
deleteComment($(this).attr('id').substring(2));
- return false;
+ });
+ $('a.comment-markup').live("click", function(event) {
+ event.preventDefault();
+ toggleCommentMarkupBox($(this).attr('id').substring(2));
});
}
- /*
- Set comp, which is a comparator function used for sorting and
- inserting comments into the list.
- */
+ /**
+ * Set comp, which is a comparator function used for sorting and
+ * inserting comments into the list.
+ */
function setComparator() {
// If the first three letters are "asc", sort in ascending order
// and remove the prefix.
@@ -106,13 +117,13 @@
// Reset link styles and format the selected sort option.
$('a.sel').attr('href', '#').removeClass('sel');
- $('a.' + by).removeAttr('href').addClass('sel');
+ $('a.by' + by).removeAttr('href').addClass('sel');
}
- /*
- Create a comp function. If the user has preferences stored in
- the sortBy cookie, use those, otherwise use the default.
- */
+ /**
+ * Create a comp function. If the user has preferences stored in
+ * the sortBy cookie, use those, otherwise use the default.
+ */
function initComparator() {
by = 'rating'; // Default to sort by rating.
// If the sortBy cookie is set, use that instead.
@@ -130,16 +141,16 @@
setComparator();
}
- /*
- Show a comment div.
- */
+ /**
+ * Show a comment div.
+ */
function show(id) {
$('#ao' + id).hide();
$('#ah' + id).show();
var context = $.extend({id: id}, opts);
var popup = $(renderTemplate(popupTemplate, context)).hide();
popup.find('textarea[name="proposal"]').hide();
- popup.find('a.' + by).addClass('sel');
+ popup.find('a.by' + by).addClass('sel');
var form = popup.find('#cf' + id);
form.submit(function(event) {
event.preventDefault();
@@ -151,9 +162,9 @@
});
}
- /*
- Hide a comment div.
- */
+ /**
+ * Hide a comment div.
+ */
function hide(id) {
$('#ah' + id).hide();
$('#ao' + id).show();
@@ -163,10 +174,10 @@
});
}
- /*
- Perform an ajax request to get comments for a node
- and insert the comments into the comments tree.
- */
+ /**
+ * Perform an ajax request to get comments for a node
+ * and insert the comments into the comments tree.
+ */
function getComments(id) {
$.ajax({
type: 'GET',
@@ -199,14 +210,22 @@
});
}
- /*
- Add a comment via ajax and insert the comment into the comment tree.
- */
+ /**
+ * Add a comment via ajax and insert the comment into the comment tree.
+ */
function addComment(form) {
- // Disable the form that is being submitted.
- form.find('textarea,input').attr('disabled', 'disabled');
var node_id = form.find('input[name="node"]').val();
var parent_id = form.find('input[name="parent"]').val();
+ var text = form.find('textarea[name="comment"]').val();
+ var proposal = form.find('textarea[name="proposal"]').val();
+
+ if (text == '') {
+ showError('Please enter a comment.');
+ return;
+ }
+
+ // Disable the form that is being submitted.
+ form.find('textarea,input').attr('disabled', 'disabled');
// Send the comment to the server.
$.ajax({
@@ -216,8 +235,8 @@
data: {
node: node_id,
parent: parent_id,
- text: form.find('textarea[name="comment"]').val(),
- proposal: form.find('textarea[name="proposal"]').val()
+ text: text,
+ proposal: proposal
},
success: function(data, textStatus, error) {
// Reset the form.
@@ -234,6 +253,13 @@
ul.data('empty', false);
}
insertComment(data.comment);
+ var ao = $('#ao' + node_id);
+ ao.find('img').attr({'src': opts.commentBrightImage});
+ if (node_id) {
+ // if this was a "root" comment, remove the commenting box
+ // (the user can get it back by reopening the comment popup)
+ $('#ca' + node_id).slideUp();
+ }
},
error: function(request, textStatus, error) {
form.find('textarea,input').removeAttr('disabled');
@@ -242,25 +268,25 @@
});
}
- /*
- Recursively append comments to the main comment list and children
- lists, creating the comment tree.
- */
+ /**
+ * Recursively append comments to the main comment list and children
+ * lists, creating the comment tree.
+ */
function appendComments(comments, ul) {
$.each(comments, function() {
var div = createCommentDiv(this);
ul.append($(document.createElement('li')).html(div));
- appendComments(this.children, div.find('ul.children'));
+ appendComments(this.children, div.find('ul.comment-children'));
// To avoid stagnating data, don't store the comments children in data.
this.children = null;
div.data('comment', this);
});
}
- /*
- After adding a new comment, it must be inserted in the correct
- location in the comment tree.
- */
+ /**
+ * After adding a new comment, it must be inserted in the correct
+ * location in the comment tree.
+ */
function insertComment(comment) {
var div = createCommentDiv(comment);
@@ -298,26 +324,10 @@
data: {id: id},
success: function(data, textStatus, request) {
$('#cm' + id).fadeOut('fast');
+ $('#cd' + id).removeClass('moderate');
},
error: function(request, textStatus, error) {
- showError("Oops, there was a problem accepting the comment.");
- }
- });
- }
-
- function rejectComment(id) {
- $.ajax({
- type: 'POST',
- url: opts.rejectCommentURL,
- data: {id: id},
- success: function(data, textStatus, request) {
- var div = $('#cd' + id);
- div.slideUp('fast', function() {
- div.remove();
- });
- },
- error: function(request, textStatus, error) {
- showError("Oops, there was a problem rejecting the comment.");
+ showError('Oops, there was a problem accepting the comment.');
}
});
}
@@ -329,10 +339,18 @@
data: {id: id},
success: function(data, textStatus, request) {
var div = $('#cd' + id);
+ if (data == 'delete') {
+ // Moderator mode: remove the comment and all children immediately
+ div.slideUp('fast', function() {
+ div.remove();
+ });
+ return;
+ }
+ // User mode: only mark the comment as deleted
div
- .find('span.user_id:first')
+ .find('span.user-id:first')
.text('[deleted]').end()
- .find('p.comment_text:first')
+ .find('div.comment-text:first')
.text('[deleted]').end()
.find('#cm' + id + ', #dc' + id + ', #ac' + id + ', #rc' + id +
', #sp' + id + ', #hp' + id + ', #cr' + id + ', #rl' + id)
@@ -343,7 +361,7 @@
div.data('comment', comment);
},
error: function(request, textStatus, error) {
- showError("Oops, there was a problem deleting the comment.");
+ showError('Oops, there was a problem deleting the comment.');
}
});
}
@@ -377,14 +395,16 @@
textarea.slideUp('fast');
}
- /*
- Handle when the user clicks on a sort by link.
- */
+ function toggleCommentMarkupBox(id) {
+ $('#mb' + id).toggle();
+ }
+
+ /** Handle when the user clicks on a sort by link. */
function handleReSort(link) {
var classes = link.attr('class').split(/\s+/);
for (var i=0; i<classes.length; i++) {
- if (classes[i] != 'sort_option') {
- by = classes[i];
+ if (classes[i] != 'sort-option') {
+ by = classes[i].substring(2);
}
}
setComparator();
@@ -393,16 +413,16 @@
expiration.setDate(expiration.getDate() + 365);
document.cookie= 'sortBy=' + escape(by) +
';expires=' + expiration.toUTCString();
- $('ul.comment_ul').each(function(index, ul) {
+ $('ul.comment-ul').each(function(index, ul) {
var comments = getChildren($(ul), true);
comments = sortComments(comments);
appendComments(comments, $(ul).empty());
});
}
- /*
- Function to process a vote when a user clicks an arrow.
- */
+ /**
+ * Function to process a vote when a user clicks an arrow.
+ */
function handleVote(link) {
if (!opts.voting) {
showError("You'll need to login to vote.");
@@ -410,6 +430,10 @@
}
var id = link.attr('id');
+ if (!id) {
+ // Didn't click on one of the voting arrows.
+ return;
+ }
// If it is an unvote, the new vote value is 0,
// Otherwise it's 1 for an upvote, or -1 for a downvote.
var value = 0;
@@ -453,14 +477,14 @@
url: opts.processVoteURL,
data: d,
error: function(request, textStatus, error) {
- showError("Oops, there was a problem casting that vote.");
+ showError('Oops, there was a problem casting that vote.');
}
});
}
- /*
- Open a reply form used to reply to an existing comment.
- */
+ /**
+ * Open a reply form used to reply to an existing comment.
+ */
function openReply(id) {
// Swap out the reply link for the hide link
$('#rl' + id).hide();
@@ -476,13 +500,19 @@
event.preventDefault();
addComment($('#rf' + id));
closeReply(id);
+ })
+ .find('input[type=button]')
+ .click(function() {
+ closeReply(id);
});
- div.slideDown('fast');
+ div.slideDown('fast', function() {
+ $('#rf' + id).find('textarea').focus();
+ });
}
- /*
- Close the reply form opened with openReply.
- */
+ /**
+ * Close the reply form opened with openReply.
+ */
function closeReply(id) {
// Remove the reply div from the DOM.
$('#rd' + id).slideUp('fast', function() {
@@ -494,9 +524,9 @@
$('#rl' + id).show();
}
- /*
- Recursively sort a tree of comments using the comp comparator.
- */
+ /**
+ * Recursively sort a tree of comments using the comp comparator.
+ */
function sortComments(comments) {
comments.sort(comp);
$.each(comments, function() {
@@ -505,61 +535,61 @@
return comments;
}
- /*
- Get the children comments from a ul. If recursive is true,
- recursively include childrens' children.
- */
+ /**
+ * Get the children comments from a ul. If recursive is true,
+ * recursively include childrens' children.
+ */
function getChildren(ul, recursive) {
var children = [];
ul.children().children("[id^='cd']")
.each(function() {
var comment = $(this).data('comment');
- if (recursive) {
+ if (recursive)
comment.children = getChildren($(this).find('#cl' + comment.id), true);
- }
children.push(comment);
});
return children;
}
- /*
- Create a div to display a comment in.
- */
+ /** Create a div to display a comment in. */
function createCommentDiv(comment) {
+ if (!comment.displayed && !opts.moderator) {
+ return $('<div class="moderate">Thank you! Your comment will show up '
+ + 'once it is has been approved by a moderator.</div>');
+ }
// Prettify the comment rating.
comment.pretty_rating = comment.rating + ' point' +
- (comment.rating == 1 ? '' : 's');
+ (comment.rating == 1 ? '' : 's');
+ // Make a class (for displaying not yet moderated comments differently)
+ comment.css_class = comment.displayed ? '' : ' moderate';
// Create a div for this comment.
var context = $.extend({}, opts, comment);
var div = $(renderTemplate(commentTemplate, context));
- // If the user has voted on this comment, highlight the correct arrow.
+ // If the user has voted on this comment, highblight the correct arrow.
if (comment.vote) {
var direction = (comment.vote == 1) ? 'u' : 'd';
div.find('#' + direction + 'v' + comment.id).hide();
div.find('#' + direction + 'u' + comment.id).show();
}
- if (comment.text != '[deleted]') {
+ if (opts.moderator || comment.text != '[deleted]') {
div.find('a.reply').show();
- if (comment.proposal_diff) {
+ if (comment.proposal_diff)
div.find('#sp' + comment.id).show();
- }
- if (opts.moderator && !comment.displayed) {
+ if (opts.moderator && !comment.displayed)
div.find('#cm' + comment.id).show();
- }
- if (opts.moderator || (opts.username == comment.username)) {
+ if (opts.moderator || (opts.username == comment.username))
div.find('#dc' + comment.id).show();
- }
}
return div;
}
- /*
- A simple template renderer. Placeholders such as <%id%> are replaced
- by context['id'] with items being escaped. Placeholders such as <#id#>
- are not escaped.
- */
+ /**
+ * A simple template renderer. Placeholders such as <%id%> are replaced
+ * by context['id'] with items being escaped. Placeholders such as <#id#>
+ * are not escaped.
+ */
function renderTemplate(template, context) {
var esc = $(document.createElement('div'));
@@ -571,34 +601,35 @@
return escape ? esc.text(cur || "").html() : cur;
}
- return template.replace(/<([%#])([\w\.]*)\1>/g, function(){
+ return template.replace(/<([%#])([\w\.]*)\1>/g, function() {
return handle(arguments[2], arguments[1] == '%' ? true : false);
});
}
+ /** Flash an error message briefly. */
function showError(message) {
- $(document.createElement('div')).attr({'class': 'popup_error'})
- .append($(document.createElement('h1')).text(message))
+ $(document.createElement('div')).attr({'class': 'popup-error'})
+ .append($(document.createElement('div'))
+ .attr({'class': 'error-message'}).text(message))
.appendTo('body')
.fadeIn("slow")
.delay(2000)
.fadeOut("slow");
}
- /*
- Add a link the user uses to open the comments popup.
- */
+ /** Add a link the user uses to open the comments popup. */
$.fn.comment = function() {
return this.each(function() {
var id = $(this).attr('id').substring(1);
var count = COMMENT_METADATA[id];
var title = count + ' comment' + (count == 1 ? '' : 's');
var image = count > 0 ? opts.commentBrightImage : opts.commentImage;
+ var addcls = count == 0 ? ' nocomment' : '';
$(this)
.append(
$(document.createElement('a')).attr({
href: '#',
- 'class': 'sphinx_comment',
+ 'class': 'sphinx-comment-open' + addcls,
id: 'ao' + id
})
.append($(document.createElement('img')).attr({
@@ -614,7 +645,7 @@
.append(
$(document.createElement('a')).attr({
href: '#',
- 'class': 'sphinx_comment_close hidden',
+ 'class': 'sphinx-comment-close hidden',
id: 'ah' + id
})
.append($(document.createElement('img')).attr({
@@ -630,13 +661,12 @@
});
};
- var opts = jQuery.extend({
- processVoteURL: '/process_vote',
- addCommentURL: '/add_comment',
- getCommentsURL: '/get_comments',
- acceptCommentURL: '/accept_comment',
- rejectCommentURL: '/reject_comment',
- deleteCommentURL: '/delete_comment',
+ var opts = {
+ processVoteURL: '/_process_vote',
+ addCommentURL: '/_add_comment',
+ getCommentsURL: '/_get_comments',
+ acceptCommentURL: '/_accept_comment',
+ deleteCommentURL: '/_delete_comment',
commentImage: '/static/_static/comment.png',
closeCommentImage: '/static/_static/comment-close.png',
loadingImage: '/static/_static/ajax-loader.gif',
@@ -647,100 +677,108 @@
downArrowPressed: '/static/_static/down-pressed.png',
voting: false,
moderator: false
- }, COMMENT_OPTIONS);
+ };
- var replyTemplate = '\
- <li>\
- <div class="reply_div" id="rd<%id%>">\
- <form id="rf<%id%>">\
- <textarea name="comment" cols="80"></textarea>\
- <input type="submit" value="add reply" />\
- <input type="hidden" name="parent" value="<%id%>" />\
- <input type="hidden" name="node" value="" />\
- </form>\
+ if (typeof COMMENT_OPTIONS != "undefined") {
+ opts = jQuery.extend(opts, COMMENT_OPTIONS);
+ }
+
+ var popupTemplate = '\
+ <div class="sphinx-comments" id="sc<%id%>">\
+ <p class="sort-options">\
+ Sort by:\
+ <a href="#" class="sort-option byrating">best rated</a>\
+ <a href="#" class="sort-option byascage">newest</a>\
+ <a href="#" class="sort-option byage">oldest</a>\
+ </p>\
+ <div class="comment-header">Comments</div>\
+ <div class="comment-loading" id="cn<%id%>">\
+ loading comments... <img src="<%loadingImage%>" alt="" /></div>\
+ <ul id="cl<%id%>" class="comment-ul"></ul>\
+ <div id="ca<%id%>">\
+ <p class="add-a-comment">Add a comment\
+ (<a href="#" class="comment-markup" id="ab<%id%>">markup</a>):</p>\
+ <div class="comment-markup-box" id="mb<%id%>">\
+ reStructured text markup: <i>*emph*</i>, <b>**strong**</b>, \
+ <tt>``code``</tt>, \
+ code blocks: <tt>::</tt> and an indented block after blank line</div>\
+ <form method="post" id="cf<%id%>" class="comment-form" action="">\
+ <textarea name="comment" cols="80"></textarea>\
+ <p class="propose-button">\
+ <a href="#" id="pc<%id%>" class="show-propose-change">\
+ Propose a change &#9657;\
+ </a>\
+ <a href="#" id="hc<%id%>" class="hide-propose-change">\
+ Propose a change &#9663;\
+ </a>\
+ </p>\
+ <textarea name="proposal" id="pt<%id%>" cols="80"\
+ spellcheck="false"></textarea>\
+ <input type="submit" value="Add comment" />\
+ <input type="hidden" name="node" value="<%id%>" />\
+ <input type="hidden" name="parent" value="" />\
+ </form>\
</div>\
- </li>';
+ </div>';
var commentTemplate = '\
- <div id="cd<%id%>" class="spxcdiv">\
+ <div id="cd<%id%>" class="sphinx-comment<%css_class%>">\
<div class="vote">\
<div class="arrow">\
- <a href="#" id="uv<%id%>" class="vote">\
+ <a href="#" id="uv<%id%>" class="vote" title="vote up">\
<img src="<%upArrow%>" />\
</a>\
- <a href="#" id="uu<%id%>" class="un vote">\
+ <a href="#" id="uu<%id%>" class="un vote" title="vote up">\
<img src="<%upArrowPressed%>" />\
</a>\
</div>\
<div class="arrow">\
- <a href="#" id="dv<%id%>" class="vote">\
+ <a href="#" id="dv<%id%>" class="vote" title="vote down">\
<img src="<%downArrow%>" id="da<%id%>" />\
</a>\
- <a href="#" id="du<%id%>" class="un vote">\
+ <a href="#" id="du<%id%>" class="un vote" title="vote down">\
<img src="<%downArrowPressed%>" />\
</a>\
</div>\
</div>\
- <div class="comment_content">\
+ <div class="comment-content">\
<p class="tagline comment">\
- <span class="user_id"><%username%></span>\
+ <span class="user-id"><%username%></span>\
<span class="rating"><%pretty_rating%></span>\
<span class="delta"><%time.delta%></span>\
</p>\
- <p class="comment_text comment"><%text%></p>\
- <p class="comment_opts comment">\
+ <div class="comment-text comment"><#text#></div>\
+ <p class="comment-opts comment">\
<a href="#" class="reply hidden" id="rl<%id%>">reply &#9657;</a>\
- <a href="#" class="close_reply" id="cr<%id%>">reply &#9663;</a>\
- <a href="#" id="sp<%id%>" class="show_proposal">\
- proposal &#9657;\
- </a>\
- <a href="#" id="hp<%id%>" class="hide_proposal">\
- proposal &#9663;\
- </a>\
- <a href="#" id="dc<%id%>" class="delete_comment hidden">\
- delete\
- </a>\
+ <a href="#" class="close-reply" id="cr<%id%>">reply &#9663;</a>\
+ <a href="#" id="sp<%id%>" class="show-proposal">proposal &#9657;</a>\
+ <a href="#" id="hp<%id%>" class="hide-proposal">proposal &#9663;</a>\
+ <a href="#" id="dc<%id%>" class="delete-comment hidden">delete</a>\
<span id="cm<%id%>" class="moderation hidden">\
- <a href="#" id="ac<%id%>" class="accept_comment">accept</a>\
- <a href="#" id="rc<%id%>" class="reject_comment">reject</a>\
+ <a href="#" id="ac<%id%>" class="accept-comment">accept</a>\
</span>\
</p>\
<pre class="proposal" id="pr<%id%>">\
<#proposal_diff#>\
</pre>\
- <ul class="children" id="cl<%id%>"></ul>\
+ <ul class="comment-children" id="cl<%id%>"></ul>\
</div>\
<div class="clearleft"></div>\
</div>\
</div>';
- var popupTemplate = '\
- <div class="sphinx_comments" id="sc<%id%>">\
- <h1>Comments</h1>\
- <form method="post" id="cf<%id%>" class="comment_form" action="/docs/add_comment">\
- <textarea name="comment" cols="80"></textarea>\
- <p class="propose_button">\
- <a href="#" id="pc<%id%>" class="show_propose_change">\
- Propose a change &#9657;\
- </a>\
- <a href="#" id="hc<%id%>" class="hide_propose_change">\
- Propose a change &#9663;\
- </a>\
- </p>\
- <textarea name="proposal" id="pt<%id%>" cols="80" spellcheck="false"></textarea>\
- <input type="submit" value="add comment" />\
- <input type="hidden" name="node" value="<%id%>" />\
- <input type="hidden" name="parent" value="" />\
- <p class="sort_options">\
- Sort by:\
- <a href="#" class="sort_option rating">top</a>\
- <a href="#" class="sort_option ascage">newest</a>\
- <a href="#" class="sort_option age">oldest</a>\
- </p>\
- </form>\
- <h3 id="cn<%id%>">loading comments... <img src="<%loadingImage%>" alt="" /></h3>\
- <ul id="cl<%id%>" class="comment_ul"></ul>\
- </div>';
+ var replyTemplate = '\
+ <li>\
+ <div class="reply-div" id="rd<%id%>">\
+ <form id="rf<%id%>">\
+ <textarea name="comment" cols="80"></textarea>\
+ <input type="submit" value="Add reply" />\
+ <input type="button" value="Cancel" />\
+ <input type="hidden" name="parent" value="<%id%>" />\
+ <input type="hidden" name="node" value="" />\
+ </form>\
+ </div>\
+ </li>';
$(document).ready(function() {
init();
@@ -748,9 +786,10 @@
})(jQuery);
$(document).ready(function() {
- $('.spxcmt').comment();
+ // add comment anchors for all paragraphs that are commentable
+ $('.sphinx-has-comment').comment();
- /** Highlight search words in search results. */
+ // highlight search words in search results
$("div.context").each(function() {
var params = $.getQueryParameters();
var terms = (params.q) ? params.q[0].split(/\s+/) : [];
@@ -759,4 +798,11 @@ $(document).ready(function() {
result.highlightText(this.toLowerCase(), 'highlighted');
});
});
+
+ // directly open comment window if requested
+ var anchor = document.location.hash;
+ if (anchor.substring(0, 9) == '#comment-') {
+ $('#ao' + anchor.substring(9)).click();
+ document.location.hash = '#s' + anchor.substring(9);
+ }
});
diff --git a/sphinx/themes/default/layout.html b/sphinx/themes/default/layout.html
index 43ee2cdf..d91a5655 100644
--- a/sphinx/themes/default/layout.html
+++ b/sphinx/themes/default/layout.html
@@ -4,7 +4,7 @@
Sphinx layout template for the default theme.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{% extends "basic/layout.html" %}
diff --git a/sphinx/themes/default/static/default.css_t b/sphinx/themes/default/static/default.css_t
index 28dff738..ba20dca7 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-2010 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2011 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
index 73185171..a45e1926 100644
--- a/sphinx/themes/default/static/sidebar.js
+++ b/sphinx/themes/default/static/sidebar.js
@@ -16,7 +16,7 @@
* Once the browser is closed the cookie is deleted and the position
* reset to the default (expanded).
*
- * :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
@@ -29,6 +29,9 @@ $(function() {
var sidebar = $('.sphinxsidebar');
var sidebarwrapper = $('.sphinxsidebarwrapper');
+ // for some reason, the document has no sidebar; do not run into errors
+ if (!sidebar.length) return;
+
// original margin-left of the bodywrapper and width of the sidebar
// with the sidebar expanded
var bw_margin_expanded = bodywrapper.css('margin-left');
diff --git a/sphinx/themes/epub/epub-cover.html b/sphinx/themes/epub/epub-cover.html
index ef54631f..f8088925 100644
--- a/sphinx/themes/epub/epub-cover.html
+++ b/sphinx/themes/epub/epub-cover.html
@@ -4,7 +4,7 @@
Sample template for the html cover page.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{% extends "layout.html" %}
diff --git a/sphinx/themes/epub/layout.html b/sphinx/themes/epub/layout.html
index 8a348bed..24395a66 100644
--- a/sphinx/themes/epub/layout.html
+++ b/sphinx/themes/epub/layout.html
@@ -4,7 +4,7 @@
Sphinx layout template for the epub theme.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{% extends "basic/layout.html" %}
diff --git a/sphinx/themes/epub/static/epub.css b/sphinx/themes/epub/static/epub.css
index de21c462..d2c51751 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-2010 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
@@ -343,6 +343,7 @@ dl.glossary dt {
pre {
font-family: "LiberationNarrow", monospace;
overflow: auto;
+ overflow-y: hidden;
}
td.linenos pre {
diff --git a/sphinx/themes/haiku/layout.html b/sphinx/themes/haiku/layout.html
index c6258089..719dba77 100644
--- a/sphinx/themes/haiku/layout.html
+++ b/sphinx/themes/haiku/layout.html
@@ -4,7 +4,7 @@
Sphinx layout template for the haiku theme.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{% extends "basic/layout.html" %}
diff --git a/sphinx/themes/haiku/static/haiku.css_t b/sphinx/themes/haiku/static/haiku.css_t
index 61f14e0d..19f4e0bf 100644
--- a/sphinx/themes/haiku/static/haiku.css_t
+++ b/sphinx/themes/haiku/static/haiku.css_t
@@ -16,7 +16,7 @@
* Braden Ewing <brewin@gmail.com>
* Humdinger <humdingerb@gmail.com>
*
- * :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2011 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 d0aa912b..f9986c90 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-2010 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2011 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
new file mode 100644
index 00000000..1887361e
--- /dev/null
+++ b/sphinx/themes/pyramid/layout.html
@@ -0,0 +1,24 @@
+{% extends "basic/layout.html" %}
+
+{%- block extrahead %}
+<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Neuton&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
+<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Nobile:regular,italic,bold,bolditalic&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
+<!--[if lte IE 6]>
+<link rel="stylesheet" href="{{ pathto('_static/ie6.css', 1) }}" type="text/css" media="screen" charset="utf-8" />
+<![endif]-->
+{% endblock %}
+
+{% block header %}
+{%- if logo %}
+<div class="header">
+ <div class="logo">
+ <a href="{{ pathto(master_doc) }}">
+ <img class="logo" src="{{ pathto('_static/' + logo, 1) }}" alt="Logo"/>
+ </a>
+ </div>
+</div>
+{%- endif %}
+{% endblock %}
+
+{%- block sidebarlogo %}{%- endblock %}
+{%- block sidebarsourcelink %}{%- endblock %}
diff --git a/sphinx/themes/pyramid/static/dialog-note.png b/sphinx/themes/pyramid/static/dialog-note.png
new file mode 100644
index 00000000..263fbd58
--- /dev/null
+++ b/sphinx/themes/pyramid/static/dialog-note.png
Binary files differ
diff --git a/sphinx/themes/pyramid/static/dialog-seealso.png b/sphinx/themes/pyramid/static/dialog-seealso.png
new file mode 100644
index 00000000..3eb7b05c
--- /dev/null
+++ b/sphinx/themes/pyramid/static/dialog-seealso.png
Binary files differ
diff --git a/sphinx/themes/pyramid/static/dialog-topic.png b/sphinx/themes/pyramid/static/dialog-topic.png
new file mode 100644
index 00000000..2ac57475
--- /dev/null
+++ b/sphinx/themes/pyramid/static/dialog-topic.png
Binary files differ
diff --git a/sphinx/themes/pyramid/static/dialog-warning.png b/sphinx/themes/pyramid/static/dialog-warning.png
new file mode 100644
index 00000000..7233d45d
--- /dev/null
+++ b/sphinx/themes/pyramid/static/dialog-warning.png
Binary files differ
diff --git a/sphinx/themes/pyramid/static/epub.css b/sphinx/themes/pyramid/static/epub.css
new file mode 100644
index 00000000..28dff738
--- /dev/null
+++ b/sphinx/themes/pyramid/static/epub.css
@@ -0,0 +1,310 @@
+/*
+ * default.css_t
+ * ~~~~~~~~~~~~~
+ *
+ * Sphinx stylesheet -- default theme.
+ *
+ * :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ * :license: BSD, see LICENSE for details.
+ *
+ */
+
+@import url("basic.css");
+
+/* -- page layout ----------------------------------------------------------- */
+
+body {
+ font-family: {{ theme_bodyfont }};
+ font-size: 100%;
+ background-color: {{ theme_footerbgcolor }};
+ color: #000;
+ margin: 0;
+ padding: 0;
+}
+
+div.document {
+ background-color: {{ theme_sidebarbgcolor }};
+}
+
+div.documentwrapper {
+ float: left;
+ width: 100%;
+}
+
+div.bodywrapper {
+ margin: 0 0 0 230px;
+}
+
+div.body {
+ background-color: {{ theme_bgcolor }};
+ color: {{ theme_textcolor }};
+ padding: 0 20px 30px 20px;
+}
+
+{%- if theme_rightsidebar|tobool %}
+div.bodywrapper {
+ margin: 0 230px 0 0;
+}
+{%- endif %}
+
+div.footer {
+ color: {{ theme_footertextcolor }};
+ width: 100%;
+ padding: 9px 0 9px 0;
+ text-align: center;
+ font-size: 75%;
+}
+
+div.footer a {
+ color: {{ theme_footertextcolor }};
+ text-decoration: underline;
+}
+
+div.related {
+ background-color: {{ theme_relbarbgcolor }};
+ line-height: 30px;
+ color: {{ theme_relbartextcolor }};
+}
+
+div.related a {
+ color: {{ theme_relbarlinkcolor }};
+}
+
+div.sphinxsidebar {
+ {%- if theme_stickysidebar|tobool %}
+ top: 30px;
+ bottom: 0;
+ margin: 0;
+ position: fixed;
+ overflow: auto;
+ height: auto;
+ {%- endif %}
+ {%- if theme_rightsidebar|tobool %}
+ float: right;
+ {%- if theme_stickysidebar|tobool %}
+ right: 0;
+ {%- endif %}
+ {%- endif %}
+}
+
+{%- if theme_stickysidebar|tobool %}
+/* this is nice, but it it leads to hidden headings when jumping
+ to an anchor */
+/*
+div.related {
+ position: fixed;
+}
+
+div.documentwrapper {
+ margin-top: 30px;
+}
+*/
+{%- endif %}
+
+div.sphinxsidebar h3 {
+ font-family: {{ theme_headfont }};
+ color: {{ theme_sidebartextcolor }};
+ font-size: 1.4em;
+ font-weight: normal;
+ margin: 0;
+ padding: 0;
+}
+
+div.sphinxsidebar h3 a {
+ color: {{ theme_sidebartextcolor }};
+}
+
+div.sphinxsidebar h4 {
+ font-family: {{ theme_headfont }};
+ color: {{ theme_sidebartextcolor }};
+ font-size: 1.3em;
+ font-weight: normal;
+ margin: 5px 0 0 0;
+ padding: 0;
+}
+
+div.sphinxsidebar p {
+ color: {{ theme_sidebartextcolor }};
+}
+
+div.sphinxsidebar p.topless {
+ margin: 5px 10px 10px 10px;
+}
+
+div.sphinxsidebar ul {
+ margin: 10px;
+ padding: 0;
+ color: {{ theme_sidebartextcolor }};
+}
+
+div.sphinxsidebar a {
+ color: {{ theme_sidebarlinkcolor }};
+}
+
+div.sphinxsidebar input {
+ border: 1px solid {{ theme_sidebarlinkcolor }};
+ font-family: sans-serif;
+ font-size: 1em;
+}
+
+{% if theme_collapsiblesidebar|tobool %}
+/* for collapsible sidebar */
+div#sidebarbutton {
+ background-color: {{ theme_sidebarbtncolor }};
+}
+{% endif %}
+
+/* -- hyperlink styles ------------------------------------------------------ */
+
+a {
+ color: {{ theme_linkcolor }};
+ text-decoration: none;
+}
+
+a:visited {
+ color: {{ theme_visitedlinkcolor }};
+ text-decoration: none;
+}
+
+a:hover {
+ text-decoration: underline;
+}
+
+{% if theme_externalrefs|tobool %}
+a.external {
+ text-decoration: none;
+ border-bottom: 1px dashed {{ theme_linkcolor }};
+}
+
+a.external:hover {
+ text-decoration: none;
+ border-bottom: none;
+}
+
+a.external:visited {
+ text-decoration: none;
+ border-bottom: 1px dashed {{ theme_visitedlinkcolor }};
+}
+{% endif %}
+
+/* -- body styles ----------------------------------------------------------- */
+
+div.body h1,
+div.body h2,
+div.body h3,
+div.body h4,
+div.body h5,
+div.body h6 {
+ font-family: {{ theme_headfont }};
+ background-color: {{ theme_headbgcolor }};
+ font-weight: normal;
+ color: {{ theme_headtextcolor }};
+ border-bottom: 1px solid #ccc;
+ margin: 20px -20px 10px -20px;
+ padding: 3px 0 3px 10px;
+}
+
+div.body h1 { margin-top: 0; font-size: 200%; }
+div.body h2 { font-size: 160%; }
+div.body h3 { font-size: 140%; }
+div.body h4 { font-size: 120%; }
+div.body h5 { font-size: 110%; }
+div.body h6 { font-size: 100%; }
+
+a.headerlink {
+ color: {{ theme_headlinkcolor }};
+ font-size: 0.8em;
+ padding: 0 4px 0 4px;
+ text-decoration: none;
+}
+
+a.headerlink:hover {
+ background-color: {{ theme_headlinkcolor }};
+ color: white;
+}
+
+div.body p, div.body dd, div.body li {
+ text-align: justify;
+ line-height: 130%;
+}
+
+div.admonition p.admonition-title + p {
+ display: inline;
+}
+
+div.admonition p {
+ margin-bottom: 5px;
+}
+
+div.admonition pre {
+ margin-bottom: 5px;
+}
+
+div.admonition ul, div.admonition ol {
+ margin-bottom: 5px;
+}
+
+div.note {
+ background-color: #eee;
+ border: 1px solid #ccc;
+}
+
+div.seealso {
+ background-color: #ffc;
+ border: 1px solid #ff6;
+}
+
+div.topic {
+ background-color: #eee;
+}
+
+div.warning {
+ background-color: #ffe4e4;
+ border: 1px solid #f66;
+}
+
+p.admonition-title {
+ display: inline;
+}
+
+p.admonition-title:after {
+ content: ":";
+}
+
+pre {
+ padding: 5px;
+ background-color: {{ theme_codebgcolor }};
+ color: {{ theme_codetextcolor }};
+ line-height: 120%;
+ border: 1px solid #ac9;
+ border-left: none;
+ border-right: none;
+}
+
+tt {
+ background-color: #ecf0f3;
+ padding: 0 1px 0 1px;
+ font-size: 0.95em;
+}
+
+th {
+ background-color: #ede;
+}
+
+.warning tt {
+ background: #efc2c2;
+}
+
+.note tt {
+ background: #d6d6d6;
+}
+
+.viewcode-back {
+ font-family: {{ theme_bodyfont }};
+}
+
+div.viewcode-block:target {
+ background-color: #f4debf;
+ border-top: 1px solid #ac9;
+ border-bottom: 1px solid #ac9;
+}
diff --git a/sphinx/themes/pyramid/static/footerbg.png b/sphinx/themes/pyramid/static/footerbg.png
new file mode 100644
index 00000000..1fbc873d
--- /dev/null
+++ b/sphinx/themes/pyramid/static/footerbg.png
Binary files differ
diff --git a/sphinx/themes/pyramid/static/headerbg.png b/sphinx/themes/pyramid/static/headerbg.png
new file mode 100644
index 00000000..0596f202
--- /dev/null
+++ b/sphinx/themes/pyramid/static/headerbg.png
Binary files differ
diff --git a/sphinx/themes/pyramid/static/ie6.css b/sphinx/themes/pyramid/static/ie6.css
new file mode 100644
index 00000000..74baa5d5
--- /dev/null
+++ b/sphinx/themes/pyramid/static/ie6.css
@@ -0,0 +1,7 @@
+* html img,
+* html .png{position:relative;behavior:expression((this.runtimeStyle.behavior="none")&&(this.pngSet?this.pngSet=true:(this.nodeName == "IMG" && this.src.toLowerCase().indexOf('.png')>-1?(this.runtimeStyle.backgroundImage = "none",
+this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.src + "',sizingMethod='image')",
+this.src = "_static/transparent.gif"):(this.origBg = this.origBg? this.origBg :this.currentStyle.backgroundImage.toString().replace('url("','').replace('")',''),
+this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.origBg + "',sizingMethod='crop')",
+this.runtimeStyle.backgroundImage = "none")),this.pngSet=true)
+);}
diff --git a/sphinx/themes/pyramid/static/middlebg.png b/sphinx/themes/pyramid/static/middlebg.png
new file mode 100644
index 00000000..2369cfb7
--- /dev/null
+++ b/sphinx/themes/pyramid/static/middlebg.png
Binary files differ
diff --git a/sphinx/themes/pyramid/static/pyramid.css_t b/sphinx/themes/pyramid/static/pyramid.css_t
new file mode 100644
index 00000000..283a96ff
--- /dev/null
+++ b/sphinx/themes/pyramid/static/pyramid.css_t
@@ -0,0 +1,323 @@
+/*
+ * pylons.css_t
+ * ~~~~~~~~~~~~
+ *
+ * Sphinx stylesheet -- pylons theme.
+ *
+ * :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ * :license: BSD, see LICENSE for details.
+ *
+ */
+
+@import url("basic.css");
+
+/* -- page layout ----------------------------------------------------------- */
+
+body {
+ font-family: "Nobile", sans-serif;
+ font-size: 100%;
+ background-color: #393939;
+ color: #ffffff;
+ margin: 0;
+ padding: 0;
+}
+
+div.documentwrapper {
+ float: left;
+ width: 100%;
+}
+
+div.bodywrapper {
+ margin: 0 0 0 230px;
+}
+
+hr {
+ border: 1px solid #B1B4B6;
+}
+
+div.document {
+ background-color: #eee;
+}
+
+div.header {
+ width:100%;
+ background: #f4ad32 url(headerbg.png) repeat-x 0 top;
+ border-bottom: 2px solid #ffffff;
+}
+
+div.logo {
+ text-align: center;
+ padding-top: 10px;
+}
+
+div.body {
+ background-color: #ffffff;
+ color: #3E4349;
+ padding: 0 30px 30px 30px;
+ font-size: 1em;
+ border: 2px solid #ddd;
+ border-right-style: none;
+ overflow: auto;
+}
+
+div.footer {
+ color: #ffffff;
+ width: 100%;
+ padding: 13px 0;
+ text-align: center;
+ font-size: 75%;
+ background: transparent;
+ clear:both;
+}
+
+div.footer a {
+ color: #ffffff;
+ text-decoration: none;
+}
+
+div.footer a:hover {
+ color: #e88f00;
+ text-decoration: underline;
+}
+
+div.related {
+ line-height: 30px;
+ color: #373839;
+ font-size: 0.8em;
+ background-color: #eee;
+}
+
+div.related a {
+ color: #1b61d6;
+}
+
+div.related ul {
+ padding-left: 240px;
+}
+
+div.sphinxsidebar {
+ font-size: 0.75em;
+ line-height: 1.5em;
+}
+
+div.sphinxsidebarwrapper{
+ padding: 10px 0;
+}
+
+div.sphinxsidebar h3,
+div.sphinxsidebar h4 {
+ font-family: "Neuton", sans-serif;
+ color: #373839;
+ font-size: 1.4em;
+ font-weight: normal;
+ margin: 0;
+ padding: 5px 10px;
+ border-bottom: 2px solid #ddd;
+}
+
+div.sphinxsidebar h4{
+ font-size: 1.3em;
+}
+
+div.sphinxsidebar h3 a {
+ color: #000000;
+}
+
+
+div.sphinxsidebar p {
+ color: #888;
+ padding: 5px 20px;
+}
+
+div.sphinxsidebar p.topless {
+}
+
+div.sphinxsidebar ul {
+ margin: 10px 20px;
+ padding: 0;
+ color: #373839;
+}
+
+div.sphinxsidebar a {
+ color: #444;
+}
+
+div.sphinxsidebar input {
+ border: 1px solid #ccc;
+ font-family: sans-serif;
+ font-size: 1em;
+}
+
+div.sphinxsidebar input[type=text]{
+ margin-left: 20px;
+}
+
+/* -- sidebars -------------------------------------------------------------- */
+
+div.sidebar {
+ margin: 0 0 0.5em 1em;
+ border: 2px solid #c6d880;
+ background-color: #e6efc2;
+ width: 40%;
+ float: right;
+ border-right-style: none;
+ border-left-style: none;
+ padding: 10px 20px;
+}
+
+p.sidebar-title {
+ font-weight: bold;
+}
+
+/* -- body styles ----------------------------------------------------------- */
+
+a, a .pre {
+ color: #1b61d6;
+ text-decoration: none;
+}
+
+a:hover, a:hover .pre {
+ text-decoration: underline;
+}
+
+div.body h1,
+div.body h2,
+div.body h3,
+div.body h4,
+div.body h5,
+div.body h6 {
+ font-family: "Neuton", sans-serif;
+ background-color: #ffffff;
+ font-weight: normal;
+ color: #373839;
+ margin: 30px 0px 10px 0px;
+ padding: 5px 0;
+}
+
+div.body h1 { border-top: 20px solid white; margin-top: 0; font-size: 200%; }
+div.body h2 { font-size: 150%; background-color: #ffffff; }
+div.body h3 { font-size: 120%; background-color: #ffffff; }
+div.body h4 { font-size: 110%; background-color: #ffffff; }
+div.body h5 { font-size: 100%; background-color: #ffffff; }
+div.body h6 { font-size: 100%; background-color: #ffffff; }
+
+a.headerlink {
+ color: #1b61d6;
+ font-size: 0.8em;
+ padding: 0 4px 0 4px;
+ text-decoration: none;
+}
+
+a.headerlink:hover {
+ text-decoration: underline;
+}
+
+div.body p, div.body dd, div.body li {
+ line-height: 1.5em;
+}
+
+div.admonition p.admonition-title + p {
+ display: inline;
+}
+
+div.highlight{
+ background-color: white;
+}
+
+div.note {
+ border: 2px solid #7a9eec;
+ border-right-style: none;
+ border-left-style: none;
+ padding: 10px 20px 10px 60px;
+ background: #e1ecfe url(dialog-note.png) no-repeat 10px 8px;
+}
+
+div.seealso {
+ background: #fff6bf url(dialog-seealso.png) no-repeat 10px 8px;
+ border: 2px solid #ffd324;
+ border-left-style: none;
+ border-right-style: none;
+ padding: 10px 20px 10px 60px;
+}
+
+div.topic {
+ background: #eeeeee;
+ border: 2px solid #C6C9CB;
+ padding: 10px 20px;
+ border-right-style: none;
+ border-left-style: none;
+}
+
+div.warning {
+ background: #fbe3e4 url(dialog-warning.png) no-repeat 10px 8px;
+ border: 2px solid #fbc2c4;
+ border-right-style: none;
+ border-left-style: none;
+ padding: 10px 20px 10px 60px;
+}
+
+p.admonition-title {
+ display: none;
+}
+
+p.admonition-title:after {
+ content: ":";
+}
+
+pre {
+ padding: 10px;
+ background-color: #fafafa;
+ color: #222;
+ line-height: 1.2em;
+ border: 2px solid #C6C9CB;
+ font-size: 1.1em;
+ margin: 1.5em 0 1.5em 0;
+ border-right-style: none;
+ border-left-style: none;
+}
+
+tt {
+ background-color: transparent;
+ color: #222;
+ font-size: 1.1em;
+ font-family: monospace;
+}
+
+.viewcode-back {
+ font-family: "Nobile", sans-serif;
+}
+
+div.viewcode-block:target {
+ background-color: #fff6bf;
+ border: 2px solid #ffd324;
+ border-left-style: none;
+ border-right-style: none;
+ padding: 10px 20px;
+}
+
+table.highlighttable {
+ width: 100%;
+}
+
+table.highlighttable td {
+ padding: 0;
+}
+
+a em.std-term {
+ color: #007f00;
+}
+
+a:hover em.std-term {
+ text-decoration: underline;
+}
+
+.download {
+ font-family: "Nobile", sans-serif;
+ font-weight: normal;
+ font-style: normal;
+}
+
+tt.xref {
+ font-weight: normal;
+ font-style: normal;
+}
diff --git a/sphinx/themes/pyramid/static/transparent.gif b/sphinx/themes/pyramid/static/transparent.gif
new file mode 100644
index 00000000..0341802e
--- /dev/null
+++ b/sphinx/themes/pyramid/static/transparent.gif
Binary files differ
diff --git a/sphinx/themes/pyramid/theme.conf b/sphinx/themes/pyramid/theme.conf
new file mode 100644
index 00000000..409579fd
--- /dev/null
+++ b/sphinx/themes/pyramid/theme.conf
@@ -0,0 +1,4 @@
+[theme]
+inherit = basic
+stylesheet = pyramid.css
+pygments_style = sphinx.pygments_styles.PyramidStyle
diff --git a/sphinx/themes/scrolls/layout.html b/sphinx/themes/scrolls/layout.html
index afec8415..92cb694d 100644
--- a/sphinx/themes/scrolls/layout.html
+++ b/sphinx/themes/scrolls/layout.html
@@ -5,7 +5,7 @@
Sphinx layout template for the scrolls theme, originally written
by Armin Ronacher.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{% extends "basic/layout.html" %}
diff --git a/sphinx/themes/scrolls/static/scrolls.css_t b/sphinx/themes/scrolls/static/scrolls.css_t
index 589f91ab..e3d26ea3 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-2010 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2011 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 2d653f9f..c9319185 100644
--- a/sphinx/themes/sphinxdoc/layout.html
+++ b/sphinx/themes/sphinxdoc/layout.html
@@ -4,7 +4,7 @@
Sphinx layout template for the sphinxdoc theme.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{% extends "basic/layout.html" %}
diff --git a/sphinx/themes/sphinxdoc/static/sphinxdoc.css b/sphinx/themes/sphinxdoc/static/sphinxdoc.css
index c7e6e335..0a428074 100644
--- a/sphinx/themes/sphinxdoc/static/sphinxdoc.css
+++ b/sphinx/themes/sphinxdoc/static/sphinxdoc.css
@@ -5,7 +5,7 @@
* Sphinx stylesheet -- sphinxdoc theme. Originally created by
* Armin Ronacher for Werkzeug.
*
- * :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
diff --git a/sphinx/themes/traditional/static/traditional.css b/sphinx/themes/traditional/static/traditional.css
index c9980fa5..bac58c0d 100644
--- a/sphinx/themes/traditional/static/traditional.css
+++ b/sphinx/themes/traditional/static/traditional.css
@@ -4,7 +4,7 @@
*
* Sphinx stylesheet -- traditional docs.python.org theme.
*
- * :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
diff --git a/sphinx/theming.py b/sphinx/theming.py
index 92e63f31..68d11a49 100644
--- a/sphinx/theming.py
+++ b/sphinx/theming.py
@@ -5,7 +5,7 @@
Theming support for HTML builders.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -30,13 +30,13 @@ class Theme(object):
themes = {}
@classmethod
- def init_themes(cls, builder):
+ def init_themes(cls, confdir, theme_path, warn=None):
"""Search all theme paths for available themes."""
- cls.themepath = list(builder.config.html_theme_path)
+ cls.themepath = list(theme_path)
cls.themepath.append(path.join(package_dir, 'themes'))
for themedir in cls.themepath[::-1]:
- themedir = path.join(builder.confdir, themedir)
+ themedir = path.join(confdir, themedir)
if not path.isdir(themedir):
continue
for theme in os.listdir(themedir):
@@ -48,8 +48,9 @@ class Theme(object):
tname = theme[:-4]
tinfo = zfile
except Exception:
- builder.warn('file %r on theme path is not a valid '
- 'zipfile or contains no theme' % theme)
+ if warn:
+ warn('file %r on theme path is not a valid '
+ 'zipfile or contains no theme' % theme)
continue
else:
if not path.isfile(path.join(themedir, theme, THEMECONF)):
diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py
index a3d30d9d..8218c04a 100644
--- a/sphinx/util/__init__.py
+++ b/sphinx/util/__init__.py
@@ -5,7 +5,7 @@
Utility functions for Sphinx.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -114,9 +114,9 @@ class FilenameUniqDict(dict):
def purge_doc(self, docname):
for filename, (docs, _) in self.items():
docs.discard(docname)
- #if not docs:
- # del self[filename]
- # self._existing.discard(filename)
+ if not docs:
+ del self[filename]
+ self._existing.discard(filename)
def __getstate__(self):
return self._existing
@@ -164,16 +164,19 @@ def copy_static_entry(source, targetdir, builder, context={},
_DEBUG_HEADER = '''\
# Sphinx version: %s
+# Python version: %s
# Docutils version: %s %s
# Jinja2 version: %s
'''
def save_traceback():
"""Save the current exception's traceback in a temporary file."""
+ import platform
exc = traceback.format_exc()
fd, path = tempfile.mkstemp('.log', 'sphinx-err-')
os.write(fd, (_DEBUG_HEADER %
(sphinx.__version__,
+ platform.python_version(),
docutils.__version__, docutils.__version_details__,
jinja2.__version__)).encode('utf-8'))
os.write(fd, exc.encode('utf-8'))
@@ -206,7 +209,7 @@ def get_module_source(modname):
lfilename = filename.lower()
if lfilename.endswith('.pyo') or lfilename.endswith('.pyc'):
filename = filename[:-1]
- elif not lfilename.endswith('.py'):
+ elif not (lfilename.endswith('.py') or lfilename.endswith('.pyw')):
raise PycodeError('source is not a .py file: %r' % filename)
if not path.isfile(filename):
raise PycodeError('source file is not present: %r' % filename)
@@ -282,6 +285,14 @@ def rpartition(s, t):
return '', s
+def split_into(n, type, value):
+ """Split an index entry into a given number of parts at semicolons."""
+ parts = map(lambda x: x.strip(), value.split(';', n-1))
+ if sum(1 for part in parts if part) < n:
+ raise ValueError('invalid %s index entry %r' % (type, value))
+ return parts
+
+
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 3fbfe4b2..1a263cff 100644
--- a/sphinx/util/compat.py
+++ b/sphinx/util/compat.py
@@ -5,7 +5,7 @@
Stuff for docutils compatibility.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 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 b0f03921..ac85034d 100644
--- a/sphinx/util/console.py
+++ b/sphinx/util/console.py
@@ -5,7 +5,7 @@
Format colored console output.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 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 89f81e8c..896319f5 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-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -129,15 +129,13 @@ class TypedField(GroupedField):
is_typed = True
def __init__(self, name, names=(), typenames=(), label=None,
- rolename=None, typerolename=None):
- GroupedField.__init__(self, name, names, label, rolename, False)
+ rolename=None, typerolename=None, can_collapse=False):
+ GroupedField.__init__(self, name, names, label, rolename, can_collapse)
self.typenames = typenames
self.typerolename = typerolename
def make_field(self, types, domain, items):
- fieldname = nodes.field_name('', self.label)
- listnode = self.list_type()
- for fieldarg, content in items:
+ def handle_item(fieldarg, content):
par = nodes.paragraph()
par += self.make_xref(self.rolename, domain, fieldarg, nodes.strong)
if fieldarg in types:
@@ -154,8 +152,17 @@ class TypedField(GroupedField):
par += nodes.Text(')')
par += nodes.Text(' -- ')
par += content
- listnode += nodes.list_item('', par)
- fieldbody = nodes.field_body('', listnode)
+ return par
+
+ fieldname = nodes.field_name('', self.label)
+ if len(items) == 1 and self.can_collapse:
+ fieldarg, content = items[0]
+ bodynode = handle_item(fieldarg, content)
+ else:
+ bodynode = self.list_type()
+ for fieldarg, content in items:
+ bodynode += nodes.list_item('', handle_item(fieldarg, content))
+ fieldbody = nodes.field_body('', bodynode)
return nodes.field('', fieldname, fieldbody)
diff --git a/sphinx/util/docstrings.py b/sphinx/util/docstrings.py
index d1a2ff8d..ba81bf00 100644
--- a/sphinx/util/docstrings.py
+++ b/sphinx/util/docstrings.py
@@ -5,33 +5,36 @@
Utilities for docstring processing.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import sys
-def prepare_docstring(s):
- """Convert a docstring into lines of parseable reST.
+def prepare_docstring(s, ignore=1):
+ """Convert a docstring into lines of parseable reST. Remove common leading
+ indentation, where the indentation of a given number of lines (usually just
+ one) is ignored.
- Return it as a list of lines usable for inserting into a docutils ViewList
- (used as argument of nested_parse().) An empty line is added to act as a
- separator between this docstring and following content.
+ Return the docstring as a list of lines usable for inserting into a docutils
+ ViewList (used as argument of nested_parse().) An empty line is added to
+ act as a separator between this docstring and following content.
"""
lines = s.expandtabs().splitlines()
- # Find minimum indentation of any non-blank lines after first line.
+ # Find minimum indentation of any non-blank lines after ignored lines.
margin = sys.maxint
- for line in lines[1:]:
+ for line in lines[ignore:]:
content = len(line.lstrip())
if content:
indent = len(line) - content
margin = min(margin, indent)
- # Remove indentation.
- if lines:
- lines[0] = lines[0].lstrip()
+ # Remove indentation from ignored lines.
+ for i in range(ignore):
+ if i < len(lines):
+ lines[i] = lines[i].lstrip()
if margin < sys.maxint:
- for i in range(1, len(lines)): lines[i] = lines[i][margin:]
+ for i in range(ignore, len(lines)): lines[i] = lines[i][margin:]
# Remove any leading blank lines.
while lines and not lines[0]:
lines.pop(0)
diff --git a/sphinx/util/inspect.py b/sphinx/util/inspect.py
index 4f1cf922..a0574003 100644
--- a/sphinx/util/inspect.py
+++ b/sphinx/util/inspect.py
@@ -5,7 +5,7 @@
Helpers for inspecting Python modules.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/util/jsdump.py b/sphinx/util/jsdump.py
index e357128c..191e2419 100644
--- a/sphinx/util/jsdump.py
+++ b/sphinx/util/jsdump.py
@@ -6,12 +6,14 @@
This module implements a simple JavaScript serializer.
Uses the basestring encode function from simplejson by Bob Ippolito.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import re
+from sphinx.util.pycompat import u
+
_str_re = re.compile(r'"(\\\\|\\"|[^"])*"')
_int_re = re.compile(r'\d+')
_name_re = re.compile(r'[a-zA-Z]\w*')
@@ -50,7 +52,7 @@ def encode_string(s):
return '"' + str(ESCAPE_ASCII.sub(replace, s)) + '"'
def decode_string(s):
- return ESCAPED.sub(lambda m: eval('u"'+m.group()+'"'), s)
+ return ESCAPED.sub(lambda m: eval(u + '"' + m.group() + '"'), s)
reswords = set("""\
diff --git a/sphinx/util/jsonimpl.py b/sphinx/util/jsonimpl.py
index f654ef22..cf5ddb42 100644
--- a/sphinx/util/jsonimpl.py
+++ b/sphinx/util/jsonimpl.py
@@ -5,7 +5,7 @@
JSON serializer implementation wrapper.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 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 3746c87c..fa6cb7e0 100644
--- a/sphinx/util/matching.py
+++ b/sphinx/util/matching.py
@@ -5,7 +5,7 @@
Pattern-matching utility functions for Sphinx.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 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 adce565c..a241f574 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-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -74,17 +74,22 @@ def split_explicit_title(text):
indextypes = [
- 'single', 'pair', 'double', 'triple',
+ 'single', 'pair', 'double', 'triple', 'see', 'seealso',
]
def process_index_entry(entry, targetid):
indexentries = []
entry = entry.strip()
+ oentry = entry
+ main = ''
+ if entry.startswith('!'):
+ main = 'main'
+ entry = entry[1:].lstrip()
for type in pairindextypes:
if entry.startswith(type+':'):
value = entry[len(type)+1:].strip()
value = pairindextypes[type] + '; ' + value
- indexentries.append(('pair', value, targetid, value))
+ indexentries.append(('pair', value, targetid, main))
break
else:
for type in indextypes:
@@ -92,15 +97,19 @@ def process_index_entry(entry, targetid):
value = entry[len(type)+1:].strip()
if type == 'double':
type = 'pair'
- indexentries.append((type, value, targetid, value))
+ indexentries.append((type, value, targetid, main))
break
# shorthand notation for single entries
else:
- for value in entry.split(','):
+ for value in oentry.split(','):
value = value.strip()
+ main = ''
+ if value.startswith('!'):
+ main = 'main'
+ value = value[1:].lstrip()
if not value:
continue
- indexentries.append(('single', value, targetid, value))
+ indexentries.append(('single', value, targetid, main))
return indexentries
diff --git a/sphinx/util/osutil.py b/sphinx/util/osutil.py
index 464e56ed..487a5afc 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-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/util/png.py b/sphinx/util/png.py
index 59c32715..50c72efd 100644
--- a/sphinx/util/png.py
+++ b/sphinx/util/png.py
@@ -5,7 +5,7 @@
PNG image manipulation helpers.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 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 319312a7..a95c9332 100644
--- a/sphinx/util/pycompat.py
+++ b/sphinx/util/pycompat.py
@@ -5,7 +5,7 @@
Stuff for Python version compatibility.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -23,6 +23,8 @@ if sys.version_info >= (3, 0):
def b(s):
return s.encode('utf-8')
bytes = bytes
+ # prefix for Unicode strings
+ u = ''
# support for running 2to3 over config files
def convert_with_2to3(filepath):
from lib2to3.refactor import RefactoringTool, get_fixers_from_package
@@ -45,6 +47,7 @@ else:
class_types = (type, ClassType)
b = str
bytes = str
+ u = 'u'
# no need to refactor on 2.x versions
convert_with_2to3 = None
diff --git a/sphinx/util/tags.py b/sphinx/util/tags.py
index 3f98e0ac..7499634d 100644
--- a/sphinx/util/tags.py
+++ b/sphinx/util/tags.py
@@ -3,7 +3,7 @@
sphinx.util.tags
~~~~~~~~~~~~~~~~
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 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 4cabd9b2..f968f2e4 100644
--- a/sphinx/util/texescape.py
+++ b/sphinx/util/texescape.py
@@ -5,7 +5,7 @@
TeX escaping helper.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 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 510ecbe0..d9b47213 100644
--- a/sphinx/util/websupport.py
+++ b/sphinx/util/websupport.py
@@ -3,9 +3,10 @@
sphinx.util.websupport
~~~~~~~~~~~~~~~~~~~~~~
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
def is_commentable(node):
- return node.__class__.__name__ in ('paragraph', 'literal_block')
+ #return node.__class__.__name__ in ('paragraph', 'literal_block')
+ return node.__class__.__name__ == 'paragraph'
diff --git a/sphinx/versioning.py b/sphinx/versioning.py
index 5b0b2127..f50f80b0 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-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 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 30303132..15c619af 100644
--- a/sphinx/websupport/__init__.py
+++ b/sphinx/websupport/__init__.py
@@ -5,10 +5,11 @@
Base Module for web support functions.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
+import cgi
import sys
import cPickle as pickle
import posixpath
@@ -16,45 +17,57 @@ from os import path
from jinja2 import Environment, FileSystemLoader
+from docutils.core import publish_parts
+
from sphinx.application import Sphinx
from sphinx.util.osutil import ensuredir
from sphinx.util.jsonimpl import dumps as dump_json
+from sphinx.websupport import errors
from sphinx.websupport.search import BaseSearch, SEARCH_ADAPTERS
from sphinx.websupport.storage import StorageBackend
-from sphinx.websupport.errors import *
-
-
-class WebSupportApp(Sphinx):
- def __init__(self, *args, **kwargs):
- self.staticdir = kwargs.pop('staticdir', None)
- self.builddir = kwargs.pop('builddir', None)
- self.search = kwargs.pop('search', None)
- self.storage = kwargs.pop('storage', None)
- Sphinx.__init__(self, *args, **kwargs)
class WebSupport(object):
"""The main API class for the web support package. All interactions
with the web support package should occur through this class.
"""
- def __init__(self, srcdir='', builddir='', datadir='', search=None,
- storage=None, status=sys.stdout, warning=sys.stderr,
- moderation_callback=None, staticdir='static',
- docroot=''):
+ def __init__(self,
+ srcdir=None, # only required for building
+ builddir='', # the dir with data/static/doctrees subdirs
+ datadir=None, # defaults to builddir/data
+ staticdir=None, # defaults to builddir/static
+ doctreedir=None, # defaults to builddir/doctrees
+ search=None, # defaults to no search
+ storage=None, # defaults to SQLite in datadir
+ status=sys.stdout,
+ warning=sys.stderr,
+ moderation_callback=None,
+ allow_anonymous_comments=True,
+ docroot='',
+ staticroot='static',
+ ):
+ # directories
self.srcdir = srcdir
self.builddir = builddir
self.outdir = path.join(builddir, 'data')
self.datadir = datadir or self.outdir
- self.staticdir = staticdir.strip('/')
+ self.staticdir = staticdir or path.join(self.builddir, 'static')
+ self.doctreedir = staticdir or path.join(self.builddir, 'doctrees')
+ # web server virtual paths
+ self.staticroot = staticroot.strip('/')
self.docroot = docroot.strip('/')
+
self.status = status
self.warning = warning
self.moderation_callback = moderation_callback
+ self.allow_anonymous_comments = allow_anonymous_comments
self._init_templating()
self._init_search(search)
self._init_storage(storage)
+ self._globalcontext = None
+
self._make_base_comment_options()
def _init_storage(self, storage):
@@ -103,19 +116,27 @@ class WebSupport(object):
It will also save node data to the database.
"""
if not self.srcdir:
- raise SrcdirNotSpecifiedError( \
- 'No srcdir associated with WebSupport object')
- doctreedir = path.join(self.outdir, 'doctrees')
- app = WebSupportApp(self.srcdir, self.srcdir,
- self.outdir, doctreedir, 'websupport',
- search=self.search, status=self.status,
- warning=self.warning, storage=self.storage,
- staticdir=self.staticdir, builddir=self.builddir)
+ raise RuntimeError('No srcdir associated with WebSupport object')
+ app = Sphinx(self.srcdir, self.srcdir, self.outdir, self.doctreedir,
+ 'websupport', status=self.status, warning=self.warning)
+ app.builder.set_webinfo(self.staticdir, self.staticroot,
+ self.search, self.storage)
self.storage.pre_build()
app.build()
self.storage.post_build()
+ def get_globalcontext(self):
+ """Load and return the "global context" pickle."""
+ if not self._globalcontext:
+ infilename = path.join(self.datadir, 'globalcontext.pickle')
+ f = open(infilename, 'rb')
+ try:
+ self._globalcontext = pickle.load(f)
+ finally:
+ f.close()
+ return self._globalcontext
+
def get_document(self, docname, username='', moderator=False):
"""Load and return a document from a pickle. The document will
be a dict object which can be used to render a template::
@@ -146,28 +167,38 @@ class WebSupport(object):
* **relbar**: A div containing links to related documents
* **title**: The title of the document
* **css**: Links to css files used by Sphinx
- * **js**: Javascript containing comment options
+ * **script**: Javascript containing comment options
This raises :class:`~sphinx.websupport.errors.DocumentNotFoundError`
if a document matching `docname` is not found.
:param docname: the name of the document to load.
"""
- infilename = path.join(self.datadir, 'pickles', docname + '.fpickle')
+ docpath = path.join(self.datadir, 'pickles', docname)
+ if path.isdir(docpath):
+ infilename = docpath + '/index.fpickle'
+ if not docname:
+ docname = 'index'
+ else:
+ docname += '/index'
+ else:
+ infilename = docpath + '.fpickle'
try:
f = open(infilename, 'rb')
except IOError:
- raise DocumentNotFoundError(
+ raise errors.DocumentNotFoundError(
'The document "%s" could not be found' % docname)
+ try:
+ document = pickle.load(f)
+ finally:
+ f.close()
- document = pickle.load(f)
comment_opts = self._make_comment_options(username, moderator)
- comment_metadata = self.storage.get_metadata(docname, moderator)
+ comment_meta = self._make_metadata(
+ self.storage.get_metadata(docname, moderator))
- document['js'] = '\n'.join([comment_opts,
- self._make_metadata(comment_metadata),
- document['js']])
+ document['script'] = comment_opts + comment_meta + document['script']
return document
def get_search_results(self, q):
@@ -181,12 +212,18 @@ class WebSupport(object):
:param q: the search query
"""
results = self.search.query(q)
- ctx = {'search_performed': True,
- 'search_results': results,
- 'q': q}
- document = self.get_document('search')
- document['body'] = self.results_template.render(ctx)
- document['title'] = 'Search Results'
+ ctx = {
+ 'q': q,
+ 'search_performed': True,
+ 'search_results': results,
+ 'docroot': '../', # XXX
+ }
+ document = {
+ 'body': self.results_template.render(ctx),
+ 'title': 'Search Results',
+ 'sidebar': '',
+ 'relbar': ''
+ }
return document
def get_data(self, node_id, username=None, moderator=False):
@@ -231,13 +268,16 @@ class WebSupport(object):
return self.storage.get_data(node_id, username, moderator)
def delete_comment(self, comment_id, username='', moderator=False):
- """Delete a comment. Doesn't actually delete the comment, but
- instead replaces the username and text files with "[deleted]" so
- as not to leave any comments orphaned.
+ """Delete a comment.
+
+ If `moderator` is True, the comment and all descendants will be deleted
+ from the database, and the function returns ``True``.
- If `moderator` is True, the comment will always be deleted. If
- `moderator` is False, the comment will only be deleted if the
- `username` matches the `username` on the comment.
+ If `moderator` is False, the comment will be marked as deleted (but not
+ removed from the database so as not to leave any comments orphaned), but
+ only if the `username` matches the `username` on the comment. The
+ username and text files are replaced with "[deleted]" . In this case,
+ the function returns ``False``.
This raises :class:`~sphinx.websupport.errors.UserNotAuthorizedError`
if moderator is False and `username` doesn't match username on the
@@ -247,7 +287,7 @@ class WebSupport(object):
:param username: the username requesting the deletion.
:param moderator: whether the requestor is a moderator.
"""
- self.storage.delete_comment(comment_id, username, moderator)
+ return self.storage.delete_comment(comment_id, username, moderator)
def add_comment(self, text, node_id='', parent_id='', displayed=True,
username=None, time=None, proposal=None,
@@ -276,9 +316,16 @@ class WebSupport(object):
:param username: the username of the user making the comment.
:param time: the time the comment was created, defaults to now.
"""
- comment = self.storage.add_comment(text, displayed, username,
+ if username is None:
+ if self.allow_anonymous_comments:
+ username = 'Anonymous'
+ else:
+ raise errors.UserNotAuthorizedError()
+ parsed = self._parse_comment_text(text)
+ comment = self.storage.add_comment(parsed, displayed, username,
time, proposal, node_id,
parent_id, moderator)
+ comment['original_text'] = text
if not displayed and self.moderation_callback:
self.moderation_callback(comment)
return comment
@@ -334,22 +381,9 @@ class WebSupport(object):
:param moderator: Whether the user making the request is a moderator.
"""
if not moderator:
- raise UserNotAuthorizedError()
+ raise errors.UserNotAuthorizedError()
self.storage.accept_comment(comment_id)
- def reject_comment(self, comment_id, moderator=False):
- """Reject a comment that is pending moderation.
-
- This raises :class:`~sphinx.websupport.errors.UserNotAuthorizedError`
- if moderator is False.
-
- :param comment_id: The id of the comment that was accepted.
- :param moderator: Whether the user making the request is a moderator.
- """
- if not moderator:
- raise UserNotAuthorizedError()
- self.storage.reject_comment(comment_id)
-
def _make_base_comment_options(self):
"""Helper method to create the part of the COMMENT_OPTIONS javascript
that remains the same throughout the lifetime of the
@@ -357,19 +391,18 @@ class WebSupport(object):
"""
self.base_comment_opts = {}
- if self.docroot is not '':
+ if self.docroot != '':
comment_urls = [
- ('addCommentURL', 'add_comment'),
- ('getCommentsURL', 'get_comments'),
- ('processVoteURL', 'process_vote'),
- ('acceptCommentURL', 'accept_comment'),
- ('rejectCommentURL', 'reject_comment'),
- ('deleteCommentURL', 'delete_comment')
+ ('addCommentURL', '_add_comment'),
+ ('getCommentsURL', '_get_comments'),
+ ('processVoteURL', '_process_vote'),
+ ('acceptCommentURL', '_accept_comment'),
+ ('deleteCommentURL', '_delete_comment')
]
for key, value in comment_urls:
self.base_comment_opts[key] = \
'/' + posixpath.join(self.docroot, value)
- if self.staticdir != 'static':
+ if self.staticroot != 'static':
static_urls = [
('commentImage', 'comment.png'),
('closeCommentImage', 'comment-close.png'),
@@ -382,7 +415,7 @@ class WebSupport(object):
]
for key, value in static_urls:
self.base_comment_opts[key] = \
- '/' + posixpath.join(self.staticdir, '_static', value)
+ '/' + posixpath.join(self.staticroot, '_static', value)
def _make_comment_options(self, username, moderator):
"""Helper method to create the parts of the COMMENT_OPTIONS
@@ -391,8 +424,6 @@ class WebSupport(object):
:param username: The username of the user making the request.
:param moderator: Whether the user making the request is a moderator.
"""
- # XXX parts is not used?
- #parts = [self.base_comment_opts]
rv = self.base_comment_opts.copy()
if username:
rv.update({
@@ -400,15 +431,25 @@ class WebSupport(object):
'username': username,
'moderator': moderator,
})
- return '\n'.join([
- '<script type="text/javascript">',
- 'var COMMENT_OPTIONS = %s;' % dump_json(rv),
- '</script>'
- ])
+ return '''\
+ <script type="text/javascript">
+ var COMMENT_OPTIONS = %s;
+ </script>
+ ''' % dump_json(rv)
def _make_metadata(self, data):
- return '\n'.join([
- '<script type="text/javascript">',
- 'var COMMENT_METADATA = %s;' % dump_json(data),
- '</script>'
- ])
+ return '''\
+ <script type="text/javascript">
+ var COMMENT_METADATA = %s;
+ </script>
+ ''' % dump_json(data)
+
+ def _parse_comment_text(self, text):
+ settings = {'file_insertion_enabled': False,
+ 'output_encoding': 'unicode'}
+ try:
+ ret = publish_parts(text, writer_name='html',
+ settings_overrides=settings)['fragment']
+ except Exception:
+ ret = cgi.escape(text)
+ return ret
diff --git a/sphinx/websupport/errors.py b/sphinx/websupport/errors.py
index 53106dfb..225b10c8 100644
--- a/sphinx/websupport/errors.py
+++ b/sphinx/websupport/errors.py
@@ -5,22 +5,15 @@
Contains Error classes for the web support package.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
-__all__ = ['DocumentNotFoundError', 'SrcdirNotSpecifiedError',
- 'UserNotAuthorizedError', 'CommentNotAllowedError',
- 'NullSearchException']
class DocumentNotFoundError(Exception):
pass
-class SrcdirNotSpecifiedError(Exception):
- pass
-
-
class UserNotAuthorizedError(Exception):
pass
diff --git a/sphinx/websupport/search/__init__.py b/sphinx/websupport/search/__init__.py
index 0cba0f77..9410973c 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-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -113,6 +113,11 @@ class BaseSearch(object):
except TypeError:
return context
+ def context_for_searchtool(self):
+ """Required by the HTML builder."""
+ return {}
+
+
# The built-in search adapters.
SEARCH_ADAPTERS = {
'xapian': ('xapiansearch', 'XapianSearch'),
diff --git a/sphinx/websupport/search/nullsearch.py b/sphinx/websupport/search/nullsearch.py
index fd6d4dcf..61f2d2fb 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-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 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 e58c7342..1ed3d714 100644
--- a/sphinx/websupport/search/whooshsearch.py
+++ b/sphinx/websupport/search/whooshsearch.py
@@ -5,7 +5,7 @@
Whoosh search adapter.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 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 b0475435..0615be84 100644
--- a/sphinx/websupport/search/xapiansearch.py
+++ b/sphinx/websupport/search/xapiansearch.py
@@ -5,7 +5,7 @@
Xapian search adapter.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 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 3d8a9ab5..77292812 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-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -113,11 +113,3 @@ class StorageBackend(object):
:param comment_id: The id of the comment being accepted.
"""
raise NotImplementedError()
-
- def reject_comment(self, comment_id):
- """Called when a moderator rejects a comment. The comment should
- then be deleted.
-
- :param comment_id: The id of the comment being accepted.
- """
- raise NotImplementedError()
diff --git a/sphinx/websupport/storage/differ.py b/sphinx/websupport/storage/differ.py
index d5225071..cd57bae8 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-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -20,19 +20,25 @@ class CombinedHtmlDiff(object):
"""
highlight_regex = re.compile(r'([\+\-\^]+)')
- def make_html(self, source, proposal):
+ def __init__(self, source, proposal):
+ proposal = escape(proposal)
+
+ differ = Differ()
+ self.diff = list(differ.compare(source.splitlines(1),
+ proposal.splitlines(1)))
+
+ def make_text(self):
+ return '\n'.join(self.diff)
+
+ def make_html(self):
"""Return the HTML representation of the differences between
`source` and `proposal`.
:param source: the original text
:param proposal: the proposed text
"""
- proposal = escape(proposal)
-
- differ = Differ()
- diff = list(differ.compare(source.splitlines(1),
- proposal.splitlines(1)))
html = []
+ diff = self.diff[:]
line = diff.pop(0)
next = diff.pop(0)
while True:
@@ -58,7 +64,7 @@ class CombinedHtmlDiff(object):
if next is not None and next[0] == '?':
tag = 'ins' if prefix == '+' else 'del'
text = self._highlight_text(text, next, tag)
- css_class = 'prop_added' if prefix == '+' else 'prop_removed'
+ css_class = 'prop-added' if prefix == '+' else 'prop-removed'
return '<span class="%s">%s</span>\n' % (css_class, text.rstrip())
diff --git a/sphinx/websupport/storage/sqlalchemy_db.py b/sphinx/websupport/storage/sqlalchemy_db.py
index 4e2757a9..dc2ec6a7 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-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -33,7 +33,7 @@ class Node(Base):
def nested_comments(self, username, moderator):
"""Create a tree of comments. First get all comments that are
- descendents of this node, then convert them to a tree form.
+ descendants of this node, then convert them to a tree form.
:param username: the name of the user to get comments for.
:param moderator: whether the user is moderator.
@@ -56,6 +56,7 @@ class Node(Base):
# Filter out all comments not descending from this node.
q = q.filter(Comment.path.like(str(self.id) + '.%'))
+ # Filter out all comments that are not moderated yet.
if not moderator:
q = q.filter(Comment.displayed == True)
@@ -97,6 +98,22 @@ class Node(Base):
self.source = source
+class CommentVote(Base):
+ """A vote a user has made on a Comment."""
+ __tablename__ = db_prefix + 'commentvote'
+
+ username = Column(String(64), primary_key=True)
+ comment_id = Column(Integer, ForeignKey(db_prefix + 'comments.id'),
+ primary_key=True)
+ # -1 if downvoted, +1 if upvoted, 0 if voted then unvoted.
+ value = Column(Integer, nullable=False)
+
+ def __init__(self, comment_id, username, value):
+ self.comment_id = comment_id
+ self.username = username
+ self.value = value
+
+
class Comment(Base):
"""An individual Comment being stored."""
__tablename__ = db_prefix + 'comments'
@@ -114,6 +131,9 @@ class Comment(Base):
node_id = Column(String, ForeignKey(db_prefix + 'nodes.id'))
node = relation(Node, backref="comments")
+ votes = relation(CommentVote, backref="comment",
+ cascade="all")
+
def __init__(self, text, displayed, username, rating, time,
proposal, proposal_diff):
self.text = text
@@ -155,7 +175,7 @@ class Comment(Base):
'delta': self.pretty_delta(delta)}
path = self.path.split('.')
- node = path[0] if len(path) == 2 else None
+ node = path[0]
parent = path[-2] if len(path) > 2 else None
return {'text': self.text,
@@ -186,20 +206,3 @@ class Comment(Base):
dt = (days, 'day')
return '%s %s ago' % dt if dt[0] == 1 else '%s %ss ago' % dt
-
-
-class CommentVote(Base):
- """A vote a user has made on a Comment."""
- __tablename__ = db_prefix + 'commentvote'
-
- username = Column(String(64), primary_key=True)
- comment_id = Column(Integer, ForeignKey(db_prefix + 'comments.id'),
- primary_key=True)
- comment = relation(Comment, backref="votes")
- # -1 if downvoted, +1 if upvoted, 0 if voted then unvoted.
- value = Column(Integer, nullable=False)
-
- def __init__(self, comment_id, username, value):
- self.comment_id = comment_id
- self.username = username
- self.value = value
diff --git a/sphinx/websupport/storage/sqlalchemystorage.py b/sphinx/websupport/storage/sqlalchemystorage.py
index 6f13c91b..b2efe82b 100644
--- a/sphinx/websupport/storage/sqlalchemystorage.py
+++ b/sphinx/websupport/storage/sqlalchemystorage.py
@@ -5,7 +5,7 @@
An SQLAlchemy storage backend.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -60,11 +60,13 @@ class SQLAlchemyStorage(StorageBackend):
proposal, node_id, parent_id, moderator):
session = Session()
proposal_diff = None
+ proposal_diff_text = None
if node_id and proposal:
node = session.query(Node).filter(Node.id == node_id).one()
- differ = CombinedHtmlDiff()
- proposal_diff = differ.make_html(node.source, proposal)
+ differ = CombinedHtmlDiff(node.source, proposal)
+ proposal_diff = differ.make_html()
+ proposal_diff_text = differ.make_text()
elif parent_id:
parent = session.query(Comment.displayed).\
filter(Comment.id == parent_id).one()
@@ -81,6 +83,8 @@ class SQLAlchemyStorage(StorageBackend):
comment.set_path(node_id, parent_id)
session.commit()
d = comment.serializable()
+ d['document'] = comment.node.document
+ d['proposal_diff_text'] = proposal_diff_text
session.close()
return d
@@ -88,12 +92,23 @@ class SQLAlchemyStorage(StorageBackend):
session = Session()
comment = session.query(Comment).\
filter(Comment.id == comment_id).one()
- if moderator or comment.username == username:
+ if moderator:
+ # moderator mode: delete the comment and all descendants
+ # find descendants via path
+ session.query(Comment).filter(
+ Comment.path.like(comment.path + '.%')).delete(False)
+ session.delete(comment)
+ session.commit()
+ session.close()
+ return True
+ elif comment.username == username:
+ # user mode: do not really delete, but remove text and proposal
comment.username = '[deleted]'
comment.text = '[deleted]'
comment.proposal = ''
session.commit()
session.close()
+ return False
else:
session.close()
raise UserNotAuthorizedError()
@@ -154,21 +169,9 @@ class SQLAlchemyStorage(StorageBackend):
def accept_comment(self, comment_id):
session = Session()
-
- # XXX assignment to "comment" needed?
- comment = session.query(Comment).filter(
- Comment.id == comment_id).update(
- {Comment.displayed: True})
-
- session.commit()
- session.close()
-
- def reject_comment(self, comment_id):
- session = Session()
-
- comment = session.query(Comment).\
- filter(Comment.id == comment_id).one()
- session.delete(comment)
+ session.query(Comment).filter(Comment.id == comment_id).update(
+ {Comment.displayed: True}
+ )
session.commit()
session.close()
diff --git a/sphinx/writers/__init__.py b/sphinx/writers/__init__.py
index da3b9abf..880e4fdb 100644
--- a/sphinx/writers/__init__.py
+++ b/sphinx/writers/__init__.py
@@ -5,6 +5,6 @@
Custom docutils writers.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 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 33d90c91..5719aca2 100644
--- a/sphinx/writers/html.py
+++ b/sphinx/writers/html.py
@@ -5,7 +5,7 @@
docutils writers handling Sphinx' custom nodes.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -59,7 +59,11 @@ class HTMLTranslator(BaseTranslator):
self.highlightlang = builder.config.highlight_language
self.highlightlinenothreshold = sys.maxint
self.protect_literal_text = 0
- self.add_permalinks = builder.config.html_add_permalinks
+ self.permalink_text = builder.config.html_add_permalinks
+ # support backwards-compatible setting to a bool
+ if not isinstance(self.permalink_text, basestring):
+ self.permalink_text = self.permalink_text and u'\u00B6' or ''
+ self.permalink_text = self.encode(self.permalink_text)
self.secnumber_suffix = builder.config.html_secnumber_suffix
def visit_start_of_file(self, node):
@@ -81,11 +85,12 @@ class HTMLTranslator(BaseTranslator):
and node['ids'] and node['first']:
self.body.append('<!--[%s]-->' % node['ids'][0])
def depart_desc_signature(self, node):
- if node['ids'] and self.add_permalinks and self.builder.add_permalinks:
+ if node['ids'] and self.permalink_text and self.builder.add_permalinks:
self.body.append(u'<a class="headerlink" href="#%s" '
% node['ids'][0] +
- u'title="%s">\u00B6</a>' %
- _('Permalink to this definition'))
+ u'title="%s">%s</a>' % (
+ _('Permalink to this definition'),
+ self.permalink_text))
self.body.append('</dt>\n')
def visit_desc_addname(self, node):
@@ -232,8 +237,10 @@ class HTMLTranslator(BaseTranslator):
lang = node['language']
if node.has_key('linenos'):
linenos = node['linenos']
- highlighted = self.highlighter.highlight_block(node.rawsource,
- lang, linenos)
+ def warner(msg):
+ self.builder.warn(msg, (self.builder.current_docname, node.line))
+ highlighted = self.highlighter.highlight_block(
+ node.rawsource, lang, linenos, warn=warner)
starttag = self.starttag(node, 'div', suffix='',
CLASS='highlight-%s' % lang)
self.body.append(starttag + highlighted + '</div>\n')
@@ -244,9 +251,6 @@ class HTMLTranslator(BaseTranslator):
# overwritten
def visit_literal(self, node):
- if len(node.children) == 1 and \
- node.children[0] in ('None', 'True', 'False'):
- node['classes'].append('xref')
self.body.append(self.starttag(node, 'tt', '',
CLASS='docutils literal'))
self.protect_literal_text += 1
@@ -479,24 +483,57 @@ class HTMLTranslator(BaseTranslator):
def depart_abbreviation(self, node):
self.body.append('</abbr>')
+ def visit_termsep(self, node):
+ self.body.append('<br />')
+ raise nodes.SkipNode
+
def depart_title(self, node):
close_tag = self.context[-1]
- if (self.add_permalinks and self.builder.add_permalinks and
+ if (self.permalink_text and self.builder.add_permalinks and
node.parent.hasattr('ids') and node.parent['ids']):
aname = node.parent['ids'][0]
# add permalink anchor
if close_tag.startswith('</h'):
self.body.append(u'<a class="headerlink" href="#%s" ' % aname +
- u'title="%s">\u00B6</a>' %
- _('Permalink to this headline'))
+ u'title="%s">%s</a>' % (
+ _('Permalink to this headline'),
+ self.permalink_text))
elif close_tag.startswith('</a></h'):
self.body.append(u'</a><a class="headerlink" href="#%s" ' %
aname +
- u'title="%s">\u00B6' %
- _('Permalink to this headline'))
+ u'title="%s">%s' % (
+ _('Permalink to this headline'),
+ self.permalink_text))
BaseTranslator.depart_title(self, node)
+ # overwritten to add even/odd classes
+
+ def visit_table(self, node):
+ self._table_row_index = 0
+ return BaseTranslator.visit_table(self, node)
+
+ def visit_row(self, node):
+ self._table_row_index += 1
+ if self._table_row_index % 2 == 0:
+ node['classes'].append('row-even')
+ else:
+ node['classes'].append('row-odd')
+ self.body.append(self.starttag(node, 'tr', ''))
+ node.column = 0
+
+ def visit_field_list(self, node):
+ self._fieldlist_row_index = 0
+ return BaseTranslator.visit_field_list(self, node)
+
+ def visit_field(self, node):
+ self._fieldlist_row_index += 1
+ if self._fieldlist_row_index % 2 == 0:
+ node['classes'].append('field-even')
+ else:
+ node['classes'].append('field-odd')
+ self.body.append(self.starttag(node, 'tr', '', CLASS='field'))
+
def unknown_visit(self, node):
raise NotImplementedError('Unknown node: ' + node.__class__.__name__)
diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py
index f6d7eb7c..c557101b 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-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -23,6 +23,7 @@ from sphinx import addnodes
from sphinx import highlighting
from sphinx.errors import SphinxError
from sphinx.locale import admonitionlabels, versionlabels, _
+from sphinx.util import split_into
from sphinx.util.osutil import ustrftime
from sphinx.util.pycompat import any
from sphinx.util.texescape import tex_escape_map, tex_replace_map
@@ -102,8 +103,10 @@ class ExtBabel(Babel):
return '\\shorthandoff{"}'
return ''
- _ISO639_TO_BABEL = Babel._ISO639_TO_BABEL.copy()
- _ISO639_TO_BABEL['sl'] = 'slovene'
+# in latest trunk, the attribute is called Babel.language_codes and already
+# includes Slovene
+if hasattr(Babel, '_ISO639_TO_BABEL'):
+ Babel._ISO639_TO_BABEL['sl'] = 'slovene'
class Table(object):
@@ -113,6 +116,7 @@ class Table(object):
self.colspec = None
self.rowcount = 0
self.had_head = False
+ self.has_problematic = False
self.has_verbatim = False
self.caption = None
self.longtable = False
@@ -191,6 +195,14 @@ class LaTeXTranslator(nodes.NodeVisitor):
lang = babel.get_language()
if lang:
self.elements['classoptions'] += ',' + babel.get_language()
+ elif builder.config.language == 'ja':
+ self.elements['classoptions'] += ',english,dvipdfm'
+ # not 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}}'
else:
self.builder.warn('no Babel option known for language %r' %
builder.config.language)
@@ -231,6 +243,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.verbatim = None
self.in_title = 0
self.in_production_list = 0
+ self.in_footnote = 0
+ self.in_caption = 0
self.first_document = 1
self.this_is_the_title = 1
self.literal_whitespace = 0
@@ -429,10 +443,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
elif self.this_is_the_title:
if len(node.children) != 1 and not isinstance(node.children[0],
nodes.Text):
- self.builder.warn(
- 'document title is not a single Text node',
- '%s:%s' % (self.builder.env.doc2path(self.curfilestack[-1]),
- node.line or ''))
+ self.builder.warn('document title is not a single Text node',
+ (self.curfilestack[-1], node.line))
if not self.elements['title']:
# text needs to be escaped since it is inserted into
# the output literally
@@ -465,8 +477,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.builder.warn(
'encountered title node not in section, topic, table, '
'admonition or sidebar',
- '%s:%s' % (self.builder.env.doc2path(self.curfilestack[-1]),
- node.line or ''))
+ (self.curfilestack[-1], node.line or ''))
self.body.append('\\textbf{')
self.context.append('}\n')
self.in_title = 1
@@ -485,6 +496,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
def visit_desc(self, node):
self.body.append('\n\n\\begin{fulllineitems}\n')
+ if self.table:
+ self.table.has_problematic = True
def depart_desc(self, node):
self.body.append('\n\\end{fulllineitems}\n\n')
@@ -584,9 +597,11 @@ class LaTeXTranslator(nodes.NodeVisitor):
raise nodes.SkipNode
def visit_collected_footnote(self, node):
+ self.in_footnote += 1
self.body.append('\\footnote{')
def depart_collected_footnote(self, node):
self.body.append('}')
+ self.in_footnote -= 1
def visit_label(self, node):
if isinstance(node.parent, nodes.citation):
@@ -618,14 +633,22 @@ class LaTeXTranslator(nodes.NodeVisitor):
u'\\capstart\\caption{%s}\n' % self.table.caption)
if self.table.longtable:
self.body.append('\n\\begin{longtable}')
+ endmacro = '\\end{longtable}\n\n'
elif self.table.has_verbatim:
self.body.append('\n\\begin{tabular}')
+ endmacro = '\\end{tabular}\n\n'
+ elif self.table.has_problematic and not self.table.colspec:
+ # if the user has given us tabularcolumns, accept them and use
+ # tabulary nevertheless
+ self.body.append('\n\\begin{tabular}')
+ endmacro = '\\end{tabular}\n\n'
else:
self.body.append('\n\\begin{tabulary}{\\linewidth}')
+ endmacro = '\\end{tabulary}\n\n'
if self.table.colspec:
self.body.append(self.table.colspec)
else:
- if self.table.has_verbatim:
+ if self.table.has_problematic:
colwidth = 0.95 / self.table.colcount
colspec = ('p{%.3f\\linewidth}|' % colwidth) * \
self.table.colcount
@@ -658,12 +681,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
else:
self.body.append('\\hline\n')
self.body.extend(self.tablebody)
- if self.table.longtable:
- self.body.append('\\end{longtable}\n\n')
- elif self.table.has_verbatim:
- self.body.append('\\end{tabular}\n\n')
- else:
- self.body.append('\\end{tabulary}\n\n')
+ self.body.append(endmacro)
if not self.table.longtable and self.table.caption is not None:
self.body.append('\\end{threeparttable}\n\n')
self.table = None
@@ -728,6 +746,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
def visit_bullet_list(self, node):
if not self.compact_list:
self.body.append('\\begin{itemize}\n' )
+ if self.table:
+ self.table.has_problematic = True
def depart_bullet_list(self, node):
if not self.compact_list:
self.body.append('\\end{itemize}\n' )
@@ -736,6 +756,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.body.append('\\begin{enumerate}\n' )
if 'start' in node:
self.body.append('\\setcounter{enumi}{%d}\n' % (node['start'] - 1))
+ if self.table:
+ self.table.has_problematic = True
def depart_enumerated_list(self, node):
self.body.append('\\end{enumerate}\n' )
@@ -748,6 +770,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
def visit_definition_list(self, node):
self.body.append('\\begin{description}\n')
+ if self.table:
+ self.table.has_problematic = True
def depart_definition_list(self, node):
self.body.append('\\end{description}\n')
@@ -765,6 +789,10 @@ class LaTeXTranslator(nodes.NodeVisitor):
def depart_term(self, node):
self.body.append(self.context.pop())
+ def visit_termsep(self, node):
+ self.body.append(', ')
+ raise nodes.SkipNode
+
def visit_classifier(self, node):
self.body.append('{[}')
def depart_classifier(self, node):
@@ -777,6 +805,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
def visit_field_list(self, node):
self.body.append('\\begin{quote}\\begin{description}\n')
+ if self.table:
+ self.table.has_problematic = True
def depart_field_list(self, node):
self.body.append('\\end{description}\\end{quote}\n')
@@ -798,6 +828,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
def visit_centered(self, node):
self.body.append('\n\\begin{center}')
+ if self.table:
+ self.table.has_problematic = True
def depart_centered(self, node):
self.body.append('\n\\end{center}')
@@ -807,6 +839,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.compact_list += 1
self.body.append('\\begin{itemize}\\setlength{\\itemsep}{0pt}'
'\\setlength{\\parskip}{0pt}\n')
+ if self.table:
+ self.table.has_problematic = True
def depart_hlist(self, node):
self.compact_list -= 1
self.body.append('\\end{itemize}\n')
@@ -921,9 +955,11 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.body.append(self.context.pop())
def visit_caption(self, node):
+ self.in_caption += 1
self.body.append('\\caption{')
def depart_caption(self, node):
self.body.append('}')
+ self.in_caption -= 1
def visit_legend(self, node):
self.body.append('{\\small ')
@@ -1028,27 +1064,36 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.body.append('\n\\end{flushright}\n')
def visit_index(self, node, scre=re.compile(r';\s*')):
+ if not node.get('inline', True):
+ self.body.append('\n')
entries = node['entries']
- for type, string, tid, _ in entries:
- if type == 'single':
- self.body.append(r'\index{%s}' %
- scre.sub('!', self.encode(string)))
- elif type == 'pair':
- parts = tuple(self.encode(x.strip())
- for x in string.split(';', 1))
- try:
- self.body.append(r'\indexii{%s}{%s}' % parts)
- except TypeError:
- self.builder.warn('invalid pair index entry %r' % string)
- elif type == 'triple':
- parts = tuple(self.encode(x.strip())
- for x in string.split(';', 2))
- try:
- self.body.append(r'\indexiii{%s}{%s}{%s}' % parts)
- except TypeError:
- self.builder.warn('invalid triple index entry %r' % string)
- else:
- self.builder.warn('unknown index entry type %s found' % type)
+ for type, string, tid, ismain in entries:
+ m = ''
+ if ismain:
+ m = '|textbf'
+ try:
+ if type == 'single':
+ p = scre.sub('!', self.encode(string))
+ self.body.append(r'\index{%s%s}' % (p, m))
+ elif type == 'pair':
+ p1, p2 = map(self.encode, split_into(2, 'pair', string))
+ self.body.append(r'\index{%s!%s%s}\index{%s!%s%s}' %
+ (p1, p2, m, p2, p1, m))
+ elif type == 'triple':
+ p1, p2, p3 = map(self.encode, split_into(3, 'triple', string))
+ self.body.append(
+ r'\index{%s!%s %s%s}\index{%s!%s, %s%s}\index{%s!%s %s%s}' %
+ (p1, p2, p3, m, p2, p3, p1, m, p3, p1, p2, m))
+ elif type == 'see':
+ p1, p2 = map(self.encode, split_into(2, 'see', string))
+ self.body.append(r'\index{%s|see{%s}}' % (p1, p2))
+ elif type == 'seealso':
+ p1, p2 = map(self.encode, split_into(2, 'seealso', string))
+ self.body.append(r'\index{%s|see{%s}}' % (p1, p2))
+ else:
+ self.builder.warn('unknown index entry type %s found' % type)
+ except ValueError, err:
+ self.builder.warn(str(err))
raise nodes.SkipNode
def visit_raw(self, node):
@@ -1066,11 +1111,17 @@ class LaTeXTranslator(nodes.NodeVisitor):
uri.startswith('https:') or uri.startswith('ftp:'):
self.body.append('\\href{%s}{' % self.encode_uri(uri))
# if configured, put the URL after the link
- if self.builder.config.latex_show_urls and \
- node.astext() != uri:
+ show_urls = self.builder.config.latex_show_urls
+ if node.astext() != uri and show_urls and show_urls != 'no':
if uri.startswith('mailto:'):
uri = uri[7:]
- self.context.append('} (%s)' % self.encode_uri(uri))
+ if show_urls == 'footnote' and not \
+ (self.in_footnote or self.in_caption):
+ # obviously, footnotes in footnotes are not going to work
+ self.context.append(
+ r'}\footnote{%s}' % self.encode_uri(uri))
+ else: # all other true values (b/w compat)
+ self.context.append('} (%s)' % self.encode_uri(uri))
else:
self.context.append('}')
elif uri.startswith('#'):
@@ -1107,10 +1158,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.body.append('\\grammartoken{')
self.context.append('}')
else:
- self.builder.warn(
- 'unusable reference target found: %s' % uri,
- '%s:%s' % (self.builder.env.doc2path(self.curfilestack[-1]),
- node.line or ''))
+ self.builder.warn('unusable reference target found: %s' % uri,
+ (self.curfilestack[-1], node.line))
self.context.append('')
def depart_reference(self, node):
self.body.append(self.context.pop())
@@ -1198,6 +1247,10 @@ class LaTeXTranslator(nodes.NodeVisitor):
if used:
self.body.append('\\footnotemark[%s]' % num)
else:
+ if self.in_caption:
+ raise UnsupportedError('%s:%s: footnotes in float captions '
+ 'are not supported by LaTeX' %
+ (self.curfilestack[-1], node.line))
footnode.walkabout(self)
self.footnotestack[-1][num][1] = True
raise nodes.SkipChildren
@@ -1215,13 +1268,17 @@ class LaTeXTranslator(nodes.NodeVisitor):
lang = node['language']
if 'linenos' in node:
linenos = node['linenos']
- hlcode = self.highlighter.highlight_block(code, lang, linenos)
+ def warner(msg):
+ self.builder.warn(msg, (self.curfilestack[-1], node.line))
+ hlcode = self.highlighter.highlight_block(code, lang, linenos,
+ warn=warner)
# workaround for Unicode issue
hlcode = hlcode.replace(u'€', u'@texteuro[]')
# must use original Verbatim environment and "tabular" environment
if self.table:
hlcode = hlcode.replace('\\begin{Verbatim}',
'\\begin{OriginalVerbatim}')
+ self.table.has_problematic = True
self.table.has_verbatim = True
# get consistent trailer
hlcode = hlcode.rstrip()[:-14] # strip \end{Verbatim}
@@ -1232,31 +1289,21 @@ class LaTeXTranslator(nodes.NodeVisitor):
visit_doctest_block = visit_literal_block
depart_doctest_block = depart_literal_block
- def visit_line_block(self, node):
- """line-block:
- * whitespace (including linebreaks) is significant
- * inline markup is supported.
- * serif typeface
- """
- self.body.append('\n{\\raggedright{}')
- self.literal_whitespace += 1
- def depart_line_block(self, node):
- self.literal_whitespace -= 1
- # remove the last \\
- del self.body[-1]
- self.body.append('}\n')
-
def visit_line(self, node):
- self._line_start = len(self.body)
+ self.body.append('\item[] ')
def depart_line(self, node):
- if self._line_start == len(self.body):
- # no output in this line -- add a nonbreaking space, else the
- # \\ command will give an error
- self.body.append('~')
- if self.table is not None:
- self.body.append('\\newline\n')
+ self.body.append('\n')
+
+ def visit_line_block(self, node):
+ if isinstance(node.parent, nodes.line_block):
+ self.body.append('\\item[]\n'
+ '\\begin{DUlineblock}{\\DUlineblockindent}\n')
else:
- self.body.append('\\\\\n')
+ self.body.append('\n\\begin{DUlineblock}{0em}\n')
+ if self.table:
+ self.table.has_problematic = True
+ def depart_line_block(self, node):
+ self.body.append('\\end{DUlineblock}\n')
def visit_block_quote(self, node):
# If the block quote contains a single object and that object
@@ -1270,6 +1317,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
done = 1
if not done:
self.body.append('\\begin{quote}\n')
+ if self.table:
+ self.table.has_problematic = True
def depart_block_quote(self, node):
done = 0
if len(node.children) == 1:
@@ -1306,6 +1355,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
def visit_option_list(self, node):
self.body.append('\\begin{optionlist}{3cm}\n')
+ if self.table:
+ self.table.has_problematic = True
def depart_option_list(self, node):
self.body.append('\\end{optionlist}\n')
diff --git a/sphinx/writers/manpage.py b/sphinx/writers/manpage.py
index 5f93c7eb..e6bccca7 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-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -161,6 +161,10 @@ class ManualPageTranslator(BaseTranslator):
def depart_versionmodified(self, node):
self.depart_paragraph(node)
+ def visit_termsep(self, node):
+ self.body.append(', ')
+ raise nodes.SkipNode
+
# overwritten -- we don't want source comments to show up
def visit_comment(self, node):
raise nodes.SkipNode
@@ -235,6 +239,19 @@ class ManualPageTranslator(BaseTranslator):
self.body.append(self.defs['reference'][0])
self.body.append(node.astext())
self.body.append(self.defs['reference'][1])
+
+ uri = node.get('refuri', '')
+ if uri.startswith('mailto:') or uri.startswith('http:') or \
+ uri.startswith('https:') or uri.startswith('ftp:'):
+ # if configured, put the URL after the link
+ if self.builder.config.man_show_urls and \
+ node.astext() != uri:
+ if uri.startswith('mailto:'):
+ uri = uri[7:]
+ self.body.extend([
+ ' <',
+ self.defs['strong'][0], uri, self.defs['strong'][1],
+ '>'])
raise nodes.SkipNode
def visit_centered(self, node):
diff --git a/sphinx/writers/texinfo.py b/sphinx/writers/texinfo.py
new file mode 100644
index 00000000..c5c3aabe
--- /dev/null
+++ b/sphinx/writers/texinfo.py
@@ -0,0 +1,1256 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.writers.texinfo
+ ~~~~~~~~~~~~~~~~~~~~~~
+
+ Custom docutils writer for Texinfo.
+
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import re
+from os import path
+
+from docutils import nodes, writers
+
+from sphinx import addnodes
+from sphinx.locale import versionlabels
+
+
+TEMPLATE = """\
+\\input texinfo @c -*-texinfo-*-
+@c %%**start of header
+@setfilename %(filename)s
+@documentencoding UTF-8
+@copying
+Generated by Sphinx
+@end copying
+@settitle %(title)s
+@defindex ge
+@paragraphindent %(paragraphindent)s
+@exampleindent %(exampleindent)s
+@afourlatex
+%(direntry)s
+@c %%**end of header
+
+@titlepage
+@title %(title)s
+@author %(author)s
+@end titlepage
+@contents
+
+@c %%** start of user preamble
+%(preamble)s
+@c %%** end of user preamble
+
+@ifnottex
+@node Top
+@top %(title)s
+@end ifnottex
+
+@c %%**start of body
+%(body)s
+@c %%**end of body
+@bye
+"""
+
+
+def find_subsections(section):
+ """Return a list of subsections for the given ``section``."""
+ result = []
+ for child in section.children:
+ if isinstance(child, nodes.section):
+ result.append(child)
+ continue
+ result.extend(find_subsections(child))
+ return result
+
+
+## Escaping
+# Which characters to escape depends on the context. In some cases,
+# namely menus and node names, it's not possible to escape certain
+# characters.
+
+def escape(s):
+ """Return a string with Texinfo command characters escaped."""
+ s = s.replace('@', '@@')
+ s = s.replace('{', '@{')
+ s = s.replace('}', '@}')
+ # Prevent "--" from being converted to an "em dash"
+ # s = s.replace('-', '@w{-}')
+ return s
+
+def escape_arg(s):
+ """Return an escaped string suitable for use as an argument
+ to a Texinfo command."""
+ s = escape(s)
+ # Commas are the argument delimeters
+ s = s.replace(',', '@comma{}')
+ # Normalize white space
+ s = ' '.join(s.split()).strip()
+ return s
+
+def escape_id(s):
+ """Return an escaped string suitable for node names, menu entries,
+ and xrefs anchors."""
+ bad_chars = ',:.()@{}'
+ for bc in bad_chars:
+ s = s.replace(bc, ' ')
+ s = ' '.join(s.split()).strip()
+ return s
+
+class TexinfoWriter(writers.Writer):
+ """Texinfo writer for generating Texinfo documents."""
+ supported = ('texinfo', 'texi')
+
+ settings_spec = (
+ 'Texinfo Specific Options',
+ None,
+ (
+ ("Name of the resulting Info file to be created by 'makeinfo'. "
+ "Should probably end with '.info'.",
+ ['--texinfo-filename'],
+ {'default': '', 'metavar': '<file>'}),
+
+ ('Specify the Info dir entry category.',
+ ['--texinfo-dir-category'],
+ {'default': 'Miscellaneous', 'metavar': '<name>'}),
+
+ ('The name to use for the Info dir entry. '
+ 'If not provided, no entry will be created.',
+ ['--texinfo-dir-entry'],
+ {'default': '', 'metavar': '<name>'}),
+
+ ('A brief description (one or two lines) to use for the '
+ 'Info dir entry.',
+ ['--texinfo-dir-description'],
+ {'default': '', 'metavar': '<desc>'}),
+ )
+ )
+
+ settings_defaults = {}
+ settings_default_overrides = {'docinfo_xform': 0}
+
+ output = None
+
+ visitor_attributes = ('output', 'fragment')
+
+ def __init__(self, builder):
+ writers.Writer.__init__(self)
+ self.builder = builder
+
+ def translate(self):
+ self.visitor = visitor = TexinfoTranslator(self.document, self.builder)
+ self.document.walkabout(visitor)
+ visitor.finish()
+ for attr in self.visitor_attributes:
+ setattr(self, attr, getattr(visitor, attr))
+
+
+class TexinfoTranslator(nodes.NodeVisitor):
+
+ ignore_missing_images = False
+
+ default_elements = {
+ 'filename': '',
+ 'title': '',
+ 'paragraphindent': 2,
+ 'exampleindent': 4,
+ 'direntry': '',
+ 'preamble': '',
+ 'body': '',
+ }
+
+ def __init__(self, document, builder):
+ nodes.NodeVisitor.__init__(self, document)
+ self.builder = builder
+ self.init_settings()
+
+ self.written_ids = set() # node names and anchors in output
+ self.referenced_ids = set() # node names and anchors that should
+ # be in output
+ self.node_names = {} # node name --> node's name to display
+ self.node_menus = {} # node name --> node's menu entries
+ self.rellinks = {} # node name --> (next, previous, up)
+
+ self.collect_node_names()
+ self.collect_node_menus()
+ self.collect_rellinks()
+
+ self.short_ids = {}
+ self.body = []
+ self.context = []
+ self.previous_section = None
+ self.section_level = 0
+ self.seen_title = False
+ self.next_section_targets = []
+ self.escape_newlines = 0
+ self.curfilestack = []
+
+ def finish(self):
+ while self.referenced_ids:
+ # Handle xrefs with missing anchors
+ r = self.referenced_ids.pop()
+ if r not in self.written_ids:
+ self.document.reporter.info(
+ "Unknown cross-reference target: `%s'" % r)
+ self.add_text('@anchor{%s}@w{%s}\n' % (r, ' ' * 30))
+ self.fragment = ''.join(self.body).strip() + '\n'
+ self.elements['body'] = self.fragment
+ self.output = TEMPLATE % self.elements
+
+
+ ## Helper routines
+
+ def init_settings(self):
+ settings = self.settings = self.document.settings
+ elements = self.elements = self.default_elements.copy()
+ elements.update({
+ # if empty, the title is set to the first section title
+ 'title': settings.title,
+ 'author': escape_arg(settings.author),
+ # if empty, use basename of input file
+ 'filename': settings.texinfo_filename,
+ })
+ # Title
+ title = elements['title']
+ if not title:
+ title = self.document.next_node(nodes.title)
+ title = (title and title.astext()) or '<untitled>'
+ elements['title'] = escape_id(title) or '<untitled>'
+ # Filename
+ if not elements['filename']:
+ elements['filename'] = self.document.get('source') or 'untitled'
+ if elements['filename'][-4:] in ('.txt', '.rst'):
+ elements['filename'] = elements['filename'][:-4]
+ elements['filename'] += '.info'
+ # Direntry
+ if settings.texinfo_dir_entry:
+ elements['direntry'] = ('@dircategory %s\n'
+ '@direntry\n'
+ '* %s: (%s). %s\n'
+ '@end direntry\n') % (
+ escape_id(settings.texinfo_dir_category),
+ escape_id(settings.texinfo_dir_entry),
+ elements['filename'],
+ escape_arg(settings.texinfo_dir_description))
+ # allow the user to override them all
+ elements.update(settings.texinfo_elements)
+
+ def collect_node_names(self):
+ """Generates a unique id for each section.
+
+ Assigns the attribute ``node_name`` to each section."""
+ self.document['node_name'] = 'Top'
+ self.node_names['Top'] = 'Top'
+ self.written_ids.update(('Top', 'top'))
+
+ for section in self.document.traverse(nodes.section):
+ title = section.next_node(nodes.Titular)
+ name = (title and title.astext()) or '<untitled>'
+ node_id = name = escape_id(name) or '<untitled>'
+ assert node_id and name
+ nth, suffix = 1, ''
+ while (node_id + suffix).lower() 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
+ assert node_id.lower() not in self.written_ids
+ section['node_name'] = node_id
+ self.node_names[node_id] = name
+ self.written_ids.update((node_id, node_id.lower()))
+
+ def collect_node_menus(self):
+ """Collect the menu entries for each "node" section."""
+ node_menus = self.node_menus
+ for node in ([self.document] +
+ self.document.traverse(nodes.section)):
+ assert 'node_name' in node and node['node_name']
+ entries = tuple(s['node_name']
+ for s in find_subsections(node))
+ node_menus[node['node_name']] = entries
+ # Try to find a suitable "Top" node
+ title = self.document.next_node(nodes.title)
+ top = (title and title.parent) or self.document
+ if not isinstance(top, (nodes.document, nodes.section)):
+ top = self.document
+ if top is not self.document:
+ entries = node_menus[top['node_name']]
+ entries += node_menus['Top'][1:]
+ node_menus['Top'] = entries
+ del node_menus[top['node_name']]
+ top['node_name'] = 'Top'
+
+ def collect_rellinks(self):
+ """Collect the relative links (next, previous, up) for each "node"."""
+ rellinks = self.rellinks
+ node_menus = self.node_menus
+ for id, entries in node_menus.items():
+ rellinks[id] = ['', '', '']
+ # Up's
+ for id, entries in node_menus.items():
+ for e in entries:
+ rellinks[e][2] = id
+ # Next's and prev's
+ for id, entries in node_menus.items():
+ for i, id in enumerate(entries):
+ # First child's prev is empty
+ if i != 0:
+ rellinks[id][1] = entries[i-1]
+ # Last child's next is empty
+ if i != len(entries) - 1:
+ rellinks[id][0] = entries[i+1]
+ # Top's next is its first child
+ try:
+ first = node_menus['Top'][0]
+ except IndexError:
+ pass
+ else:
+ rellinks['Top'][0] = first
+ rellinks[first][1] = 'Top'
+
+ def add_text(self, text, fresh=False):
+ """Add some text to the output.
+
+ Optional argument ``fresh`` means to insert a newline before
+ the text if the last character out was not a newline."""
+ if fresh:
+ if self.body and not self.body[-1].endswith('\n'):
+ self.body.append('\n')
+ self.body.append(text)
+
+ def rstrip(self):
+ """Strip trailing whitespace from the current output."""
+ while self.body and not self.body[-1].strip():
+ del self.body[-1]
+ if not self.body:
+ return
+ self.body[-1] = self.body[-1].rstrip()
+
+ def add_menu_entries(self, entries):
+ for entry in entries:
+ name = self.node_names[entry]
+ if name == entry:
+ self.add_text('* %s::\n' % name, fresh=1)
+ else:
+ self.add_text('* %s: %s.\n' % (name, entry), fresh=1)
+
+ def add_menu(self, section, master=False):
+ entries = self.node_menus[section['node_name']]
+ if not entries:
+ return
+ self.add_text('\n@menu\n')
+ self.add_menu_entries(entries)
+ if master:
+ # Write the "detailed menu"
+ started_detail = False
+ for entry in entries:
+ subentries = self.node_menus[entry]
+ if not subentries:
+ continue
+ if not started_detail:
+ started_detail = True
+ self.add_text('\n@detailmenu\n'
+ ' --- The Detailed Node Listing ---\n')
+ self.add_text('\n%s\n\n' % self.node_names[entry])
+ self.add_menu_entries(subentries)
+ if started_detail:
+ self.rstrip()
+ self.add_text('\n@end detailmenu\n')
+ self.rstrip()
+ self.add_text('\n@end menu\n\n')
+
+
+ def tex_image_length(self, width_str):
+ match = re.match('(\d*\.?\d*)\s*(\S*)', width_str)
+ if not match:
+ # fallback
+ return width_str
+ res = width_str
+ amount, unit = match.groups()[:2]
+ if not unit or unit == "px":
+ # pixels: let TeX alone
+ return ''
+ elif unit == "%":
+ # a4paper: textwidth=418.25368pt
+ res = "%d.0pt" % (float(amount) * 4.1825368)
+ return res
+
+ ## xref handling
+
+ def get_short_id(self, id):
+ """Return a shorter 'id' associated with ``id``."""
+ # Shorter ids improve paragraph filling in places
+ # that the id is hidden by Emacs.
+ try:
+ sid = self.short_ids[id]
+ except KeyError:
+ sid = hex(len(self.short_ids))[2:]
+ self.short_ids[id] = sid
+ return sid
+
+ def add_anchor(self, id, msg_node=None):
+ # Anchors can be referenced by their original id
+ # or by the generated shortened id
+ id = escape_id(id).lower()
+ ids = (self.get_short_id(id), id)
+ for id in ids:
+ if id not in self.written_ids:
+ self.add_text('@anchor{%s}' % id)
+ self.written_ids.add(id)
+
+ def add_xref(self, ref, name, node):
+ ref = self.get_short_id(escape_id(ref).lower())
+ name = ' '.join(name.split()).strip()
+ if not name or ref == name:
+ self.add_text('@pxref{%s}' % ref)
+ else:
+ self.add_text('@pxref{%s,,%s}' % (ref, name))
+ self.referenced_ids.add(ref)
+
+ ## Visiting
+
+ def visit_document(self, node):
+ pass
+ def depart_document(self, node):
+ pass
+
+ def visit_Text(self, node):
+ s = escape(node.astext())
+ if self.escape_newlines:
+ s = s.replace('\n', ' ')
+ self.add_text(s)
+ def depart_Text(self, node):
+ pass
+
+ def visit_section(self, node):
+ self.next_section_targets.extend(node.get('ids', []))
+ if not self.seen_title:
+ return
+ if self.previous_section:
+ self.add_menu(self.previous_section)
+ else:
+ self.add_menu(self.document, master=True)
+
+ node_name = node['node_name']
+ pointers = tuple([node_name] + self.rellinks[node_name])
+ self.add_text('\n@node %s,%s,%s,%s\n' % pointers)
+ if node_name != node_name.lower():
+ self.add_text('@anchor{%s}' % node_name.lower())
+ for id in self.next_section_targets:
+ self.add_anchor(id, node)
+
+ self.next_section_targets = []
+ self.previous_section = node
+ self.section_level += 1
+
+ def depart_section(self, node):
+ self.section_level -= 1
+
+ headings = (
+ '@unnumbered',
+ '@chapter',
+ '@section',
+ '@subsection',
+ '@subsubsection',
+ )
+
+ rubrics = (
+ '@heading',
+ '@subheading',
+ '@subsubheading',
+ )
+
+ def visit_title(self, node):
+ if not self.seen_title:
+ self.seen_title = 1
+ raise nodes.SkipNode
+ parent = node.parent
+ if isinstance(parent, nodes.table):
+ return
+ if isinstance(parent, nodes.Admonition):
+ raise nodes.SkipNode
+ elif isinstance(parent, nodes.sidebar):
+ self.visit_rubric(node)
+ elif isinstance(parent, nodes.topic):
+ raise nodes.SkipNode
+ elif not isinstance(parent, nodes.section):
+ self.document.reporter.warning(
+ 'encountered title node not in section, topic, table, '
+ 'admonition or sidebar', base_node=node)
+ self.visit_rubric(node)
+ else:
+ try:
+ heading = self.headings[self.section_level]
+ except IndexError:
+ heading = self.headings[-1]
+ self.add_text('%s ' % heading, fresh=1)
+
+ def depart_title(self, node):
+ self.add_text('', fresh=1)
+
+ def visit_rubric(self, node):
+ try:
+ rubric = self.rubrics[self.section_level]
+ except IndexError:
+ rubric = self.rubrics[-1]
+ self.add_text('%s ' % rubric, fresh=1)
+ def depart_rubric(self, node):
+ self.add_text('', fresh=1)
+
+ def visit_subtitle(self, node):
+ self.add_text('\n\n@noindent\n')
+ def depart_subtitle(self, node):
+ self.add_text('\n\n')
+
+ ## References
+
+ def visit_target(self, node):
+ if node.get('ids'):
+ self.add_anchor(node['ids'][0], node)
+ elif node.get('refid'):
+ # Section targets need to go after the start of the section.
+ next = node.next_node(ascend=1, siblings=1)
+ while isinstance(next, nodes.target):
+ next = next.next_node(ascend=1, siblings=1)
+ if isinstance(next, nodes.section):
+ self.next_section_targets.append(node['refid'])
+ return
+ self.add_anchor(node['refid'], node)
+ elif node.get('refuri'):
+ pass
+ else:
+ self.document.reporter.error("Unknown target type: %r" % node)
+
+ def visit_reference(self, node):
+ if isinstance(node.parent, nodes.title):
+ return
+ if isinstance(node[0], nodes.image):
+ return
+ if isinstance(node.parent, addnodes.desc_type):
+ return
+ name = node.get('name', node.astext()).strip()
+ if node.get('refid'):
+ self.add_xref(escape_id(node['refid']),
+ escape_id(name), node)
+ raise nodes.SkipNode
+ if not node.get('refuri'):
+ self.document.reporter.error("Unknown reference type: %s" % node)
+ return
+ uri = node['refuri']
+ if uri.startswith('#'):
+ self.add_xref(escape_id(uri[1:]), escape_id(name), node)
+ elif uri.startswith('%'):
+ id = uri[1:]
+ if '#' in id:
+ src, id = uri[1:].split('#', 1)
+ assert '#' not in id
+ self.add_xref(escape_id(id), escape_id(name), node)
+ elif uri.startswith('mailto:'):
+ uri = escape_arg(uri[7:])
+ name = escape_arg(name)
+ if not name or name == uri:
+ self.add_text('@email{%s}' % uri)
+ else:
+ self.add_text('@email{%s,%s}' % (uri, name))
+ elif uri.startswith('info:'):
+ uri = uri[5:].replace('_', ' ')
+ uri = escape_arg(uri)
+ id = 'Top'
+ if '#' in uri:
+ uri, id = uri.split('#', 1)
+ id = escape_id(id)
+ name = escape_id(name)
+ if name == id:
+ self.add_text('@pxref{%s,,,%s}' % (id, uri))
+ else:
+ self.add_text('@pxref{%s,,%s,%s}' % (id, name, uri))
+ else:
+ uri = escape_arg(uri)
+ name = escape_arg(name)
+ if not name or uri == name:
+ self.add_text('@indicateurl{%s}' % uri)
+ else:
+ self.add_text('@uref{%s,%s}' % (uri, name))
+ raise nodes.SkipNode
+
+ def depart_reference(self, node):
+ pass
+
+ def visit_title_reference(self, node):
+ text = node.astext()
+ self.add_text('@cite{%s}' % escape_arg(text))
+ raise nodes.SkipNode
+ def depart_title_reference(self, node):
+ pass
+
+ ## Blocks
+
+ def visit_paragraph(self, node):
+ if 'continued' in node or isinstance(node.parent, nodes.compound):
+ self.add_text('@noindent\n', fresh=1)
+ def depart_paragraph(self, node):
+ self.add_text('\n\n')
+
+ def visit_block_quote(self, node):
+ self.rstrip()
+ self.add_text('\n\n@quotation\n')
+ def depart_block_quote(self, node):
+ self.rstrip()
+ self.add_text('\n@end quotation\n\n')
+
+ def visit_literal_block(self, node):
+ self.rstrip()
+ self.add_text('\n\n@example\n')
+ def depart_literal_block(self, node):
+ self.rstrip()
+ self.add_text('\n@end example\n\n'
+ '@noindent\n')
+
+ visit_doctest_block = visit_literal_block
+ depart_doctest_block = depart_literal_block
+
+ def visit_line_block(self, node):
+ self.add_text('@display\n', fresh=1)
+ def depart_line_block(self, node):
+ self.add_text('@end display\n', fresh=1)
+
+ def visit_line(self, node):
+ self.rstrip()
+ self.add_text('\n')
+ self.escape_newlines += 1
+ def depart_line(self, node):
+ self.add_text('@w{ }\n')
+ self.escape_newlines -= 1
+
+ ## Inline
+
+ def visit_strong(self, node):
+ self.add_text('@strong{')
+ def depart_strong(self, node):
+ self.add_text('}')
+
+ def visit_emphasis(self, node):
+ self.add_text('@emph{')
+ def depart_emphasis(self, node):
+ self.add_text('}')
+
+ def visit_literal(self, node):
+ self.add_text('@code{')
+ def depart_literal(self, node):
+ self.add_text('}')
+
+ def visit_superscript(self, node):
+ self.add_text('@w{^')
+ def depart_superscript(self, node):
+ self.add_text('}')
+
+ def visit_subscript(self, node):
+ self.add_text('@w{[')
+ def depart_subscript(self, node):
+ self.add_text(']}')
+
+ ## Footnotes
+
+ def visit_footnote(self, node):
+ self.visit_block_quote(node)
+ def depart_footnote(self, node):
+ self.depart_block_quote(node)
+
+ def visit_footnote_reference(self, node):
+ self.add_text('@w{(')
+ def depart_footnote_reference(self, node):
+ self.add_text(')}')
+
+ visit_citation = visit_footnote
+ depart_citation = depart_footnote
+
+ def visit_citation_reference(self, node):
+ self.add_text('@w{[')
+ def depart_citation_reference(self, node):
+ self.add_text(']}')
+
+ ## Lists
+
+ def visit_bullet_list(self, node):
+ bullet = node.get('bullet', '*')
+ self.rstrip()
+ self.add_text('\n\n@itemize %s\n' % bullet)
+ def depart_bullet_list(self, node):
+ self.rstrip()
+ self.add_text('\n@end itemize\n\n')
+
+ def visit_enumerated_list(self, node):
+ # Doesn't support Roman numerals
+ enum = node.get('enumtype', 'arabic')
+ starters = {'arabic': '',
+ 'loweralpha': 'a',
+ 'upperalpha': 'A',}
+ start = node.get('start', starters.get(enum, ''))
+ self.rstrip()
+ self.add_text('\n\n@enumerate %s\n' % start)
+ def depart_enumerated_list(self, node):
+ self.rstrip()
+ self.add_text('\n@end enumerate\n\n')
+
+ def visit_list_item(self, node):
+ self.rstrip()
+ self.add_text('\n@item\n')
+ def depart_list_item(self, node):
+ pass
+
+ ## Option List
+
+ def visit_option_list(self, node):
+ self.add_text('\n@table @option\n')
+ def depart_option_list(self, node):
+ self.rstrip()
+ self.add_text('\n@end table\n\n')
+
+ def visit_option_list_item(self, node):
+ pass
+ def depart_option_list_item(self, node):
+ pass
+
+ def visit_option_group(self, node):
+ self.at_item_x = '@item'
+ def depart_option_group(self, node):
+ pass
+
+ def visit_option(self, node):
+ self.add_text(self.at_item_x + ' ', fresh=1)
+ self.at_item_x = '@itemx'
+ def depart_option(self, node):
+ pass
+
+ def visit_option_string(self, node):
+ pass
+ def depart_option_string(self, node):
+ pass
+
+ def visit_option_argument(self, node):
+ self.add_text(node.get('delimiter', ' '))
+ def depart_option_argument(self, node):
+ pass
+
+ def visit_description(self, node):
+ self.add_text('', fresh=1)
+ def depart_description(self, node):
+ pass
+
+ ## Definitions
+
+ def visit_definition_list(self, node):
+ self.add_text('\n@table @asis\n')
+ def depart_definition_list(self, node):
+ self.rstrip()
+ self.add_text('\n@end table\n\n')
+
+ def visit_definition_list_item(self, node):
+ self.at_item_x = '@item'
+ def depart_definition_list_item(self, node):
+ pass
+
+ def visit_term(self, node):
+ if node.get('ids') and node['ids'][0]:
+ self.add_anchor(node['ids'][0], node)
+ self.add_text(self.at_item_x + ' ', fresh=1)
+ self.at_item_x = '@itemx'
+ def depart_term(self, node):
+ pass
+
+ def visit_termsep(self, node):
+ self.add_text(self.at_item_x + ' ', fresh=1)
+
+ def visit_classifier(self, node):
+ self.add_text(' : ')
+ def depart_classifier(self, node):
+ pass
+
+ def visit_definition(self, node):
+ self.add_text('', fresh=1)
+ def depart_definition(self, node):
+ pass
+
+ ## Tables
+
+ def visit_table(self, node):
+ self.entry_sep = '@item'
+ def depart_table(self, node):
+ self.rstrip()
+ self.add_text('\n@end multitable\n\n')
+
+ def visit_tabular_col_spec(self, node):
+ pass
+ def depart_tabular_col_spec(self, node):
+ pass
+
+ def visit_colspec(self, node):
+ self.colwidths.append(node['colwidth'])
+ if len(self.colwidths) != self.n_cols:
+ return
+ self.add_text('@multitable ', fresh=1)
+ for i, n in enumerate(self.colwidths):
+ self.add_text('{%s} ' %('x' * (n+2)))
+ def depart_colspec(self, node):
+ pass
+
+ def visit_tgroup(self, node):
+ self.colwidths = []
+ self.n_cols = node['cols']
+ def depart_tgroup(self, node):
+ pass
+
+ def visit_thead(self, node):
+ self.entry_sep = '@headitem'
+ def depart_thead(self, node):
+ pass
+
+ def visit_tbody(self, node):
+ pass
+ def depart_tbody(self, node):
+ pass
+
+ def visit_row(self, node):
+ pass
+ def depart_row(self, node):
+ self.entry_sep = '@item'
+
+ def visit_entry(self, node):
+ self.rstrip()
+ self.add_text('\n%s ' % self.entry_sep)
+ self.entry_sep = '@tab'
+ def depart_entry(self, node):
+ for i in xrange(node.get('morecols', 0)):
+ self.add_text('@tab\n', fresh=1)
+ self.add_text('', fresh=1)
+
+ ## Field Lists
+
+ def visit_field_list(self, node):
+ self.add_text('\n@itemize @w\n')
+ def depart_field_list(self, node):
+ self.rstrip()
+ self.add_text('\n@end itemize\n\n')
+
+ def visit_field(self, node):
+ if not isinstance(node.parent, nodes.field_list):
+ self.visit_field_list(None)
+ def depart_field(self, node):
+ if not isinstance(node.parent, nodes.field_list):
+ self.depart_field_list(None)
+
+ def visit_field_name(self, node):
+ self.add_text('@item ', fresh=1)
+ def depart_field_name(self, node):
+ self.add_text(':')
+
+ def visit_field_body(self, node):
+ self.add_text('', fresh=1)
+ def depart_field_body(self, node):
+ pass
+
+ ## Admonitions
+
+ def visit_admonition(self, node):
+ title = escape(node[0].astext())
+ self.add_text('\n@cartouche\n'
+ '@quotation %s\n' % title)
+ def depart_admonition(self, node):
+ self.rstrip()
+ self.add_text('\n@end quotation\n'
+ '@end cartouche\n\n')
+
+ def _make_visit_admonition(typ):
+ def visit(self, node):
+ title = escape(typ)
+ self.add_text('\n@cartouche\n'
+ '@quotation %s\n' % title)
+ return visit
+
+ visit_attention = _make_visit_admonition('Attention')
+ visit_caution = _make_visit_admonition('Caution')
+ visit_danger = _make_visit_admonition('Danger')
+ visit_error = _make_visit_admonition('Error')
+ visit_important = _make_visit_admonition('Important')
+ visit_note = _make_visit_admonition('Note')
+ visit_tip = _make_visit_admonition('Tip')
+ visit_hint = _make_visit_admonition('Hint')
+ visit_warning = _make_visit_admonition('Warning')
+
+ depart_attention = depart_admonition
+ depart_caution = depart_admonition
+ depart_danger = depart_admonition
+ depart_error = depart_admonition
+ depart_important = depart_admonition
+ depart_note = depart_admonition
+ depart_tip = depart_admonition
+ depart_hint = depart_admonition
+ depart_warning = depart_admonition
+
+ ## Misc
+
+ def visit_docinfo(self, node):
+ # No 'docinfo_xform'
+ raise nodes.SkipNode
+
+ def visit_topic(self, node):
+ # Ignore TOC's since we have to have a "menu" anyway
+ if 'contents' in node.get('classes', []):
+ raise nodes.SkipNode
+ title = node[0]
+ self.visit_rubric(title)
+ self.add_text('%s\n' % escape(title.astext()))
+ self.visit_block_quote(node)
+ def depart_topic(self, node):
+ self.depart_block_quote(node)
+
+ def visit_generated(self, node):
+ raise nodes.SkipNode
+ def depart_generated(self, node):
+ pass
+
+ def visit_transition(self, node):
+ self.add_text('\n\n@noindent\n'
+ '@exdent @w{%s}\n\n'
+ '@noindent\n' % ('_' * 70))
+ def depart_transition(self, node):
+ pass
+
+ def visit_attribution(self, node):
+ self.add_text('@flushright\n', fresh=1)
+ def depart_attribution(self, node):
+ self.add_text('@end flushright\n', fresh=1)
+
+ def visit_raw(self, node):
+ format = node.get('format', '').split()
+ if 'texinfo' in format or 'texi' in format:
+ self.add_text(node.astext())
+ raise nodes.SkipNode
+ def depart_raw(self, node):
+ pass
+
+ def visit_figure(self, node):
+ self.add_text('\n@float Figure\n')
+ def depart_figure(self, node):
+ self.rstrip()
+ self.add_text('\n@end float\n\n')
+
+ def visit_caption(self, node):
+ if not isinstance(node.parent, nodes.figure):
+ self.document.reporter.warning('Caption not inside a figure.',
+ base_node=node)
+ return
+ self.add_text('@caption{', fresh=1)
+ def depart_caption(self, node):
+ if isinstance(node.parent, nodes.figure):
+ self.rstrip()
+ self.add_text('}\n')
+
+ def visit_image(self, node):
+ if node['uri'] in self.builder.images:
+ uri = self.builder.images[node['uri']]
+ else:
+ # missing image!
+ if self.ignore_missing_images:
+ return
+ uri = node['uri']
+ if uri.find('://') != -1:
+ # ignore remote images
+ return
+ name, ext = path.splitext(uri)
+ attrs = node.attributes
+ # ignored in non-tex output
+ width = self.tex_image_length(attrs.get('width', ''))
+ height = self.tex_image_length(attrs.get('height', ''))
+ alt = escape_arg(attrs.get('alt', ''))
+ self.add_text('\n\n@image{%s,%s,%s,%s,%s}\n\n' %
+ (name, width, height, alt, ext[1:]))
+ def depart_image(self, node):
+ pass
+
+ def visit_compound(self, node):
+ pass
+ def depart_compound(self, node):
+ pass
+
+ def visit_sidebar(self, node):
+ pass
+ def depart_sidebar(self, node):
+ pass
+
+ def visit_label(self, node):
+ self.add_text('@w{(')
+ def depart_label(self, node):
+ self.add_text(')} ')
+
+ def visit_legend(self, node):
+ pass
+ def depart_legend(self, node):
+ pass
+
+ def visit_substitution_reference(self, node):
+ pass
+ def depart_substitution_reference(self, node):
+ pass
+
+ def visit_substitution_definition(self, node):
+ raise nodes.SkipNode
+ def depart_substitution_definition(self, node):
+ pass
+
+ def visit_system_message(self, node):
+ self.add_text('\n@format\n'
+ '---------- SYSTEM MESSAGE -----------\n')
+ def depart_system_message(self, node):
+ self.rstrip()
+ self.add_text('\n------------------------------------\n'
+ '@end format\n')
+
+ def visit_comment(self, node):
+ for line in node.astext().splitlines():
+ self.add_text('@c %s\n' % line, fresh=1)
+ raise nodes.SkipNode
+
+ def visit_problematic(self, node):
+ self.add_text('>')
+ def depart_problematic(self, node):
+ self.add_text('<')
+
+ def unimplemented_visit(self, node):
+ self.document.reporter.error("Unimplemented node type: `%s'"
+ % node.__class__.__name__, base_node=node)
+
+ def unknown_visit(self, node):
+ self.document.reporter.error("Unknown node type: `%s'"
+ % node.__class__.__name__, base_node=node)
+ def unknown_departure(self, node):
+ pass
+
+ ### Sphinx specific
+
+ def visit_productionlist(self, node):
+ self.visit_literal_block(None)
+ names = []
+ for production in node:
+ names.append(production['tokenname'])
+ maxlen = max(len(name) for name in names)
+ for production in node:
+ if production['tokenname']:
+ s = production['tokenname'].ljust(maxlen) + ' ::='
+ lastname = production['tokenname']
+ else:
+ s = '%s ' % (' '*len(lastname))
+ self.add_text(escape(s))
+ self.add_text(escape(production.astext() + '\n'))
+ self.depart_literal_block(None)
+ raise nodes.SkipNode
+ def depart_productionlist(self, node):
+ pass
+
+ def visit_literal_emphasis(self, node):
+ self.add_text('@code{')
+ def depart_literal_emphasis(self, node):
+ self.add_text('}')
+
+ def visit_module(self, node):
+ modname = escape_id(node['modname'])
+ self.add_anchor(modname, node)
+
+ def visit_index(self, node):
+ # Throws off table alignment
+ if isinstance(node.parent, nodes.term):
+ return
+ for entry in node['entries']:
+ typ, text, tid, text2 = entry
+ text = text.replace('!', ' ').replace(';', ' ')
+ text = escape_id(text)
+ self.add_text('@geindex %s\n' % text, fresh=1)
+
+ def visit_autosummary_table(self, node):
+ pass
+ def depart_autosummary_table(self, node):
+ pass
+
+ def visit_todo_node(self, node):
+ self.visit_transition(node)
+ self.visit_admonition(node)
+ def depart_todo_node(self, node):
+ self.depart_admonition(node)
+ self.visit_transition(node)
+
+ def visit_refcount(self, node):
+ self.add_text('\n')
+ def depart_refcount(self, node):
+ self.add_text('\n\n')
+
+ def visit_versionmodified(self, node):
+ intro = versionlabels[node['type']] % node['version']
+ if node.children:
+ intro += ': '
+ else:
+ intro += '.'
+ self.add_text('%s' % escape(intro), fresh=1)
+ def depart_versionmodified(self, node):
+ self.rstrip()
+ self.add_text('\n\n', fresh=1)
+
+ def visit_start_of_file(self, node):
+ self.curfilestack.append(node.get('docname', ''))
+ if node.get('docname'):
+ self.next_section_targets.append(node['docname'])
+ def depart_start_of_file(self, node):
+ self.curfilestack.pop()
+
+ def visit_centered(self, node):
+ txt = escape_arg(node.astext())
+ self.add_text('@center %s\n' % txt, fresh=1)
+ raise nodes.SkipNode
+ def depart_centered(self, node):
+ pass
+
+ def visit_seealso(self, node):
+ pass
+ def depart_seealso(self, node):
+ pass
+
+ def visit_meta(self, node):
+ raise nodes.SkipNode
+ def depart_meta(self, node):
+ pass
+
+ def visit_glossary(self, node):
+ pass
+ def depart_glossary(self, node):
+ pass
+
+ def visit_acks(self, node):
+ pass
+ def depart_acks(self, node):
+ pass
+
+ def visit_highlightlang(self, node):
+ pass
+ def depart_highlightlang(self, node):
+ pass
+
+ ## Desc
+
+ desc_map = {
+ 'function' : 'Function',
+ 'class': 'Class',
+ 'method': 'Method',
+ 'classmethod': 'Class Method',
+ 'staticmethod': 'Static Method',
+ 'exception': 'Exception',
+ 'data': 'Data',
+ 'attribute': 'Attribute',
+ 'opcode': 'Opcode',
+ 'cfunction': 'C Function',
+ 'cmember': 'C Member',
+ 'cmacro': 'C Macro',
+ 'ctype': 'C Type',
+ 'cvar': 'C Variable',
+ 'cmdoption': 'Option',
+ 'describe': 'Description',
+ }
+
+ def visit_desc(self, node):
+ self.at_deffnx = '@deffn'
+ def depart_desc(self, node):
+ self.rstrip()
+ self.add_text('@end deffn\n\n', fresh=1)
+ def visit_desc_signature(self, node):
+ self.desctype = node.parent['desctype'].strip()
+ if self.desctype != 'describe' and node['ids']:
+ self.add_anchor(node['ids'][0], node)
+ typ = self.desc_map.get(self.desctype, self.desctype)
+ self.add_text('%s {%s} ' % (self.at_deffnx, escape_arg(typ)), fresh=1)
+ self.at_deffnx = '@deffnx'
+ def depart_desc_signature(self, node):
+ self.add_text("", fresh=1)
+
+ def visit_desc_name(self, node):
+ pass
+ def depart_desc_name(self, node):
+ pass
+
+ def visit_desc_addname(self, node):
+ pass
+ def depart_desc_addname(self, node):
+ pass
+
+ def visit_desc_type(self, node):
+ pass
+ def depart_desc_type(self, node):
+ pass
+
+ def visit_desc_returns(self, node):
+ self.add_text(' -> ')
+ def depart_desc_returns(self, node):
+ pass
+
+ def visit_desc_parameterlist(self, node):
+ self.add_text(' (')
+ self.first_param = 1
+ def depart_desc_parameterlist(self, node):
+ self.add_text(')')
+
+ def visit_desc_parameter(self, node):
+ if not self.first_param:
+ self.add_text(', ')
+ else:
+ self.first_param = 0
+ self.add_text(escape(node.astext()))
+ raise nodes.SkipNode
+ def depart_desc_parameter(self, node):
+ pass
+
+ def visit_desc_optional(self, node):
+ self.add_text('[')
+ def depart_desc_optional(self, node):
+ self.add_text(']')
+
+ def visit_desc_annotation(self, node):
+ raise nodes.SkipNode
+ def depart_desc_annotation(self, node):
+ pass
+
+ def visit_desc_content(self, node):
+ self.add_text("", fresh=1)
+ def depart_desc_content(self, node):
+ pass
+
+ def visit_inline(self, node):
+ # stub
+ pass
+ def depart_inline(self, node):
+ pass
+
+ def visit_abbreviation(self, node):
+ self.add_text('@abbr{')
+ if node.hasattr('explanation'):
+ self.context.append(', %s}' % escape_arg(node['explanation']))
+ else:
+ self.context.append('}')
+ def depart_abbreviation(self, node):
+ self.body.append(self.context.pop())
+
+ def visit_download_reference(self, node):
+ pass
+ def depart_download_reference(self, node):
+ pass
+
+ def visit_hlist(self, node):
+ # stub
+ self.visit_bullet_list(node)
+ def depart_hlist(self, node):
+ self.depart_bullet_list(node)
+
+ def visit_hlistcol(self, node):
+ pass
+ def depart_hlistcol(self, node):
+ pass
diff --git a/sphinx/writers/text.py b/sphinx/writers/text.py
index b28b2379..1a7d2a7d 100644
--- a/sphinx/writers/text.py
+++ b/sphinx/writers/text.py
@@ -5,10 +5,10 @@
Custom docutils writer for plain text.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
-
+import os
import re
import textwrap
@@ -52,6 +52,14 @@ class TextTranslator(nodes.NodeVisitor):
def __init__(self, document, builder):
nodes.NodeVisitor.__init__(self, document)
+ newlines = builder.config.text_newlines
+ if newlines == 'windows':
+ self.nl = '\r\n'
+ elif newlines == 'native':
+ self.nl = os.linesep
+ else:
+ self.nl = '\n'
+ self.sectionchars = builder.config.text_sectionchars
self.states = [[]]
self.stateindent = [0]
self.list_counter = []
@@ -98,9 +106,9 @@ class TextTranslator(nodes.NodeVisitor):
self.new_state(0)
def depart_document(self, node):
self.end_state()
- self.body = '\n'.join(line and (' '*indent + line)
- for indent, lines in self.states[0]
- for line in lines)
+ self.body = self.nl.join(line and (' '*indent + line)
+ for indent, lines in self.states[0]
+ for line in lines)
# XXX header/footer?
def visit_highlightlang(self, node):
@@ -225,7 +233,7 @@ class TextTranslator(nodes.NodeVisitor):
def visit_desc_content(self, node):
self.new_state()
- self.add_text('\n')
+ self.add_text(self.nl)
def depart_desc_content(self, node):
self.end_state()
@@ -251,7 +259,7 @@ class TextTranslator(nodes.NodeVisitor):
lastname = production['tokenname']
else:
self.add_text('%s ' % (' '*len(lastname)))
- self.add_text(production.astext() + '\n')
+ self.add_text(production.astext() + self.nl)
self.end_state(wrap=False)
raise nodes.SkipNode
@@ -351,7 +359,7 @@ class TextTranslator(nodes.NodeVisitor):
'not implemented.')
self.new_state(0)
def depart_entry(self, node):
- text = '\n'.join('\n'.join(x[1]) for x in self.states.pop())
+ text = self.nl.join(self.nl.join(x[1]) for x in self.states.pop())
self.stateindent.pop()
self.table[-1].append(text)
@@ -387,7 +395,7 @@ class TextTranslator(nodes.NodeVisitor):
for width in realwidths:
out.append(char * (width+2))
out.append('+')
- self.add_text(''.join(out) + '\n')
+ self.add_text(''.join(out) + self.nl)
def writerow(row):
lines = zip(*row)
@@ -399,7 +407,7 @@ class TextTranslator(nodes.NodeVisitor):
else:
out.append(' ' * (realwidths[i] + 2))
out.append('|')
- self.add_text(''.join(out) + '\n')
+ self.add_text(''.join(out) + self.nl)
for i, row in enumerate(fmted_rows):
if separator and i == separator:
@@ -475,6 +483,10 @@ class TextTranslator(nodes.NodeVisitor):
if not self._li_has_classifier:
self.end_state(end=None)
+ def visit_termsep(self, node):
+ self.add_text(', ')
+ raise nodes.SkipNode
+
def visit_classifier(self, node):
self.add_text(' : ')
def depart_classifier(self, node):
@@ -651,9 +663,9 @@ class TextTranslator(nodes.NodeVisitor):
self.add_text('*')
def visit_literal(self, node):
- self.add_text('``')
+ self.add_text('"')
def depart_literal(self, node):
- self.add_text('``')
+ self.add_text('"')
def visit_subscript(self, node):
self.add_text('_')
diff --git a/sphinx/writers/websupport.py b/sphinx/writers/websupport.py
index bb80fb7e..f75cd47b 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-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -20,7 +20,7 @@ class WebSupportTranslator(HTMLTranslator):
def __init__(self, builder, *args, **kwargs):
HTMLTranslator.__init__(self, builder, *args, **kwargs)
- self.comment_class = 'spxcmt'
+ self.comment_class = 'sphinx-has-comment'
def dispatch_visit(self, node):
if is_commentable(node):
@@ -39,7 +39,7 @@ class WebSupportTranslator(HTMLTranslator):
node.attributes['classes'].append(self.comment_class)
def add_db_node(self, node):
- storage = self.builder.app.storage
+ storage = self.builder.storage
if not storage.has_node(node.uid):
storage.add_node(id=node.uid,
document=self.builder.cur_docname,
diff --git a/tests/coverage.py b/tests/coverage.py
index 117fd4f2..95f6f844 100755
--- a/tests/coverage.py
+++ b/tests/coverage.py
@@ -362,8 +362,9 @@ class coverage:
settings[o[2:]] = 1
elif o[2:] + '=' in long_opts:
settings[o[2:]+'='] = a
- else: #pragma: no cover
- pass # Can't get here, because getopt won't return anything unknown.
+ else: #pragma: no cover
+ # Can't get here, because getopt won't return anything unknown.
+ pass
if settings.get('help'):
help_fn()
@@ -645,20 +646,23 @@ class coverage:
def find_docstring_pass_pair(self, tree, spots):
for i in range(1, len(tree)):
- if self.is_string_constant(tree[i]) and self.is_pass_stmt(tree[i+1]):
+ if (self.is_string_constant(tree[i])
+ and self.is_pass_stmt(tree[i+1])):
first_line = self.first_line_of_tree(tree[i])
last_line = self.last_line_of_tree(tree[i+1])
self.record_multiline(spots, first_line, last_line)
def is_string_constant(self, tree):
try:
- return tree[0] == symbol.stmt and tree[1][1][1][0] == symbol.expr_stmt
+ return (tree[0] == symbol.stmt
+ and tree[1][1][1][0] == symbol.expr_stmt)
except:
return False
def is_pass_stmt(self, tree):
try:
- return tree[0] == symbol.stmt and tree[1][1][1][0] == symbol.pass_stmt
+ return (tree[0] == symbol.stmt
+ and tree[1][1][1][0] == symbol.pass_stmt)
except:
return False
@@ -761,7 +765,7 @@ class coverage:
pairs = []
while i < len(statements) and j < len(lines):
if statements[i] == lines[j]:
- if start == None:
+ if start is None:
start = lines[j]
end = lines[j]
j = j + 1
@@ -831,7 +835,8 @@ class coverage:
def morf_name_compare(self, x, y):
return cmp(self.morf_name(x), self.morf_name(y))
- def report(self, morfs, show_missing=1, ignore_errors=0, file=None, omit_prefixes=[]):
+ def report(self, morfs, show_missing=1, ignore_errors=0, file=None,
+ omit_prefixes=[]):
if not isinstance(morfs, types.ListType):
morfs = [morfs]
# On windows, the shell doesn't expand wildcards. Do it here.
@@ -898,19 +903,23 @@ class coverage:
blank_re = re.compile(r"\s*(#|$)")
else_re = re.compile(r"\s*else\s*:\s*(#|$)")
- def annotate(self, morfs, directory=None, ignore_errors=0, omit_prefixes=[]):
+ def annotate(self, morfs, directory=None, ignore_errors=0,
+ omit_prefixes=[]):
morfs = self.filter_by_prefix(morfs, omit_prefixes)
for morf in morfs:
try:
- filename, statements, excluded, missing, _ = self.analysis2(morf)
- self.annotate_file(filename, statements, excluded, missing, directory)
+ (filename, statements, excluded,
+ missing, _) = self.analysis2(morf)
+ self.annotate_file(filename, statements, excluded, missing,
+ directory)
except KeyboardInterrupt:
raise
except:
if not ignore_errors:
raise
- def annotate_file(self, filename, statements, excluded, missing, directory=None):
+ def annotate_file(self, filename, statements, excluded, missing,
+ directory=None):
source = open(filename, 'r')
if directory:
dest_file = os.path.join(directory,
diff --git a/tests/root/autodoc.txt b/tests/root/autodoc.txt
index c718feb4..5c03f947 100644
--- a/tests/root/autodoc.txt
+++ b/tests/root/autodoc.txt
@@ -26,3 +26,9 @@ Just testing a few autodoc possibilities...
.. autoclass:: CustomDict
:show-inheritance:
:members:
+
+
+.. automodule:: autodoc_fodder
+ :noindex:
+
+ .. autoclass:: MarkupError
diff --git a/tests/root/autodoc_fodder.py b/tests/root/autodoc_fodder.py
new file mode 100644
index 00000000..e5fd7413
--- /dev/null
+++ b/tests/root/autodoc_fodder.py
@@ -0,0 +1,7 @@
+
+class MarkupError(object):
+ """
+ .. note:: This is a docstring with a
+ small markup error which should have
+ correct location information.
+ """
diff --git a/tests/root/conf.py b/tests/root/conf.py
index 89804a30..b97ddfcc 100644
--- a/tests/root/conf.py
+++ b/tests/root/conf.py
@@ -49,6 +49,11 @@ latex_documents = [
latex_additional_files = ['svgimg.svg']
+texinfo_documents = [
+ ('contents', 'SphinxTests', 'Sphinx Tests',
+ 'Georg Brandl \\and someone else', 'Sphinx Testing', 'Miscellaneous'),
+]
+
value_from_conf_py = 84
coverage_c_path = ['special/*.h']
diff --git a/tests/root/doctest.txt b/tests/root/doctest.txt
index ba9a72c5..d029cd88 100644
--- a/tests/root/doctest.txt
+++ b/tests/root/doctest.txt
@@ -121,3 +121,9 @@ Special directives
.. testoutput:: group2
16
+
+
+.. testcleanup:: *
+
+ import test_doctest
+ test_doctest.cleanup_call()
diff --git a/tests/root/markup.txt b/tests/root/markup.txt
index 84d9581a..4c6b1086 100644
--- a/tests/root/markup.txt
+++ b/tests/root/markup.txt
@@ -34,6 +34,16 @@ Option list:
-h help
--help also help
+Line block:
+
+| line1
+| line2
+| line3
+| line4
+| line5
+| line6
+| line7
+
Body directives
^^^^^^^^^^^^^^^
@@ -229,13 +239,25 @@ This tests :CLASS:`role names in uppercase`.
* Monty Python
.. glossary::
+ :sorted:
boson
Particle with integer spin.
- fermion
+ *fermion*
Particle with half-integer spin.
+ tauon
+ myon
+ electron
+ Examples for fermions.
+
+ über
+ Gewisse
+
+ änhlich
+ Dinge
+
.. productionlist::
try_stmt: `try1_stmt` | `try2_stmt`
try1_stmt: "try" ":" `suite`
@@ -255,6 +277,8 @@ Index markup
double: entry; double
triple: index; entry; triple
keyword: with
+ see: from; to
+ seealso: fromalso; toalso
Invalid index markup...
@@ -263,6 +287,11 @@ Invalid index markup...
pair:
keyword:
+.. index::
+ !Main, !Other
+ !single: entry; pair
+
+:index:`!Main`
.. _ölabel:
diff --git a/tests/root/objects.txt b/tests/root/objects.txt
index e62f6d96..51ecee91 100644
--- a/tests/root/objects.txt
+++ b/tests/root/objects.txt
@@ -13,6 +13,9 @@ Testing object descriptions
.. function:: func_with_module
:module: foolib
+Referring to :func:`func with no index <func_noindex>`.
+Referring to :func:`nothing <>`.
+
.. module:: mod
:synopsis: Module synopsis.
:platform: UNIX
@@ -47,6 +50,8 @@ Testing object descriptions
.. class:: TimeInt
+ Has only one parameter (triggers special behavior...)
+
:param moo: |test|
:type moo: |test|
@@ -54,8 +59,8 @@ Testing object descriptions
.. class:: Time(hour, minute, isdst)
- :param hour: The year.
- :type hour: TimeInt
+ :param year: The year.
+ :type year: TimeInt
:param TimeInt minute: The minute.
:param isdst: whether it's DST
:type isdst: * some complex
@@ -66,8 +71,10 @@ Testing object descriptions
:ivar int hour: like *hour*
:ivar minute: like *minute*
:vartype minute: int
+ :param hour: Some parameter
+ :type hour: DuplicateType
:param hour: Duplicate param. Should not lead to crashes.
- :type hour: Duplicate type.
+ :type hour: DuplicateType
C items
@@ -154,3 +161,14 @@ User markup
Referencing :userdescrole:`myobj`.
+
+
+CPP domain
+==========
+
+.. cpp:class:: n::Array<T,d>
+
+ .. cpp:function:: T& operator[]( unsigned j )
+
+ .. cpp:function:: const T& operator[]( unsigned j ) const
+
diff --git a/tests/root/subdir.po b/tests/root/subdir.po
new file mode 100644
index 00000000..f515f220
--- /dev/null
+++ b/tests/root/subdir.po
@@ -0,0 +1,9 @@
+#, fuzzy
+msgid ""
+msgstr ""
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "Including in subdir"
+msgstr "translation"
diff --git a/tests/run.py b/tests/run.py
index 50567fbc..40c4fbed 100755
--- a/tests/run.py
+++ b/tests/run.py
@@ -6,7 +6,7 @@
This script runs the Sphinx unit test suite.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/tests/test_application.py b/tests/test_application.py
index d1154863..4baabcec 100644
--- a/tests/test_application.py
+++ b/tests/test_application.py
@@ -5,7 +5,7 @@
Test the Sphinx class.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py
index 8f983471..3e9b4f25 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-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -17,12 +17,14 @@ from docutils.statemachine import ViewList
from sphinx.ext.autodoc import AutoDirective, add_documenter, \
ModuleLevelDocumenter, FunctionDocumenter, cut_lines, between, ALL
+from StringIO import StringIO
def setup_module():
global app, lid, options, directive
app = TestApp()
app.builder.env.app = app
+ app.builder.env.temp_data['docname'] = 'dummy'
app.connect('autodoc-process-docstring', process_docstring)
app.connect('autodoc-process-signature', process_signature)
app.connect('autodoc-skip-member', skip_member)
@@ -30,6 +32,8 @@ def setup_module():
options = Struct(
inherited_members = False,
undoc_members = False,
+ private_members = False,
+ special_members = False,
show_inheritance = False,
noindex = False,
synopsis = '',
@@ -416,6 +420,7 @@ def test_generate():
('attribute', 'test_autodoc.Class.attr'),
('attribute', 'test_autodoc.Class.docattr'),
('attribute', 'test_autodoc.Class.udocattr'),
+ ('attribute', 'test_autodoc.Class.mdocattr'),
('attribute', 'test_autodoc.Class.inst_attr_comment'),
('attribute', 'test_autodoc.Class.inst_attr_string')
])
@@ -484,11 +489,26 @@ def test_generate():
' .. py:attribute:: Class.prop',
' .. py:attribute:: Class.docattr',
' .. py:attribute:: Class.udocattr',
+ ' .. py:attribute:: Class.mdocattr',
' .. py:attribute:: Class.inst_attr_comment',
' .. py:attribute:: Class.inst_attr_string',
' .. py:method:: Class.inheritedmeth()',
],
'class', 'Class', member_order='bysource', all_members=True)
+ del directive.env.temp_data['py:module']
+
+ # test attribute initialized to class instance from other module
+ directive.env.temp_data['autodoc:class'] = 'test_autodoc.Class'
+ assert_result_contains(u' should be documented as well - s\xfc\xdf',
+ 'attribute', 'mdocattr')
+ del directive.env.temp_data['autodoc:class']
+
+ # test autodoc_docstring_signature
+ assert_result_contains(
+ '.. py:method:: DocstringSig.meth(FOO, BAR=1) -> BAZ', 'method',
+ 'test_autodoc.DocstringSig.meth')
+ assert_result_contains(
+ ' rest of docstring', 'method', 'test_autodoc.DocstringSig.meth')
# --- generate fodder ------------
@@ -552,6 +572,10 @@ class Class(Base):
udocattr = 'quux'
u"""should be documented as well - süß"""
+ # initialized to any class imported from another module
+ mdocattr = StringIO()
+ """should be documented as well - süß"""
+
def __init__(self, arg):
#: a documented instance attribute
self.inst_attr_comment = None
@@ -580,3 +604,12 @@ class Outer(object):
# should be documented as an alias
factory = dict
+
+
+class DocstringSig(object):
+ def meth(self):
+ """meth(FOO, BAR=1) -> BAZ
+First line of docstring
+
+ rest of docstring
+ """
diff --git a/tests/test_autosummary.py b/tests/test_autosummary.py
index 20fb06e0..7783cc7b 100644
--- a/tests/test_autosummary.py
+++ b/tests/test_autosummary.py
@@ -5,7 +5,7 @@
Test the autosummary extension.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 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 d571febd..5f24f89a 100644
--- a/tests/test_build.py
+++ b/tests/test_build.py
@@ -5,7 +5,7 @@
Test all builders that have no special checks.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/tests/test_build_gettext.py b/tests/test_build_gettext.py
index ba2440fd..ab68289e 100644
--- a/tests/test_build_gettext.py
+++ b/tests/test_build_gettext.py
@@ -14,6 +14,7 @@ import os
from subprocess import Popen, PIPE
from util import *
+from util import SkipTest
def teardown_module():
@@ -21,13 +22,21 @@ def teardown_module():
@with_app(buildername='gettext')
+def test_all(app):
+ # Generic build; should fail only when the builder is horribly broken.
+ app.builder.build_all()
+
+
+@with_app(buildername='gettext')
def test_build(app):
+ # Do messages end up in the correct location?
app.builder.build(['extapi', 'subdir/includes'])
- # documents end up in a message catalog
+ # top-level documents end up in a message catalog
assert (app.outdir / 'extapi.pot').isfile()
- # ..and are grouped into sections
+ # directory items are grouped into sections
assert (app.outdir / 'subdir.pot').isfile()
+
@with_app(buildername='gettext')
def test_gettext(app):
app.builder.build(['markup'])
@@ -41,7 +50,7 @@ def test_gettext(app):
'--locale', 'en_US'],
stdout=PIPE, stderr=PIPE)
except OSError:
- return # most likely msginit was not found
+ raise SkipTest # most likely msginit was not found
else:
stdout, stderr = p.communicate()
if p.returncode != 0:
@@ -55,7 +64,7 @@ def test_gettext(app):
os.path.join('en', 'LC_MESSAGES', 'test_root.mo')],
stdout=PIPE, stderr=PIPE)
except OSError:
- return # most likely msgfmt was not found
+ raise SkipTest # most likely msgfmt was not found
else:
stdout, stderr = p.communicate()
if p.returncode != 0:
@@ -70,41 +79,3 @@ 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')
-def test_all(app):
- app.builder.build_all()
-
-
-def setup_patch():
- (test_root / 'xx' / 'LC_MESSAGES').makedirs()
- try:
- p = Popen(['msgfmt', test_root / 'bom.po', '-o',
- test_root / 'xx' / 'LC_MESSAGES' / 'bom.mo'],
- stdout=PIPE, stderr=PIPE)
- except OSError:
- return # 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' / 'bom.mo').isfile(), \
- 'msgfmt failed'
-
-def teardown_patch():
- (test_root / 'xx').rmtree()
-
-@with_app(buildername='text',
- confoverrides={'language': 'xx', 'locale_dirs': ['.']})
-def test_patch(app):
- app.builder.build(['bom'])
- result = (app.outdir / 'bom.txt').text(encoding='utf-8')
- expect = (u"\nDatei mit UTF-8"
- u"\n***************\n" # underline matches new translation
- u"\nThis file has umlauts: äöü.\n")
- assert result == expect
-
-test_patch.setup = setup_patch
-test_patch.teardown = teardown_patch
diff --git a/tests/test_build_html.py b/tests/test_build_html.py
index fee4b389..7e68f8db 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-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -32,6 +32,9 @@ def teardown_module():
html_warnfile = StringIO()
ENV_WARNINGS = """\
+%(root)s/autodoc_fodder.py:docstring of autodoc_fodder\\.MarkupError:2: \
+\\(WARNING/2\\) Explicit markup ends without a blank line; unexpected \
+unindent\\.\\n?
%(root)s/images.txt:9: WARNING: image file not readable: foo.png
%(root)s/images.txt:23: WARNING: nonlocal image URI found: \
http://www.python.org/logo.png
@@ -46,7 +49,7 @@ http://sphinx.pocoo.org/domains.html
HTML_WARNINGS = ENV_WARNINGS + """\
%(root)s/images.txt:20: WARNING: no matching candidate for image URI u'foo.\\*'
-%(root)s/markup.txt:: WARNING: invalid index entry u''
+%(root)s/markup.txt:: WARNING: invalid single index entry u''
%(root)s/markup.txt:: WARNING: invalid pair index entry u''
%(root)s/markup.txt:: WARNING: invalid pair index entry u'keyword; '
"""
@@ -190,9 +193,13 @@ HTML_XPATH = {
# custom sidebar
(".//h4", 'Custom sidebar'),
# docfields
- (".//td[@class='field-body']/ul/li/strong", '^moo$'),
- (".//td[@class='field-body']/ul/li/strong",
+ (".//td[@class='field-body']/strong", '^moo$'),
+ (".//td[@class='field-body']/strong",
tail_check(r'\(Moo\) .* Moo')),
+ (".//td[@class='field-body']/ul/li/strong", '^hour$'),
+ (".//td[@class='field-body']/ul/li/em", '^DuplicateType$'),
+ (".//td[@class='field-body']/ul/li/em",
+ tail_check(r'.* Some parameter')),
],
'contents.html': [
(".//meta[@name='hc'][@content='hcval']", ''),
@@ -225,6 +232,14 @@ HTML_XPATH = {
'_static/statictmpl.html': [
(".//project", 'Sphinx <Tests>'),
],
+ 'genindex.html': [
+ # index entries
+ (".//a/strong", "Main"),
+ (".//a/strong", "[1]"),
+ (".//a/strong", "Other"),
+ (".//a", "entry"),
+ (".//dt/a", "double"),
+ ]
}
if pygments:
diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py
index 6c1ccad9..68a8c07c 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-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -30,6 +30,7 @@ latex_warnfile = StringIO()
LATEX_WARNINGS = ENV_WARNINGS + """\
None:None: WARNING: no matching candidate for image URI u'foo.\\*'
WARNING: invalid pair index entry u''
+WARNING: invalid pair index entry u'keyword; '
"""
if sys.version_info >= (3, 0):
diff --git a/tests/test_build_texinfo.py b/tests/test_build_texinfo.py
new file mode 100644
index 00000000..ce689a69
--- /dev/null
+++ b/tests/test_build_texinfo.py
@@ -0,0 +1,66 @@
+# -*- coding: utf-8 -*-
+"""
+ test_build_texinfo
+ ~~~~~~~~~~~~~~~~~~
+
+ Test the build process with Texinfo builder with the test root.
+
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import os
+import re
+import sys
+from StringIO import StringIO
+from subprocess import Popen, PIPE
+
+from sphinx.writers.texinfo import TexinfoTranslator
+
+from util import *
+from test_build_html import ENV_WARNINGS
+
+
+def teardown_module():
+ (test_root / '_build').rmtree(True)
+
+
+texinfo_warnfile = StringIO()
+
+TEXINFO_WARNINGS = ENV_WARNINGS + """\
+None:None: WARNING: no matching candidate for image URI u'foo.\\*'
+"""
+
+if sys.version_info >= (3, 0):
+ TEXINFO_WARNINGS = remove_unicode_literals(TEXINFO_WARNINGS)
+
+
+@with_app(buildername='texinfo', warning=texinfo_warnfile, cleanenv=True)
+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': app.srcdir}
+ assert re.match(texinfo_warnings_exp + '$', texinfo_warnings), \
+ 'Warnings don\'t match:\n' + \
+ '--- Expected (regex):\n' + texinfo_warnings_exp + \
+ '--- Got:\n' + texinfo_warnings
+ # now, try to run makeinfo over it
+ cwd = os.getcwd()
+ os.chdir(app.outdir)
+ try:
+ try:
+ p = Popen(['makeinfo', '--no-split', 'SphinxTests.texi'],
+ stdout=PIPE, stderr=PIPE)
+ except OSError:
+ pass # most likely makeinfo was not found
+ else:
+ stdout, stderr = p.communicate()
+ retcode = p.returncode
+ if retcode != 0:
+ print stdout
+ print stderr
+ del app.cleanup_trees[:]
+ assert False, 'makeinfo exited with return code %s' % retcode
+ finally:
+ os.chdir(cwd)
diff --git a/tests/test_config.py b/tests/test_config.py
index b5f88a6f..b07a71bf 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-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import sys
@@ -39,7 +39,7 @@ def test_core_config(app):
# complex default values
assert 'html_title' not in cfg.__dict__
- assert cfg.html_title == 'Sphinx <Tests> v0.6alpha1 documentation'
+ assert cfg.html_title == 'Sphinx <Tests> 0.6alpha1 documentation'
# complex default values mustn't raise
for valuename in cfg.config_values:
diff --git a/tests/test_coverage.py b/tests/test_coverage.py
index cb831635..c554c52c 100644
--- a/tests/test_coverage.py
+++ b/tests/test_coverage.py
@@ -5,7 +5,7 @@
Test the coverage builder.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -27,6 +27,8 @@ def test_build(app):
assert ' * function\n' not in py_undoc # these two are documented
assert ' * Class\n' not in py_undoc # in autodoc.txt
+ assert ' * mod -- No module named mod' # in the "failed import" section
+
c_undoc = (app.outdir / 'c.txt').text()
assert c_undoc.startswith('Undocumented C API elements\n'
'===========================\n')
diff --git a/tests/test_cpp_domain.py b/tests/test_cpp_domain.py
index 1e722602..296de1c8 100644
--- a/tests/test_cpp_domain.py
+++ b/tests/test_cpp_domain.py
@@ -5,7 +5,7 @@
Tests the C++ Domain
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/tests/test_doctest.py b/tests/test_doctest.py
index b4b6bbe2..577fcb00 100644
--- a/tests/test_doctest.py
+++ b/tests/test_doctest.py
@@ -5,7 +5,7 @@
Test the doctest extension.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -15,10 +15,20 @@ import StringIO
from util import *
status = StringIO.StringIO()
+cleanup_called = 0
@with_app(buildername='doctest', status=status)
def test_build(app):
+ global cleanup_called
+ cleanup_called = 0
app.builder.build_all()
if app.statuscode != 0:
print >>sys.stderr, status.getvalue()
assert False, 'failures in doctests'
+ # in doctest.txt, there are two named groups and the default group,
+ # so the cleanup function must be called three times
+ assert cleanup_called == 3, 'testcleanup did not get executed enough times'
+
+def cleanup_call():
+ global cleanup_called
+ cleanup_called += 1
diff --git a/tests/test_env.py b/tests/test_env.py
index 124ed08c..e62db33b 100644
--- a/tests/test_env.py
+++ b/tests/test_env.py
@@ -5,7 +5,7 @@
Test the BuildEnvironment class.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import sys
@@ -29,7 +29,7 @@ def teardown_module():
def warning_emitted(file, text):
for warning in warnings:
- if len(warning) == 2 and file+':' in warning[1] and text in warning[0]:
+ if len(warning) == 2 and file in warning[1] and text in warning[0]:
return True
return False
@@ -47,8 +47,8 @@ def test_first_update():
assert 'subdir/excluded' not in env.found_docs
def test_images():
- assert warning_emitted('images.txt', 'image file not readable: foo.png')
- assert warning_emitted('images.txt', 'nonlocal image URI found: '
+ assert warning_emitted('images', 'image file not readable: foo.png')
+ assert warning_emitted('images', 'nonlocal image URI found: '
'http://www.python.org/logo.png')
tree = env.get_doctree('images')
diff --git a/tests/test_highlighting.py b/tests/test_highlighting.py
index 7496690b..949552c1 100644
--- a/tests/test_highlighting.py
+++ b/tests/test_highlighting.py
@@ -5,7 +5,7 @@
Test the Pygments highlighting bridge.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 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 135cc3e1..930108e9 100644
--- a/tests/test_i18n.py
+++ b/tests/test_i18n.py
@@ -5,7 +5,7 @@
Test locale features.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 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 990e35bd..839a3e3d 100644
--- a/tests/test_intersphinx.py
+++ b/tests/test_intersphinx.py
@@ -5,7 +5,7 @@
Test the intersphinx extension.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/tests/test_intl.py b/tests/test_intl.py
new file mode 100644
index 00000000..9459a1b7
--- /dev/null
+++ b/tests/test_intl.py
@@ -0,0 +1,60 @@
+# -*- coding: utf-8 -*-
+"""
+ test_intl
+ ~~~~~~~~~
+
+ Test message patching for internationalization purposes. Runs the text
+ builder in the test root.
+
+ :copyright: Copyright 2010 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+from subprocess import Popen, PIPE
+
+from util import *
+from util import SkipTest
+
+
+def setup_module():
+ (test_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:
+ 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'
+
+
+def teardown_module():
+ (test_root / '_build').rmtree(True)
+ (test_root / 'xx').rmtree(True)
+
+
+@with_app(buildername='text',
+ confoverrides={'language': 'xx', 'locale_dirs': ['.']})
+def test_simple(app):
+ app.builder.build(['bom'])
+ result = (app.outdir / 'bom.txt').text(encoding='utf-8')
+ expect = (u"\nDatei mit UTF-8"
+ u"\n***************\n" # underline matches new translation
+ u"\nThis file has umlauts: äöü.\n")
+ assert result == expect
+
+
+@with_app(buildername='text',
+ confoverrides={'language': 'xx', 'locale_dirs': ['.']})
+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")
diff --git a/tests/test_markup.py b/tests/test_markup.py
index 092113bb..09f617f4 100644
--- a/tests/test_markup.py
+++ b/tests/test_markup.py
@@ -5,7 +5,7 @@
Test various Sphinx-specific markup extensions.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/tests/test_metadata.py b/tests/test_metadata.py
index 8fb6cb21..1652b49b 100644
--- a/tests/test_metadata.py
+++ b/tests/test_metadata.py
@@ -5,7 +5,7 @@
Test our handling of metadata in files with bibliographic metadata.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/tests/test_quickstart.py b/tests/test_quickstart.py
index 541959bd..798d8d56 100644
--- a/tests/test_quickstart.py
+++ b/tests/test_quickstart.py
@@ -5,7 +5,7 @@
Test the sphinx.quickstart module.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -171,6 +171,10 @@ def test_quickstart_all_answers(tempdir):
assert ns['man_pages'] == [
('contents', 'stasi', u'STASI™ Documentation',
[u'Wolfgang Schäuble & G\'Beckstein'], 1)]
+ assert ns['texinfo_documents'] == [
+ ('contents', 'STASI', u'STASI™ Documentation',
+ u'Wolfgang Schäuble & G\'Beckstein', 'STASI',
+ 'One line description of project.', 'Miscellaneous'),]
assert (tempdir / 'build').isdir()
assert (tempdir / 'source' / '.static').isdir()
diff --git a/tests/test_rst_domain.py b/tests/test_rst_domain.py
index 10617095..db5c5c07 100644
--- a/tests/test_rst_domain.py
+++ b/tests/test_rst_domain.py
@@ -5,7 +5,7 @@
Tests the reStructuredText domain.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/tests/test_search.py b/tests/test_search.py
index c0750366..d9dcfb81 100644
--- a/tests/test_search.py
+++ b/tests/test_search.py
@@ -5,7 +5,7 @@
Test the search index builder.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -36,7 +36,7 @@ def test_wordcollector():
doc['file'] = 'dummy'
parser.parse(FILE_CONTENTS, doc)
- ix = IndexBuilder(None)
+ ix = IndexBuilder(None, 'en', {})
ix.feed('filename', 'title', doc)
assert 'boson' not in ix._mapping
assert 'fermion' in ix._mapping
diff --git a/tests/test_searchadapters.py b/tests/test_searchadapters.py
index cf5accb9..c20a74e3 100644
--- a/tests/test_searchadapters.py
+++ b/tests/test_searchadapters.py
@@ -5,7 +5,7 @@
Test the Web Support Package search adapters.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/tests/test_theming.py b/tests/test_theming.py
index 0cad0f8d..c924223c 100644
--- a/tests/test_theming.py
+++ b/tests/test_theming.py
@@ -5,7 +5,7 @@
Test the Theme class.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -25,7 +25,8 @@ def test_theme_api(app):
# test Theme class API
assert set(Theme.themes.keys()) == \
set(['basic', 'default', 'scrolls', 'agogo', 'sphinxdoc', 'haiku',
- 'traditional', 'testtheme', 'ziptheme', 'epub', 'nature'])
+ 'traditional', 'testtheme', 'ziptheme', 'epub', 'nature',
+ 'pyramid'])
assert Theme.themes['testtheme'][1] is None
assert isinstance(Theme.themes['ziptheme'][1], zipfile.ZipFile)
diff --git a/tests/test_versioning.py b/tests/test_versioning.py
index 923da203..1a77fb6e 100644
--- a/tests/test_versioning.py
+++ b/tests/test_versioning.py
@@ -5,7 +5,7 @@
Test the versioning implementation.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import pickle
diff --git a/tests/test_websupport.py b/tests/test_websupport.py
index 65957378..9cf64338 100644
--- a/tests/test_websupport.py
+++ b/tests/test_websupport.py
@@ -5,7 +5,7 @@
Test the Web Support Package
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -65,7 +65,7 @@ class NullStorage(StorageBackend):
@with_support(storage=NullStorage())
def test_no_srcdir(support):
"""Make sure the correct exception is raised if srcdir is not given."""
- raises(SrcdirNotSpecifiedError, support.build)
+ raises(RuntimeError, support.build)
@skip_if(sqlalchemy_missing, 'needs sqlalchemy')
@@ -271,9 +271,9 @@ def test_moderation(support):
def test_differ():
- differ = CombinedHtmlDiff()
source = 'Lorem ipsum dolor sit amet,\nconsectetur adipisicing elit,\n' \
'sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.'
prop = 'Lorem dolor sit amet,\nconsectetur nihil adipisicing elit,\n' \
'sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.'
- differ.make_html(source, prop)
+ differ = CombinedHtmlDiff(source, prop)
+ differ.make_html()
diff --git a/tests/util.py b/tests/util.py
index d56f3464..3711de72 100644
--- a/tests/util.py
+++ b/tests/util.py
@@ -3,7 +3,7 @@
Sphinx test suite utilities
~~~~~~~~~~~~~~~~~~~~~~~~~~~
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/utils/check_sources.py b/utils/check_sources.py
index c412742b..6b030d5d 100755
--- a/utils/check_sources.py
+++ b/utils/check_sources.py
@@ -7,7 +7,7 @@
Make sure each Python file has a correct file header
including copyright and license information.
- :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""