summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorg Brandl <georg@python.org>2008-12-28 21:32:12 +0100
committerGeorg Brandl <georg@python.org>2008-12-28 21:32:12 +0100
commitb4e20a52f3da165d216bb8f24807cdbcc0f20d94 (patch)
treead03ef24eb87fe73e58881bfcd1d2eb82b1ad5ea
parent2174f62f0889fd01ce5b2c8c631d0639167aa095 (diff)
parent846f9073eef533512e102765c141c0095a913943 (diff)
downloadsphinx-b4e20a52f3da165d216bb8f24807cdbcc0f20d94.tar.gz
merge
-rw-r--r--CHANGES68
-rw-r--r--EXAMPLES1
-rw-r--r--LICENSE56
-rw-r--r--doc/Makefile12
-rw-r--r--doc/builders.rst8
-rw-r--r--doc/concepts.rst15
-rw-r--r--doc/config.rst31
-rw-r--r--doc/ext/appapi.rst9
-rw-r--r--doc/ext/builderapi.rst8
-rw-r--r--doc/ext/doctest.rst8
-rw-r--r--doc/glossary.rst2
-rw-r--r--doc/intro.rst8
-rw-r--r--doc/markup/inline.rst48
-rw-r--r--doc/templating.rst8
-rwxr-xr-xsphinx-build.py2
-rwxr-xr-xsphinx-quickstart.py2
-rw-r--r--sphinx/__init__.py15
-rw-r--r--sphinx/_jinja.py5
-rw-r--r--sphinx/addnodes.py16
-rw-r--r--sphinx/application.py32
-rw-r--r--sphinx/builder.py1280
-rw-r--r--sphinx/builders/__init__.py345
-rw-r--r--sphinx/builders/changes.py137
-rw-r--r--sphinx/builders/html.py625
-rw-r--r--sphinx/builders/htmlhelp.py244
-rw-r--r--sphinx/builders/latex.py186
-rw-r--r--sphinx/builders/linkcheck.py (renamed from sphinx/linkcheck.py)8
-rw-r--r--sphinx/builders/text.py68
-rw-r--r--sphinx/cmdline.py2
-rw-r--r--sphinx/config.py11
-rw-r--r--sphinx/directives/__init__.py2
-rw-r--r--sphinx/directives/code.py2
-rw-r--r--sphinx/directives/desc.py35
-rw-r--r--sphinx/directives/other.py16
-rw-r--r--sphinx/environment.py70
-rw-r--r--sphinx/ext/__init__.py2
-rw-r--r--sphinx/ext/autodoc.py173
-rw-r--r--sphinx/ext/coverage.py4
-rw-r--r--sphinx/ext/doctest.py16
-rw-r--r--sphinx/ext/ifconfig.py2
-rw-r--r--sphinx/ext/intersphinx.py4
-rw-r--r--sphinx/ext/jsmath.py2
-rw-r--r--sphinx/ext/mathbase.py2
-rw-r--r--sphinx/ext/pngmath.py2
-rw-r--r--sphinx/ext/refcounting.py2
-rw-r--r--sphinx/ext/todo.py2
-rw-r--r--sphinx/highlighting.py32
-rw-r--r--sphinx/htmlhelp.py220
-rw-r--r--sphinx/locale/__init__.py2
-rw-r--r--sphinx/locale/it/LC_MESSAGES/sphinx.js1
-rw-r--r--sphinx/locale/it/LC_MESSAGES/sphinx.mobin0 -> 8415 bytes
-rw-r--r--sphinx/locale/it/LC_MESSAGES/sphinx.po599
-rw-r--r--sphinx/quickstart.py6
-rw-r--r--sphinx/roles.py11
-rw-r--r--sphinx/search.py2
-rw-r--r--sphinx/templates/layout.html26
-rw-r--r--sphinx/templates/modindex.html20
-rw-r--r--sphinx/templates/page.html8
-rw-r--r--sphinx/util/__init__.py44
-rw-r--r--sphinx/util/compat.py2
-rw-r--r--sphinx/util/console.py2
-rw-r--r--sphinx/util/jsdump.py2
-rw-r--r--sphinx/util/png.py2
-rw-r--r--sphinx/util/texescape.py2
-rw-r--r--sphinx/writers/__init__.py10
-rw-r--r--sphinx/writers/html.py (renamed from sphinx/htmlwriter.py)26
-rw-r--r--sphinx/writers/latex.py (renamed from sphinx/latexwriter.py)33
-rw-r--r--sphinx/writers/text.py (renamed from sphinx/textwriter.py)16
-rw-r--r--tests/root/includes.txt8
-rwxr-xr-xtests/run.py2
-rw-r--r--tests/test_application.py2
-rw-r--r--tests/test_autodoc.py68
-rw-r--r--tests/test_build.py10
-rw-r--r--tests/test_config.py6
-rw-r--r--tests/test_coverage.py2
-rw-r--r--tests/test_env.py11
-rw-r--r--tests/test_highlighting.py51
-rw-r--r--tests/test_i18n.py2
-rw-r--r--tests/test_markup.py6
-rw-r--r--tests/test_quickstart.py2
-rw-r--r--tests/util.py4
81 files changed, 3025 insertions, 1811 deletions
diff --git a/CHANGES b/CHANGES
index c82c5b71..50b4751b 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,71 @@
+Release 0.6 (in development)
+============================
+
+New features added
+------------------
+
+* Incompatible changes:
+
+ - The ``autodoc_skip_member`` event now also gets to decide
+ whether to skip members whose name starts with underscores.
+ Previously, these members were always automatically skipped.
+ Therefore, if you handle this event, add something like this
+ to your event handler to restore the old behavior::
+
+ if name.startswith('_'):
+ return True
+
+* Markup:
+
+ - Due to popular demand, added a ``:doc:`` role which directly
+ links to another document without the need of creating a
+ label to which a ``:ref:`` could link to.
+
+ - #4: Added a ``:download:`` role that marks a non-document file
+ for inclusion into the HTML output and links to it.
+
+ - The ``toctree`` directive now supports a ``:hidden:`` flag,
+ which will prevent links from being generated in place of
+ the directive -- this allows you to define your document
+ structure, but place the links yourself.
+
+ - #77: If a description environment with info field list only
+ contains one ``:param:`` entry, no bullet list is generated.
+
+* Configuration:
+
+ - The new ``html_add_permalinks`` config value can be used to
+ switch off the generated "paragraph sign" permalinks for each
+ heading and definition environment.
+
+ - The new ``html_show_sourcelink`` config value can be used to
+ switch off the links to the reST sources in the sidebar.
+
+ - The new ``html_link_suffix`` config value can be used to select
+ the suffix of generated links between HTML files.
+
+* New translations:
+
+ - Italian by Sandro Dentella.
+
+* Extensions and API:
+
+ - Autodoc now handles inner classes and their methods.
+
+ - There is now a ``Sphinx.add_lexer()`` method to be able to use
+ custom Pygments lexers easily.
+
+* Other changes:
+
+ - Config overrides for single dict keys can now be given on the
+ command line.
+
+ - There is now a ``doctest_global_setup`` config value that can
+ be used to give setup code for all doctests in the documentation.
+
+ - Source links in HTML are now generated with ``rel="nofollow"``.
+
+
Release 0.5.2 (in development)
==============================
diff --git a/EXAMPLES b/EXAMPLES
index 1ab76433..6b61ec04 100644
--- a/EXAMPLES
+++ b/EXAMPLES
@@ -18,6 +18,7 @@ included, please mail to `the Google group
* Grok: http://grok.zope.org/doc/current/
* IFM: http://fluffybunny.memebot.com/ifm-docs/index.html
* Jinja: http://jinja.pocoo.org/2/documentation/
+* MapServer: http://mapserver.osgeo.org/
* Matplotlib: http://matplotlib.sourceforge.net/
* Mayavi: http://code.enthought.com/projects/mayavi/docs/development/html/mayavi
* Mixin.com: http://dev.mixin.com/
diff --git a/LICENSE b/LICENSE
index 2db70e5f..fd441170 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,30 +1,26 @@
-Copyright (c) 2007-2008 by the respective authors (see AUTHORS file).
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials provided
- with the distribution.
-
- * The names of the contributors may not be used to endorse or
- promote products derived from this software without specific
- prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+Copyright (c) 2007-2008 by the respective authors (see AUTHORS file).
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/doc/Makefile b/doc/Makefile
index cb5fc645..7cb1ceb7 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -16,7 +16,7 @@ ALLSPHINXOPTS = -d _build/doctrees $(PAPEROPT_$(PAPER)) \
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
- @echo " web to make files usable by Sphinx.web"
+ @echo " pickle to make pickle files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " changes to make an overview over all changed/added/deprecated items"
@@ -31,13 +31,15 @@ html:
@echo
@echo "Build finished. The HTML pages are in _build/html."
+text:
+ mkdir -p _build/text _build/doctrees
+ $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) _build/text
+ @echo
+ @echo "Build finished."
+
pickle:
mkdir -p _build/pickle _build/doctrees
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) _build/pickle
- @echo
- @echo "Build finished; now you can run"
- @echo " python -m sphinx.web _build/pickle"
- @echo "to start the server."
htmlhelp:
mkdir -p _build/htmlhelp _build/doctrees
diff --git a/doc/builders.rst b/doc/builders.rst
index 508ab3c5..dd07a96a 100644
--- a/doc/builders.rst
+++ b/doc/builders.rst
@@ -3,7 +3,7 @@
Available builders
==================
-.. module:: sphinx.builder
+.. module:: sphinx.builders
:synopsis: Available built-in builder classes.
These are the built-in Sphinx builders. More builders can be added by
@@ -13,6 +13,7 @@ The builder's "name" must be given to the **-b** command-line option of
:program:`sphinx-build` to select a builder.
+.. module:: sphinx.builders.html
.. class:: StandaloneHTMLBuilder
This is the standard HTML builder. Its output is a directory with HTML
@@ -30,6 +31,7 @@ The builder's "name" must be given to the **-b** command-line option of
Its name is ``htmlhelp``.
+.. module:: sphinx.builders.latex
.. class:: LaTeXBuilder
This builder produces a bunch of LaTeX files in the output directory. You
@@ -50,6 +52,7 @@ The builder's "name" must be given to the **-b** command-line option of
Its name is ``latex``.
+.. module:: sphinx.builders.text
.. class:: TextBuilder
This builder produces a text file for each reST file -- this is almost the
@@ -60,6 +63,7 @@ The builder's "name" must be given to the **-b** command-line option of
.. versionadded:: 0.4
+.. currentmodule:: sphinx.builders.html
.. class:: SerializingHTMLBuilder
This builder uses a module that implements the Python serialization API
@@ -135,6 +139,7 @@ The builder's "name" must be given to the **-b** command-line option of
.. versionadded:: 0.5
+.. module:: sphinx.builders.changes
.. class:: ChangesBuilder
This builder produces an HTML overview of all :dir:`versionadded`,
@@ -144,6 +149,7 @@ The builder's "name" must be given to the **-b** command-line option of
Its name is ``changes``.
+.. module:: sphinx.builders.linkcheck
.. class:: CheckExternalLinksBuilder
This builder scans all documents for external links, tries to open them with
diff --git a/doc/concepts.rst b/doc/concepts.rst
index ca7aaf7c..379888ff 100644
--- a/doc/concepts.rst
+++ b/doc/concepts.rst
@@ -85,6 +85,18 @@ tables of contents. The ``toctree`` directive is the central element.
This includes first all documents whose names start with ``intro``, then all
documents in the ``recipe`` folder, then all remaining documents (except the
one containing the directive, of course.) [#]_
+
+ You can also give a "hidden" option to the directive, like this::
+
+ .. toctree::
+ :hidden:
+
+ doc_1
+ doc_2
+
+ This will still notify Sphinx of the document hierarchy, but not insert links
+ into the document at the location of the directive -- this makes sense if you
+ intend to insert these links yourself, in a different style.
In the end, all documents in the :term:`source directory` (or subdirectories)
must occur in some ``toctree`` directive; Sphinx will emit a warning if it
@@ -100,6 +112,9 @@ tables of contents. The ``toctree`` directive is the central element.
.. versionchanged:: 0.3
Added "globbing" option.
+ .. versionchanged:: 0.6
+ Added "hidden" option.
+
Special names
-------------
diff --git a/doc/config.rst b/doc/config.rst
index e6af9aae..dd25cf22 100644
--- a/doc/config.rst
+++ b/doc/config.rst
@@ -22,8 +22,8 @@ Important points to note:
* The term "fully-qualified name" refers to a string that names an importable
Python object inside a module; for example, the FQN
- ``"sphinx.builder.Builder"`` means the ``Builder`` class in the
- ``sphinx.builder`` module.
+ ``"sphinx.builders.Builder"`` means the ``Builder`` class in the
+ ``sphinx.builders`` module.
* Remember that document names use ``/`` as the path separator and don't contain
the file name extension.
@@ -205,6 +205,7 @@ Project information
* ``en`` -- English
* ``es`` -- Spanish
* ``fr`` -- French
+ * ``it`` -- Italian
* ``nl`` -- Dutch
* ``pl`` -- Polish
* ``pt_BR`` -- Brazilian Portuguese
@@ -332,6 +333,15 @@ that use Sphinx' HTMLWriter class.
If true, *SmartyPants* will be used to convert quotes and dashes to
typographically correct entities. Default: ``True``.
+.. 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``.
+
+ .. versionadded:: 0.6
+ Previously, this was always activated.
+
.. confval:: html_sidebars
Custom sidebar templates, must be a dictionary that maps document names to
@@ -399,6 +409,13 @@ that use Sphinx' HTMLWriter class.
will only display the titles of matching documents, and no excerpt from
the matching contents.
+.. confval:: html_show_sourcelink
+
+ If true (and :confval:`html_copy_source` is true as well), links to the
+ reST sources will be added to the sidebar. The default is ``True``.
+
+ .. versionadded:: 0.6
+
.. confval:: html_use_opensearch
If nonempty, an `OpenSearch <http://opensearch.org>` description file will be
@@ -415,10 +432,18 @@ that use Sphinx' HTMLWriter class.
.. versionadded:: 0.4
+.. confval:: html_link_suffix
+
+ Suffix for generated links to HTML files. The default is whatever
+ :confval:`html_file_suffix` is set to; it can be set differently (e.g. to
+ support different web server setups).
+
+ .. versionadded:: 0.6
+
.. confval:: html_translator_class
A string with the fully-qualified name of a HTML Translator class, that is, a
- subclass of Sphinx' :class:`~sphinx.htmlwriter.HTMLTranslator`, that is used
+ subclass of Sphinx' :class:`~sphinx.writers.html.HTMLTranslator`, that is used
to translate document trees to HTML. Default is ``None`` (use the builtin
translator).
diff --git a/doc/ext/appapi.rst b/doc/ext/appapi.rst
index fcc29e38..3dd5282b 100644
--- a/doc/ext/appapi.rst
+++ b/doc/ext/appapi.rst
@@ -13,7 +13,7 @@ the following public API:
.. method:: Sphinx.add_builder(builder)
Register a new builder. *builder* must be a class that inherits from
- :class:`~sphinx.builder.Builder`.
+ :class:`~sphinx.builders.Builder`.
.. method:: Sphinx.add_config_value(name, default, rebuild_env)
@@ -167,6 +167,13 @@ the following public API:
:confval:`the docs for the config value <html_static_path>`.
.. versionadded:: 0.5
+
+.. method:: Sphinx.add_lexer(alias, lexer)
+
+ Use *lexer*, which must be an instance of a Pygments lexer class, to
+ highlight code blocks with the given language *alias*.
+
+ .. versionadded:: 0.6
.. method:: Sphinx.connect(event, callback)
diff --git a/doc/ext/builderapi.rst b/doc/ext/builderapi.rst
index adc41016..5c671f2d 100644
--- a/doc/ext/builderapi.rst
+++ b/doc/ext/builderapi.rst
@@ -5,7 +5,7 @@ Writing new builders
.. todo:: Expand this.
-.. currentmodule:: sphinx.builder
+.. currentmodule:: sphinx.builders
.. class:: Builder
@@ -20,7 +20,7 @@ Writing new builders
.. automethod:: build_update
.. automethod:: build
- These methods must be overridden in concrete builder classes:
+ These methods can be overridden in concrete builder classes:
.. automethod:: init
.. automethod:: get_outdated_docs
@@ -28,3 +28,7 @@ Writing new builders
.. automethod:: prepare_writing
.. automethod:: write_doc
.. automethod:: finish
+
+ Useful helpers:
+
+ .. automethod:: init_templates
diff --git a/doc/ext/doctest.rst b/doc/ext/doctest.rst
index 9de6ba9e..7117f6a9 100644
--- a/doc/ext/doctest.rst
+++ b/doc/ext/doctest.rst
@@ -149,6 +149,14 @@ There are also these config values for customizing the doctest extension:
A list of directories that will be added to :data:`sys.path` when the doctest
builder is used. (Make sure it contains absolute paths.)
+.. confval:: doctest_global_setup
+
+ Python code that is treated like it were put in a ``testsetup`` directive for
+ *every* file that is tested, and for every group. You can use this to
+ e.g. import modules you will always need in your doctests.
+
+ .. versionadded:: 0.6
+
.. confval:: doctest_test_doctest_blocks
If this is a nonempty string (the default is ``'default'``), standard reST
diff --git a/doc/glossary.rst b/doc/glossary.rst
index 6a80ad36..7ec787ff 100644
--- a/doc/glossary.rst
+++ b/doc/glossary.rst
@@ -6,7 +6,7 @@ Glossary
.. glossary::
builder
- A class (inheriting from :class:`~sphinx.builder.Builder`) that takes
+ A class (inheriting from :class:`~sphinx.builders.Builder`) that takes
parsed documents and performs an action on them. Normally, builders
translate the documents to an output format, but it is also possible to
use the builder builders that e.g. check for broken links in the
diff --git a/doc/intro.rst b/doc/intro.rst
index 47e016b3..7ce9d1fa 100644
--- a/doc/intro.rst
+++ b/doc/intro.rst
@@ -111,8 +111,12 @@ The :program:`sphinx-build` script has several more options:
.. versionadded:: 0.5
**-D** *setting=value*
- Override a configuration value set in the :file:`conf.py` file. (The value
- must be a string value.)
+ Override a configuration value set in the :file:`conf.py` file. The value
+ must be a string or dictionary value. For the latter, supply the setting
+ name and key like this: ``-D latex_elements.docclass=scrartcl``.
+
+ .. versionchanged:: 0.6
+ The value can now be a dictionary value.
**-A** *name=value*
Make the *name* assigned to *value* in the HTML templates.
diff --git a/doc/markup/inline.rst b/doc/markup/inline.rst
index 5dbd638e..9f8a01a5 100644
--- a/doc/markup/inline.rst
+++ b/doc/markup/inline.rst
@@ -224,7 +224,50 @@ to labels:
Using :role:`ref` is advised over standard reStructuredText links to sections
(like ```Section title`_``) because it works across files, when section headings
are changed, and for all builders that support cross-references.
-
+
+
+Cross-referencing documents
+---------------------------
+
+.. versionadded:: 0.6
+
+There is also a way to directly link to documents:
+
+.. role:: doc
+
+ Link to the specified document; the document name can be specified in
+ absolute or relative fashion. For example, if the reference
+ ``:doc:`parrot``` occurs in the document ``sketches/index``, then the link
+ refers to ``sketches/parrot``. If the reference is ``:doc:`/people``` or
+ ``:doc:`../people```, the link refers to ``people``.
+
+ If no explicit link text is given (like usual: ``:doc:`Monty Python members
+ </people>```), the link caption will be the title of the given document.
+
+
+Referencing downloadable files
+------------------------------
+
+.. versionadded:: 0.6
+
+.. role:: download
+
+ This role lets you link to files within your source tree that are not reST
+ documents that can be viewed, but files that can be downloaded.
+
+ When you use this role, the referenced file is automatically marked for
+ inclusion in the output when building (obviously, for HTML output only).
+ All downloadable files are put into the ``_downloads`` subdirectory of the
+ output directory; duplicate filenames are handled.
+
+ An example::
+
+ See :download:`this example script <../example.py>`.
+
+ The given filename is relative to the directory the current source file is
+ contained in. The ``../example.py`` file will be copied to the output
+ directory, and a suitable link generated to it.
+
Other semantic markup
---------------------
@@ -330,7 +373,7 @@ in a different style:
curly braces to indicate a "variable" part, as in ``:file:``.
If you don't need the "variable part" indication, use the standard
- ````code```` instead.
+ ````code```` instead.
The following roles generate external links:
@@ -351,6 +394,7 @@ The following roles generate external links:
Note that there are no special roles for including hyperlinks as you can use
the standard reST markup for that purpose.
+
.. _default-substitutions:
Substitutions
diff --git a/doc/templating.rst b/doc/templating.rst
index 61a8a72b..ff4bdd51 100644
--- a/doc/templating.rst
+++ b/doc/templating.rst
@@ -19,10 +19,10 @@ No. You have several other options:
configuration value accordingly.
* You can :ref:`write a custom builder <writing-builders>` that derives from
- :class:`~sphinx.builder.StandaloneHTMLBuilder` and calls your template engine
+ :class:`~sphinx.builders.StandaloneHTMLBuilder` and calls your template engine
of choice.
-* You can use the :class:`~sphinx.builder.PickleHTMLBuilder` that produces
+* You can use the :class:`~sphinx.builders.PickleHTMLBuilder` that produces
pickle files with the page contents, and postprocess them using a custom tool,
or use them in your Web application.
@@ -135,6 +135,10 @@ The following blocks exist in the ``layout`` template:
`sidebarrel`
The relation links (previous, next document) within the sidebar.
+`sidebarsourcelink`
+ The "Show source" link within the sidebar (normally only shown if this is
+ enabled by :confval:`html_show_sourcelink`).
+
`sidebarsearch`
The search box within the sidebar. Override this if you want to place some
content at the bottom of the sidebar.
diff --git a/sphinx-build.py b/sphinx-build.py
index 4e2a868d..228c5b7b 100755
--- a/sphinx-build.py
+++ b/sphinx-build.py
@@ -5,7 +5,7 @@
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:copyright: 2007-2008 by Georg Brandl.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
import sys
diff --git a/sphinx-quickstart.py b/sphinx-quickstart.py
index 579b3558..768adf41 100755
--- a/sphinx-quickstart.py
+++ b/sphinx-quickstart.py
@@ -5,7 +5,7 @@
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:copyright: 2008 by Georg Brandl.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
import sys
diff --git a/sphinx/__init__.py b/sphinx/__init__.py
index 7c7e3537..ee0034ff 100644
--- a/sphinx/__init__.py
+++ b/sphinx/__init__.py
@@ -6,14 +6,17 @@
The Sphinx documentation toolchain.
:copyright: 2007-2008 by Georg Brandl.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
import sys
+from os import path
__revision__ = '$Revision$'
-__version__ = '0.5.1+'
-__released__ = '0.5.1'
+__version__ = '0.6'
+__released__ = '0.6 (hg)'
+
+package_dir = path.abspath(path.dirname(__file__))
def main(argv=sys.argv):
@@ -28,17 +31,23 @@ def main(argv=sys.argv):
errstr = str(err)
if errstr.lower().startswith('no module named'):
whichmod = errstr[16:]
+ hint = ''
if whichmod.startswith('docutils'):
whichmod = 'Docutils library'
elif whichmod.startswith('jinja'):
whichmod = 'Jinja library'
elif whichmod == 'roman':
whichmod = 'roman module (which is distributed with Docutils)'
+ hint = ('This can happen if you upgraded docutils using\n'
+ 'easy_install without uninstalling the old version'
+ 'first.')
else:
whichmod += ' module'
print >>sys.stderr, \
'Error: The %s cannot be found. Did you install Sphinx '\
'and its dependencies correctly?' % whichmod
+ if hint:
+ print >> sys.stderr, hint
return 1
raise
return cmdline.main(argv)
diff --git a/sphinx/_jinja.py b/sphinx/_jinja.py
index d6e98b21..34917701 100644
--- a/sphinx/_jinja.py
+++ b/sphinx/_jinja.py
@@ -6,12 +6,13 @@
Jinja glue.
:copyright: 2007-2008 by Georg Brandl, Horst Gutmann.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
import codecs
from os import path
+from sphinx import package_dir
from sphinx.util import mtimes_of_files
from sphinx.application import TemplateBridge
@@ -88,7 +89,7 @@ class TranslatorEnvironment(Environment):
class BuiltinTemplates(TemplateBridge):
def init(self, builder):
self.templates = {}
- base_templates_path = path.join(path.dirname(__file__), 'templates')
+ base_templates_path = path.join(package_dir, 'templates')
ext_templates_path = [path.join(builder.confdir, dir)
for dir in builder.config.templates_path]
self.templates_path = [base_templates_path] + ext_templates_path
diff --git a/sphinx/addnodes.py b/sphinx/addnodes.py
index 8b20f7ac..1d719d88 100644
--- a/sphinx/addnodes.py
+++ b/sphinx/addnodes.py
@@ -6,7 +6,7 @@
Additional docutils nodes.
:copyright: 2007-2008 by Georg Brandl.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
from docutils import nodes
@@ -23,8 +23,12 @@ class desc(nodes.Admonition, nodes.Element): pass
class desc_addname(nodes.Part, nodes.Inline, nodes.TextElement): pass
# compatibility alias
desc_classname = desc_addname
-# return type (C); object type, e.g. -> annotation (Python)
+# return type (C); object type
class desc_type(nodes.Part, nodes.Inline, nodes.TextElement): pass
+# -> annotation (Python)
+class desc_returns(desc_type):
+ def astext(self):
+ return ' -> ' + nodes.TextElement.astext(self)
# main name of object
class desc_name(nodes.Part, nodes.Inline, nodes.TextElement): pass
# argument list
@@ -64,6 +68,9 @@ class pending_xref(nodes.Element): pass
# compact paragraph -- never makes a <p>
class compact_paragraph(nodes.paragraph): pass
+# reference to a file to download
+class download_reference(nodes.reference): pass
+
# for the ACKS list
class acks(nodes.Element): pass
@@ -90,8 +97,9 @@ class meta(nodes.Special, nodes.PreBibliographic, nodes.Element): pass
# make them known to docutils. this is needed, because the HTML writer
# will choke at some point if these are not added
-nodes._add_node_class_names("""index desc desc_content desc_signature desc_type
- desc_addname desc_name desc_parameterlist desc_parameter desc_optional
+nodes._add_node_class_names("""index desc desc_content desc_signature
+ desc_type desc_returns desc_addname desc_name desc_parameterlist
+ desc_parameter desc_optional download_reference
centered versionmodified seealso productionlist production toctree
pending_xref compact_paragraph highlightlang literal_emphasis
glossary acks module start_of_file tabular_col_spec meta""".split())
diff --git a/sphinx/application.py b/sphinx/application.py
index e97fe734..c990edc1 100644
--- a/sphinx/application.py
+++ b/sphinx/application.py
@@ -9,7 +9,7 @@
:copyright: 2008 by Georg Brandl, Armin Ronacher.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
import sys
@@ -22,7 +22,7 @@ from docutils.parsers.rst import directives, roles
import sphinx
from sphinx.roles import xfileref_role, innernodetypes
from sphinx.config import Config
-from sphinx.builder import builtin_builders, StandaloneHTMLBuilder
+from sphinx.builders import BUILTIN_BUILDERS
from sphinx.directives import desc_directive, target_directive, additional_xref_types
from sphinx.environment import SphinxStandaloneReader
from sphinx.util.console import bold
@@ -77,7 +77,7 @@ class Sphinx(object):
confoverrides, status, warning=sys.stderr, freshenv=False):
self.next_listener_id = 0
self._listeners = {}
- self.builderclasses = builtin_builders.copy()
+ self.builderclasses = BUILTIN_BUILDERS.copy()
self.builder = None
self.srcdir = srcdir
@@ -125,6 +125,11 @@ class Sphinx(object):
buildername)))
builderclass = self.builderclasses[buildername]
+ if isinstance(builderclass, tuple):
+ # builtin builder
+ mod, cls = builderclass
+ builderclass = getattr(
+ __import__('sphinx.builders.' + mod, None, None, [cls]), cls)
self.builder = builderclass(self, freshenv=freshenv)
self.emit('builder-inited')
@@ -227,8 +232,12 @@ class Sphinx(object):
if not hasattr(builder, 'name'):
raise ExtensionError('Builder class %s has no "name" attribute' % builder)
if builder.name in self.builderclasses:
- raise ExtensionError('Builder %r already exists (in module %s)' % (
- builder.name, self.builderclasses[builder.name].__module__))
+ if isinstance(self.builderclasses[builder.name], tuple):
+ raise ExtensionError('Builder %r is a builtin builder' %
+ builder.name)
+ else:
+ raise ExtensionError('Builder %r already exists (in module %s)' % (
+ builder.name, self.builderclasses[builder.name].__module__))
self.builderclasses[builder.name] = builder
def add_config_value(self, name, default, rebuild_env):
@@ -250,11 +259,11 @@ class Sphinx(object):
raise ExtensionError('Value for key %r must be a (visit, depart) '
'function tuple' % key)
if key == 'html':
- from sphinx.htmlwriter import HTMLTranslator as translator
+ from sphinx.writers.html import HTMLTranslator as translator
elif key == 'latex':
- from sphinx.latexwriter import LaTeXTranslator as translator
+ from sphinx.writers.latex import LaTeXTranslator as translator
elif key == 'text':
- from sphinx.textwriter import TextTranslator as translator
+ from sphinx.writers.text import TextTranslator as translator
else:
# ignore invalid keys for compatibility
continue
@@ -291,9 +300,16 @@ class Sphinx(object):
SphinxStandaloneReader.transforms.append(transform)
def add_javascript(self, filename):
+ from sphinx.builders.html import StandaloneHTMLBuilder
StandaloneHTMLBuilder.script_files.append(
posixpath.join('_static', filename))
+ def add_lexer(self, alias, lexer):
+ from sphinx.highlighting import lexers
+ if lexers is None:
+ return
+ lexers[alias] = lexer
+
class TemplateBridge(object):
"""
diff --git a/sphinx/builder.py b/sphinx/builder.py
index 81fc2486..65d5da34 100644
--- a/sphinx/builder.py
+++ b/sphinx/builder.py
@@ -3,1270 +3,26 @@
sphinx.builder
~~~~~~~~~~~~~~
- Builder classes for different output formats.
+ .. warning::
- :copyright: 2007-2008 by Georg Brandl, Sebastian Wiesner, Horst Gutmann.
- :license: BSD.
-"""
-
-import os
-import time
-import codecs
-import shutil
-import gettext
-import cPickle as pickle
-from os import path
-from cgi import escape
-
-from docutils import nodes
-from docutils.io import StringOutput, FileOutput, DocTreeInput
-from docutils.core import publish_parts
-from docutils.utils import new_document
-from docutils.frontend import OptionParser
-from docutils.readers.doctree import Reader as DoctreeReader
-
-from sphinx import addnodes, locale, __version__
-from sphinx.util import ensuredir, relative_uri, SEP, os_path, texescape, ustrftime
-from sphinx.htmlhelp import build_hhx
-from sphinx.htmlwriter import HTMLWriter, HTMLTranslator, SmartyPantsHTMLTranslator
-from sphinx.textwriter import TextWriter
-from sphinx.latexwriter import LaTeXWriter
-from sphinx.environment import BuildEnvironment, NoUri
-from sphinx.highlighting import PygmentsBridge
-from sphinx.util.console import bold, purple, darkgreen
-from sphinx.search import js_index
-
-try:
- import json
-except ImportError:
- try:
- import simplejson as json
- except ImportError:
- json = None
-
-# side effect: registers roles and directives
-from sphinx import roles
-from sphinx import directives
-
-ENV_PICKLE_FILENAME = 'environment.pickle'
-LAST_BUILD_FILENAME = 'last_build'
-INVENTORY_FILENAME = 'objects.inv'
-
-
-class Builder(object):
- """
- Builds target formats from the reST sources.
- """
-
- # builder's name, for the -b command line options
- name = ''
-
- def __init__(self, app, env=None, freshenv=False):
- self.srcdir = app.srcdir
- self.confdir = app.confdir
- self.outdir = app.outdir
- self.doctreedir = app.doctreedir
- if not path.isdir(self.doctreedir):
- os.makedirs(self.doctreedir)
-
- self.app = app
- self.warn = app.warn
- self.info = app.info
- self.config = app.config
-
- self.load_i18n()
-
- # images that need to be copied over (source -> dest)
- self.images = {}
-
- # if None, this is set in load_env()
- self.env = env
- self.freshenv = freshenv
-
- self.init()
- self.load_env()
-
- # helper methods
-
- def init(self):
- """Load necessary templates and perform initialization."""
- raise NotImplementedError
-
- def init_templates(self):
- # Call this from init() if you need templates.
- if self.config.template_bridge:
- self.templates = self.app.import_object(
- self.config.template_bridge, 'template_bridge setting')()
- else:
- from sphinx._jinja import BuiltinTemplates
- self.templates = BuiltinTemplates()
- self.templates.init(self)
-
- def get_target_uri(self, docname, typ=None):
- """
- Return the target URI for a document name (typ can be used to qualify
- the link characteristic for individual builders).
- """
- raise NotImplementedError
-
- def get_relative_uri(self, from_, to, typ=None):
- """
- Return a relative URI between two source filenames. May raise environment.NoUri
- if there's no way to return a sensible URI.
- """
- return relative_uri(self.get_target_uri(from_),
- self.get_target_uri(to, typ))
-
- def get_outdated_docs(self):
- """
- Return an iterable of output files that are outdated, or a string describing
- what an update build will build.
- """
- raise NotImplementedError
-
- def status_iterator(self, iterable, summary, colorfunc=darkgreen):
- l = -1
- for item in iterable:
- if l == -1:
- self.info(bold(summary), nonl=1)
- l = 0
- self.info(colorfunc(item) + ' ', nonl=1)
- yield item
- if l == 0:
- self.info()
-
- supported_image_types = []
-
- def post_process_images(self, doctree):
- """
- Pick the best candidate for all image URIs.
- """
- for node in doctree.traverse(nodes.image):
- if '?' in node['candidates']:
- # don't rewrite nonlocal image URIs
- continue
- if '*' not in node['candidates']:
- for imgtype in self.supported_image_types:
- candidate = node['candidates'].get(imgtype, None)
- if candidate:
- break
- else:
- self.warn('%s:%s: no matching candidate for image URI %r' %
- (node.source, getattr(node, 'lineno', ''), node['uri']))
- continue
- node['uri'] = candidate
- else:
- candidate = node['uri']
- if candidate not in self.env.images:
- # non-existing URI; let it alone
- continue
- self.images[candidate] = self.env.images[candidate][1]
-
- # build methods
-
- def load_i18n(self):
- """
- Load translated strings from the configured localedirs if
- enabled in the configuration.
- """
- self.translator = None
- if self.config.language is not None:
- self.info(bold('loading translations [%s]... ' % self.config.language),
- nonl=True)
- locale_dirs = [path.join(path.dirname(__file__), 'locale')] + \
- [path.join(self.srcdir, x) for x in self.config.locale_dirs]
- for dir_ in locale_dirs:
- try:
- trans = gettext.translation('sphinx', localedir=dir_,
- languages=[self.config.language])
- if self.translator is None:
- self.translator = trans
- else:
- self.translator._catalog.update(trans.catalog)
- except Exception:
- # Language couldn't be found in the specified path
- pass
- if self.translator is not None:
- self.info('done')
- else:
- self.info('locale not available')
- if self.translator is None:
- self.translator = gettext.NullTranslations()
- self.translator.install(unicode=True)
- locale.init() # translate common labels
-
- def load_env(self):
- """Set up the build environment."""
- if self.env:
- return
- if not self.freshenv:
- try:
- self.info(bold('loading pickled environment... '), nonl=True)
- self.env = BuildEnvironment.frompickle(self.config,
- path.join(self.doctreedir, ENV_PICKLE_FILENAME))
- self.info('done')
- except Exception, err:
- if type(err) is IOError and err.errno == 2:
- self.info('not found')
- else:
- self.info('failed: %s' % err)
- self.env = BuildEnvironment(self.srcdir, self.doctreedir, self.config)
- self.env.find_files(self.config)
- else:
- self.env = BuildEnvironment(self.srcdir, self.doctreedir, self.config)
- self.env.find_files(self.config)
- self.env.set_warnfunc(self.warn)
-
- def build_all(self):
- """Build all source files."""
- self.build(None, summary='all source files', method='all')
-
- def build_specific(self, filenames):
- """Only rebuild as much as needed for changes in the source_filenames."""
- # bring the filenames to the canonical format, that is,
- # relative to the source directory and without source_suffix.
- dirlen = len(self.srcdir) + 1
- to_write = []
- suffix = self.config.source_suffix
- for filename in filenames:
- filename = path.abspath(filename)[dirlen:]
- if filename.endswith(suffix):
- filename = filename[:-len(suffix)]
- filename = filename.replace(os.path.sep, SEP)
- to_write.append(filename)
- self.build(to_write, method='specific',
- summary='%d source files given on command '
- 'line' % len(to_write))
-
- def build_update(self):
- """Only rebuild files changed or added since last build."""
- to_build = self.get_outdated_docs()
- if isinstance(to_build, str):
- self.build(['__all__'], to_build)
- else:
- to_build = list(to_build)
- self.build(to_build,
- summary='targets for %d source files that are '
- 'out of date' % len(to_build))
-
- def build(self, docnames, summary=None, method='update'):
- if summary:
- self.info(bold('building [%s]: ' % self.name), nonl=1)
- self.info(summary)
-
- updated_docnames = []
- # while reading, collect all warnings from docutils
- warnings = []
- self.env.set_warnfunc(warnings.append)
- self.info(bold('updating environment: '), nonl=1)
- iterator = self.env.update(self.config, self.srcdir, self.doctreedir, self.app)
- # the first item in the iterator is a summary message
- self.info(iterator.next())
- for docname in self.status_iterator(iterator, 'reading sources... ', purple):
- updated_docnames.append(docname)
- # nothing further to do, the environment has already done the reading
- for warning in warnings:
- if warning.strip():
- self.warn(warning)
- self.env.set_warnfunc(self.warn)
-
- if updated_docnames:
- # save the environment
- self.info(bold('pickling environment... '), nonl=True)
- self.env.topickle(path.join(self.doctreedir, ENV_PICKLE_FILENAME))
- self.info('done')
-
- # global actions
- self.info(bold('checking consistency... '), nonl=True)
- self.env.check_consistency()
- self.info('done')
- else:
- if method == 'update' and not docnames:
- self.info(bold('no targets are out of date.'))
- return
-
- # another indirection to support methods which don't build files
- # individually
- self.write(docnames, updated_docnames, method)
-
- # finish (write static files etc.)
- self.finish()
- status = self.app.statuscode == 0 and 'succeeded' or 'finished with problems'
- if self.app._warncount:
- self.info(bold('build %s, %s warning%s.' %
- (status, self.app._warncount,
- self.app._warncount != 1 and 's' or '')))
- else:
- self.info(bold('build %s.' % status))
-
- def write(self, build_docnames, updated_docnames, method='update'):
- if build_docnames is None or build_docnames == ['__all__']:
- # build_all
- build_docnames = self.env.found_docs
- if method == 'update':
- # build updated ones as well
- docnames = set(build_docnames) | set(updated_docnames)
- else:
- docnames = set(build_docnames)
-
- # 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)
- docnames.add(self.config.master_doc)
-
- self.info(bold('preparing documents... '), nonl=True)
- self.prepare_writing(docnames)
- self.info('done')
-
- # write target files
- warnings = []
- self.env.set_warnfunc(warnings.append)
- for docname in self.status_iterator(sorted(docnames),
- 'writing output... ', darkgreen):
- doctree = self.env.get_and_resolve_doctree(docname, self)
- self.write_doc(docname, doctree)
- for warning in warnings:
- if warning.strip():
- self.warn(warning)
- self.env.set_warnfunc(self.warn)
-
- def prepare_writing(self, docnames):
- raise NotImplementedError
-
- def write_doc(self, docname, doctree):
- raise NotImplementedError
-
- def finish(self):
- raise NotImplementedError
-
-
-class StandaloneHTMLBuilder(Builder):
- """
- Builds standalone HTML docs.
- """
- name = 'html'
- copysource = True
- out_suffix = '.html'
- indexer_format = js_index
- supported_image_types = ['image/svg+xml', 'image/png', 'image/gif',
- 'image/jpeg']
- searchindex_filename = 'searchindex.js'
- add_header_links = True
- add_definition_links = True
-
- # This is a class attribute because it is mutated by Sphinx.add_javascript.
- script_files = ['_static/jquery.js', '_static/doctools.js']
-
- def init(self):
- """Load templates."""
- self.init_templates()
- self.init_translator_class()
- if self.config.html_file_suffix:
- self.out_suffix = self.config.html_file_suffix
-
- if self.config.language is not None:
- jsfile = path.join(path.dirname(__file__), 'locale', self.config.language,
- 'LC_MESSAGES', 'sphinx.js')
- if path.isfile(jsfile):
- self.script_files.append('_static/translations.js')
-
- def init_translator_class(self):
- if self.config.html_translator_class:
- self.translator_class = self.app.import_object(
- self.config.html_translator_class, 'html_translator_class setting')
- elif self.config.html_use_smartypants:
- self.translator_class = SmartyPantsHTMLTranslator
- else:
- self.translator_class = HTMLTranslator
-
- def render_partial(self, node):
- """Utility: Render a lone doctree node."""
- doc = new_document('<partial node>')
- doc.append(node)
- return publish_parts(
- doc,
- source_class=DocTreeInput,
- reader=DoctreeReader(),
- writer=HTMLWriter(self),
- settings_overrides={'output_encoding': 'unicode'}
- )
-
- def prepare_writing(self, docnames):
- from sphinx.search import IndexBuilder
-
- self.indexer = IndexBuilder(self.env)
- self.load_indexer(docnames)
- self.docwriter = HTMLWriter(self)
- self.docsettings = OptionParser(
- defaults=self.env.settings,
- components=(self.docwriter,)).get_default_values()
-
- # format the "last updated on" string, only once is enough since it
- # typically doesn't include the time of day
- lufmt = self.config.html_last_updated_fmt
- if lufmt is not None:
- self.last_updated = ustrftime(lufmt or _('%b %d, %Y'))
- else:
- self.last_updated = None
-
- logo = self.config.html_logo and \
- path.basename(self.config.html_logo) or ''
-
- favicon = self.config.html_favicon and \
- path.basename(self.config.html_favicon) or ''
- if favicon and os.path.splitext(favicon)[1] != '.ico':
- self.warn('html_favicon is not an .ico file')
-
- if not isinstance(self.config.html_use_opensearch, basestring):
- self.warn('html_use_opensearch config value must now be a string')
-
- self.relations = self.env.collect_relations()
-
- rellinks = []
- if self.config.html_use_index:
- rellinks.append(('genindex', _('General Index'), 'I', _('index')))
- if self.config.html_use_modindex and self.env.modules:
- rellinks.append(('modindex', _('Global Module Index'), 'M', _('modules')))
-
- self.globalcontext = dict(
- project = self.config.project,
- release = self.config.release,
- version = self.config.version,
- last_updated = self.last_updated,
- copyright = self.config.copyright,
- master_doc = self.config.master_doc,
- style = self.config.html_style,
- use_opensearch = self.config.html_use_opensearch,
- docstitle = self.config.html_title,
- shorttitle = self.config.html_short_title,
- show_sphinx = self.config.html_show_sphinx,
- has_source = self.config.html_copy_source,
- file_suffix = self.out_suffix,
- script_files = self.script_files,
- sphinx_version = __version__,
- rellinks = rellinks,
- builder = self.name,
- parents = [],
- logo = logo,
- favicon = favicon,
- )
- self.globalcontext.update(self.config.html_context)
-
- def get_doc_context(self, docname, body, metatags):
- """Collect items for the template context of a page."""
- # find out relations
- prev = next = None
- parents = []
- rellinks = self.globalcontext['rellinks'][:]
- related = self.relations.get(docname)
- titles = self.env.titles
- if related and related[2]:
- try:
- next = {'link': self.get_relative_uri(docname, related[2]),
- 'title': self.render_partial(titles[related[2]])['title']}
- rellinks.append((related[2], next['title'], 'N', _('next')))
- except KeyError:
- next = None
- if related and related[1]:
- try:
- prev = {'link': self.get_relative_uri(docname, related[1]),
- 'title': self.render_partial(titles[related[1]])['title']}
- rellinks.append((related[1], prev['title'], 'P', _('previous')))
- except KeyError:
- # the relation is (somehow) not in the TOC tree, handle that gracefully
- prev = None
- while related and related[0]:
- try:
- parents.append(
- {'link': self.get_relative_uri(docname, related[0]),
- 'title': self.render_partial(titles[related[0]])['title']})
- except KeyError:
- pass
- related = self.relations.get(related[0])
- if parents:
- parents.pop() # remove link to the master file; we have a generic
- # "back to index" link already
- parents.reverse()
-
- # title rendered as HTML
- title = titles.get(docname)
- title = title and self.render_partial(title)['title'] or ''
- # the name for the copied source
- sourcename = self.config.html_copy_source and docname + '.txt' or ''
-
- # metadata for the document
- meta = self.env.metadata.get(docname)
-
- return dict(
- parents = parents,
- prev = prev,
- next = next,
- title = title,
- meta = meta,
- body = body,
- metatags = metatags,
- rellinks = rellinks,
- sourcename = sourcename,
- toc = self.render_partial(self.env.get_toc_for(docname))['fragment'],
- # only display a TOC if there's more than one item to show
- display_toc = (self.env.toc_num_entries[docname] > 1),
- )
-
- def write_doc(self, docname, doctree):
- self.post_process_images(doctree)
- destination = StringOutput(encoding='utf-8')
- doctree.settings = self.docsettings
-
- self.imgpath = relative_uri(self.get_target_uri(docname), '_images')
- self.docwriter.write(doctree, destination)
- self.docwriter.assemble_parts()
- body = self.docwriter.parts['fragment']
- metatags = self.docwriter.clean_meta
-
- ctx = self.get_doc_context(docname, body, metatags)
- self.index_page(docname, doctree, ctx.get('title', ''))
- self.handle_page(docname, ctx, event_arg=doctree)
-
- def finish(self):
- self.info(bold('writing additional files...'), nonl=1)
-
- # the global general index
-
- if self.config.html_use_index:
- # the total count of lines for each index letter, used to distribute
- # the entries into two columns
- genindex = self.env.create_index(self)
- indexcounts = []
- for _, entries in genindex:
- indexcounts.append(sum(1 + len(subitems)
- for _, (_, subitems) in entries))
-
- genindexcontext = dict(
- genindexentries = genindex,
- genindexcounts = indexcounts,
- split_index = self.config.html_split_index,
- )
- self.info(' genindex', nonl=1)
-
- if self.config.html_split_index:
- self.handle_page('genindex', genindexcontext, 'genindex-split.html')
- self.handle_page('genindex-all', genindexcontext, 'genindex.html')
- for (key, entries), count in zip(genindex, indexcounts):
- ctx = {'key': key, 'entries': entries, 'count': count,
- 'genindexentries': genindex}
- self.handle_page('genindex-' + key, ctx, 'genindex-single.html')
- else:
- self.handle_page('genindex', genindexcontext, 'genindex.html')
-
- # the global module index
-
- if self.config.html_use_modindex and self.env.modules:
- # the sorted list of all modules, for the global module index
- modules = sorted(((mn, (self.get_relative_uri('modindex', fn) +
- '#module-' + mn, sy, pl, dep))
- for (mn, (fn, sy, pl, dep)) in
- self.env.modules.iteritems()),
- key=lambda x: x[0].lower())
- # collect all platforms
- platforms = set()
- # sort out collapsable modules
- modindexentries = []
- letters = []
- pmn = ''
- num_toplevels = 0
- num_collapsables = 0
- cg = 0 # collapse group
- fl = '' # first letter
- for mn, (fn, sy, pl, dep) in modules:
- pl = pl and pl.split(', ') or []
- platforms.update(pl)
- if fl != mn[0].lower() and mn[0] != '_':
- # heading
- modindexentries.append(['', False, 0, False,
- mn[0].upper(), '', [], False])
- letters.append(mn[0].upper())
- tn = mn.split('.')[0]
- if tn != mn:
- # submodule
- if pmn == tn:
- # first submodule - make parent collapsable
- modindexentries[-1][1] = True
- num_collapsables += 1
- elif not pmn.startswith(tn):
- # submodule without parent in list, add dummy entry
- cg += 1
- modindexentries.append([tn, True, cg, False, '', '', [], False])
- else:
- num_toplevels += 1
- cg += 1
- modindexentries.append([mn, False, cg, (tn != mn), fn, sy, pl, dep])
- pmn = mn
- fl = mn[0].lower()
- platforms = sorted(platforms)
-
- # apply heuristics when to collapse modindex at page load:
- # only collapse if number of toplevel modules is larger than
- # number of submodules
- collapse = len(modules) - num_toplevels < num_toplevels
-
- modindexcontext = dict(
- modindexentries = modindexentries,
- platforms = platforms,
- letters = letters,
- collapse_modindex = collapse,
- )
- self.info(' modindex', nonl=1)
- self.handle_page('modindex', modindexcontext, 'modindex.html')
-
- # the search page
- if self.name != 'htmlhelp':
- self.info(' search', nonl=1)
- self.handle_page('search', {}, 'search.html')
-
- # additional pages from conf.py
- for pagename, template in self.config.html_additional_pages.items():
- self.info(' '+pagename, nonl=1)
- self.handle_page(pagename, {}, template)
+ This module is only kept for API compatibility; new code should
+ import these classes directly from the sphinx.builders package.
- if self.config.html_use_opensearch and self.name != 'htmlhelp':
- self.info(' opensearch', nonl=1)
- fn = path.join(self.outdir, '_static', 'opensearch.xml')
- self.handle_page('opensearch', {}, 'opensearch.xml', outfilename=fn)
-
- self.info()
-
- # copy image files
- if self.images:
- self.info(bold('copying images...'), nonl=True)
- ensuredir(path.join(self.outdir, '_images'))
- for src, dest in self.images.iteritems():
- self.info(' '+src, nonl=1)
- shutil.copyfile(path.join(self.srcdir, src),
- path.join(self.outdir, '_images', dest))
- self.info()
-
- # copy static files
- self.info(bold('copying static files... '), nonl=True)
- ensuredir(path.join(self.outdir, '_static'))
- # first, create pygments style file
- f = open(path.join(self.outdir, '_static', 'pygments.css'), 'w')
- f.write(PygmentsBridge('html', self.config.pygments_style).get_stylesheet())
- f.close()
- # then, copy translations JavaScript file
- if self.config.language is not None:
- jsfile = path.join(path.dirname(__file__), 'locale', self.config.language,
- 'LC_MESSAGES', 'sphinx.js')
- if path.isfile(jsfile):
- shutil.copyfile(jsfile, path.join(self.outdir, '_static',
- 'translations.js'))
- # then, copy over all user-supplied static files
- staticdirnames = [path.join(path.dirname(__file__), 'static')] + \
- [path.join(self.confdir, spath)
- for spath in self.config.html_static_path]
- for staticdirname in staticdirnames:
- for filename in os.listdir(staticdirname):
- if filename.startswith('.'):
- continue
- fullname = path.join(staticdirname, filename)
- targetname = path.join(self.outdir, '_static', filename)
- if path.isfile(fullname):
- shutil.copyfile(fullname, targetname)
- elif path.isdir(fullname):
- if filename in self.config.exclude_dirnames:
- continue
- if path.exists(targetname):
- shutil.rmtree(targetname)
- shutil.copytree(fullname, targetname)
- # last, copy logo file (handled differently)
- if self.config.html_logo:
- logobase = path.basename(self.config.html_logo)
- shutil.copyfile(path.join(self.confdir, self.config.html_logo),
- path.join(self.outdir, '_static', logobase))
- self.info('done')
-
- # dump the search index
- self.handle_finish()
-
- def get_outdated_docs(self):
- if self.templates:
- template_mtime = self.templates.newest_template_mtime()
- else:
- template_mtime = 0
- for docname in self.env.found_docs:
- if docname not in self.env.all_docs:
- yield docname
- continue
- targetname = self.env.doc2path(docname, self.outdir, self.out_suffix)
- try:
- targetmtime = path.getmtime(targetname)
- except Exception:
- targetmtime = 0
- try:
- srcmtime = max(path.getmtime(self.env.doc2path(docname)),
- template_mtime)
- if srcmtime > targetmtime:
- yield docname
- except EnvironmentError:
- # source doesn't exist anymore
- pass
-
- def load_indexer(self, docnames):
- keep = set(self.env.all_docs) - set(docnames)
- try:
- f = open(path.join(self.outdir, self.searchindex_filename), 'rb')
- try:
- self.indexer.load(f, self.indexer_format)
- finally:
- f.close()
- except (IOError, OSError, ValueError):
- if keep:
- self.warn("search index couldn't be loaded, but not all documents "
- "will be built: the index will be incomplete.")
- # delete all entries for files that will be rebuilt
- self.indexer.prune(keep)
-
- def index_page(self, pagename, doctree, title):
- # only index pages with title
- if self.indexer is not None and title:
- self.indexer.feed(pagename, title, doctree)
-
- # --------- these are overwritten by the serialization builder
-
- def get_target_uri(self, docname, typ=None):
- return docname + self.out_suffix
-
- def handle_page(self, pagename, addctx, templatename='page.html',
- outfilename=None, event_arg=None):
- ctx = self.globalcontext.copy()
- # current_page_name is backwards compatibility
- ctx['pagename'] = ctx['current_page_name'] = pagename
-
- def pathto(otheruri, resource=False,
- baseuri=self.get_target_uri(pagename)):
- if not resource:
- otheruri = self.get_target_uri(otheruri)
- return relative_uri(baseuri, otheruri)
- ctx['pathto'] = pathto
- ctx['hasdoc'] = lambda name: name in self.env.all_docs
- ctx['customsidebar'] = self.config.html_sidebars.get(pagename)
- ctx.update(addctx)
-
- self.app.emit('html-page-context', pagename, templatename, ctx, event_arg)
-
- output = self.templates.render(templatename, ctx)
- if not outfilename:
- outfilename = path.join(self.outdir, os_path(pagename) + self.out_suffix)
- ensuredir(path.dirname(outfilename)) # normally different from self.outdir
- try:
- f = codecs.open(outfilename, 'w', 'utf-8')
- try:
- f.write(output)
- finally:
- f.close()
- except (IOError, OSError), err:
- self.warn("Error writing file %s: %s" % (outfilename, err))
- if self.copysource and ctx.get('sourcename'):
- # copy the source file for the "show source" link
- source_name = path.join(self.outdir, '_sources', os_path(ctx['sourcename']))
- ensuredir(path.dirname(source_name))
- shutil.copyfile(self.env.doc2path(pagename), source_name)
-
- def handle_finish(self):
- self.info(bold('dumping search index... '), nonl=True)
- self.indexer.prune(self.env.all_docs)
- f = open(path.join(self.outdir, self.searchindex_filename), 'wb')
- try:
- self.indexer.dump(f, self.indexer_format)
- finally:
- f.close()
- self.info('done')
-
- self.info(bold('dumping object inventory... '), nonl=True)
- f = open(path.join(self.outdir, INVENTORY_FILENAME), 'w')
- try:
- f.write('# Sphinx inventory version 1\n')
- f.write('# Project: %s\n' % self.config.project.encode('utf-8'))
- f.write('# Version: %s\n' % self.config.version)
- for modname, info in self.env.modules.iteritems():
- f.write('%s mod %s\n' % (modname, self.get_target_uri(info[0])))
- for refname, (docname, desctype) in self.env.descrefs.iteritems():
- f.write('%s %s %s\n' % (refname, desctype, self.get_target_uri(docname)))
- finally:
- f.close()
- self.info('done')
-
-
-class SerializingHTMLBuilder(StandaloneHTMLBuilder):
- """
- An abstract builder that serializes the HTML generated.
- """
- #: the serializing implementation to use. Set this to a module that
- #: implements a `dump`, `load`, `dumps` and `loads` functions
- #: (pickle, simplejson etc.)
- implementation = None
-
- #: the filename for the global context file
- globalcontext_filename = None
-
- supported_image_types = ('image/svg+xml', 'image/png', 'image/gif',
- 'image/jpeg')
-
- def init(self):
- self.init_translator_class()
- self.templates = None # no template bridge necessary
-
- def get_target_uri(self, docname, typ=None):
- if docname == 'index':
- return ''
- if docname.endswith(SEP + 'index'):
- return docname[:-5] # up to sep
- return docname + SEP
-
- def handle_page(self, pagename, ctx, templatename='page.html',
- outfilename=None, event_arg=None):
- ctx['current_page_name'] = pagename
- sidebarfile = self.config.html_sidebars.get(pagename)
- if sidebarfile:
- ctx['customsidebar'] = sidebarfile
-
- if not outfilename:
- outfilename = path.join(self.outdir, os_path(pagename) + self.out_suffix)
-
- self.app.emit('html-page-context', pagename, templatename, ctx, event_arg)
-
- ensuredir(path.dirname(outfilename))
- f = open(outfilename, 'wb')
- try:
- self.implementation.dump(ctx, f, 2)
- finally:
- f.close()
-
- # if there is a source file, copy the source file for the
- # "show source" link
- if ctx.get('sourcename'):
- source_name = path.join(self.outdir, '_sources',
- os_path(ctx['sourcename']))
- ensuredir(path.dirname(source_name))
- shutil.copyfile(self.env.doc2path(pagename), source_name)
-
- def handle_finish(self):
- # dump the global context
- outfilename = path.join(self.outdir, self.globalcontext_filename)
- f = open(outfilename, 'wb')
- try:
- self.implementation.dump(self.globalcontext, f, 2)
- finally:
- f.close()
-
- # super here to dump the search index
- StandaloneHTMLBuilder.handle_finish(self)
-
- # copy the environment file from the doctree dir to the output dir
- # as needed by the web app
- shutil.copyfile(path.join(self.doctreedir, ENV_PICKLE_FILENAME),
- path.join(self.outdir, ENV_PICKLE_FILENAME))
-
- # touch 'last build' file, used by the web application to determine
- # when to reload its environment and clear the cache
- open(path.join(self.outdir, LAST_BUILD_FILENAME), 'w').close()
-
-
-class PickleHTMLBuilder(SerializingHTMLBuilder):
- """
- A Builder that dumps the generated HTML into pickle files.
- """
- implementation = pickle
- indexer_format = pickle
- name = 'pickle'
- out_suffix = '.fpickle'
- globalcontext_filename = 'globalcontext.pickle'
- searchindex_filename = 'searchindex.pickle'
-
-
-class JSONHTMLBuilder(SerializingHTMLBuilder):
- """
- A builder that dumps the generated HTML into JSON files.
- """
- implementation = json
- indexer_format = json
- name = 'json'
- out_suffix = '.fjson'
- globalcontext_filename = 'globalcontext.json'
- searchindex_filename = 'searchindex.json'
-
- def init(self):
- if json is None:
- from sphinx.application import SphinxError
- raise SphinxError('The module simplejson (or json in Python >= 2.6) '
- 'is not available. The JSONHTMLBuilder builder '
- 'will not work.')
- SerializingHTMLBuilder.init(self)
-
-
-class HTMLHelpBuilder(StandaloneHTMLBuilder):
- """
- Builder that also outputs Windows HTML help project, contents and index files.
- Adapted from the original Doc/tools/prechm.py.
- """
- name = 'htmlhelp'
-
- # don't copy the reST source
- copysource = False
- supported_image_types = ['image/png', 'image/gif', 'image/jpeg']
-
- # don't add links
- add_header_links = False
- add_definition_links = False
-
- def init(self):
- StandaloneHTMLBuilder.init(self)
- # the output files for HTML help must be .html only
- self.out_suffix = '.html'
-
- def handle_finish(self):
- build_hhx(self, self.outdir, self.config.htmlhelp_basename)
-
-
-class LaTeXBuilder(Builder):
- """
- Builds LaTeX output to create PDF.
- """
- name = 'latex'
- supported_image_types = ['application/pdf', 'image/png', 'image/gif',
- 'image/jpeg']
-
- def init(self):
- self.docnames = []
- self.document_data = []
- texescape.init()
-
- def get_outdated_docs(self):
- return 'all documents' # for now
-
- def get_target_uri(self, docname, typ=None):
- if typ == 'token':
- # token references are always inside production lists and must be
- # replaced by \token{} in LaTeX
- return '@token'
- if docname not in self.docnames:
- raise NoUri
- else:
- return ''
-
- def init_document_data(self):
- preliminary_document_data = map(list, self.config.latex_documents)
- if not preliminary_document_data:
- self.warn('No "latex_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('"latex_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):
- # first, assemble the "appendix" docs that are in every PDF
- appendices = []
- for fname in self.config.latex_appendices:
- appendices.append(self.env.get_doctree(fname))
-
- docwriter = LaTeXWriter(self)
- docsettings = OptionParser(
- defaults=self.env.settings,
- components=(docwriter,)).get_default_values()
-
- self.init_document_data()
-
- for entry in self.document_data:
- docname, targetname, title, author, docclass = entry[:5]
- toctree_only = False
- if len(entry) > 5:
- toctree_only = entry[5]
- 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=(docclass == 'manual') and appendices or [])
- self.post_process_images(doctree)
- self.info("writing... ", nonl=1)
- doctree.settings = docsettings
- doctree.settings.author = author
- doctree.settings.title = title
- doctree.settings.docname = docname
- doctree.settings.docclass = docclass
- 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)
- def process_tree(docname, tree):
- tree = tree.deepcopy()
- for toctreenode in tree.traverse(addnodes.toctree):
- newnodes = []
- includefiles = map(str, toctreenode['includefiles'])
- for includefile in includefiles:
- try:
- self.info(darkgreen(includefile) + " ", nonl=1)
- subtree = process_tree(includefile,
- self.env.get_doctree(includefile))
- self.docnames.add(includefile)
- except Exception:
- self.warn('%s: toctree contains ref to nonexisting file %r' %
- (docname, includefile))
- else:
- sof = addnodes.start_of_file()
- sof.children = subtree.children
- newnodes.append(sof)
- toctreenode.parent.replace(toctreenode, newnodes)
- return tree
- tree = self.env.get_doctree(indexfile)
- if toctree_only:
- # extract toctree nodes from the tree and put them in a fresh document
- new_tree = new_document('<latex 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 = process_tree(indexfile, tree)
- largetree.extend(appendices)
- self.info()
- self.info("resolving references...")
- self.env.resolve_references(largetree, indexfile, self)
- # resolve :ref:s to distant tex files -- we can't add a cross-reference,
- # but append the document name
- 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)
- shutil.copyfile(path.join(self.srcdir, src),
- path.join(self.outdir, dest))
- self.info()
-
- # the logo is handled differently
- if self.config.latex_logo:
- logobase = path.basename(self.config.latex_logo)
- shutil.copyfile(path.join(self.confdir, self.config.latex_logo),
- path.join(self.outdir, logobase))
-
- self.info(bold('copying TeX support files... '), nonl=True)
- staticdirname = path.join(path.dirname(__file__), 'texinputs')
- for filename in os.listdir(staticdirname):
- if not filename.startswith('.'):
- shutil.copyfile(path.join(staticdirname, filename),
- path.join(self.outdir, filename))
- self.info('done')
-
-
-class ChangesBuilder(Builder):
- """
- Write a summary with all versionadded/changed directives.
- """
- name = 'changes'
-
- def init(self):
- self.init_templates()
-
- def get_outdated_docs(self):
- return self.outdir
-
- typemap = {
- 'versionadded': 'added',
- 'versionchanged': 'changed',
- 'deprecated': 'deprecated',
- }
-
- def write(self, *ignored):
- version = self.config.version
- libchanges = {}
- apichanges = []
- otherchanges = {}
- if version not in self.env.versionchanges:
- self.info(bold('no changes in this version.'))
- return
- self.info(bold('writing summary file...'))
- for type, docname, lineno, module, descname, content in \
- self.env.versionchanges[version]:
- ttext = self.typemap[type]
- context = content.replace('\n', ' ')
- if descname and docname.startswith('c-api'):
- if not descname:
- continue
- if context:
- entry = '<b>%s</b>: <i>%s:</i> %s' % (descname, ttext, context)
- else:
- entry = '<b>%s</b>: <i>%s</i>.' % (descname, ttext)
- apichanges.append((entry, docname, lineno))
- elif descname or module:
- if not module:
- module = _('Builtins')
- if not descname:
- descname = _('Module level')
- if context:
- entry = '<b>%s</b>: <i>%s:</i> %s' % (descname, ttext, context)
- else:
- entry = '<b>%s</b>: <i>%s</i>.' % (descname, ttext)
- libchanges.setdefault(module, []).append((entry, docname, lineno))
- else:
- if not context:
- continue
- entry = '<i>%s:</i> %s' % (ttext.capitalize(), context)
- title = self.env.titles[docname].astext()
- otherchanges.setdefault((docname, title), []).append(
- (entry, docname, lineno))
-
- ctx = {
- 'project': self.config.project,
- 'version': version,
- 'docstitle': self.config.html_title,
- 'shorttitle': self.config.html_short_title,
- 'libchanges': sorted(libchanges.iteritems()),
- 'apichanges': sorted(apichanges),
- 'otherchanges': sorted(otherchanges.iteritems()),
- 'show_sphinx': self.config.html_show_sphinx,
- }
- f = open(path.join(self.outdir, 'index.html'), 'w')
- try:
- f.write(self.templates.render('changes/frameset.html', ctx))
- finally:
- f.close()
- f = open(path.join(self.outdir, 'changes.html'), 'w')
- try:
- f.write(self.templates.render('changes/versionchanges.html', ctx))
- finally:
- f.close()
-
- hltext = ['.. versionadded:: %s' % version,
- '.. versionchanged:: %s' % version,
- '.. deprecated:: %s' % version]
-
- def hl(no, line):
- line = '<a name="L%s"> </a>' % no + escape(line)
- for x in hltext:
- if x in line:
- line = '<span class="hl">%s</span>' % line
- break
- return line
-
- self.info(bold('copying source files...'))
- for docname in self.env.all_docs:
- f = open(self.env.doc2path(docname))
- lines = f.readlines()
- targetfn = path.join(self.outdir, 'rst', os_path(docname)) + '.html'
- ensuredir(path.dirname(targetfn))
- f = codecs.open(targetfn, 'w', 'utf8')
- try:
- text = ''.join(hl(i+1, line) for (i, line) in enumerate(lines))
- ctx = {'filename': self.env.doc2path(docname, None), 'text': text}
- f.write(self.templates.render('changes/rstsource.html', ctx))
- finally:
- f.close()
- shutil.copyfile(path.join(path.dirname(__file__), 'static', 'default.css'),
- path.join(self.outdir, 'default.css'))
-
- def hl(self, text, version):
- text = escape(text)
- for directive in ['versionchanged', 'versionadded', 'deprecated']:
- text = text.replace('.. %s:: %s' % (directive, version),
- '<b>.. %s:: %s</b>' % (directive, version))
- return text
-
- def finish(self):
- pass
-
-
-class TextBuilder(Builder):
- name = 'text'
- out_suffix = '.txt'
-
- def init(self):
- pass
-
- def get_outdated_docs(self):
- for docname in self.env.found_docs:
- if docname not in self.env.all_docs:
- yield docname
- continue
- targetname = self.env.doc2path(docname, self.outdir, self.out_suffix)
- try:
- targetmtime = path.getmtime(targetname)
- except Exception:
- targetmtime = 0
- try:
- srcmtime = path.getmtime(self.env.doc2path(docname))
- if srcmtime > targetmtime:
- yield docname
- except EnvironmentError:
- # source doesn't exist anymore
- pass
-
- def get_target_uri(self, docname, typ=None):
- return ''
-
- def prepare_writing(self, docnames):
- self.writer = TextWriter(self)
-
- def write_doc(self, docname, doctree):
- destination = StringOutput(encoding='utf-8')
- self.writer.write(doctree, destination)
- outfilename = path.join(self.outdir, os_path(docname) + self.out_suffix)
- ensuredir(path.dirname(outfilename)) # normally different from self.outdir
- try:
- f = codecs.open(outfilename, 'w', 'utf-8')
- try:
- f.write(self.writer.output)
- finally:
- f.close()
- except (IOError, OSError), err:
- self.warn("Error writing file %s: %s" % (outfilename, err))
-
- def finish(self):
- pass
-
-
-# compatibility alias
-WebHTMLBuilder = PickleHTMLBuilder
+ :copyright: 2008 by Georg Brandl.
+ :license: BSD, see LICENSE for details.
+"""
+import warnings
-from sphinx.linkcheck import CheckExternalLinksBuilder
+from sphinx.builders import Builder
+from sphinx.builders.text import TextBuilder
+from sphinx.builders.html import StandaloneHTMLBuilder, WebHTMLBuilder, \
+ PickleHTMLBuilder, JSONHTMLBuilder
+from sphinx.builders.latex import LaTeXBuilder
+from sphinx.builders.changes import ChangesBuilder
+from sphinx.builders.htmlhelp import HTMLHelpBuilder
+from sphinx.builders.linkcheck import CheckExternalLinksBuilder
-builtin_builders = {
- 'html': StandaloneHTMLBuilder,
- 'pickle': PickleHTMLBuilder,
- 'json': JSONHTMLBuilder,
- 'web': PickleHTMLBuilder,
- 'htmlhelp': HTMLHelpBuilder,
- 'latex': LaTeXBuilder,
- 'text': TextBuilder,
- 'changes': ChangesBuilder,
- 'linkcheck': CheckExternalLinksBuilder,
-}
+warnings.warn('The sphinx.builder module is deprecated; please import '
+ 'builders from the respective sphinx.builders submodules.',
+ DeprecationWarning, stacklevel=2)
diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py
new file mode 100644
index 00000000..c9286a27
--- /dev/null
+++ b/sphinx/builders/__init__.py
@@ -0,0 +1,345 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.builders
+ ~~~~~~~~~~~~~~~
+
+ Builder superclass for all builders.
+
+ :copyright: 2007-2008 by Georg Brandl, Sebastian Wiesner, Horst Gutmann.
+ :license: BSD, see LICENSE for details.
+"""
+
+import os
+import gettext
+from os import path
+
+from docutils import nodes
+
+from sphinx import package_dir, locale
+from sphinx.util import SEP, relative_uri
+from sphinx.environment import BuildEnvironment
+from sphinx.util.console import bold, purple, darkgreen
+
+# side effect: registers roles and directives
+from sphinx import roles
+from sphinx import directives
+
+
+ENV_PICKLE_FILENAME = 'environment.pickle'
+
+
+class Builder(object):
+ """
+ Builds target formats from the reST sources.
+ """
+
+ # builder's name, for the -b command line options
+ name = ''
+
+ def __init__(self, app, env=None, freshenv=False):
+ self.srcdir = app.srcdir
+ self.confdir = app.confdir
+ self.outdir = app.outdir
+ self.doctreedir = app.doctreedir
+ if not path.isdir(self.doctreedir):
+ os.makedirs(self.doctreedir)
+
+ self.app = app
+ self.warn = app.warn
+ self.info = app.info
+ self.config = app.config
+
+ self.load_i18n()
+
+ # images that need to be copied over (source -> dest)
+ self.images = {}
+
+ # if None, this is set in load_env()
+ self.env = env
+ self.freshenv = freshenv
+
+ self.init()
+ self.load_env()
+
+ # helper methods
+
+ def init(self):
+ """
+ Load necessary templates and perform initialization. The default
+ implementation does nothing.
+ """
+ pass
+
+ def init_templates(self):
+ """
+ Initialize the template system.
+
+ Call this method from init() if you need templates in your builder.
+ """
+ if self.config.template_bridge:
+ self.templates = self.app.import_object(
+ self.config.template_bridge, 'template_bridge setting')()
+ else:
+ from sphinx._jinja import BuiltinTemplates
+ self.templates = BuiltinTemplates()
+ self.templates.init(self)
+
+ def get_target_uri(self, docname, typ=None):
+ """
+ Return the target URI for a document name (*typ* can be used to qualify
+ the link characteristic for individual builders).
+ """
+ raise NotImplementedError
+
+ def get_relative_uri(self, from_, to, typ=None):
+ """
+ Return a relative URI between two source filenames. May raise environment.NoUri
+ if there's no way to return a sensible URI.
+ """
+ return relative_uri(self.get_target_uri(from_),
+ self.get_target_uri(to, typ))
+
+ def get_outdated_docs(self):
+ """
+ Return an iterable of output files that are outdated, or a string describing
+ what an update build will build.
+
+ If the builder does not output individual files corresponding to source files,
+ return a string here. If it does, return an iterable of those files that need
+ to be written.
+ """
+ raise NotImplementedError
+
+ def status_iterator(self, iterable, summary, colorfunc=darkgreen):
+ l = -1
+ for item in iterable:
+ if l == -1:
+ self.info(bold(summary), nonl=1)
+ l = 0
+ self.info(colorfunc(item) + ' ', nonl=1)
+ yield item
+ if l == 0:
+ self.info()
+
+ supported_image_types = []
+
+ def post_process_images(self, doctree):
+ """
+ Pick the best candidate for all image URIs.
+ """
+ for node in doctree.traverse(nodes.image):
+ if '?' in node['candidates']:
+ # don't rewrite nonlocal image URIs
+ continue
+ if '*' not in node['candidates']:
+ for imgtype in self.supported_image_types:
+ candidate = node['candidates'].get(imgtype, None)
+ if candidate:
+ break
+ else:
+ self.warn('%s:%s: no matching candidate for image URI %r' %
+ (node.source, getattr(node, 'lineno', ''), node['uri']))
+ continue
+ node['uri'] = candidate
+ else:
+ candidate = node['uri']
+ if candidate not in self.env.images:
+ # non-existing URI; let it alone
+ continue
+ self.images[candidate] = self.env.images[candidate][1]
+
+ # build methods
+
+ def load_i18n(self):
+ """
+ Load translated strings from the configured localedirs if
+ enabled in the configuration.
+ """
+ self.translator = None
+ if self.config.language is not None:
+ self.info(bold('loading translations [%s]... ' % self.config.language),
+ nonl=True)
+ locale_dirs = [path.join(package_dir, 'locale')] + \
+ [path.join(self.srcdir, x) for x in self.config.locale_dirs]
+ for dir_ in locale_dirs:
+ try:
+ trans = gettext.translation('sphinx', localedir=dir_,
+ languages=[self.config.language])
+ if self.translator is None:
+ self.translator = trans
+ else:
+ self.translator._catalog.update(trans.catalog)
+ except Exception:
+ # Language couldn't be found in the specified path
+ pass
+ if self.translator is not None:
+ self.info('done')
+ else:
+ self.info('locale not available')
+ if self.translator is None:
+ self.translator = gettext.NullTranslations()
+ self.translator.install(unicode=True)
+ locale.init() # translate common labels
+
+ def load_env(self):
+ """Set up the build environment."""
+ if self.env:
+ return
+ if not self.freshenv:
+ try:
+ self.info(bold('loading pickled environment... '), nonl=True)
+ self.env = BuildEnvironment.frompickle(self.config,
+ path.join(self.doctreedir, ENV_PICKLE_FILENAME))
+ self.info('done')
+ except Exception, err:
+ if type(err) is IOError and err.errno == 2:
+ self.info('not found')
+ else:
+ self.info('failed: %s' % err)
+ self.env = BuildEnvironment(self.srcdir, self.doctreedir, self.config)
+ self.env.find_files(self.config)
+ else:
+ self.env = BuildEnvironment(self.srcdir, self.doctreedir, self.config)
+ self.env.find_files(self.config)
+ self.env.set_warnfunc(self.warn)
+
+ def build_all(self):
+ """Build all source files."""
+ self.build(None, summary='all source files', method='all')
+
+ def build_specific(self, filenames):
+ """Only rebuild as much as needed for changes in the *filenames*."""
+ # bring the filenames to the canonical format, that is,
+ # relative to the source directory and without source_suffix.
+ dirlen = len(self.srcdir) + 1
+ to_write = []
+ suffix = self.config.source_suffix
+ for filename in filenames:
+ filename = path.abspath(filename)[dirlen:]
+ if filename.endswith(suffix):
+ filename = filename[:-len(suffix)]
+ filename = filename.replace(os.path.sep, SEP)
+ to_write.append(filename)
+ self.build(to_write, method='specific',
+ summary='%d source files given on command '
+ 'line' % len(to_write))
+
+ def build_update(self):
+ """Only rebuild what was changed or added since last build."""
+ to_build = self.get_outdated_docs()
+ if isinstance(to_build, str):
+ self.build(['__all__'], to_build)
+ else:
+ to_build = list(to_build)
+ self.build(to_build,
+ summary='targets for %d source files that are '
+ 'out of date' % len(to_build))
+
+ def build(self, docnames, summary=None, method='update'):
+ """
+ Main build method. First updates the environment, and then calls :meth:`write`.
+ """
+ if summary:
+ self.info(bold('building [%s]: ' % self.name), nonl=1)
+ self.info(summary)
+
+ updated_docnames = []
+ # while reading, collect all warnings from docutils
+ warnings = []
+ self.env.set_warnfunc(warnings.append)
+ self.info(bold('updating environment: '), nonl=1)
+ iterator = self.env.update(self.config, self.srcdir, self.doctreedir, self.app)
+ # the first item in the iterator is a summary message
+ self.info(iterator.next())
+ for docname in self.status_iterator(iterator, 'reading sources... ', purple):
+ updated_docnames.append(docname)
+ # nothing further to do, the environment has already done the reading
+ for warning in warnings:
+ if warning.strip():
+ self.warn(warning)
+ self.env.set_warnfunc(self.warn)
+
+ if updated_docnames:
+ # save the environment
+ self.info(bold('pickling environment... '), nonl=True)
+ self.env.topickle(path.join(self.doctreedir, ENV_PICKLE_FILENAME))
+ self.info('done')
+
+ # global actions
+ self.info(bold('checking consistency... '), nonl=True)
+ self.env.check_consistency()
+ self.info('done')
+ else:
+ if method == 'update' and not docnames:
+ self.info(bold('no targets are out of date.'))
+ return
+
+ # another indirection to support builders that don't build files individually
+ self.write(docnames, updated_docnames, method)
+
+ # finish (write static files etc.)
+ self.finish()
+ status = self.app.statuscode == 0 and 'succeeded' or 'finished with problems'
+ if self.app._warncount:
+ self.info(bold('build %s, %s warning%s.' %
+ (status, self.app._warncount,
+ self.app._warncount != 1 and 's' or '')))
+ else:
+ self.info(bold('build %s.' % status))
+
+ def write(self, build_docnames, updated_docnames, method='update'):
+ if build_docnames is None or build_docnames == ['__all__']:
+ # build_all
+ build_docnames = self.env.found_docs
+ if method == 'update':
+ # build updated ones as well
+ docnames = set(build_docnames) | set(updated_docnames)
+ else:
+ docnames = set(build_docnames)
+
+ # 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)
+ docnames.add(self.config.master_doc)
+
+ self.info(bold('preparing documents... '), nonl=True)
+ self.prepare_writing(docnames)
+ self.info('done')
+
+ # write target files
+ warnings = []
+ self.env.set_warnfunc(warnings.append)
+ for docname in self.status_iterator(sorted(docnames),
+ 'writing output... ', darkgreen):
+ doctree = self.env.get_and_resolve_doctree(docname, self)
+ self.write_doc(docname, doctree)
+ for warning in warnings:
+ if warning.strip():
+ self.warn(warning)
+ self.env.set_warnfunc(self.warn)
+
+ def prepare_writing(self, docnames):
+ raise NotImplementedError
+
+ def write_doc(self, docname, doctree):
+ raise NotImplementedError
+
+ def finish(self):
+ """
+ Finish the building process. The default implementation does nothing.
+ """
+ pass
+
+
+BUILTIN_BUILDERS = {
+ 'html': ('html', 'StandaloneHTMLBuilder'),
+ 'pickle': ('html', 'PickleHTMLBuilder'),
+ 'json': ('html', 'JSONHTMLBuilder'),
+ 'web': ('html', 'PickleHTMLBuilder'),
+ 'htmlhelp': ('htmlhelp', 'HTMLHelpBuilder'),
+ 'latex': ('latex', 'LaTeXBuilder'),
+ 'text': ('text', 'TextBuilder'),
+ 'changes': ('changes', 'ChangesBuilder'),
+ 'linkcheck': ('linkcheck', 'CheckExternalLinksBuilder'),
+}
diff --git a/sphinx/builders/changes.py b/sphinx/builders/changes.py
new file mode 100644
index 00000000..8665b6e7
--- /dev/null
+++ b/sphinx/builders/changes.py
@@ -0,0 +1,137 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.builders.changes
+ ~~~~~~~~~~~~~~~~~~~~~~~
+
+ Changelog builder.
+
+ :copyright: 2008 by Georg Brandl.
+ :license: BSD, see LICENSE for details.
+"""
+
+import codecs
+import shutil
+from os import path
+from cgi import escape
+
+from sphinx import package_dir
+from sphinx.util import ensuredir, os_path
+from sphinx.builders import Builder
+from sphinx.util.console import bold
+
+
+class ChangesBuilder(Builder):
+ """
+ Write a summary with all versionadded/changed directives.
+ """
+ name = 'changes'
+
+ def init(self):
+ self.init_templates()
+
+ def get_outdated_docs(self):
+ return self.outdir
+
+ typemap = {
+ 'versionadded': 'added',
+ 'versionchanged': 'changed',
+ 'deprecated': 'deprecated',
+ }
+
+ def write(self, *ignored):
+ version = self.config.version
+ libchanges = {}
+ apichanges = []
+ otherchanges = {}
+ if version not in self.env.versionchanges:
+ self.info(bold('no changes in this version.'))
+ return
+ self.info(bold('writing summary file...'))
+ for type, docname, lineno, module, descname, content in \
+ self.env.versionchanges[version]:
+ ttext = self.typemap[type]
+ context = content.replace('\n', ' ')
+ if descname and docname.startswith('c-api'):
+ if not descname:
+ continue
+ if context:
+ entry = '<b>%s</b>: <i>%s:</i> %s' % (descname, ttext, context)
+ else:
+ entry = '<b>%s</b>: <i>%s</i>.' % (descname, ttext)
+ apichanges.append((entry, docname, lineno))
+ elif descname or module:
+ if not module:
+ module = _('Builtins')
+ if not descname:
+ descname = _('Module level')
+ if context:
+ entry = '<b>%s</b>: <i>%s:</i> %s' % (descname, ttext, context)
+ else:
+ entry = '<b>%s</b>: <i>%s</i>.' % (descname, ttext)
+ libchanges.setdefault(module, []).append((entry, docname, lineno))
+ else:
+ if not context:
+ continue
+ entry = '<i>%s:</i> %s' % (ttext.capitalize(), context)
+ title = self.env.titles[docname].astext()
+ otherchanges.setdefault((docname, title), []).append(
+ (entry, docname, lineno))
+
+ ctx = {
+ 'project': self.config.project,
+ 'version': version,
+ 'docstitle': self.config.html_title,
+ 'shorttitle': self.config.html_short_title,
+ 'libchanges': sorted(libchanges.iteritems()),
+ 'apichanges': sorted(apichanges),
+ 'otherchanges': sorted(otherchanges.iteritems()),
+ 'show_sphinx': self.config.html_show_sphinx,
+ }
+ f = open(path.join(self.outdir, 'index.html'), 'w')
+ try:
+ f.write(self.templates.render('changes/frameset.html', ctx))
+ finally:
+ f.close()
+ f = open(path.join(self.outdir, 'changes.html'), 'w')
+ try:
+ f.write(self.templates.render('changes/versionchanges.html', ctx))
+ finally:
+ f.close()
+
+ hltext = ['.. versionadded:: %s' % version,
+ '.. versionchanged:: %s' % version,
+ '.. deprecated:: %s' % version]
+
+ def hl(no, line):
+ line = '<a name="L%s"> </a>' % no + escape(line)
+ for x in hltext:
+ if x in line:
+ line = '<span class="hl">%s</span>' % line
+ break
+ return line
+
+ self.info(bold('copying source files...'))
+ for docname in self.env.all_docs:
+ f = open(self.env.doc2path(docname))
+ lines = f.readlines()
+ targetfn = path.join(self.outdir, 'rst', os_path(docname)) + '.html'
+ ensuredir(path.dirname(targetfn))
+ f = codecs.open(targetfn, 'w', 'utf8')
+ try:
+ text = ''.join(hl(i+1, line) for (i, line) in enumerate(lines))
+ ctx = {'filename': self.env.doc2path(docname, None), 'text': text}
+ f.write(self.templates.render('changes/rstsource.html', ctx))
+ finally:
+ f.close()
+ shutil.copyfile(path.join(package_dir, 'static', 'default.css'),
+ path.join(self.outdir, 'default.css'))
+
+ def hl(self, text, version):
+ text = escape(text)
+ for directive in ['versionchanged', 'versionadded', 'deprecated']:
+ text = text.replace('.. %s:: %s' % (directive, version),
+ '<b>.. %s:: %s</b>' % (directive, version))
+ return text
+
+ def finish(self):
+ pass
diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py
new file mode 100644
index 00000000..86d3f0b7
--- /dev/null
+++ b/sphinx/builders/html.py
@@ -0,0 +1,625 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.builders.html
+ ~~~~~~~~~~~~~~~~~~~~
+
+ Several HTML builders.
+
+ :copyright: 2007-2008 by Georg Brandl, Armin Ronacher.
+ :license: BSD, see LICENSE for details.
+"""
+
+import os
+import codecs
+import shutil
+import cPickle as pickle
+from os import path
+
+from docutils.io import DocTreeInput, StringOutput
+from docutils.core import publish_parts
+from docutils.utils import new_document
+from docutils.frontend import OptionParser
+from docutils.readers.doctree import Reader as DoctreeReader
+
+from sphinx import package_dir, __version__
+from sphinx.util import SEP, os_path, relative_uri, ensuredir, ustrftime
+from sphinx.search import js_index
+from sphinx.builders import Builder, ENV_PICKLE_FILENAME
+from sphinx.highlighting import PygmentsBridge
+from sphinx.util.console import bold
+from sphinx.writers.html import HTMLWriter, HTMLTranslator, SmartyPantsHTMLTranslator
+
+try:
+ import json
+except ImportError:
+ try:
+ import simplejson as json
+ except ImportError:
+ json = None
+
+
+INVENTORY_FILENAME = 'objects.inv'
+LAST_BUILD_FILENAME = 'last_build'
+
+
+class StandaloneHTMLBuilder(Builder):
+ """
+ Builds standalone HTML docs.
+ """
+ name = 'html'
+ copysource = True
+ out_suffix = '.html'
+ link_suffix = '.html' # defaults to matching out_suffix
+ indexer_format = js_index
+ supported_image_types = ['image/svg+xml', 'image/png', 'image/gif',
+ 'image/jpeg']
+ searchindex_filename = 'searchindex.js'
+ add_permalinks = True
+
+ # This is a class attribute because it is mutated by Sphinx.add_javascript.
+ script_files = ['_static/jquery.js', '_static/doctools.js']
+
+ def init(self):
+ """Load templates."""
+ self.init_templates()
+ self.init_translator_class()
+ if self.config.html_file_suffix:
+ self.out_suffix = self.config.html_file_suffix
+
+ if self.config.html_link_suffix is not None:
+ self.link_suffix = self.config.html_link_suffix
+ else:
+ self.link_suffix = self.out_suffix
+
+ if self.config.language is not None:
+ jsfile = path.join(package_dir, 'locale', self.config.language,
+ 'LC_MESSAGES', 'sphinx.js')
+ if path.isfile(jsfile):
+ self.script_files.append('_static/translations.js')
+
+ def init_translator_class(self):
+ if self.config.html_translator_class:
+ self.translator_class = self.app.import_object(
+ self.config.html_translator_class, 'html_translator_class setting')
+ elif self.config.html_use_smartypants:
+ self.translator_class = SmartyPantsHTMLTranslator
+ else:
+ self.translator_class = HTMLTranslator
+
+ def render_partial(self, node):
+ """Utility: Render a lone doctree node."""
+ doc = new_document('<partial node>')
+ doc.append(node)
+ return publish_parts(
+ doc,
+ source_class=DocTreeInput,
+ reader=DoctreeReader(),
+ writer=HTMLWriter(self),
+ settings_overrides={'output_encoding': 'unicode'}
+ )
+
+ def prepare_writing(self, docnames):
+ from sphinx.search import IndexBuilder
+
+ self.indexer = IndexBuilder(self.env)
+ self.load_indexer(docnames)
+ self.docwriter = HTMLWriter(self)
+ self.docsettings = OptionParser(
+ defaults=self.env.settings,
+ components=(self.docwriter,)).get_default_values()
+
+ # format the "last updated on" string, only once is enough since it
+ # typically doesn't include the time of day
+ lufmt = self.config.html_last_updated_fmt
+ if lufmt is not None:
+ self.last_updated = ustrftime(lufmt or _('%b %d, %Y'))
+ else:
+ self.last_updated = None
+
+ logo = self.config.html_logo and \
+ path.basename(self.config.html_logo) or ''
+
+ favicon = self.config.html_favicon and \
+ path.basename(self.config.html_favicon) or ''
+ if favicon and os.path.splitext(favicon)[1] != '.ico':
+ self.warn('html_favicon is not an .ico file')
+
+ if not isinstance(self.config.html_use_opensearch, basestring):
+ self.warn('html_use_opensearch config value must now be a string')
+
+ self.relations = self.env.collect_relations()
+
+ rellinks = []
+ if self.config.html_use_index:
+ rellinks.append(('genindex', _('General Index'), 'I', _('index')))
+ if self.config.html_use_modindex and self.env.modules:
+ rellinks.append(('modindex', _('Global Module Index'), 'M', _('modules')))
+
+ self.globalcontext = dict(
+ project = self.config.project,
+ release = self.config.release,
+ version = self.config.version,
+ last_updated = self.last_updated,
+ copyright = self.config.copyright,
+ master_doc = self.config.master_doc,
+ style = self.config.html_style,
+ use_opensearch = self.config.html_use_opensearch,
+ docstitle = self.config.html_title,
+ shorttitle = self.config.html_short_title,
+ show_sphinx = self.config.html_show_sphinx,
+ has_source = self.config.html_copy_source,
+ show_source = self.config.html_show_sourcelink,
+ file_suffix = self.out_suffix,
+ script_files = self.script_files,
+ sphinx_version = __version__,
+ rellinks = rellinks,
+ builder = self.name,
+ parents = [],
+ logo = logo,
+ favicon = favicon,
+ )
+ self.globalcontext.update(self.config.html_context)
+
+ def get_doc_context(self, docname, body, metatags):
+ """Collect items for the template context of a page."""
+ # find out relations
+ prev = next = None
+ parents = []
+ rellinks = self.globalcontext['rellinks'][:]
+ related = self.relations.get(docname)
+ titles = self.env.titles
+ if related and related[2]:
+ try:
+ next = {'link': self.get_relative_uri(docname, related[2]),
+ 'title': self.render_partial(titles[related[2]])['title']}
+ rellinks.append((related[2], next['title'], 'N', _('next')))
+ except KeyError:
+ next = None
+ if related and related[1]:
+ try:
+ prev = {'link': self.get_relative_uri(docname, related[1]),
+ 'title': self.render_partial(titles[related[1]])['title']}
+ rellinks.append((related[1], prev['title'], 'P', _('previous')))
+ except KeyError:
+ # the relation is (somehow) not in the TOC tree, handle that gracefully
+ prev = None
+ while related and related[0]:
+ try:
+ parents.append(
+ {'link': self.get_relative_uri(docname, related[0]),
+ 'title': self.render_partial(titles[related[0]])['title']})
+ except KeyError:
+ pass
+ related = self.relations.get(related[0])
+ if parents:
+ parents.pop() # remove link to the master file; we have a generic
+ # "back to index" link already
+ parents.reverse()
+
+ # title rendered as HTML
+ title = titles.get(docname)
+ title = title and self.render_partial(title)['title'] or ''
+ # the name for the copied source
+ sourcename = self.config.html_copy_source and docname + '.txt' or ''
+
+ # metadata for the document
+ meta = self.env.metadata.get(docname)
+
+ return dict(
+ parents = parents,
+ prev = prev,
+ next = next,
+ title = title,
+ meta = meta,
+ body = body,
+ metatags = metatags,
+ rellinks = rellinks,
+ sourcename = sourcename,
+ toc = self.render_partial(self.env.get_toc_for(docname))['fragment'],
+ # only display a TOC if there's more than one item to show
+ display_toc = (self.env.toc_num_entries[docname] > 1),
+ )
+
+ def write_doc(self, docname, doctree):
+ self.post_process_images(doctree)
+ destination = StringOutput(encoding='utf-8')
+ doctree.settings = self.docsettings
+
+ self.imgpath = relative_uri(self.get_target_uri(docname), '_images')
+ self.dlpath = relative_uri(self.get_target_uri(docname), '_downloads')
+ self.docwriter.write(doctree, destination)
+ self.docwriter.assemble_parts()
+ body = self.docwriter.parts['fragment']
+ metatags = self.docwriter.clean_meta
+
+ ctx = self.get_doc_context(docname, body, metatags)
+ self.index_page(docname, doctree, ctx.get('title', ''))
+ self.handle_page(docname, ctx, event_arg=doctree)
+
+ def finish(self):
+ self.info(bold('writing additional files...'), nonl=1)
+
+ # the global general index
+
+ if self.config.html_use_index:
+ # the total count of lines for each index letter, used to distribute
+ # the entries into two columns
+ genindex = self.env.create_index(self)
+ indexcounts = []
+ for _, entries in genindex:
+ indexcounts.append(sum(1 + len(subitems)
+ for _, (_, subitems) in entries))
+
+ genindexcontext = dict(
+ genindexentries = genindex,
+ genindexcounts = indexcounts,
+ split_index = self.config.html_split_index,
+ )
+ self.info(' genindex', nonl=1)
+
+ if self.config.html_split_index:
+ self.handle_page('genindex', genindexcontext, 'genindex-split.html')
+ self.handle_page('genindex-all', genindexcontext, 'genindex.html')
+ for (key, entries), count in zip(genindex, indexcounts):
+ ctx = {'key': key, 'entries': entries, 'count': count,
+ 'genindexentries': genindex}
+ self.handle_page('genindex-' + key, ctx, 'genindex-single.html')
+ else:
+ self.handle_page('genindex', genindexcontext, 'genindex.html')
+
+ # the global module index
+
+ if self.config.html_use_modindex and self.env.modules:
+ # the sorted list of all modules, for the global module index
+ modules = sorted(((mn, (self.get_relative_uri('modindex', fn) +
+ '#module-' + mn, sy, pl, dep))
+ for (mn, (fn, sy, pl, dep)) in
+ self.env.modules.iteritems()),
+ key=lambda x: x[0].lower())
+ # collect all platforms
+ platforms = set()
+ # sort out collapsable modules
+ modindexentries = []
+ letters = []
+ pmn = ''
+ num_toplevels = 0
+ num_collapsables = 0
+ cg = 0 # collapse group
+ fl = '' # first letter
+ for mn, (fn, sy, pl, dep) in modules:
+ pl = pl and pl.split(', ') or []
+ platforms.update(pl)
+ if fl != mn[0].lower() and mn[0] != '_':
+ # heading
+ modindexentries.append(['', False, 0, False,
+ mn[0].upper(), '', [], False])
+ letters.append(mn[0].upper())
+ tn = mn.split('.')[0]
+ if tn != mn:
+ # submodule
+ if pmn == tn:
+ # first submodule - make parent collapsable
+ modindexentries[-1][1] = True
+ num_collapsables += 1
+ elif not pmn.startswith(tn):
+ # submodule without parent in list, add dummy entry
+ cg += 1
+ modindexentries.append([tn, True, cg, False, '', '', [], False])
+ else:
+ num_toplevels += 1
+ cg += 1
+ modindexentries.append([mn, False, cg, (tn != mn), fn, sy, pl, dep])
+ pmn = mn
+ fl = mn[0].lower()
+ platforms = sorted(platforms)
+
+ # apply heuristics when to collapse modindex at page load:
+ # only collapse if number of toplevel modules is larger than
+ # number of submodules
+ collapse = len(modules) - num_toplevels < num_toplevels
+
+ modindexcontext = dict(
+ modindexentries = modindexentries,
+ platforms = platforms,
+ letters = letters,
+ collapse_modindex = collapse,
+ )
+ self.info(' modindex', nonl=1)
+ self.handle_page('modindex', modindexcontext, 'modindex.html')
+
+ # the search page
+ if self.name != 'htmlhelp':
+ self.info(' search', nonl=1)
+ self.handle_page('search', {}, 'search.html')
+
+ # additional pages from conf.py
+ for pagename, template in self.config.html_additional_pages.items():
+ self.info(' '+pagename, nonl=1)
+ self.handle_page(pagename, {}, template)
+
+ if self.config.html_use_opensearch and self.name != 'htmlhelp':
+ self.info(' opensearch', nonl=1)
+ fn = path.join(self.outdir, '_static', 'opensearch.xml')
+ self.handle_page('opensearch', {}, 'opensearch.xml', outfilename=fn)
+
+ self.info()
+
+ # copy image files
+ if self.images:
+ self.info(bold('copying images...'), nonl=True)
+ ensuredir(path.join(self.outdir, '_images'))
+ for src, dest in self.images.iteritems():
+ self.info(' '+src, nonl=1)
+ shutil.copyfile(path.join(self.srcdir, src),
+ path.join(self.outdir, '_images', dest))
+ self.info()
+
+ # copy downloadable files
+ if self.env.dlfiles:
+ self.info(bold('copying downloadable files...'), nonl=True)
+ ensuredir(path.join(self.outdir, '_downloads'))
+ for src, (_, dest) in self.env.dlfiles.iteritems():
+ self.info(' '+src, nonl=1)
+ shutil.copyfile(path.join(self.srcdir, src),
+ path.join(self.outdir, '_downloads', dest))
+ self.info()
+
+ # copy static files
+ self.info(bold('copying static files... '), nonl=True)
+ ensuredir(path.join(self.outdir, '_static'))
+ # first, create pygments style file
+ f = open(path.join(self.outdir, '_static', 'pygments.css'), 'w')
+ f.write(PygmentsBridge('html', self.config.pygments_style).get_stylesheet())
+ f.close()
+ # then, copy translations JavaScript file
+ if self.config.language is not None:
+ jsfile = path.join(package_dir, 'locale', self.config.language,
+ 'LC_MESSAGES', 'sphinx.js')
+ if path.isfile(jsfile):
+ shutil.copyfile(jsfile, path.join(self.outdir, '_static',
+ 'translations.js'))
+ # then, copy over all user-supplied static files
+ staticdirnames = [path.join(package_dir, 'static')] + \
+ [path.join(self.confdir, spath)
+ for spath in self.config.html_static_path]
+ for staticdirname in staticdirnames:
+ for filename in os.listdir(staticdirname):
+ if filename.startswith('.'):
+ continue
+ fullname = path.join(staticdirname, filename)
+ targetname = path.join(self.outdir, '_static', filename)
+ if path.isfile(fullname):
+ shutil.copyfile(fullname, targetname)
+ elif path.isdir(fullname):
+ if filename in self.config.exclude_dirnames:
+ continue
+ if path.exists(targetname):
+ shutil.rmtree(targetname)
+ shutil.copytree(fullname, targetname)
+ # last, copy logo file (handled differently)
+ if self.config.html_logo:
+ logobase = path.basename(self.config.html_logo)
+ shutil.copyfile(path.join(self.confdir, self.config.html_logo),
+ path.join(self.outdir, '_static', logobase))
+ self.info('done')
+
+ # dump the search index
+ self.handle_finish()
+
+ def get_outdated_docs(self):
+ if self.templates:
+ template_mtime = self.templates.newest_template_mtime()
+ else:
+ template_mtime = 0
+ for docname in self.env.found_docs:
+ if docname not in self.env.all_docs:
+ yield docname
+ continue
+ targetname = self.env.doc2path(docname, self.outdir, self.out_suffix)
+ try:
+ targetmtime = path.getmtime(targetname)
+ except Exception:
+ targetmtime = 0
+ try:
+ srcmtime = max(path.getmtime(self.env.doc2path(docname)),
+ template_mtime)
+ if srcmtime > targetmtime:
+ yield docname
+ except EnvironmentError:
+ # source doesn't exist anymore
+ pass
+
+ def load_indexer(self, docnames):
+ keep = set(self.env.all_docs) - set(docnames)
+ try:
+ f = open(path.join(self.outdir, self.searchindex_filename), 'rb')
+ try:
+ self.indexer.load(f, self.indexer_format)
+ finally:
+ f.close()
+ except (IOError, OSError, ValueError):
+ if keep:
+ self.warn("search index couldn't be loaded, but not all documents "
+ "will be built: the index will be incomplete.")
+ # delete all entries for files that will be rebuilt
+ self.indexer.prune(keep)
+
+ def index_page(self, pagename, doctree, title):
+ # only index pages with title
+ if self.indexer is not None and title:
+ self.indexer.feed(pagename, title, doctree)
+
+ # --------- these are overwritten by the serialization builder
+
+ def get_target_uri(self, docname, typ=None):
+ return docname + self.link_suffix
+
+ def handle_page(self, pagename, addctx, templatename='page.html',
+ outfilename=None, event_arg=None):
+ ctx = self.globalcontext.copy()
+ # current_page_name is backwards compatibility
+ ctx['pagename'] = ctx['current_page_name'] = pagename
+
+ def pathto(otheruri, resource=False,
+ baseuri=self.get_target_uri(pagename)):
+ if not resource:
+ otheruri = self.get_target_uri(otheruri)
+ return relative_uri(baseuri, otheruri)
+ ctx['pathto'] = pathto
+ ctx['hasdoc'] = lambda name: name in self.env.all_docs
+ ctx['customsidebar'] = self.config.html_sidebars.get(pagename)
+ ctx.update(addctx)
+
+ self.app.emit('html-page-context', pagename, templatename, ctx, event_arg)
+
+ output = self.templates.render(templatename, ctx)
+ if not outfilename:
+ outfilename = path.join(self.outdir, os_path(pagename) + self.out_suffix)
+ ensuredir(path.dirname(outfilename)) # normally different from self.outdir
+ try:
+ f = codecs.open(outfilename, 'w', 'utf-8')
+ try:
+ f.write(output)
+ finally:
+ f.close()
+ except (IOError, OSError), err:
+ self.warn("Error writing file %s: %s" % (outfilename, err))
+ if self.copysource and ctx.get('sourcename'):
+ # copy the source file for the "show source" link
+ source_name = path.join(self.outdir, '_sources', os_path(ctx['sourcename']))
+ ensuredir(path.dirname(source_name))
+ shutil.copyfile(self.env.doc2path(pagename), source_name)
+
+ def handle_finish(self):
+ self.info(bold('dumping search index... '), nonl=True)
+ self.indexer.prune(self.env.all_docs)
+ f = open(path.join(self.outdir, self.searchindex_filename), 'wb')
+ try:
+ self.indexer.dump(f, self.indexer_format)
+ finally:
+ f.close()
+ self.info('done')
+
+ self.info(bold('dumping object inventory... '), nonl=True)
+ f = open(path.join(self.outdir, INVENTORY_FILENAME), 'w')
+ try:
+ f.write('# Sphinx inventory version 1\n')
+ f.write('# Project: %s\n' % self.config.project.encode('utf-8'))
+ f.write('# Version: %s\n' % self.config.version)
+ for modname, info in self.env.modules.iteritems():
+ f.write('%s mod %s\n' % (modname, self.get_target_uri(info[0])))
+ for refname, (docname, desctype) in self.env.descrefs.iteritems():
+ f.write('%s %s %s\n' % (refname, desctype, self.get_target_uri(docname)))
+ finally:
+ f.close()
+ self.info('done')
+
+
+class SerializingHTMLBuilder(StandaloneHTMLBuilder):
+ """
+ An abstract builder that serializes the HTML generated.
+ """
+ #: the serializing implementation to use. Set this to a module that
+ #: implements a `dump`, `load`, `dumps` and `loads` functions
+ #: (pickle, simplejson etc.)
+ implementation = None
+
+ #: the filename for the global context file
+ globalcontext_filename = None
+
+ supported_image_types = ('image/svg+xml', 'image/png', 'image/gif',
+ 'image/jpeg')
+
+ def init(self):
+ self.init_translator_class()
+ self.templates = None # no template bridge necessary
+
+ def get_target_uri(self, docname, typ=None):
+ if docname == 'index':
+ return ''
+ if docname.endswith(SEP + 'index'):
+ return docname[:-5] # up to sep
+ return docname + SEP
+
+ def handle_page(self, pagename, ctx, templatename='page.html',
+ outfilename=None, event_arg=None):
+ ctx['current_page_name'] = pagename
+ sidebarfile = self.config.html_sidebars.get(pagename)
+ if sidebarfile:
+ ctx['customsidebar'] = sidebarfile
+
+ if not outfilename:
+ outfilename = path.join(self.outdir, os_path(pagename) + self.out_suffix)
+
+ self.app.emit('html-page-context', pagename, templatename, ctx, event_arg)
+
+ ensuredir(path.dirname(outfilename))
+ f = open(outfilename, 'wb')
+ try:
+ self.implementation.dump(ctx, f, 2)
+ finally:
+ f.close()
+
+ # if there is a source file, copy the source file for the
+ # "show source" link
+ if ctx.get('sourcename'):
+ source_name = path.join(self.outdir, '_sources',
+ os_path(ctx['sourcename']))
+ ensuredir(path.dirname(source_name))
+ shutil.copyfile(self.env.doc2path(pagename), source_name)
+
+ def handle_finish(self):
+ # dump the global context
+ outfilename = path.join(self.outdir, self.globalcontext_filename)
+ f = open(outfilename, 'wb')
+ try:
+ self.implementation.dump(self.globalcontext, f, 2)
+ finally:
+ f.close()
+
+ # super here to dump the search index
+ StandaloneHTMLBuilder.handle_finish(self)
+
+ # copy the environment file from the doctree dir to the output dir
+ # as needed by the web app
+ shutil.copyfile(path.join(self.doctreedir, ENV_PICKLE_FILENAME),
+ path.join(self.outdir, ENV_PICKLE_FILENAME))
+
+ # touch 'last build' file, used by the web application to determine
+ # when to reload its environment and clear the cache
+ open(path.join(self.outdir, LAST_BUILD_FILENAME), 'w').close()
+
+
+class PickleHTMLBuilder(SerializingHTMLBuilder):
+ """
+ A Builder that dumps the generated HTML into pickle files.
+ """
+ implementation = pickle
+ indexer_format = pickle
+ name = 'pickle'
+ out_suffix = '.fpickle'
+ globalcontext_filename = 'globalcontext.pickle'
+ searchindex_filename = 'searchindex.pickle'
+
+# compatibility alias
+WebHTMLBuilder = PickleHTMLBuilder
+
+
+class JSONHTMLBuilder(SerializingHTMLBuilder):
+ """
+ A builder that dumps the generated HTML into JSON files.
+ """
+ implementation = json
+ indexer_format = json
+ name = 'json'
+ out_suffix = '.fjson'
+ globalcontext_filename = 'globalcontext.json'
+ searchindex_filename = 'searchindex.json'
+
+ def init(self):
+ if json is None:
+ from sphinx.application import SphinxError
+ raise SphinxError('The module simplejson (or json in Python >= 2.6) '
+ 'is not available. The JSONHTMLBuilder builder '
+ 'will not work.')
+ SerializingHTMLBuilder.init(self)
diff --git a/sphinx/builders/htmlhelp.py b/sphinx/builders/htmlhelp.py
new file mode 100644
index 00000000..68d3de28
--- /dev/null
+++ b/sphinx/builders/htmlhelp.py
@@ -0,0 +1,244 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.builders.htmlhelp
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Build HTML help support files.
+ Parts adapted from Python's Doc/tools/prechm.py.
+
+ :copyright: 2007-2008 by Georg Brandl.
+ :license: BSD, see LICENSE for details.
+"""
+
+import os
+import cgi
+from os import path
+
+from docutils import nodes
+
+from sphinx import addnodes
+from sphinx.builders.html import StandaloneHTMLBuilder
+
+
+# Project file (*.hhp) template. 'outname' is the file basename (like
+# the pythlp in pythlp.hhp); 'version' is the doc version number (like
+# the 2.2 in Python 2.2).
+# The magical numbers in the long line under [WINDOWS] set most of the
+# user-visible features (visible buttons, tabs, etc).
+# About 0x10384e: This defines the buttons in the help viewer. The
+# following defns are taken from htmlhelp.h. Not all possibilities
+# actually work, and not all those that work are available from the Help
+# Workshop GUI. In particular, the Zoom/Font button works and is not
+# available from the GUI. The ones we're using are marked with 'x':
+#
+# 0x000002 Hide/Show x
+# 0x000004 Back x
+# 0x000008 Forward x
+# 0x000010 Stop
+# 0x000020 Refresh
+# 0x000040 Home x
+# 0x000080 Forward
+# 0x000100 Back
+# 0x000200 Notes
+# 0x000400 Contents
+# 0x000800 Locate x
+# 0x001000 Options x
+# 0x002000 Print x
+# 0x004000 Index
+# 0x008000 Search
+# 0x010000 History
+# 0x020000 Favorites
+# 0x040000 Jump 1
+# 0x080000 Jump 2
+# 0x100000 Zoom/Font x
+# 0x200000 TOC Next
+# 0x400000 TOC Prev
+
+project_template = '''\
+[OPTIONS]
+Binary TOC=Yes
+Binary Index=No
+Compiled file=%(outname)s.chm
+Contents file=%(outname)s.hhc
+Default Window=%(outname)s
+Default topic=index.html
+Display compile progress=No
+Full text search stop list file=%(outname)s.stp
+Full-text search=Yes
+Index file=%(outname)s.hhk
+Language=0x409
+Title=%(title)s
+
+[WINDOWS]
+%(outname)s="%(title)s","%(outname)s.hhc","%(outname)s.hhk",\
+"index.html","index.html",,,,,0x63520,220,0x10384e,[0,0,1024,768],,,,,,,0
+
+[FILES]
+'''
+
+contents_header = '''\
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<HTML>
+<HEAD>
+<meta name="GENERATOR" content="Microsoft&reg; HTML Help Workshop 4.1">
+<!-- Sitemap 1.0 -->
+</HEAD><BODY>
+<OBJECT type="text/site properties">
+ <param name="Window Styles" value="0x801227">
+ <param name="ImageType" value="Folder">
+</OBJECT>
+<UL>
+'''
+
+contents_footer = '''\
+</UL></BODY></HTML>
+'''
+
+object_sitemap = '''\
+<OBJECT type="text/sitemap">
+ <param name="Name" value="%s">
+ <param name="Local" value="%s">
+</OBJECT>
+'''
+
+# List of words the full text search facility shouldn't index. This
+# becomes file outname.stp. Note that this list must be pretty small!
+# Different versions of the MS docs claim the file has a maximum size of
+# 256 or 512 bytes (including \r\n at the end of each line).
+# Note that "and", "or", "not" and "near" are operators in the search
+# language, so no point indexing them even if we wanted to.
+stopwords = """
+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 HTMLHelpBuilder(StandaloneHTMLBuilder):
+ """
+ Builder that also outputs Windows HTML help project, contents and index files.
+ Adapted from the original Doc/tools/prechm.py.
+ """
+ name = 'htmlhelp'
+
+ # don't copy the reST source
+ copysource = False
+ supported_image_types = ['image/png', 'image/gif', 'image/jpeg']
+
+ # don't add links
+ add_permalinks = False
+
+ def init(self):
+ StandaloneHTMLBuilder.init(self)
+ # the output files for HTML help must be .html only
+ self.out_suffix = '.html'
+
+ def handle_finish(self):
+ self.build_hhx(self.outdir, self.config.htmlhelp_basename)
+
+ def build_hhx(self, outdir, outname):
+ self.info('dumping stopword list...')
+ f = open(path.join(outdir, outname+'.stp'), 'w')
+ try:
+ for word in sorted(stopwords):
+ print >>f, word
+ finally:
+ f.close()
+
+ self.info('writing project file...')
+ f = open(path.join(outdir, outname+'.hhp'), 'w')
+ try:
+ f.write(project_template % {'outname': outname,
+ 'title': self.config.html_title,
+ 'version': self.config.version,
+ 'project': self.config.project})
+ if not outdir.endswith(os.sep):
+ outdir += os.sep
+ olen = len(outdir)
+ for root, dirs, files in os.walk(outdir):
+ staticdir = (root == path.join(outdir, '_static'))
+ for fn in files:
+ if (staticdir and not fn.endswith('.js')) or fn.endswith('.html'):
+ print >>f, path.join(root, fn)[olen:].replace(os.sep, '\\')
+ finally:
+ f.close()
+
+ self.info('writing TOC file...')
+ f = open(path.join(outdir, outname+'.hhc'), 'w')
+ try:
+ f.write(contents_header)
+ # special books
+ f.write('<LI> ' + object_sitemap % (self.config.html_short_title,
+ 'index.html'))
+ if self.config.html_use_modindex:
+ f.write('<LI> ' + object_sitemap % (_('Global Module Index'),
+ 'modindex.html'))
+ # the TOC
+ tocdoc = self.env.get_and_resolve_doctree(self.config.master_doc, self,
+ prune_toctrees=False)
+ def write_toc(node, ullevel=0):
+ if isinstance(node, nodes.list_item):
+ f.write('<LI> ')
+ for subnode in node:
+ write_toc(subnode, ullevel)
+ elif isinstance(node, nodes.reference):
+ link = node['refuri']
+ title = cgi.escape(node.astext()).replace('"','&quot;')
+ item = object_sitemap % (title, link)
+ f.write(item.encode('ascii', 'xmlcharrefreplace'))
+ elif isinstance(node, nodes.bullet_list):
+ if ullevel != 0:
+ f.write('<UL>\n')
+ for subnode in node:
+ write_toc(subnode, ullevel+1)
+ if ullevel != 0:
+ f.write('</UL>\n')
+ elif isinstance(node, addnodes.compact_paragraph):
+ for subnode in node:
+ write_toc(subnode, ullevel)
+ istoctree = lambda node: isinstance(node, addnodes.compact_paragraph) and \
+ node.has_key('toctree')
+ for node in tocdoc.traverse(istoctree):
+ write_toc(node)
+ f.write(contents_footer)
+ finally:
+ f.close()
+
+ self.info('writing index file...')
+ index = self.env.create_index(self)
+ f = open(path.join(outdir, outname+'.hhk'), 'w')
+ try:
+ f.write('<UL>\n')
+ 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'))
+ title = cgi.escape(title)
+ f.write('<LI> <OBJECT type="text/sitemap">\n')
+ write_param('Keyword', title)
+ if len(refs) == 0:
+ write_param('See Also', title)
+ elif len(refs) == 1:
+ write_param('Local', refs[0])
+ else:
+ for i, ref in enumerate(refs):
+ write_param('Name', '[%d] %s' % (i, ref)) # XXX: better title?
+ write_param('Local', ref)
+ f.write('</OBJECT>\n')
+ if subitems:
+ f.write('<UL> ')
+ for subitem in subitems:
+ write_index(subitem[0], subitem[1], [])
+ f.write('</UL>')
+ for (key, group) in index:
+ for title, (refs, subitems) in group:
+ write_index(title, refs, subitems)
+ f.write('</UL>\n')
+ finally:
+ f.close()
diff --git a/sphinx/builders/latex.py b/sphinx/builders/latex.py
new file mode 100644
index 00000000..2bcbc0d0
--- /dev/null
+++ b/sphinx/builders/latex.py
@@ -0,0 +1,186 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.builders.latex
+ ~~~~~~~~~~~~~~~~~~~~~
+
+ LaTeX builder.
+
+ :copyright: 2008 by Georg Brandl.
+ :license: BSD, see LICENSE for details.
+"""
+
+import os
+import shutil
+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 package_dir, addnodes
+from sphinx.util import SEP, texescape
+from sphinx.builders import Builder
+from sphinx.environment import NoUri
+from sphinx.util.console import bold, darkgreen
+from sphinx.writers.latex import LaTeXWriter
+
+
+class LaTeXBuilder(Builder):
+ """
+ Builds LaTeX output to create PDF.
+ """
+ name = 'latex'
+ supported_image_types = ['application/pdf', 'image/png', 'image/gif',
+ 'image/jpeg']
+
+ def init(self):
+ self.docnames = []
+ self.document_data = []
+ texescape.init()
+
+ def get_outdated_docs(self):
+ return 'all documents' # for now
+
+ def get_target_uri(self, docname, typ=None):
+ if typ == 'token':
+ # token references are always inside production lists and must be
+ # replaced by \token{} in LaTeX
+ return '@token'
+ if docname not in self.docnames:
+ raise NoUri
+ else:
+ return '%' + docname
+
+ def init_document_data(self):
+ preliminary_document_data = map(list, self.config.latex_documents)
+ if not preliminary_document_data:
+ self.warn('No "latex_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('"latex_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):
+ # first, assemble the "appendix" docs that are in every PDF
+ appendices = []
+ for fname in self.config.latex_appendices:
+ appendices.append(self.env.get_doctree(fname))
+
+ docwriter = LaTeXWriter(self)
+ docsettings = OptionParser(
+ defaults=self.env.settings,
+ components=(docwriter,)).get_default_values()
+
+ self.init_document_data()
+
+ for entry in self.document_data:
+ docname, targetname, title, author, docclass = entry[:5]
+ toctree_only = False
+ if len(entry) > 5:
+ toctree_only = entry[5]
+ 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=(docclass == 'manual') and appendices or [])
+ self.post_process_images(doctree)
+ self.info("writing... ", nonl=1)
+ doctree.settings = docsettings
+ doctree.settings.author = author
+ doctree.settings.title = title
+ doctree.settings.docname = docname
+ doctree.settings.docclass = docclass
+ 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)
+ def process_tree(docname, tree):
+ tree = tree.deepcopy()
+ for toctreenode in tree.traverse(addnodes.toctree):
+ newnodes = []
+ includefiles = map(str, toctreenode['includefiles'])
+ for includefile in includefiles:
+ try:
+ self.info(darkgreen(includefile) + " ", nonl=1)
+ subtree = process_tree(includefile,
+ self.env.get_doctree(includefile))
+ self.docnames.add(includefile)
+ except Exception:
+ self.warn('%s: toctree contains ref to nonexisting file %r' %
+ (docname, includefile))
+ else:
+ sof = addnodes.start_of_file(docname=includefile)
+ sof.children = subtree.children
+ newnodes.append(sof)
+ toctreenode.parent.replace(toctreenode, newnodes)
+ return tree
+ 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('<latex 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 = process_tree(indexfile, tree)
+ largetree.extend(appendices)
+ self.info()
+ self.info("resolving references...")
+ self.env.resolve_references(largetree, indexfile, self)
+ # resolve :ref:s to distant tex files -- we can't add a cross-reference,
+ # but append the document name
+ 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)
+ shutil.copyfile(path.join(self.srcdir, src),
+ path.join(self.outdir, dest))
+ self.info()
+
+ # the logo is handled differently
+ if self.config.latex_logo:
+ logobase = path.basename(self.config.latex_logo)
+ shutil.copyfile(path.join(self.confdir, self.config.latex_logo),
+ path.join(self.outdir, logobase))
+
+ self.info(bold('copying TeX support files... '), nonl=True)
+ staticdirname = path.join(package_dir, 'texinputs')
+ for filename in os.listdir(staticdirname):
+ if not filename.startswith('.'):
+ shutil.copyfile(path.join(staticdirname, filename),
+ path.join(self.outdir, filename))
+ self.info('done')
diff --git a/sphinx/linkcheck.py b/sphinx/builders/linkcheck.py
index 37aeb7a7..a8536f43 100644
--- a/sphinx/linkcheck.py
+++ b/sphinx/builders/linkcheck.py
@@ -1,12 +1,12 @@
# -*- coding: utf-8 -*-
"""
- sphinx.linkcheck
- ~~~~~~~~~~~~~~~~
+ sphinx.builders.linkcheck
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
The CheckExternalLinksBuilder class.
:copyright: 2008 by Georg Brandl, Thomas Lamb.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
import socket
@@ -15,7 +15,7 @@ from urllib2 import build_opener, HTTPError
from docutils import nodes
-from sphinx.builder import Builder
+from sphinx.builders import Builder
from sphinx.util.console import purple, red, darkgreen
# create an opener that will simulate a browser user-agent
diff --git a/sphinx/builders/text.py b/sphinx/builders/text.py
new file mode 100644
index 00000000..e3eadcbc
--- /dev/null
+++ b/sphinx/builders/text.py
@@ -0,0 +1,68 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.builders.text
+ ~~~~~~~~~~~~~~~~~~~~
+
+ Plain-text Sphinx builder.
+
+ :copyright: 2008 by Georg Brandl.
+ :license: BSD, see LICENSE for details.
+"""
+
+import codecs
+from os import path
+
+from docutils.io import StringOutput
+
+from sphinx.util import ensuredir, os_path
+from sphinx.builders import Builder
+from sphinx.writers.text import TextWriter
+
+
+class TextBuilder(Builder):
+ name = 'text'
+ out_suffix = '.txt'
+
+ def init(self):
+ pass
+
+ def get_outdated_docs(self):
+ for docname in self.env.found_docs:
+ if docname not in self.env.all_docs:
+ yield docname
+ continue
+ targetname = self.env.doc2path(docname, self.outdir, self.out_suffix)
+ try:
+ targetmtime = path.getmtime(targetname)
+ except Exception:
+ targetmtime = 0
+ try:
+ srcmtime = path.getmtime(self.env.doc2path(docname))
+ if srcmtime > targetmtime:
+ yield docname
+ except EnvironmentError:
+ # source doesn't exist anymore
+ pass
+
+ def get_target_uri(self, docname, typ=None):
+ return ''
+
+ def prepare_writing(self, docnames):
+ self.writer = TextWriter(self)
+
+ def write_doc(self, docname, doctree):
+ destination = StringOutput(encoding='utf-8')
+ self.writer.write(doctree, destination)
+ outfilename = path.join(self.outdir, os_path(docname) + self.out_suffix)
+ ensuredir(path.dirname(outfilename)) # normally different from self.outdir
+ try:
+ f = codecs.open(outfilename, 'w', 'utf-8')
+ try:
+ f.write(self.writer.output)
+ finally:
+ f.close()
+ except (IOError, OSError), err:
+ self.warn("Error writing file %s: %s" % (outfilename, err))
+
+ def finish(self):
+ pass
diff --git a/sphinx/cmdline.py b/sphinx/cmdline.py
index 1add8193..558d2e0a 100644
--- a/sphinx/cmdline.py
+++ b/sphinx/cmdline.py
@@ -6,7 +6,7 @@
sphinx-build command-line handling.
:copyright: 2008 by Georg Brandl.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
import os
diff --git a/sphinx/config.py b/sphinx/config.py
index fa04ac2a..7eb2fb41 100644
--- a/sphinx/config.py
+++ b/sphinx/config.py
@@ -6,7 +6,7 @@
Build configuration file handling.
:copyright: 2008 by Georg Brandl.
- :license: BSD license.
+ :license: BSD, see LICENSE for details license.
"""
import os
@@ -65,11 +65,14 @@ class Config(object):
html_sidebars = ({}, False),
html_additional_pages = ({}, False),
html_use_modindex = (True, False),
+ html_add_permalinks = (True, False),
html_use_index = (True, False),
html_split_index = (False, False),
html_copy_source = (True, False),
+ html_show_sourcelink = (True, False),
html_use_opensearch = ('', False),
html_file_suffix = (None, False),
+ html_link_suffix = (None, False),
html_show_sphinx = (True, False),
html_context = ({}, False),
@@ -111,6 +114,12 @@ class Config(object):
def init_values(self):
config = self._raw_config
+ for valname, value in self.overrides.iteritems():
+ if '.' in valname:
+ realvalname, key = valname.split('.', 1)
+ config.setdefault(realvalname, {})[key] = value
+ else:
+ config[valname] = value
config.update(self.overrides)
for name in config:
if name in self.values:
diff --git a/sphinx/directives/__init__.py b/sphinx/directives/__init__.py
index 462b2cff..ed85dc77 100644
--- a/sphinx/directives/__init__.py
+++ b/sphinx/directives/__init__.py
@@ -6,7 +6,7 @@
Handlers for additional ReST directives.
:copyright: 2008 by Georg Brandl.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
from sphinx.directives.desc import *
diff --git a/sphinx/directives/code.py b/sphinx/directives/code.py
index 5ea47763..b4364535 100644
--- a/sphinx/directives/code.py
+++ b/sphinx/directives/code.py
@@ -4,7 +4,7 @@
~~~~~~~~~~~~~~~~~~~~~~
:copyright: 2007-2008 by Georg Brandl.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
import sys
diff --git a/sphinx/directives/desc.py b/sphinx/directives/desc.py
index 68706acf..b71cf176 100644
--- a/sphinx/directives/desc.py
+++ b/sphinx/directives/desc.py
@@ -4,7 +4,7 @@
~~~~~~~~~~~~~~~~~~~~~~
:copyright: 2007-2008 by Georg Brandl.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
import re
@@ -137,7 +137,8 @@ def handle_doc_fields(node, env):
for child in node.children:
if not isinstance(child, nodes.field_list):
continue
- params = None
+ params = []
+ pfield = None
param_nodes = {}
param_types = {}
new_list = nodes.field_list()
@@ -152,11 +153,8 @@ def handle_doc_fields(node, env):
children = fbody.children
if typdesc == '%param':
if not params:
+ # add the field that later gets all the parameters
pfield = nodes.field()
- pfield += nodes.field_name('', _('Parameters'))
- pfield += nodes.field_body()
- params = nodes.bullet_list()
- pfield[1] += params
new_list += pfield
dlitem = nodes.list_item()
dlpar = nodes.paragraph()
@@ -165,7 +163,7 @@ def handle_doc_fields(node, env):
dlpar += children
param_nodes[obj] = dlpar
dlitem += dlpar
- params += dlitem
+ params.append(dlitem)
elif typdesc == '%type':
typenodes = fbody.children
if _is_only_paragraph(fbody):
@@ -198,6 +196,17 @@ def handle_doc_fields(node, env):
typ = fnametext.capitalize()
fname[0] = nodes.Text(typ)
new_list += field
+ if params:
+ if len(params) == 1:
+ pfield += nodes.field_name('', _('Parameter'))
+ pfield += nodes.field_body()
+ pfield[1] += params[0][0]
+ else:
+ pfield += nodes.field_name('', _('Parameters'))
+ pfield += nodes.field_body()
+ pfield[1] += nodes.bullet_list()
+ pfield[1][0].extend(params)
+
for param, type in param_types.iteritems():
if param in param_nodes:
param_nodes[param][1:1] = type
@@ -209,8 +218,9 @@ def handle_doc_fields(node, env):
py_sig_re = re.compile(
r'''^ ([\w.]*\.)? # class name(s)
(\w+) \s* # thing name
- (?: \((.*)\) # optional arguments
- (\s* -> \s* .*)? )? $ # optional return annotation
+ (?: \((.*)\) # optional: arguments
+ (?:\s* -> \s* (.*))? # return annotation
+ )? $ # and nothing more
''', re.VERBOSE)
py_paramlist_re = re.compile(r'([\[\],])') # split at '[', ']' and ','
@@ -229,9 +239,6 @@ def parse_py_signature(signode, sig, desctype, module, env):
raise ValueError
classname, name, arglist, retann = m.groups()
- if retann:
- retann = u' \N{RIGHTWARDS ARROW} ' + retann.strip()[2:]
-
if env.currclass:
add_module = False
if classname and classname.startswith(env.currclass):
@@ -267,7 +274,7 @@ def parse_py_signature(signode, sig, desctype, module, env):
# for callables, add an empty parameter list
signode += addnodes.desc_parameterlist()
if retann:
- signode += addnodes.desc_type(retann, retann)
+ signode += addnodes.desc_returns(retann, retann)
return fullname, classname
signode += addnodes.desc_parameterlist()
@@ -290,7 +297,7 @@ def parse_py_signature(signode, sig, desctype, module, env):
if len(stack) != 1:
raise ValueError
if retann:
- signode += addnodes.desc_type(retann, retann)
+ signode += addnodes.desc_returns(retann, retann)
return fullname, classname
diff --git a/sphinx/directives/other.py b/sphinx/directives/other.py
index b63d139c..2aa5a7fb 100644
--- a/sphinx/directives/other.py
+++ b/sphinx/directives/other.py
@@ -4,18 +4,17 @@
~~~~~~~~~~~~~~~~~~~~~~~
:copyright: 2007-2008 by Georg Brandl.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
import re
-import posixpath
from docutils import nodes
from docutils.parsers.rst import directives
from sphinx import addnodes
from sphinx.locale import pairindextypes
-from sphinx.util import patfilter, ws_re, caption_ref_re
+from sphinx.util import patfilter, ws_re, caption_ref_re, docname_join
from sphinx.util.compat import make_admonition
@@ -25,11 +24,9 @@ def toctree_directive(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
env = state.document.settings.env
suffix = env.config.source_suffix
- dirname = posixpath.dirname(env.docname)
glob = 'glob' in options
ret = []
- subnode = addnodes.toctree()
includefiles = []
includetitles = {}
all_docnames = env.found_docs.copy()
@@ -50,14 +47,14 @@ def toctree_directive(name, arguments, options, content, lineno,
if docname.endswith(suffix):
docname = docname[:-len(suffix)]
# absolutize filenames
- docname = posixpath.normpath(posixpath.join(dirname, docname))
+ docname = docname_join(env.docname, docname)
if docname not in env.found_docs:
ret.append(state.document.reporter.warning(
'toctree references unknown document %r' % docname, line=lineno))
else:
includefiles.append(docname)
else:
- patname = posixpath.normpath(posixpath.join(dirname, entry))
+ patname = docname_join(env.docname, entry)
docnames = sorted(patfilter(all_docnames, patname))
for docname in docnames:
all_docnames.remove(docname) # don't include it again
@@ -66,15 +63,18 @@ def toctree_directive(name, arguments, options, content, lineno,
ret.append(state.document.reporter.warning(
'toctree glob pattern %r didn\'t match any documents' % entry,
line=lineno))
+ subnode = addnodes.toctree()
subnode['includefiles'] = includefiles
subnode['includetitles'] = includetitles
subnode['maxdepth'] = options.get('maxdepth', -1)
subnode['glob'] = glob
+ subnode['hidden'] = 'hidden' in options
ret.append(subnode)
return ret
toctree_directive.content = 1
-toctree_directive.options = {'maxdepth': int, 'glob': directives.flag}
+toctree_directive.options = {'maxdepth': int, 'glob': directives.flag,
+ 'hidden': directives.flag}
directives.register_directive('toctree', toctree_directive)
diff --git a/sphinx/environment.py b/sphinx/environment.py
index 826fe8fa..f77eb32e 100644
--- a/sphinx/environment.py
+++ b/sphinx/environment.py
@@ -6,7 +6,7 @@
Global creation environment.
:copyright: 2007-2008 by Georg Brandl.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
import re
@@ -42,7 +42,8 @@ from docutils.transforms import Transform
from docutils.transforms.parts import ContentsFilter
from sphinx import addnodes
-from sphinx.util import get_matching_docs, SEP, ustrftime
+from sphinx.util import get_matching_docs, SEP, ustrftime, docname_join, \
+ FilenameUniqDict
from sphinx.directives import additional_xref_types
default_settings = {
@@ -57,7 +58,7 @@ default_settings = {
# This is increased every time an environment attribute is added
# or changed to properly invalidate pickle files.
-ENV_VERSION = 26
+ENV_VERSION = 27
default_substitutions = set([
@@ -276,7 +277,8 @@ class BuildEnvironment:
# (type, string, target, aliasname)
self.versionchanges = {} # version -> list of
# (type, docname, lineno, module, descname, content)
- self.images = {} # absolute path -> (docnames, unique filename)
+ self.images = FilenameUniqDict() # absolute path -> (docnames, unique filename)
+ self.dlfiles = FilenameUniqDict() # absolute path -> (docnames, unique filename)
# These are set while parsing a file
self.docname = None # current document name
@@ -317,6 +319,8 @@ class BuildEnvironment:
self.filemodules.pop(docname, None)
self.indexentries.pop(docname, None)
self.glob_toctrees.discard(docname)
+ self.images.purge_doc(docname)
+ self.dlfiles.purge_doc(docname)
for subfn, fnset in self.files_to_rebuild.items():
fnset.discard(docname)
@@ -340,10 +344,6 @@ class BuildEnvironment:
for version, changes in self.versionchanges.items():
new = [change for change in changes if change[1] != docname]
changes[:] = new
- for fullpath, (docs, _) in self.images.items():
- docs.discard(docname)
- if not docs:
- del self.images[fullpath]
def doc2path(self, docname, base=True, suffix=None):
"""
@@ -480,12 +480,6 @@ class BuildEnvironment:
self.doc2path(config.master_doc))
self.app = None
-
- # remove all non-existing images from inventory
- for imgsrc in self.images.keys():
- if not os.access(path.join(self.srcdir, imgsrc), os.R_OK):
- del self.images[imgsrc]
-
if app:
app.emit('env-updated', self)
@@ -544,6 +538,7 @@ class BuildEnvironment:
self.filter_messages(doctree)
self.process_dependencies(docname, doctree)
self.process_images(docname, doctree)
+ self.process_downloads(docname, doctree)
self.process_metadata(docname, doctree)
self.create_title_from(docname, doctree)
self.note_labels_from(docname, doctree)
@@ -608,11 +603,25 @@ class BuildEnvironment:
dep = path.join(docdir, dep)
self.dependencies.setdefault(docname, set()).add(dep)
+ def process_downloads(self, docname, doctree):
+ """
+ Process downloadable file paths.
+ """
+ docdir = path.dirname(self.doc2path(docname, base=None))
+ for node in doctree.traverse(addnodes.download_reference):
+ filepath = path.normpath(path.join(docdir, node['reftarget']))
+ self.dependencies.setdefault(docname, set()).add(filepath)
+ if not os.access(path.join(self.srcdir, filepath), os.R_OK):
+ self.warn(docname, 'Download file not readable: %s' % filepath,
+ getattr(node, 'line', None))
+ continue
+ uniquename = self.dlfiles.add_file(docname, filepath)
+ node['filename'] = uniquename
+
def process_images(self, docname, doctree):
"""
Process and rewrite image URIs.
"""
- existing_names = set(v[1] for v in self.images.itervalues())
docdir = path.dirname(self.doc2path(docname, base=None))
for node in doctree.traverse(nodes.image):
# Map the mimetype to the corresponding image. The writer may
@@ -656,17 +665,8 @@ class BuildEnvironment:
if not os.access(path.join(self.srcdir, imgpath), os.R_OK):
self.warn(docname, 'Image file not readable: %s' % imgpath,
node.line)
- if imgpath in self.images:
- self.images[imgpath][0].add(docname)
continue
- uniquename = path.basename(imgpath)
- base, ext = path.splitext(uniquename)
- i = 0
- while uniquename in existing_names:
- i += 1
- uniquename = '%s%s%s' % (base, i, ext)
- self.images[imgpath] = (set([docname]), uniquename)
- existing_names.add(uniquename)
+ self.images.add_file(docname, imgpath)
def process_metadata(self, docname, doctree):
"""
@@ -902,6 +902,8 @@ class BuildEnvironment:
If *titles_only* is True, only toplevel document titles will be in the
resulting tree.
"""
+ if toctree.get('hidden', False):
+ return None
def _walk_depth(node, depth, maxdepth, titleoverrides):
"""Utility: Cut a TOC at a specified depth."""
@@ -1027,6 +1029,24 @@ class BuildEnvironment:
if labelid:
newnode['refuri'] += '#' + labelid
newnode.append(innernode)
+ elif typ == 'doc':
+ # directly reference to document by source name; can be absolute
+ # or relative
+ docname = docname_join(fromdocname, target)
+ if docname not in self.all_docs:
+ newnode = doctree.reporter.system_message(
+ 2, 'unknown document: %s' % docname)
+ else:
+ if node['refcaption']:
+ # reference with explicit title
+ caption = node.astext()
+ else:
+ caption = self.titles[docname].astext()
+ innernode = nodes.emphasis(caption, caption)
+ newnode = nodes.reference('', '')
+ newnode['refuri'] = builder.get_relative_uri(
+ fromdocname, docname)
+ newnode.append(innernode)
elif typ == 'keyword':
# keywords are referenced by named labels
docname, labelid, _ = self.labels.get(target, ('','',''))
diff --git a/sphinx/ext/__init__.py b/sphinx/ext/__init__.py
index d111f11b..402dd578 100644
--- a/sphinx/ext/__init__.py
+++ b/sphinx/ext/__init__.py
@@ -6,5 +6,5 @@
Contains Sphinx features not activated by default.
:copyright: 2008 by Georg Brandl.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/ext/autodoc.py b/sphinx/ext/autodoc.py
index ddaba04c..bb717e0e 100644
--- a/sphinx/ext/autodoc.py
+++ b/sphinx/ext/autodoc.py
@@ -8,23 +8,22 @@
for those who like elaborate docstrings.
:copyright: 2008 by Georg Brandl, Pauli Virtanen, Martin Hans.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
import re
import sys
-import types
import inspect
import linecache
-from types import FunctionType, BuiltinMethodType, MethodType
+from types import FunctionType, BuiltinFunctionType, MethodType, ClassType
from docutils import nodes
from docutils.parsers.rst import directives
from docutils.statemachine import ViewList
from sphinx.util import rpartition, nested_parse_with_titles
-from sphinx.directives.desc import py_sig_re
+clstypes = (type, ClassType)
try:
base_exception = BaseException
except NameError:
@@ -33,6 +32,15 @@ except NameError:
_charset_re = re.compile(r'coding[:=]\s*([-\w.]+)')
_module_charsets = {}
+py_ext_sig_re = re.compile(
+ r'''^ ([\w.]+::)? # explicit module name
+ ([\w.]+\.)? # module and/or class name(s)
+ (\w+) \s* # thing name
+ (?: \((.*)\) # optional: arguments
+ (?:\s* -> \s* (.*))? # return annotation
+ )? $ # and nothing more
+ ''', re.VERBOSE)
+
class Options(object):
pass
@@ -42,7 +50,7 @@ def is_static_method(obj):
"""Check if the object given is a static method."""
if isinstance(obj, (FunctionType, classmethod)):
return True
- elif isinstance(obj, BuiltinMethodType):
+ elif isinstance(obj, BuiltinFunctionType):
return obj.__self__ is not None
elif isinstance(obj, MethodType):
return obj.im_self is not None
@@ -282,55 +290,64 @@ class RstGenerator(object):
# first, parse the definition -- auto directives for classes and functions
# can contain a signature which is then used instead of an autogenerated one
try:
- path, base, args, retann = py_sig_re.match(name).groups()
+ mod, path, base, args, retann = py_ext_sig_re.match(name).groups()
except:
self.warn('invalid signature for auto%s (%r)' % (what, name))
- return
- # fullname is the fully qualified name, base the name after the last dot
- fullname = (path or '') + base
+ return None, [], None, None
+
+ # support explicit module and class name separation via ::
+ if mod is not None:
+ mod = mod[:-2]
+ parents = path and path.rstrip('.').split('.') or []
+ else:
+ parents = []
if what == 'module':
+ if mod is not None:
+ self.warn('"::" in automodule name doesn\'t make sense')
if args or retann:
self.warn('ignoring signature arguments and return annotation '
- 'for automodule %s' % fullname)
- return fullname, fullname, [], None, None
+ 'for automodule %s' % name)
+ return (path or '') + base, [], None, None
- elif what in ('class', 'exception', 'function'):
- if path:
- mod = path.rstrip('.')
- else:
- mod = None
- # if documenting a toplevel object without explicit module, it can
- # be contained in another auto directive ...
- if hasattr(self.env, 'autodoc_current_module'):
+ elif what in ('exception', 'function', 'class'):
+ if mod is None:
+ if path:
+ mod = path.rstrip('.')
+ else:
+ # if documenting a toplevel object without explicit module, it can
+ # be contained in another auto directive ...
+ if hasattr(self.env, 'autodoc_current_module'):
+ mod = self.env.autodoc_current_module
+ # ... or in the scope of a module directive
+ if not mod:
+ mod = self.env.currmodule
+ return mod, parents + [base], args, retann
+
+ else:
+ if mod is None:
+ if path:
+ mod_cls = path.rstrip('.')
+ else:
+ mod_cls = None
+ # if documenting a class-level object without path, there must be a
+ # current class, either from a parent auto directive ...
+ if hasattr(self.env, 'autodoc_current_class'):
+ mod_cls = self.env.autodoc_current_class
+ # ... or from a class directive
+ if mod_cls is None:
+ mod_cls = self.env.currclass
+ # ... if still None, there's no way to know
+ if mod_cls is None:
+ return None, [], None, None
+ mod, cls = rpartition(mod_cls, '.')
+ parents = [cls]
+ # if the module name is still missing, get it like above
+ if not mod and hasattr(self.env, 'autodoc_current_module'):
mod = self.env.autodoc_current_module
- # ... or in the scope of a module directive
if not mod:
mod = self.env.currmodule
- return fullname, mod, [base], args, retann
-
- else:
- if path:
- mod_cls = path.rstrip('.')
- else:
- mod_cls = None
- # if documenting a class-level object without path, there must be a
- # current class, either from a parent auto directive ...
- if hasattr(self.env, 'autodoc_current_class'):
- mod_cls = self.env.autodoc_current_class
- # ... or from a class directive
- if mod_cls is None:
- mod_cls = self.env.currclass
- # ... if still None, there's no way to know
- if mod_cls is None:
- return fullname, None, [], args, retann
- mod, cls = rpartition(mod_cls, '.')
- # if the module name is still missing, get it like above
- if not mod and hasattr(self.env, 'autodoc_current_module'):
- mod = self.env.autodoc_current_module
- if not mod:
- mod = self.env.currmodule
- return fullname, mod, [cls, base], args, retann
+ return mod, parents + [base], args, retann
def format_signature(self, what, name, obj, args, retann):
"""
@@ -375,24 +392,27 @@ class RstGenerator(object):
args, retann = result
if args is not None:
- return '%s%s' % (args, retann or '')
+ return '%s%s' % (args, retann and (' -> %s' % retann) or '')
elif err:
# re-raise the error for perusal of the handler in generate()
raise RuntimeError(err)
else:
return ''
- def generate(self, what, name, members, add_content, indent=u'', check_module=False):
+ def generate(self, what, name, members, add_content, indent=u'', check_module=False,
+ no_docstring=False):
"""
Generate reST for the object in self.result.
"""
- fullname, mod, objpath, args, retann = self.resolve_name(what, name)
+ mod, objpath, args, retann = self.resolve_name(what, name)
if not mod:
# need a module to import
self.warn('don\'t know which module to import for autodocumenting %r '
'(try placing a "module" or "currentmodule" directive in the '
- 'document, or giving an explicit module name)' % fullname)
+ 'document, or giving an explicit module name)' % name)
return
+ # fully-qualified name
+ fullname = mod + (objpath and '.' + '.'.join(objpath) or '')
# the name to put into the generated directive -- doesn't contain the module
name_in_directive = '.'.join(objpath) or mod
@@ -423,7 +443,7 @@ class RstGenerator(object):
# format the object's signature, if any
try:
- sig = self.format_signature(what, name, todoc, args, retann)
+ sig = self.format_signature(what, fullname, todoc, args, retann)
except Exception, err:
self.warn('error while formatting signature for %s: %s' %
(fullname, err))
@@ -477,8 +497,9 @@ class RstGenerator(object):
sourcename = 'docstring of %s' % fullname
# add content from docstrings
- for i, line in enumerate(self.get_doc(what, fullname, todoc)):
- self.result.append(indent + line, sourcename, i)
+ if not no_docstring:
+ for i, line in enumerate(self.get_doc(what, fullname, todoc)):
+ self.result.append(indent + line, sourcename, i)
# add source content, if present
if add_content:
@@ -486,7 +507,7 @@ class RstGenerator(object):
self.result.append(indent + line, src[0], src[1])
# document members?
- if not members or what in ('function', 'method', 'attribute'):
+ if not members or what in ('function', 'method', 'data', 'attribute'):
return
# set current namespace for finding members
@@ -525,14 +546,15 @@ class RstGenerator(object):
all_members = sorted(todoc.__dict__.iteritems())
else:
all_members = [(mname, getattr(todoc, mname)) for mname in members]
+
for (membername, member) in all_members:
- # ignore members whose name starts with _ by default
if _all and membername.startswith('_'):
- continue
-
- # ignore undocumented members if :undoc-members: is not given
- doc = getattr(member, '__doc__', None)
- skip = not self.options.undoc_members and not doc
+ # ignore members whose name starts with _ by default
+ skip = True
+ else:
+ # ignore undocumented members if :undoc-members: is not given
+ doc = getattr(member, '__doc__', None)
+ skip = not self.options.undoc_members and not doc
# give the user a chance to decide whether this member should be skipped
if self.env.app:
# let extensions preprocess docstrings
@@ -543,13 +565,17 @@ class RstGenerator(object):
if skip:
continue
+ content = None
if what == 'module':
- if isinstance(member, (types.FunctionType,
- types.BuiltinFunctionType)):
+ if isinstance(member, (FunctionType, BuiltinFunctionType)):
memberwhat = 'function'
- elif isinstance(member, types.ClassType) or \
- isinstance(member, type):
- if issubclass(member, base_exception):
+ elif isinstance(member, clstypes):
+ if member.__name__ != membername:
+ # assume it's aliased
+ memberwhat = 'data'
+ content = ViewList([_('alias of :class:`%s`') % member.__name__],
+ source='')
+ elif issubclass(member, base_exception):
memberwhat = 'exception'
else:
memberwhat = 'class'
@@ -557,16 +583,27 @@ class RstGenerator(object):
# XXX: todo -- attribute docs
continue
else:
- if callable(member):
+ if isinstance(member, clstypes):
+ if member.__name__ != membername:
+ # assume it's aliased
+ memberwhat = 'attribute'
+ content = ViewList([_('alias of :class:`%s`') % member.__name__],
+ source='')
+ else:
+ memberwhat = 'class'
+ elif callable(member):
memberwhat = 'method'
elif isdescriptor(member):
memberwhat = 'attribute'
else:
# XXX: todo -- attribute docs
continue
- full_membername = fullname + '.' + membername
- self.generate(memberwhat, full_membername, ['__all__'], None, indent,
- check_module=members_check_module)
+ # give explicitly separated module name, so that members of inner classes
+ # can be documented
+ full_membername = mod + '::' + '.'.join(objpath + [membername])
+ self.generate(memberwhat, full_membername, ['__all__'],
+ add_content=content, no_docstring=bool(content),
+ indent=indent, check_module=members_check_module)
self.env.autodoc_current_module = None
self.env.autodoc_current_class = None
@@ -641,6 +678,8 @@ def setup(app):
1, (1, 0, 1), **cls_options)
app.add_directive('autoexception', autoclass_directive,
1, (1, 0, 1), **cls_options)
+ app.add_directive('autodata', auto_directive, 1, (1, 0, 1),
+ noindex=directives.flag)
app.add_directive('autofunction', auto_directive, 1, (1, 0, 1),
noindex=directives.flag)
app.add_directive('automethod', auto_directive, 1, (1, 0, 1),
diff --git a/sphinx/ext/coverage.py b/sphinx/ext/coverage.py
index f03dbc1e..af6031fd 100644
--- a/sphinx/ext/coverage.py
+++ b/sphinx/ext/coverage.py
@@ -7,7 +7,7 @@
Dzolonga for the Google Highly Open Participation contest.
:copyright: 2008 by Josip Dzolonga, Georg Brandl.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
import re
@@ -16,7 +16,7 @@ import inspect
import cPickle as pickle
from os import path
-from sphinx.builder import Builder
+from sphinx.builders import Builder
# utility
diff --git a/sphinx/ext/doctest.py b/sphinx/ext/doctest.py
index 87c8cf17..5ea1dde9 100644
--- a/sphinx/ext/doctest.py
+++ b/sphinx/ext/doctest.py
@@ -7,7 +7,7 @@
their results.
:copyright: 2008 by Georg Brandl.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
import re
@@ -22,7 +22,7 @@ doctest = __import__('doctest')
from docutils import nodes
from docutils.parsers.rst import directives
-from sphinx.builder import Builder
+from sphinx.builders import Builder
from sphinx.util.console import bold
blankline_re = re.compile(r'^\s*<BLANKLINE>', re.MULTILINE)
@@ -99,9 +99,12 @@ class TestGroup(object):
self.setup = []
self.tests = []
- def add_code(self, code):
+ def add_code(self, code, prepend=False):
if code.type == 'testsetup':
- self.setup.append(code)
+ if prepend:
+ self.setup.insert(0, code)
+ else:
+ self.setup.append(code)
elif code.type == 'doctest':
self.tests.append([code])
elif code.type == 'testcode':
@@ -258,6 +261,10 @@ Doctest summary
for code in add_to_all_groups:
for group in groups.itervalues():
group.add_code(code)
+ if self.config.doctest_global_setup:
+ code = TestCode(self.config.doctest_global_setup, 'testsetup', lineno=0)
+ for group in groups.itervalues():
+ group.add_code(code, prepend=True)
if not groups:
return
@@ -335,3 +342,4 @@ def setup(app):
# this config value adds to sys.path
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)
diff --git a/sphinx/ext/ifconfig.py b/sphinx/ext/ifconfig.py
index 204178f2..10cab525 100644
--- a/sphinx/ext/ifconfig.py
+++ b/sphinx/ext/ifconfig.py
@@ -17,7 +17,7 @@
are available.)
:copyright: 2008 by Georg Brandl.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
from docutils import nodes
diff --git a/sphinx/ext/intersphinx.py b/sphinx/ext/intersphinx.py
index 0c034e0d..e94b3d67 100644
--- a/sphinx/ext/intersphinx.py
+++ b/sphinx/ext/intersphinx.py
@@ -21,7 +21,7 @@
without Internet access.
:copyright: 2008 by Georg Brandl.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
import time
@@ -31,7 +31,7 @@ from os import path
from docutils import nodes
-from sphinx.builder import INVENTORY_FILENAME
+from sphinx.builders.html import INVENTORY_FILENAME
def fetch_inventory(app, uri, inv):
diff --git a/sphinx/ext/jsmath.py b/sphinx/ext/jsmath.py
index bd2579de..4bc38b51 100644
--- a/sphinx/ext/jsmath.py
+++ b/sphinx/ext/jsmath.py
@@ -7,7 +7,7 @@
via JavaScript.
:copyright: 2008 by Georg Brandl.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
from docutils import nodes
diff --git a/sphinx/ext/mathbase.py b/sphinx/ext/mathbase.py
index fc002c60..825eabef 100644
--- a/sphinx/ext/mathbase.py
+++ b/sphinx/ext/mathbase.py
@@ -6,7 +6,7 @@
Set up math support in source files and LaTeX/text output.
:copyright: 2008 by Georg Brandl.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
from docutils import nodes, utils
diff --git a/sphinx/ext/pngmath.py b/sphinx/ext/pngmath.py
index e3c812a1..047af9bb 100644
--- a/sphinx/ext/pngmath.py
+++ b/sphinx/ext/pngmath.py
@@ -6,7 +6,7 @@
Render math in HTML via dvipng.
:copyright: 2008 by Georg Brandl.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
import re
diff --git a/sphinx/ext/refcounting.py b/sphinx/ext/refcounting.py
index c6e5a76f..e42e7e2c 100644
--- a/sphinx/ext/refcounting.py
+++ b/sphinx/ext/refcounting.py
@@ -10,7 +10,7 @@
count data file.
:copyright: 2008 by Georg Brandl.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
from os import path
diff --git a/sphinx/ext/todo.py b/sphinx/ext/todo.py
index f4bed740..4c1b142c 100644
--- a/sphinx/ext/todo.py
+++ b/sphinx/ext/todo.py
@@ -9,7 +9,7 @@
original location.
:copyright: 2008 Daniel Bültmann.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
from docutils import nodes
diff --git a/sphinx/highlighting.py b/sphinx/highlighting.py
index 1637b2c3..e347d01e 100644
--- a/sphinx/highlighting.py
+++ b/sphinx/highlighting.py
@@ -6,7 +6,7 @@
Highlight code blocks using Pygments.
:copyright: 2007-2008 by Georg Brandl.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
import sys
@@ -30,6 +30,8 @@ try:
from pygments.token import Generic, Comment, Number
except ImportError:
pygments = None
+ lexers = None
+ HtmlFormatter = LatexFormatter = None
else:
class SphinxStyle(Style):
"""
@@ -81,6 +83,11 @@ if sys.version_info < (2, 5):
class PygmentsBridge(object):
+ # Set these attributes if you want to have different Pygments formatters
+ # than the default ones.
+ html_formatter = HtmlFormatter
+ latex_formatter = LatexFormatter
+
def __init__(self, dest='html', stylename='sphinx'):
self.dest = dest
if not pygments:
@@ -89,14 +96,17 @@ class PygmentsBridge(object):
style = SphinxStyle
elif '.' in stylename:
module, stylename = stylename.rsplit('.', 1)
- style = getattr(__import__(module, None, None, ['']), stylename)
+ style = getattr(__import__(module, None, None, ['__name__']), stylename)
else:
style = get_style_by_name(stylename)
- self.hfmter = {False: HtmlFormatter(style=style),
- True: HtmlFormatter(style=style, linenos=True)}
- self.lfmter = {False: LatexFormatter(style=style, commandprefix='PYG'),
- True: LatexFormatter(style=style, linenos=True,
- commandprefix='PYG')}
+ if dest == 'html':
+ self.fmter = {False: self.html_formatter(style=style),
+ True: self.html_formatter(style=style, linenos=True)}
+ else:
+ self.fmter = {False: self.latex_formatter(style=style,
+ commandprefix='PYG'),
+ True: self.latex_formatter(style=style, linenos=True,
+ commandprefix='PYG')}
def unhighlighted(self, source):
if self.dest == 'html':
@@ -170,9 +180,9 @@ class PygmentsBridge(object):
lexer.add_filter('raiseonerror')
try:
if self.dest == 'html':
- return highlight(source, lexer, self.hfmter[bool(linenos)])
+ return highlight(source, lexer, self.fmter[bool(linenos)])
else:
- hlsource = highlight(source, lexer, self.lfmter[bool(linenos)])
+ hlsource = highlight(source, lexer, self.fmter[bool(linenos)])
return hlsource.translate(tex_hl_escape_map)
except ErrorToken:
# this is most probably not the selected language,
@@ -186,9 +196,9 @@ class PygmentsBridge(object):
# no HTML styles needed
return ''
if self.dest == 'html':
- return self.hfmter[0].get_style_defs()
+ return self.fmter[0].get_style_defs()
else:
- styledefs = self.lfmter[0].get_style_defs()
+ styledefs = self.fmter[0].get_style_defs()
# workaround for Pygments < 0.12
if styledefs.startswith('\\newcommand\\at{@}'):
styledefs += _LATEX_STYLES
diff --git a/sphinx/htmlhelp.py b/sphinx/htmlhelp.py
deleted file mode 100644
index 4cc68bc9..00000000
--- a/sphinx/htmlhelp.py
+++ /dev/null
@@ -1,220 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- sphinx.htmlhelp
- ~~~~~~~~~~~~~~~
-
- Build HTML help support files.
- Adapted from the original Doc/tools/prechm.py.
-
- :copyright: 2007-2008 by Georg Brandl.
- :license: BSD.
-"""
-
-import os
-import cgi
-from os import path
-
-from docutils import nodes
-
-from sphinx import addnodes
-
-# Project file (*.hhp) template. 'outname' is the file basename (like
-# the pythlp in pythlp.hhp); 'version' is the doc version number (like
-# the 2.2 in Python 2.2).
-# The magical numbers in the long line under [WINDOWS] set most of the
-# user-visible features (visible buttons, tabs, etc).
-# About 0x10384e: This defines the buttons in the help viewer. The
-# following defns are taken from htmlhelp.h. Not all possibilities
-# actually work, and not all those that work are available from the Help
-# Workshop GUI. In particular, the Zoom/Font button works and is not
-# available from the GUI. The ones we're using are marked with 'x':
-#
-# 0x000002 Hide/Show x
-# 0x000004 Back x
-# 0x000008 Forward x
-# 0x000010 Stop
-# 0x000020 Refresh
-# 0x000040 Home x
-# 0x000080 Forward
-# 0x000100 Back
-# 0x000200 Notes
-# 0x000400 Contents
-# 0x000800 Locate x
-# 0x001000 Options x
-# 0x002000 Print x
-# 0x004000 Index
-# 0x008000 Search
-# 0x010000 History
-# 0x020000 Favorites
-# 0x040000 Jump 1
-# 0x080000 Jump 2
-# 0x100000 Zoom/Font x
-# 0x200000 TOC Next
-# 0x400000 TOC Prev
-
-project_template = '''\
-[OPTIONS]
-Binary TOC=Yes
-Binary Index=No
-Compiled file=%(outname)s.chm
-Contents file=%(outname)s.hhc
-Default Window=%(outname)s
-Default topic=index.html
-Display compile progress=No
-Full text search stop list file=%(outname)s.stp
-Full-text search=Yes
-Index file=%(outname)s.hhk
-Language=0x409
-Title=%(title)s
-
-[WINDOWS]
-%(outname)s="%(title)s","%(outname)s.hhc","%(outname)s.hhk",\
-"index.html","index.html",,,,,0x63520,220,0x10384e,[0,0,1024,768],,,,,,,0
-
-[FILES]
-'''
-
-contents_header = '''\
-<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
-<HTML>
-<HEAD>
-<meta name="GENERATOR" content="Microsoft&reg; HTML Help Workshop 4.1">
-<!-- Sitemap 1.0 -->
-</HEAD><BODY>
-<OBJECT type="text/site properties">
- <param name="Window Styles" value="0x801227">
- <param name="ImageType" value="Folder">
-</OBJECT>
-<UL>
-'''
-
-contents_footer = '''\
-</UL></BODY></HTML>
-'''
-
-object_sitemap = '''\
-<OBJECT type="text/sitemap">
- <param name="Name" value="%s">
- <param name="Local" value="%s">
-</OBJECT>
-'''
-
-# List of words the full text search facility shouldn't index. This
-# becomes file outname.stp. Note that this list must be pretty small!
-# Different versions of the MS docs claim the file has a maximum size of
-# 256 or 512 bytes (including \r\n at the end of each line).
-# Note that "and", "or", "not" and "near" are operators in the search
-# language, so no point indexing them even if we wanted to.
-stopwords = """
-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()
-
-
-def build_hhx(builder, outdir, outname):
- builder.info('dumping stopword list...')
- f = open(path.join(outdir, outname+'.stp'), 'w')
- try:
- for word in sorted(stopwords):
- print >>f, word
- finally:
- f.close()
-
- builder.info('writing project file...')
- f = open(path.join(outdir, outname+'.hhp'), 'w')
- try:
- f.write(project_template % {'outname': outname,
- 'title': builder.config.html_title,
- 'version': builder.config.version,
- 'project': builder.config.project})
- if not outdir.endswith(os.sep):
- outdir += os.sep
- olen = len(outdir)
- for root, dirs, files in os.walk(outdir):
- staticdir = (root == path.join(outdir, '_static'))
- for fn in files:
- if (staticdir and not fn.endswith('.js')) or fn.endswith('.html'):
- print >>f, path.join(root, fn)[olen:].replace(os.sep, '\\')
- finally:
- f.close()
-
- builder.info('writing TOC file...')
- f = open(path.join(outdir, outname+'.hhc'), 'w')
- try:
- f.write(contents_header)
- # special books
- f.write('<LI> ' + object_sitemap % (builder.config.html_short_title,
- 'index.html'))
- if builder.config.html_use_modindex:
- f.write('<LI> ' + object_sitemap % (_('Global Module Index'),
- 'modindex.html'))
- # the TOC
- tocdoc = builder.env.get_and_resolve_doctree(builder.config.master_doc, builder,
- prune_toctrees=False)
- def write_toc(node, ullevel=0):
- if isinstance(node, nodes.list_item):
- f.write('<LI> ')
- for subnode in node:
- write_toc(subnode, ullevel)
- elif isinstance(node, nodes.reference):
- link = node['refuri']
- title = cgi.escape(node.astext()).replace('"','&quot;')
- item = object_sitemap % (title, link)
- f.write(item.encode('ascii', 'xmlcharrefreplace'))
- elif isinstance(node, nodes.bullet_list):
- if ullevel != 0:
- f.write('<UL>\n')
- for subnode in node:
- write_toc(subnode, ullevel+1)
- if ullevel != 0:
- f.write('</UL>\n')
- elif isinstance(node, addnodes.compact_paragraph):
- for subnode in node:
- write_toc(subnode, ullevel)
- istoctree = lambda node: isinstance(node, addnodes.compact_paragraph) and \
- node.has_key('toctree')
- for node in tocdoc.traverse(istoctree):
- write_toc(node)
- f.write(contents_footer)
- finally:
- f.close()
-
- builder.info('writing index file...')
- index = builder.env.create_index(builder)
- f = open(path.join(outdir, outname+'.hhk'), 'w')
- try:
- f.write('<UL>\n')
- 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'))
- title = cgi.escape(title)
- f.write('<LI> <OBJECT type="text/sitemap">\n')
- write_param('Keyword', title)
- if len(refs) == 0:
- write_param('See Also', title)
- elif len(refs) == 1:
- write_param('Local', refs[0])
- else:
- for i, ref in enumerate(refs):
- write_param('Name', '[%d] %s' % (i, ref)) # XXX: better title?
- write_param('Local', ref)
- f.write('</OBJECT>\n')
- if subitems:
- f.write('<UL> ')
- for subitem in subitems:
- write_index(subitem[0], subitem[1], [])
- f.write('</UL>')
- for (key, group) in index:
- for title, (refs, subitems) in group:
- write_index(title, refs, subitems)
- f.write('</UL>\n')
- finally:
- f.close()
diff --git a/sphinx/locale/__init__.py b/sphinx/locale/__init__.py
index bedade3c..37c714b4 100644
--- a/sphinx/locale/__init__.py
+++ b/sphinx/locale/__init__.py
@@ -6,7 +6,7 @@
Locale utilities.
:copyright: 2008 by Georg Brandl.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
_ = lambda x: x
diff --git a/sphinx/locale/it/LC_MESSAGES/sphinx.js b/sphinx/locale/it/LC_MESSAGES/sphinx.js
new file mode 100644
index 00000000..c2c8be7b
--- /dev/null
+++ b/sphinx/locale/it/LC_MESSAGES/sphinx.js
@@ -0,0 +1 @@
+Documentation.addTranslations({"locale": "it", "plural_expr": "(n != 1)", "messages": {"module, in ": "modulo, in", "Preparing search...": "Preparazione della ricerca", "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories.": "La tua ricerca non ha trovato alcun risultato. Controlla la corettezzadei termini di ricerca e di avere selezionato un numero sufficiente di categorie", "Search finished, found %s page(s) matching the search query.": "Ricera terminata, trovate %s pagine corrispondenti alla ricerca.", ", in ": ", in ", "Permalink to this headline": "link permanente per questa inestazione", "Searching": "Ricerca in corso", "Permalink to this definition": "link permanente per questa definizione", "Hide Search Matches": "Nascondi i risultati della ricerca", "Search Results": "Risultati della ricerca"}}); \ No newline at end of file
diff --git a/sphinx/locale/it/LC_MESSAGES/sphinx.mo b/sphinx/locale/it/LC_MESSAGES/sphinx.mo
new file mode 100644
index 00000000..7818e876
--- /dev/null
+++ b/sphinx/locale/it/LC_MESSAGES/sphinx.mo
Binary files differ
diff --git a/sphinx/locale/it/LC_MESSAGES/sphinx.po b/sphinx/locale/it/LC_MESSAGES/sphinx.po
new file mode 100644
index 00000000..e7c796b0
--- /dev/null
+++ b/sphinx/locale/it/LC_MESSAGES/sphinx.po
@@ -0,0 +1,599 @@
+# Translations template for Sphinx.
+# Copyright (C) 2008 ORGANIZATION
+# This file is distributed under the same license as the Sphinx project.
+# Sandro Dentella <sandro@e-den.it>, 2008.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Sphinx 0.5\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2008-11-27 18:39+0100\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.4\n"
+
+#: sphinx/builder.py:408
+#, python-format
+msgid "%b %d, %Y"
+msgstr "%d/%b/%Y"
+
+#: sphinx/builder.py:427 sphinx/templates/defindex.html:21
+msgid "General Index"
+msgstr "Indice generale"
+
+#: sphinx/builder.py:427
+msgid "index"
+msgstr "indice"
+
+#: sphinx/builder.py:429 sphinx/htmlhelp.py:156
+#: sphinx/templates/defindex.html:19 sphinx/templates/modindex.html:2
+#: sphinx/templates/modindex.html:13
+msgid "Global Module Index"
+msgstr "Indice dei moduli"
+
+#: sphinx/builder.py:429
+msgid "modules"
+msgstr "moduli"
+
+#: sphinx/builder.py:466
+msgid "next"
+msgstr "successivo"
+
+#: sphinx/builder.py:473
+msgid "previous"
+msgstr "precedente"
+
+#: sphinx/builder.py:1054
+msgid " (in "
+msgstr " (in "
+
+#: sphinx/builder.py:1129
+msgid "Builtins"
+msgstr "Builtin"
+
+#: sphinx/builder.py:1131
+msgid "Module level"
+msgstr "Modulo"
+
+#: sphinx/environment.py:102 sphinx/latexwriter.py:169
+#, python-format
+msgid "%B %d, %Y"
+msgstr "%d %B %Y"
+
+#: sphinx/environment.py:291 sphinx/latexwriter.py:175
+#: 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 "Indice"
+
+#: sphinx/environment.py:292 sphinx/latexwriter.py:174
+msgid "Module Index"
+msgstr "Indice dei Moduli"
+
+#: sphinx/environment.py:293 sphinx/templates/defindex.html:16
+msgid "Search Page"
+msgstr "Cerca"
+
+#: sphinx/htmlwriter.py:79 sphinx/static/doctools.js:145
+msgid "Permalink to this definition"
+msgstr "link permanente per questa definizione"
+
+#: sphinx/htmlwriter.py:399 sphinx/static/doctools.js:139
+msgid "Permalink to this headline"
+msgstr "link permanente per questa inestazione"
+
+#: sphinx/latexwriter.py:172
+msgid "Relelase"
+msgstr "Release"
+
+#: sphinx/roles.py:53 sphinx/directives/desc.py:537
+#, python-format
+msgid "environment variable; %s"
+msgstr "variabile dámbiente, %s"
+
+#: sphinx/roles.py:60
+#, python-format
+msgid "Python Enhancement Proposals!PEP %s"
+msgstr "Python Enhancement Proposals!PEP %s"
+
+#: sphinx/textwriter.py:166
+#, python-format
+msgid "Platform: %s"
+msgstr "Piattaforma: %s"
+
+#: sphinx/textwriter.py:422
+msgid "[image]"
+msgstr "[immagine]"
+
+#: sphinx/directives/desc.py:25
+#, python-format
+msgid "%s() (built-in function)"
+msgstr "%s() (funzione built-in)"
+
+#: sphinx/directives/desc.py:26 sphinx/directives/desc.py:42
+#: sphinx/directives/desc.py:54
+#, python-format
+msgid "%s() (in module %s)"
+msgstr "%s() (nel modulo %s)"
+
+#: sphinx/directives/desc.py:29
+#, python-format
+msgid "%s (built-in variable)"
+msgstr "%s (variabile built-in)"
+
+#: sphinx/directives/desc.py:30 sphinx/directives/desc.py:66
+#, python-format
+msgid "%s (in module %s)"
+msgstr "%s (nel modulo %s)"
+
+#: sphinx/directives/desc.py:33
+#, python-format
+msgid "%s (built-in class)"
+msgstr "%s (classe built-in)"
+
+#: sphinx/directives/desc.py:34
+#, python-format
+msgid "%s (class in %s)"
+msgstr "%s (classe in %s)"
+
+#: sphinx/directives/desc.py:46
+#, python-format
+msgid "%s() (%s.%s method)"
+msgstr "%s() (%s.%s metodo)"
+
+#: sphinx/directives/desc.py:48
+#, python-format
+msgid "%s() (%s method)"
+msgstr "%s() (%s metodo)"
+
+#: sphinx/directives/desc.py:58
+#, python-format
+msgid "%s() (%s.%s static method)"
+msgstr "%s() (%s.%s metodo statico)"
+
+#: sphinx/directives/desc.py:60
+#, python-format
+msgid "%s() (%s static method)"
+msgstr "%s() (%s metodo statico)"
+
+#: sphinx/directives/desc.py:70
+#, python-format
+msgid "%s (%s.%s attribute)"
+msgstr "%s (%s.%s attributo)"
+
+#: sphinx/directives/desc.py:72
+#, python-format
+msgid "%s (%s attribute)"
+msgstr "%s (%s attributo)"
+
+#: sphinx/directives/desc.py:74
+#, python-format
+msgid "%s (C function)"
+msgstr "%s (functione C)"
+
+#: sphinx/directives/desc.py:76
+#, python-format
+msgid "%s (C member)"
+msgstr "%s (membro C )"
+
+#: sphinx/directives/desc.py:78
+#, python-format
+msgid "%s (C macro)"
+msgstr "%s (macro C)"
+
+#: sphinx/directives/desc.py:80
+#, python-format
+msgid "%s (C type)"
+msgstr "%s (tipo C)"
+
+#: sphinx/directives/desc.py:82
+#, python-format
+msgid "%s (C variable)"
+msgstr "%s (variabile C)"
+
+#: sphinx/directives/desc.py:100
+msgid "Raises"
+msgstr "Solleva"
+
+#: sphinx/directives/desc.py:104
+msgid "Variable"
+msgstr "Variabile"
+
+#: sphinx/directives/desc.py:107
+msgid "Returns"
+msgstr "Ritorna"
+
+#: sphinx/directives/desc.py:116
+msgid "Return type"
+msgstr "Tipo di ritorno"
+
+#: sphinx/directives/desc.py:143
+msgid "Parameters"
+msgstr "Parametri"
+
+#: sphinx/directives/desc.py:423
+#, python-format
+msgid "%scommand line option; %s"
+msgstr "%sopzione di linea di comando; %s"
+
+#: sphinx/directives/other.py:101
+msgid "Platforms: "
+msgstr "Piattaforme:"
+
+#: sphinx/directives/other.py:106
+#, python-format
+msgid "%s (module)"
+msgstr "%s (modulo)"
+
+#: sphinx/directives/other.py:146
+msgid "Section author: "
+msgstr "Autore della sezione"
+
+#: sphinx/directives/other.py:148
+msgid "Module author: "
+msgstr "Autore del modulo"
+
+#: sphinx/directives/other.py:150
+msgid "Author: "
+msgstr "Autore: "
+
+#: sphinx/directives/other.py:246
+msgid "See also"
+msgstr "Vedi anche"
+
+#: sphinx/ext/todo.py:31
+msgid "Todo"
+msgstr "Da fare"
+
+#: sphinx/ext/todo.py:75
+#, python-format
+msgid "(The original entry is located in %s, line %d and can be found "
+msgstr "(La riga originale si trova in %s, linea %d e può essere trovata "
+
+#: sphinx/ext/todo.py:81
+msgid "here"
+msgstr "qui"
+
+#: sphinx/locale/__init__.py:15
+msgid "Attention"
+msgstr "Attenzione"
+
+#: sphinx/locale/__init__.py:16
+msgid "Caution"
+msgstr "Attenzione"
+
+#: sphinx/locale/__init__.py:17
+msgid "Danger"
+msgstr "Pericolo"
+
+#: sphinx/locale/__init__.py:18
+msgid "Error"
+msgstr "Errore"
+
+#: sphinx/locale/__init__.py:19
+msgid "Hint"
+msgstr "Consiglio"
+
+#: sphinx/locale/__init__.py:20
+msgid "Important"
+msgstr "Importante"
+
+#: sphinx/locale/__init__.py:21
+msgid "Note"
+msgstr "Nota"
+
+#: sphinx/locale/__init__.py:22
+msgid "See Also"
+msgstr "Vedi anche"
+
+#: sphinx/locale/__init__.py:23
+msgid "Tip"
+msgstr "Suggerimento"
+
+#: sphinx/locale/__init__.py:24
+msgid "Warning"
+msgstr "Avvertimento"
+
+#: sphinx/locale/__init__.py:28
+#, python-format
+msgid "New in version %s"
+msgstr "Nuovo nella versione %s"
+
+#: sphinx/locale/__init__.py:29
+#, python-format
+msgid "Changed in version %s"
+msgstr "Cambiato nella versione %s"
+
+#: sphinx/locale/__init__.py:30
+#, python-format
+msgid "Deprecated since version %s"
+msgstr "Deprecato dalla versione %s"
+
+#: sphinx/locale/__init__.py:34
+msgid "module"
+msgstr "modulo"
+
+#: sphinx/locale/__init__.py:35
+msgid "keyword"
+msgstr "keyword"
+
+#: sphinx/locale/__init__.py:36
+msgid "operator"
+msgstr "operatore"
+
+#: sphinx/locale/__init__.py:37
+msgid "object"
+msgstr "oggetto"
+
+#: sphinx/locale/__init__.py:38
+msgid "exception"
+msgstr "eccezione"
+
+#: sphinx/locale/__init__.py:39
+msgid "statement"
+msgstr "statement"
+
+#: sphinx/locale/__init__.py:40
+msgid "built-in function"
+msgstr "funzione built-in"
+
+#: sphinx/static/doctools.js:174
+msgid "Hide Search Matches"
+msgstr "Nascondi i risultati della ricerca"
+
+#: sphinx/static/searchtools.js:274
+msgid "Searching"
+msgstr "Ricerca in corso"
+
+#: sphinx/static/searchtools.js:279
+msgid "Preparing search..."
+msgstr "Preparazione della ricerca"
+
+#: sphinx/static/searchtools.js:338
+msgid "module, in "
+msgstr "modulo, in"
+
+#: sphinx/static/searchtools.js:347
+msgid ", in "
+msgstr ", in "
+
+#: sphinx/static/searchtools.js:447 sphinx/templates/search.html:18
+msgid "Search Results"
+msgstr "Risultati della ricerca"
+
+#: 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 ""
+"La tua ricerca non ha trovato alcun risultato. Controlla la corettezza"
+"dei termini di ricerca e di avere selezionato un numero sufficiente di categorie"
+
+#: sphinx/static/searchtools.js:451
+#, python-format
+msgid "Search finished, found %s page(s) matching the search query."
+msgstr "Ricera terminata, trovate %s pagine corrispondenti alla ricerca."
+
+#: sphinx/templates/defindex.html:2
+msgid "Overview"
+msgstr "Sintesi"
+
+#: sphinx/templates/defindex.html:11
+msgid "Indices and tables:"
+msgstr "Indici e tabelle:"
+
+#: sphinx/templates/defindex.html:14
+msgid "Complete Table of Contents"
+msgstr "Tabella dei contenuti completa"
+
+#: sphinx/templates/defindex.html:15
+msgid "lists all sections and subsections"
+msgstr "elenca l'insieme delle sezioni e sottosezioni"
+
+#: sphinx/templates/defindex.html:17
+msgid "search this documentation"
+msgstr "cerca in questa documentazione"
+
+#: sphinx/templates/defindex.html:20
+msgid "quick access to all modules"
+msgstr "accesso veloce ai moduli"
+
+#: sphinx/templates/defindex.html:22
+msgid "all functions, classes, terms"
+msgstr "tutte le funzioni, classi e moduli"
+
+#: sphinx/templates/genindex-single.html:5
+#, python-format
+msgid "Index &ndash; %(key)s"
+msgstr "Indice &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 "Indice completo in una pagina"
+
+#: sphinx/templates/genindex-split.html:7
+msgid "Index pages by letter"
+msgstr "Indice delle pagine per lettera"
+
+#: sphinx/templates/genindex-split.html:15
+msgid "can be huge"
+msgstr "può essere enorme"
+
+#: sphinx/templates/layout.html:9
+msgid "Navigation"
+msgstr "Navigazione"
+
+#: sphinx/templates/layout.html:40
+msgid "Table Of Contents"
+msgstr "Tablella dei contenuti"
+
+#: sphinx/templates/layout.html:46
+msgid "Previous topic"
+msgstr "Argomento precedente"
+
+#: sphinx/templates/layout.html:47
+msgid "previous chapter"
+msgstr "capitolo precedente"
+
+#: sphinx/templates/layout.html:50
+msgid "Next topic"
+msgstr "Argomento successivo"
+
+#: sphinx/templates/layout.html:51
+msgid "next chapter"
+msgstr "capitolo successivo"
+
+#: sphinx/templates/layout.html:55
+msgid "This Page"
+msgstr "Questa pagina"
+
+#: sphinx/templates/layout.html:59
+msgid "Suggest Change"
+msgstr "Suggerisci una modifica"
+
+#: sphinx/templates/layout.html:60 sphinx/templates/layout.html:62
+msgid "Show Source"
+msgstr "Mostra sorgente"
+
+#: sphinx/templates/layout.html:71
+msgid "Quick search"
+msgstr "Ricerca veloce"
+
+#: sphinx/templates/layout.html:71
+msgid "Keyword search"
+msgstr "Ricerca per parola chiave"
+
+#: sphinx/templates/layout.html:73
+msgid "Go"
+msgstr "Vai"
+
+#: sphinx/templates/layout.html:78
+msgid "Enter a module, class or function name."
+msgstr "Inserisci un modulo, classe o nome di funzione"
+
+#: sphinx/templates/layout.html:119
+#, python-format
+msgid "Search within %(docstitle)s"
+msgstr "Cerca in %(docstitle)s"
+
+#: sphinx/templates/layout.html:128
+msgid "About these documents"
+msgstr "A proposito di questi documenti"
+
+#: sphinx/templates/layout.html:131 sphinx/templates/search.html:2
+#: sphinx/templates/search.html:5
+msgid "Search"
+msgstr "Cerca"
+
+#: sphinx/templates/layout.html:133
+msgid "Copyright"
+msgstr "Copyright"
+
+#: sphinx/templates/layout.html:178
+#, python-format
+msgid "&copy; <a href=\"%(path)s\">Copyright</a> %(copyright)s."
+msgstr "&copy; <a href=\"%(path)s\">Copyright</a> %(copyright)s."
+
+#: sphinx/templates/layout.html:180
+#, python-format
+msgid "&copy; Copyright %(copyright)s."
+msgstr "&copy; Copyright %(copyright)s."
+
+#: sphinx/templates/layout.html:183
+#, python-format
+msgid "Last updated on %(last_updated)s."
+msgstr "Ultimo Aggiornamento on %(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 ""
+"Creato con <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> "
+"%(sphinx_version)s."
+
+#: sphinx/templates/modindex.html:15
+msgid "Most popular modules:"
+msgstr "Moduli più utilizzati"
+
+#: sphinx/templates/modindex.html:24
+msgid "Show modules only available on these platforms"
+msgstr "Mostra solo i moduli disponibili su questa piattaforma"
+
+#: sphinx/templates/modindex.html:56
+msgid "Deprecated"
+msgstr "Deprecato"
+
+#: sphinx/templates/opensearch.xml:4
+#, python-format
+msgid "Search %(docstitle)s"
+msgstr "Cerca %(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 ""
+"<strong>Nota:</strong> Hai chiesto un URL non più valido."
+" Abbiamo provato a ridirigerti verso il nuovo indirizzo, ma potrebbe "
+" non essere quello giusto"
+
+#: 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 fewer words won't appear in the result list."
+msgstr ""
+"Puoi effettuare una ricerca in questi documenti. Immetti le parole chiave \n"
+" della tua ricerca nel riquadro sottostante \"search\". Nota che la funzione\n"
+" di ricerca cerca automaticamente per tutte le parole. Le pagine\n"
+" che contendono meno parole non compariranno nei risultati di ricerca."
+
+#: sphinx/templates/search.html:14
+msgid "search"
+msgstr "cerca"
+
+#: sphinx/templates/search.html:20
+msgid "Your search did not match any results."
+msgstr "La tua ricerca non ha ottenuto risultati"
+
+#: sphinx/templates/changes/frameset.html:5
+#: sphinx/templates/changes/versionchanges.html:12
+#, python-format
+msgid "Changes in Version %(version)s &mdash; %(docstitle)s"
+msgstr "Modifiche nella Versione %(version)s &mdash; %(docstitle)s"
+
+#: sphinx/templates/changes/rstsource.html:5
+#, python-format
+msgid "%(filename)s &mdash; %(docstitle)s"
+msgstr "%(filename)s &mdash; %(docstitle)s"
+
+#: sphinx/templates/changes/versionchanges.html:17
+#, python-format
+msgid "Automatically generated list of changes in version %(version)s"
+msgstr "Lista delle modifiche generata automaticamente nella versione %(version)s"
+
+#: sphinx/templates/changes/versionchanges.html:18
+msgid "Library changes"
+msgstr "Modifiche nela libreria"
+
+#: sphinx/templates/changes/versionchanges.html:23
+msgid "C API changes"
+msgstr "Modifche nelle API C"
+
+#: sphinx/templates/changes/versionchanges.html:25
+msgid "Other changes"
+msgstr "Altre modifiche"
+
diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py
index 1f076575..8c5ffa3d 100644
--- a/sphinx/quickstart.py
+++ b/sphinx/quickstart.py
@@ -6,7 +6,7 @@
Quickly setup documentation source to work with Sphinx.
:copyright: 2008 by Georg Brandl.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
import sys, os, time
@@ -165,8 +165,8 @@ html_static_path = ['%(dot)sstatic']
# If true, the index is split into individual pages for each letter.
#html_split_index = False
-# If true, the reST sources are included in the HTML build as _sources/<name>.
-#html_copy_source = True
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
diff --git a/sphinx/roles.py b/sphinx/roles.py
index d2811238..d942ddc9 100644
--- a/sphinx/roles.py
+++ b/sphinx/roles.py
@@ -6,7 +6,7 @@
Handlers for additional ReST roles.
:copyright: 2007-2008 by Georg Brandl.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
import re
@@ -96,6 +96,7 @@ innernodetypes = {
'term': nodes.emphasis,
'token': nodes.strong,
'envvar': nodes.strong,
+ 'download': nodes.strong,
'option': addnodes.literal_emphasis,
}
@@ -122,8 +123,10 @@ def xfileref_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
return [innernodetypes.get(typ, nodes.literal)(
rawtext, text, classes=['xref'])], []
# we want a cross-reference, create the reference node
- pnode = addnodes.pending_xref(rawtext, reftype=typ, refcaption=False,
- modname=env.currmodule, classname=env.currclass)
+ nodeclass = (typ == 'download') and addnodes.download_reference or \
+ addnodes.pending_xref
+ pnode = nodeclass(rawtext, reftype=typ, refcaption=False,
+ modname=env.currmodule, classname=env.currclass)
# we may need the line number for warnings
pnode.line = lineno
# the link title may differ from the target, but by default they are the same
@@ -235,6 +238,8 @@ specific_docroles = {
'token': xfileref_role,
'term': xfileref_role,
'option': xfileref_role,
+ 'doc': xfileref_role,
+ 'download': xfileref_role,
'menuselection': menusel_role,
'file': emph_literal_role,
diff --git a/sphinx/search.py b/sphinx/search.py
index 46ff986d..a823c8f3 100644
--- a/sphinx/search.py
+++ b/sphinx/search.py
@@ -6,7 +6,7 @@
Create a search index for offline search.
:copyright: 2007-2008 by Armin Ronacher.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
import re
import cPickle as pickle
diff --git a/sphinx/templates/layout.html b/sphinx/templates/layout.html
index d9c9045d..e49a0c81 100644
--- a/sphinx/templates/layout.html
+++ b/sphinx/templates/layout.html
@@ -51,32 +51,26 @@
<p class="topless"><a href="{{ next.link|e }}" title="{{ _('next chapter') }}">{{ next.title }}</a></p>
{%- endif %}
{%- endblock %}
- {%- if sourcename %}
+ {%- block sidebarsourcelink %}
+ {%- if show_source and has_source %}
<h3>{{ _('This Page') }}</h3>
<ul class="this-page-menu">
- {%- if builder == 'web' %}
- <li><a href="#comments">Comments ({{ comments|length }} so far)</a></li>
- <li><a href="{{ pathto('@edit/' + sourcename)|e }}">{{ _('Suggest Change') }}</a></li>
- <li><a href="{{ pathto('@source/' + sourcename)|e }}">{{ _('Show Source') }}</a></li>
- {%- elif builder == 'html' %}
- <li><a href="{{ pathto('_sources/' + sourcename, true)|e }}">{{ _('Show Source') }}</a></li>
- {%- endif %}
+ <li><a href="{{ pathto('_sources/' + sourcename, true)|e }}" rel="nofollow">{{ _('Show Source') }}</a></li>
</ul>
{%- endif %}
+ {%- endblock %}
{%- if customsidebar %}
{{ rendertemplate(customsidebar) }}
{%- endif %}
{%- block sidebarsearch %}
{%- if pagename != "search" %}
- <h3>{% if builder == 'web' %}{{ _('Keyword search')}}{% else %}{{ _('Quick search') }}{% endif %}</h3>
+ <h3>{{ _('Quick search') }}</h3>
<form class="search" action="{{ pathto('search') }}" method="get">
<input type="text" name="q" size="18" /> <input type="submit" value="{{ _('Go') }}" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
- {%- if builder == 'web' %}
- <p style="font-size: 90%">{{ _('Enter a module, class or function name.') }}</p>
- {%- endif %}
+ <p style="font-size: 90%">{{ _('Enter search terms or a module, class or function name.') }}</p>
{%- endif %}
{%- endblock %}
</div>
@@ -92,16 +86,8 @@
{%- set titlesuffix = " &mdash; " + docstitle|e %}
{%- endif %}
<title>{{ title|striptags }}{{ titlesuffix }}</title>
- {%- if builder == 'web' %}
- <link rel="stylesheet" href="{{ pathto('index') }}?do=stylesheet{%
- if in_admin_panel %}&admin=yes{% endif %}" type="text/css" />
- {%- for link, type, title in page_links %}
- <link rel="alternate" type="{{ type|e(true) }}" title="{{ title|e(true) }}" href="{{ link|e(true) }}" />
- {%- endfor %}
- {%- else %}
<link rel="stylesheet" href="{{ pathto('_static/' + style, 1) }}" type="text/css" />
<link rel="stylesheet" href="{{ pathto('_static/pygments.css', 1) }}" type="text/css" />
- {%- endif %}
{%- if builder != 'htmlhelp' %}
<script type="text/javascript">
var DOCUMENTATION_OPTIONS = {
diff --git a/sphinx/templates/modindex.html b/sphinx/templates/modindex.html
index d6b505da..7a83eb46 100644
--- a/sphinx/templates/modindex.html
+++ b/sphinx/templates/modindex.html
@@ -11,26 +11,6 @@
{% block body %}
<h1 id="global-module-index">{{ _('Global Module Index') }}</h1>
-{% if builder == 'web' and freqentries %}
- <p>{{ _('Most popular modules:') }}</p>
- <div class="modulecloud">
- {%- for module in freqentries %}
- <a href="../q/{{ module.name|e }}/" style="font-size: {{ module.size }}%">{{ module.name|e }}</a>
- {%- endfor %}
- </div>
-{% endif %}
-{% if builder == 'web' %}
- <form class="pfform" action="" method="get">
- {{ _('Show modules only available on these platforms') }}:<br>
- {% for pl in platforms -%}
- <input type="checkbox" name="pf" value="{{ pl }}" id="pl-{{ pl }}"
- {%- if pl in showpf %} checked="checked"{% endif %}>
- <label for="pl-{{ pl }}">{{ pl }}</label>
- {% endfor %}
- <input type="hidden" name="newpf" value="true">
- <input type="submit" value="Apply">
- </form>
-{% endif %}
{%- for letter in letters %}
<a href="#cap-{{ letter }}"><strong>{{ letter }}</strong></a> {% if not loop.last %}| {% endif %}
diff --git a/sphinx/templates/page.html b/sphinx/templates/page.html
index 4de23b13..17a93016 100644
--- a/sphinx/templates/page.html
+++ b/sphinx/templates/page.html
@@ -1,12 +1,4 @@
{% extends "layout.html" %}
-{% set page_links = [
- (pathto('@rss/' + sourcename), 'application/rss+xml', 'Page Comments'),
-] %}
{% block body %}
- {% if oldurl %}
- <div class="docwarning">
- {% trans %}<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.{% endtrans %}
- </div>
- {% endif %}
{{ body }}
{% endblock %}
diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py
index 13b53ac7..e25bc5a1 100644
--- a/sphinx/util/__init__.py
+++ b/sphinx/util/__init__.py
@@ -6,7 +6,7 @@
Utility functions for Sphinx.
:copyright: 2007-2008 by Georg Brandl.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
import os
@@ -15,6 +15,7 @@ import sys
import time
import fnmatch
import tempfile
+import posixpath
import traceback
from os import path
@@ -48,6 +49,11 @@ def relative_uri(base, to):
return ('..' + SEP) * (len(b2)-1) + SEP.join(t2)
+def docname_join(basedocname, docname):
+ return posixpath.normpath(
+ posixpath.join('/' + basedocname, '..', docname))[1:]
+
+
def ensuredir(path):
"""Ensure that a path exists."""
try:
@@ -282,3 +288,39 @@ def nested_parse_with_titles(state, content, node):
def ustrftime(format, *args):
# strftime for unicode strings
return time.strftime(unicode(format).encode('utf-8'), *args).decode('utf-8')
+
+
+class FilenameUniqDict(dict):
+ """
+ A dictionary that automatically generates unique names for its keys,
+ interpreted as filenames, and keeps track of a set of docnames they
+ appear in. Used for images and downloadable files in the environment.
+ """
+ def __init__(self):
+ self._existing = set()
+
+ def add_file(self, docname, newfile):
+ if newfile in self:
+ self[newfile][0].add(docname)
+ return
+ uniquename = path.basename(newfile)
+ base, ext = path.splitext(uniquename)
+ i = 0
+ while uniquename in self._existing:
+ i += 1
+ uniquename = '%s%s%s' % (base, i, ext)
+ self[newfile] = (set([docname]), uniquename)
+ self._existing.add(uniquename)
+ return uniquename
+
+ def purge_doc(self, docname):
+ for filename, (docs, _) in self.items():
+ docs.discard(docname)
+ if not docs:
+ del self[filename]
+
+ def __getstate__(self):
+ return self._existing
+
+ def __setstate__(self, state):
+ self._existing = state
diff --git a/sphinx/util/compat.py b/sphinx/util/compat.py
index 208b8218..05a1c416 100644
--- a/sphinx/util/compat.py
+++ b/sphinx/util/compat.py
@@ -6,7 +6,7 @@
Stuff for docutils compatibility.
:copyright: 2008 by Georg Brandl.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
from docutils import nodes
diff --git a/sphinx/util/console.py b/sphinx/util/console.py
index d6ebcb94..a783f700 100644
--- a/sphinx/util/console.py
+++ b/sphinx/util/console.py
@@ -6,7 +6,7 @@
Format colored console output.
:copyright: 2007-2008 by Georg Brandl.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
import os
diff --git a/sphinx/util/jsdump.py b/sphinx/util/jsdump.py
index 3d246d0c..d7f8f5a8 100644
--- a/sphinx/util/jsdump.py
+++ b/sphinx/util/jsdump.py
@@ -7,7 +7,7 @@
Uses the basestring encode function from simplejson.
:copyright: 2008 by Armin Ronacher, Bob Ippolito, Georg Brandl.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
import re
diff --git a/sphinx/util/png.py b/sphinx/util/png.py
index a22acba2..feafc463 100644
--- a/sphinx/util/png.py
+++ b/sphinx/util/png.py
@@ -6,7 +6,7 @@
PNG image manipulation helpers.
:copyright: 2008 by Georg Brandl.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
import struct
diff --git a/sphinx/util/texescape.py b/sphinx/util/texescape.py
index 8412a3a3..41fc042c 100644
--- a/sphinx/util/texescape.py
+++ b/sphinx/util/texescape.py
@@ -6,7 +6,7 @@
TeX escaping helper.
:copyright: 2008 by Georg Brandl.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
tex_replacements = [
diff --git a/sphinx/writers/__init__.py b/sphinx/writers/__init__.py
new file mode 100644
index 00000000..454f54eb
--- /dev/null
+++ b/sphinx/writers/__init__.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.writers
+ ~~~~~~~~~~~~~~
+
+ Custom docutils writers.
+
+ :copyright: 2008 by Georg Brandl.
+ :license: BSD, see LICENSE for details.
+"""
diff --git a/sphinx/htmlwriter.py b/sphinx/writers/html.py
index 0505fd08..69c1de72 100644
--- a/sphinx/htmlwriter.py
+++ b/sphinx/writers/html.py
@@ -1,12 +1,12 @@
# -*- coding: utf-8 -*-
"""
- sphinx.htmlwriter
- ~~~~~~~~~~~~~~~~~
+ sphinx.writers.html
+ ~~~~~~~~~~~~~~~~~~~
docutils writers handling Sphinx' custom nodes.
:copyright: 2007-2008 by Georg Brandl.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
import sys
@@ -58,6 +58,7 @@ 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
def visit_desc(self, node):
self.body.append(self.starttag(node, 'dl', CLASS=node['desctype']))
@@ -73,7 +74,7 @@ class HTMLTranslator(BaseTranslator):
if node.parent['desctype'] in ('class', 'exception'):
self.body.append('%s ' % node.parent['desctype'])
def depart_desc_signature(self, node):
- if node['ids'] and self.builder.add_definition_links:
+ if node['ids'] and self.add_permalinks 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'))
@@ -89,6 +90,11 @@ class HTMLTranslator(BaseTranslator):
def depart_desc_type(self, node):
pass
+ def visit_desc_returns(self, node):
+ self.body.append(' &rarr; ')
+ def depart_desc_returns(self, node):
+ pass
+
def visit_desc_name(self, node):
self.body.append(self.starttag(node, 'tt', '', CLASS='descname'))
def depart_desc_name(self, node):
@@ -253,6 +259,16 @@ class HTMLTranslator(BaseTranslator):
def depart_highlightlang(self, node):
pass
+ def visit_download_reference(self, node):
+ if node.hasattr('filename'):
+ self.body.append('<a href="%s">' % posixpath.join(
+ self.builder.dlpath, node['filename']))
+ self.context.append('</a>')
+ else:
+ self.context.append('')
+ def depart_download_reference(self, node):
+ self.body.append(self.context.pop())
+
# overwritten
def visit_image(self, node):
olduri = node['uri']
@@ -388,7 +404,7 @@ class HTMLTranslator(BaseTranslator):
def depart_title(self, node):
close_tag = self.context[-1]
- if self.builder.add_header_links and \
+ if self.add_permalinks and self.builder.add_permalinks and \
(close_tag.startswith('</h') or
close_tag.startswith('</a></h')) and \
node.parent.hasattr('ids') and node.parent['ids']:
diff --git a/sphinx/latexwriter.py b/sphinx/writers/latex.py
index 3d01f3b7..7695d6c5 100644
--- a/sphinx/latexwriter.py
+++ b/sphinx/writers/latex.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
"""
- sphinx.latexwriter
- ~~~~~~~~~~~~~~~~~~
+ sphinx.writers.latex
+ ~~~~~~~~~~~~~~~~~~~~
Custom docutils writer for LaTeX.
@@ -9,7 +9,7 @@
docutils sandbox.
:copyright: 2007-2008 by Georg Brandl, Dave Kuhlman.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
import re
@@ -91,7 +91,7 @@ class LaTeXWriter(writers.Writer):
class ExtBabel(Babel):
def get_shorthandoff(self):
shortlang = self.language.split('_')[0]
- if shortlang in ('de', 'sl', 'pt', 'es', 'nl', 'pl'):
+ if shortlang in ('de', 'sl', 'pt', 'es', 'nl', 'pl', 'it'):
return '\\shorthandoff{"}'
return ''
@@ -112,7 +112,8 @@ class Table(object):
class Desc(object):
def __init__(self, node):
self.env = LaTeXTranslator.desc_map.get(node['desctype'], 'describe')
- self.type = self.cls = self.name = self.params = self.annotation = ''
+ self.type = self.cls = self.name = self.params = \
+ self.annotation = self.returns = ''
self.count = 0
@@ -237,6 +238,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
# ... and all others are the appendices
self.body.append('\n\\appendix\n')
self.first_document = -1
+ if 'docname' in node:
+ self.body.append('\\hypertarget{--doc-%s}{}' % node['docname'])
# "- 1" because the level is increased before the title is visited
self.sectionlevel = self.top_sectionlevel - 1
def depart_document(self, node):
@@ -259,6 +262,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.body.append('\n\\resetcurrentobjects\n')
# and also, new footnotes
self.footnotestack.append(self.collect_footnotes(node))
+ # also add a document target
+ self.body.append('\\hypertarget{--doc-%s}{}' % node['docname'])
def collect_footnotes(self, node):
fnotes = {}
@@ -477,6 +482,14 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.descstack[-1].type = self.encode(node.astext().strip())
raise nodes.SkipNode
+ def visit_desc_returns(self, node):
+ d = self.descstack[-1]
+ if d.env == 'describe':
+ d.name += ' $\\rightarrow$ ' + self.encode(node.astext())
+ else:
+ self.descstack[-1].returns = self.encode(node.astext().strip())
+ raise nodes.SkipNode
+
def visit_desc_name(self, node):
d = self.descstack[-1]
if d.env == 'describe':
@@ -923,6 +936,11 @@ class LaTeXTranslator(nodes.NodeVisitor):
elif uri.startswith('#'):
self.body.append('\\hyperlink{%s}{' % uri[1:])
self.context.append('}')
+ elif uri.startswith('%'):
+ hashindex = uri.find('#')
+ targetname = (hashindex == -1) and '--doc-' + uri[1:] or uri[hashindex+1:]
+ self.body.append('\\hyperlink{%s}{' % targetname)
+ self.context.append('}')
elif uri.startswith('@token'):
if self.in_production_list:
self.body.append('\\token{')
@@ -935,6 +953,11 @@ class LaTeXTranslator(nodes.NodeVisitor):
def depart_reference(self, node):
self.body.append(self.context.pop())
+ def visit_download_reference(self, node):
+ pass
+ def depart_download_reference(self, node):
+ pass
+
def visit_pending_xref(self, node):
pass
def depart_pending_xref(self, node):
diff --git a/sphinx/textwriter.py b/sphinx/writers/text.py
index 4aa18039..f54a6386 100644
--- a/sphinx/textwriter.py
+++ b/sphinx/writers/text.py
@@ -1,12 +1,12 @@
# -*- coding: utf-8 -*-
"""
- sphinx.textwriter
- ~~~~~~~~~~~~~~~~~
+ sphinx.writers.text
+ ~~~~~~~~~~~~~~~~~~~
Custom docutils writer for plain text.
:copyright: 2008 by Georg Brandl.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
import re
@@ -195,6 +195,11 @@ class TextTranslator(nodes.NodeVisitor):
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
@@ -609,6 +614,11 @@ class TextTranslator(nodes.NodeVisitor):
def depart_reference(self, node):
pass
+ def visit_download_reference(self, node):
+ pass
+ def depart_download_reference(self, node):
+ pass
+
def visit_emphasis(self, node):
self.add_text('*')
def depart_emphasis(self, node):
diff --git a/tests/root/includes.txt b/tests/root/includes.txt
index ad507fc6..d2964d3f 100644
--- a/tests/root/includes.txt
+++ b/tests/root/includes.txt
@@ -14,3 +14,11 @@ Test file and literal inclusion
:encoding: latin-1
.. include:: wrongenc.inc
:encoding: latin-1
+
+
+Testing downloadable files
+==========================
+
+Download :download:`img.png` here.
+Download :download:`this <subdir/img.png>` there.
+Don't download :download:`this <nonexisting.png>`.
diff --git a/tests/run.py b/tests/run.py
index f67a3432..e8f6e86d 100755
--- a/tests/run.py
+++ b/tests/run.py
@@ -7,7 +7,7 @@
This script runs the Sphinx unit test suite.
:copyright: 2008 by Georg Brandl.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
import sys
diff --git a/tests/test_application.py b/tests/test_application.py
index e00e990f..b4029d3b 100644
--- a/tests/test_application.py
+++ b/tests/test_application.py
@@ -6,7 +6,7 @@
Test the Sphinx class.
:copyright: 2008 by Benjamin Peterson.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
from StringIO import StringIO
diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py
index ee3fdf1d..cca1848c 100644
--- a/tests/test_autodoc.py
+++ b/tests/test_autodoc.py
@@ -7,7 +7,7 @@
directives are tested in a test source file translated by test_build.
:copyright: 2008 by Georg Brandl.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
from util import *
@@ -24,6 +24,7 @@ def setup_module():
app.builder.env.app = app
app.connect('autodoc-process-docstring', process_docstring)
app.connect('autodoc-process-signature', process_signature)
+ app.connect('autodoc-skip-member', skip_member)
options = Struct(
inherited_members = False,
@@ -71,44 +72,51 @@ def process_signature(app, what, name, obj, options, args, retann):
return '42', None
+def skip_member(app, what, name, obj, skip, options):
+ if name.startswith('_'):
+ return True
+ if name == 'skipmeth':
+ return True
+
+
def test_resolve_name():
# for modules
assert gen.resolve_name('module', 'test_autodoc') == \
- ('test_autodoc', 'test_autodoc', [], None, None)
+ ('test_autodoc', [], None, None)
assert gen.resolve_name('module', 'test.test_autodoc') == \
- ('test.test_autodoc', 'test.test_autodoc', [], None, None)
+ ('test.test_autodoc', [], None, None)
assert gen.resolve_name('module', 'test(arg)') == \
- ('test', 'test', [], None, None)
+ ('test', [], None, None)
assert 'ignoring signature arguments' in gen.warnings[0]
del gen.warnings[:]
# for functions/classes
assert gen.resolve_name('function', 'util.raises') == \
- ('util.raises', 'util', ['raises'], None, None)
+ ('util', ['raises'], None, None)
assert gen.resolve_name('function', 'util.raises(exc) -> None') == \
- ('util.raises', 'util', ['raises'], 'exc', ' -> None')
+ ('util', ['raises'], 'exc', 'None')
gen.env.autodoc_current_module = 'util'
assert gen.resolve_name('function', 'raises') == \
- ('raises', 'util', ['raises'], None, None)
+ ('util', ['raises'], None, None)
gen.env.autodoc_current_module = None
gen.env.currmodule = 'util'
assert gen.resolve_name('function', 'raises') == \
- ('raises', 'util', ['raises'], None, None)
+ ('util', ['raises'], None, None)
assert gen.resolve_name('class', 'TestApp') == \
- ('TestApp', 'util', ['TestApp'], None, None)
+ ('util', ['TestApp'], None, None)
# for members
gen.env.currmodule = 'foo'
assert gen.resolve_name('method', 'util.TestApp.cleanup') == \
- ('util.TestApp.cleanup', 'util', ['TestApp', 'cleanup'], None, None)
+ ('util', ['TestApp', 'cleanup'], None, None)
gen.env.currmodule = 'util'
gen.env.currclass = 'Foo'
gen.env.autodoc_current_class = 'TestApp'
assert gen.resolve_name('method', 'cleanup') == \
- ('cleanup', 'util', ['TestApp', 'cleanup'], None, None)
+ ('util', ['TestApp', 'cleanup'], None, None)
assert gen.resolve_name('method', 'TestApp.cleanup') == \
- ('TestApp.cleanup', 'util', ['TestApp', 'cleanup'], None, None)
+ ('util', ['TestApp', 'cleanup'], None, None)
# and clean up
gen.env.currmodule = None
@@ -126,7 +134,7 @@ def test_format_signature():
assert gen.format_signature('function', 'f', f, None, None) == '(a, b, c=1, **d)'
assert gen.format_signature('function', 'f', f, 'a, b, c, d', None) == \
'(a, b, c, d)'
- assert gen.format_signature('function', 'f', f, None, ' -> None') == \
+ assert gen.format_signature('function', 'f', f, None, 'None') == \
'(a, b, c=1, **d) -> None'
# test for classes
@@ -144,7 +152,7 @@ def test_format_signature():
pass
for C in (F, G):
assert gen.format_signature('class', 'C', C, None, None) == '(a, b=None)'
- assert gen.format_signature('class', 'C', D, 'a, b', ' -> X') == '(a, b) -> X'
+ assert gen.format_signature('class', 'C', D, 'a, b', 'X') == '(a, b) -> X'
# test for methods
class H:
@@ -313,17 +321,17 @@ def test_generate():
assert_works('exception', 'test_autodoc.CustomEx', [], None)
# test diverse inclusion settings for members
- should = [('class', 'Class')]
+ should = [('class', 'test_autodoc.Class')]
assert_processes(should, 'class', 'Class', [], None)
- should.extend([('method', 'Class.meth')])
+ should.extend([('method', 'test_autodoc.Class.meth')])
assert_processes(should, 'class', 'Class', ['meth'], None)
- should.extend([('attribute', 'Class.prop')])
+ should.extend([('attribute', 'test_autodoc.Class.prop')])
assert_processes(should, 'class', 'Class', ['__all__'], None)
options.undoc_members = True
- should.append(('method', 'Class.undocmeth'))
+ should.append(('method', 'test_autodoc.Class.undocmeth'))
assert_processes(should, 'class', 'Class', ['__all__'], None)
options.inherited_members = True
- should.append(('method', 'Class.inheritedmeth'))
+ should.append(('method', 'test_autodoc.Class.inheritedmeth'))
assert_processes(should, 'class', 'Class', ['__all__'], None)
# test module flags
@@ -355,6 +363,12 @@ def test_generate():
assert_result_contains('.. class:: CustomDict', 'class', 'CustomDict',
['__all__'], None)
+ # test inner class handling
+ assert_processes([('class', 'test_autodoc.Outer'),
+ ('class', 'test_autodoc.Outer.Inner'),
+ ('method', 'test_autodoc.Outer.Inner.meth')],
+ 'class', 'Outer', ['__all__'], None)
+
# --- generate fodder ------------
@@ -380,6 +394,10 @@ class Class(Base):
def undocmeth(self):
pass
+ def skipmeth(self):
+ """Method that should be skipped."""
+ pass
+
@property
def prop(self):
"""Property."""
@@ -392,3 +410,15 @@ def function(foo, *args, **kwds):
Return spam.
"""
pass
+
+
+class Outer(object):
+ """Foo"""
+
+ class Inner(object):
+ """Foo"""
+
+ def meth(self):
+ """Foo"""
+
+ factory = dict
diff --git a/tests/test_build.py b/tests/test_build.py
index b48cccc3..91506dad 100644
--- a/tests/test_build.py
+++ b/tests/test_build.py
@@ -6,7 +6,7 @@
Test the entire build process with the test root.
:copyright: 2008 by Georg Brandl.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
import os
@@ -19,8 +19,9 @@ from subprocess import Popen, PIPE
from util import *
from etree13 import ElementTree as ET
-from sphinx.builder import StandaloneHTMLBuilder, LaTeXBuilder
-from sphinx.latexwriter import LaTeXTranslator
+from sphinx.builders.html import StandaloneHTMLBuilder
+from sphinx.builders.latex import LaTeXBuilder
+from sphinx.writers.latex import LaTeXTranslator
html_warnfile = StringIO()
@@ -31,6 +32,7 @@ WARNING: %(root)s/images.txt:9: Image file not readable: foo.png
WARNING: %(root)s/images.txt:23: Nonlocal image URI found: http://www.python.org/logo.png
WARNING: %(root)s/includes.txt:: (WARNING/2) Encoding 'utf-8' used for reading included \
file u'wrongenc.inc' seems to be wrong, try giving an :encoding: option
+WARNING: %(root)s/includes.txt:34: Download file not readable: nonexisting.png
"""
HTML_WARNINGS = ENV_WARNINGS + """\
@@ -57,6 +59,8 @@ HTML_XPATH = {
'includes.html': {
".//pre/span[@class='s']": u'üöä',
".//pre": u'Max Strauß',
+ ".//a[@href='_downloads/img.png']": '',
+ ".//a[@href='_downloads/img1.png']": '',
},
'autodoc.html': {
".//dt[@id='test_autodoc.Class']": '',
diff --git a/tests/test_config.py b/tests/test_config.py
index 57d1936b..f49eb4fb 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -7,7 +7,7 @@
Application class.
:copyright: 2008 by Georg Brandl.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
from util import *
@@ -15,7 +15,8 @@ from util import *
from sphinx.application import ExtensionError
-@with_app(confoverrides={'master_doc': 'master', 'nonexisting_value': 'True'})
+@with_app(confoverrides={'master_doc': 'master', 'nonexisting_value': 'True',
+ 'latex_elements.docclass': 'scrartcl'})
def test_core_config(app):
cfg = app.config
@@ -26,6 +27,7 @@ def test_core_config(app):
# overrides
assert cfg.master_doc == 'master'
+ assert cfg.latex_elements['docclass'] == 'scrartcl'
# simple default values
assert 'exclude_dirs' not in cfg.__dict__
diff --git a/tests/test_coverage.py b/tests/test_coverage.py
index 82279f50..64bba45f 100644
--- a/tests/test_coverage.py
+++ b/tests/test_coverage.py
@@ -6,7 +6,7 @@
Test the coverage builder.
:copyright: 2008 by Georg Brandl.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
import pickle
diff --git a/tests/test_env.py b/tests/test_env.py
index ec651957..1040b88a 100644
--- a/tests/test_env.py
+++ b/tests/test_env.py
@@ -6,13 +6,14 @@
Test the BuildEnvironment class.
:copyright: 2008 by Georg Brandl.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
from util import *
from sphinx.environment import BuildEnvironment
-from sphinx.builder import StandaloneHTMLBuilder, LaTeXBuilder
+from sphinx.builders.html import StandaloneHTMLBuilder
+from sphinx.builders.latex import LaTeXBuilder
app = env = None
warnings = []
@@ -76,11 +77,13 @@ def test_second_update():
(root / 'new.txt').write_text('New file\n========\n')
it = env.update(app.config, app.srcdir, app.doctreedir, app)
msg = it.next()
- assert '1 added, 1 changed, 1 removed' in msg
+ assert '1 added, 2 changed, 1 removed' in msg
docnames = set()
for docname in it:
docnames.add(docname)
- assert docnames == set(['contents', 'new'])
+ # "includes" is in there because it contains a reference to a nonexisting
+ # downloadable file, which is given another chance to exist
+ assert docnames == set(['contents', 'new', 'includes'])
assert 'images' not in env.all_docs
assert 'images' not in env.found_docs
diff --git a/tests/test_highlighting.py b/tests/test_highlighting.py
new file mode 100644
index 00000000..1d16b982
--- /dev/null
+++ b/tests/test_highlighting.py
@@ -0,0 +1,51 @@
+# -*- coding: utf-8 -*-
+"""
+ test_highlighting
+ ~~~~~~~~~~~~~~~~~
+
+ Test the Pygments highlighting bridge.
+
+ :copyright: 2008 by Georg Brandl.
+ :license: BSD, see LICENSE for details.
+"""
+
+from util import *
+
+from pygments.lexer import RegexLexer
+from pygments.token import Text, Name
+from pygments.formatters.html import HtmlFormatter
+
+from sphinx.highlighting import PygmentsBridge
+
+
+class MyLexer(RegexLexer):
+ name = 'testlexer'
+
+ tokens = {
+ 'root': [
+ ('a', Name),
+ ('b', Text),
+ ],
+ }
+
+class MyFormatter(HtmlFormatter):
+ def format(self, tokensource, outfile):
+ outfile.write('test')
+
+
+@with_app()
+def test_add_lexer(app):
+ app.add_lexer('test', MyLexer())
+
+ bridge = PygmentsBridge('html')
+ ret = bridge.highlight_block('ab', 'test')
+ assert '<span class="n">a</span>b' in ret
+
+def test_set_formatter():
+ PygmentsBridge.html_formatter = MyFormatter
+ try:
+ bridge = PygmentsBridge('html')
+ ret = bridge.highlight_block('foo', 'python')
+ assert ret == 'test'
+ finally:
+ PygmentsBridge.html_formatter = HtmlFormatter
diff --git a/tests/test_i18n.py b/tests/test_i18n.py
index da0af246..636b7007 100644
--- a/tests/test_i18n.py
+++ b/tests/test_i18n.py
@@ -6,7 +6,7 @@
Test locale features.
:copyright: 2008 by Georg Brandl.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
from util import *
diff --git a/tests/test_markup.py b/tests/test_markup.py
index f1125103..86889a2a 100644
--- a/tests/test_markup.py
+++ b/tests/test_markup.py
@@ -6,7 +6,7 @@
Test various Sphinx-specific markup extensions.
:copyright: 2008 by Georg Brandl.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
import re
@@ -17,8 +17,8 @@ from docutils import frontend, utils, nodes
from docutils.parsers import rst
from sphinx import addnodes
-from sphinx.htmlwriter import HTMLWriter, SmartyPantsHTMLTranslator
-from sphinx.latexwriter import LaTeXWriter, LaTeXTranslator
+from sphinx.writers.html import HTMLWriter, SmartyPantsHTMLTranslator
+from sphinx.writers.latex import LaTeXWriter, LaTeXTranslator
def setup_module():
global app, settings, parser
diff --git a/tests/test_quickstart.py b/tests/test_quickstart.py
index a76a1cdf..02954525 100644
--- a/tests/test_quickstart.py
+++ b/tests/test_quickstart.py
@@ -6,7 +6,7 @@
Test the sphinx.quickstart module.
:copyright: 2008 by Georg Brandl.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
import sys
diff --git a/tests/util.py b/tests/util.py
index c4c53592..01ef5503 100644
--- a/tests/util.py
+++ b/tests/util.py
@@ -4,7 +4,7 @@
~~~~~~~~~~~~~~~~~~~~~~~~~~~
:copyright: 2008 by Georg Brandl.
- :license: BSD.
+ :license: BSD, see LICENSE for details.
"""
import sys
@@ -19,7 +19,7 @@ except ImportError:
# functools is new in 2.4
wraps = lambda f: (lambda w: w)
-from sphinx import application, builder
+from sphinx import application
from path import path