summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYoshiki Shibukawa <yoshiki@shibu.jp>2014-01-22 00:10:09 -0800
committerYoshiki Shibukawa <yoshiki@shibu.jp>2014-01-22 00:10:09 -0800
commit5a6a7d60281dbbb7b46e560766e5490147126bdc (patch)
tree8d1543c47b5b9a73fd30c04db50937d6f28e67b4
parent6fb5828937fcf407d3931d98e57bfc98f37aafc8 (diff)
parent930d996dbe5bcccbb0d86de5940376b70ed7c3db (diff)
downloadsphinx-5a6a7d60281dbbb7b46e560766e5490147126bdc.tar.gz
merge default
-rw-r--r--AUTHORS1
-rw-r--r--CHANGES74
-rw-r--r--doc/Makefile160
-rw-r--r--doc/conf.py1
-rw-r--r--doc/config.rst54
-rw-r--r--doc/contents.rst1
-rw-r--r--doc/devguide.rst31
-rw-r--r--doc/ext/autodoc.rst24
-rw-r--r--doc/ext/example_google.py223
-rw-r--r--doc/ext/example_google.rst15
-rw-r--r--doc/ext/example_numpy.py272
-rw-r--r--doc/ext/example_numpy.rst15
-rw-r--r--doc/ext/napoleon.rst375
-rw-r--r--doc/extdev/appapi.rst (renamed from doc/ext/appapi.rst)89
-rw-r--r--doc/extdev/builderapi.rst (renamed from doc/ext/builderapi.rst)4
-rw-r--r--doc/extdev/domainapi.rst14
-rw-r--r--doc/extdev/envapi.rst54
-rw-r--r--doc/extdev/index.rst33
-rw-r--r--doc/extdev/markupapi.rst11
-rw-r--r--doc/extdev/nodes.rst57
-rw-r--r--doc/extdev/tutorial.rst (renamed from doc/ext/tutorial.rst)79
-rw-r--r--doc/extensions.rst33
-rw-r--r--doc/install.rst6
-rw-r--r--doc/intro.rst2
-rw-r--r--doc/invocation.rst4
-rw-r--r--doc/markup/code.rst19
-rw-r--r--doc/markup/toctree.rst9
-rw-r--r--setup.py12
-rwxr-xr-xsphinx-build.py7
-rw-r--r--sphinx/__init__.py11
-rw-r--r--sphinx/apidoc.py57
-rw-r--r--sphinx/application.py40
-rw-r--r--sphinx/builders/__init__.py7
-rw-r--r--sphinx/builders/epub.py8
-rw-r--r--sphinx/builders/html.py25
-rw-r--r--sphinx/builders/linkcheck.py3
-rw-r--r--sphinx/cmdline.py43
-rw-r--r--sphinx/config.py4
-rw-r--r--sphinx/directives/code.py10
-rw-r--r--sphinx/domains/cpp.py1
-rw-r--r--sphinx/environment.py34
-rw-r--r--sphinx/ext/autodoc.py42
-rw-r--r--sphinx/ext/graphviz.py3
-rw-r--r--sphinx/ext/inheritance_diagram.py26
-rw-r--r--sphinx/ext/napoleon/__init__.py375
-rw-r--r--sphinx/ext/napoleon/docstring.py714
-rw-r--r--sphinx/ext/napoleon/iterators.py244
-rw-r--r--sphinx/ext/viewcode.py22
-rw-r--r--sphinx/highlighting.py18
-rw-r--r--sphinx/make_mode.py252
-rw-r--r--sphinx/pycode/__init__.py2
-rw-r--r--sphinx/pycode/pgen2/parse.c89
-rw-r--r--sphinx/quickstart.py75
-rw-r--r--sphinx/setup_command.py14
-rw-r--r--sphinx/texinputs/Makefile28
-rw-r--r--sphinx/themes/agogo/static/agogo.css_t7
-rw-r--r--sphinx/themes/basic/searchbox.html2
-rw-r--r--sphinx/themes/basic/static/basic.css_t14
-rw-r--r--sphinx/themes/basic/static/doctools.js3
-rw-r--r--sphinx/themes/default/static/default.css_t5
-rw-r--r--sphinx/themes/epub/static/epub.css7
-rw-r--r--sphinx/themes/nature/static/nature.css_t6
-rw-r--r--sphinx/themes/pyramid/static/pyramid.css_t5
-rw-r--r--sphinx/themes/sphinxdoc/static/sphinxdoc.css_t6
-rw-r--r--sphinx/themes/traditional/static/traditional.css_t4
-rw-r--r--sphinx/transforms.py14
-rw-r--r--sphinx/util/__init__.py23
-rw-r--r--sphinx/util/inspect.py9
-rw-r--r--sphinx/util/jsonimpl.py20
-rw-r--r--sphinx/util/osutil.py9
-rw-r--r--sphinx/util/pycompat.py185
-rw-r--r--sphinx/versioning.py3
-rw-r--r--sphinx/writers/html.py9
-rw-r--r--sphinx/writers/latex.py6
-rw-r--r--tests/root/autodoc.txt2
-rw-r--r--tests/root/autodoc_missing_imports.py9
-rw-r--r--tests/root/conf.py8
-rw-r--r--tests/test_autodoc.py27
-rw-r--r--tests/test_intl.py6
-rw-r--r--tests/test_napoleon.py190
-rw-r--r--tests/test_napoleon_docstring.py259
-rw-r--r--tests/test_napoleon_iterators.py346
-rw-r--r--tests/test_versioning.py1
-rw-r--r--tests/test_websupport.py7
-rw-r--r--tests/util.py7
-rw-r--r--tox.ini11
86 files changed, 4205 insertions, 831 deletions
diff --git a/AUTHORS b/AUTHORS
index 2a9dbbac..24b9be74 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -32,6 +32,7 @@ Other contributors, listed alphabetically, are:
* Christopher Perkins -- autosummary integration
* Benjamin Peterson -- unittests
* T. Powers -- HTML output improvements
+* Rob Ruana -- napoleon extension
* Stefan Seefeld -- toctree improvements
* Shibukawa Yoshiki -- pluggable search API and Japanese search
* Antonio Valentino -- qthelp builder
diff --git a/CHANGES b/CHANGES
index 020058ee..2415a382 100644
--- a/CHANGES
+++ b/CHANGES
@@ -4,20 +4,39 @@ Release 1.3 (in development)
Incompatible changes
--------------------
+* Dropped support for Python 2.5 and 3.1.
+
* Removed the ``sphinx.ext.oldcmarkup`` extension.
+* The deprecated config values ``exclude_trees``, ``exclude_dirnames`` and
+ ``unused_docs`` have been removed.
+
+* A new node, ``sphinx.addnodes.literal_strong``, has been added, for text that
+ should appear literally (i.e. no smart quotes) in strong font. Custom writers
+ will have to be adapted to handle this node.
+
New features
------------
+* Added ``sphinx.ext.napoleon`` extension for NumPy and Google style docstring
+ support.
+
* PR#202: Allow "." and "~" prefixed references in ``:param:`` doc fields
for Python.
+* PR#184: Add :confval:`autodoc_mock_imports`, allowing to mock imports of
+ external modules that need not be present when autodocumenting.
+
* #925: Allow list-typed config values to be provided on the command line,
like ``-D key=val1,val2``.
* #668: Allow line numbering of ``code-block`` and ``literalinclude`` directives
to start at an arbitrary line number, with a new ``lineno-start`` option.
+* PR#172: The :rst:dir:`code-block` and :rst:dir:`literalinclude` directives now
+ can have a ``filename`` option that shows a filename before the code in the
+ output.
+
* Prompt for the document language in sphinx-quickstart.
Bugs fixed
@@ -25,101 +44,86 @@ Bugs fixed
* #1174: Fix smart quotes being applied inside roles like :rst:role:`program` or
:rst:role:`makevar`.
-
* #1335: Fix autosummary template overloading with exclamation prefix like
``{% extends "!autosummary/class.rst" %}`` cause infinite recursive function
- call. This caused by PR#181.
-
+ call. This was caused by PR#181.
* #1337: Fix autodoc with ``autoclass_content="both"`` uses useless
``object.__init__`` docstring when class does not have ``__init__``.
This was caused by a change for #1138.
-
* #1340: Can't search alphabetical words on the HTML quick search generated
with language='ja'.
-
* #1319: Do not crash if the :confval:`html_logo` file does not exist.
-
* #603: Do not use the HTML-ized title for building the search index (that
resulted in "literal" being found on every page with a literal in the
title).
-
* #751: Allow production lists longer than a page in LaTeX by using longtable.
-
* #764: Always look for stopwords lowercased in JS search.
-
* #814: autodoc: Guard against strange type objects that don't have
``__bases__``.
-
* #932: autodoc: Do not crash if ``__doc__`` is not a string.
-
* #933: Do not crash if an :rst:role:`option` value is malformed (contains
spaces but no option name).
-
* #908: On Python 3, handle error messages from LaTeX correctly in the pngmath
extension.
-
* #943: In autosummary, recognize "first sentences" to pull from the docstring
if they contain uppercase letters.
-
* #923: Take the entire LaTeX document into account when caching
pngmath-generated images. This rebuilds them correctly when
:confval:`pngmath_latex_preamble` changes.
-
* #901: Emit a warning when using docutils' new "math" markup without a Sphinx
math extension active.
-
* #845: In code blocks, when the selected lexer fails, display line numbers
nevertheless if configured.
-
* #929: Support parsed-literal blocks in LaTeX output correctly.
-
* #949: Update the tabulary.sty packed with Sphinx.
-
* #1050: Add anonymous labels into ``objects.inv`` to be referenced via
:mod:`~sphinx.ext.intersphinx`.
-
* #1095: Fix print-media stylesheet being included always in the "scrolls"
theme.
-
* #1085: Fix current classname not getting set if class description has
``:noindex:`` set.
-
* #1181: Report option errors in autodoc directives more gracefully.
-
* #1155: Fix autodocumenting C-defined methods as attributes in Python 3.
-
* #1233: Allow finding both Python classes and exceptions with the "class" and
"exc" roles in intersphinx.
-
* #1198: Allow "image" for the "figwidth" option of the :rst:dir:`figure`
directive as documented by docutils.
-
* #1152: Fix pycode parsing errors of Python 3 code by including two grammar
versions for Python 2 and 3, and loading the appropriate version for the
running Python version.
-
* #1017: Be helpful and tell the user when the argument to :rst:dir:`option`
does not match the required format.
-
* #1345: Fix two bugs with :confval:`nitpick_ignore`; now you don't have to
remove the store environment for changes to have effect.
-
* #1072: In the JS search, fix issues searching for upper-cased words by
lowercasing words before stemming.
-
* #1299: Make behavior of the :rst:dir:`math` directive more consistent and
avoid producing empty environments in LaTeX output.
-
* #1308: Strip HTML tags from the content of "raw" nodes before feeding it
to the search indexer.
-
* #1249: Fix duplicate LaTeX page numbering for manual documents.
-
* #1292: In the linkchecker, retry HEAD requests when denied by HTTP 405.
Also make the redirect code apparent and tweak the output a bit to be
more obvious.
-
* #1285: Avoid name clashes between C domain objects and section titles.
+* #848: Always take the newest code in incremental rebuilds with the
+ :mod:`sphinx.ext.viewcode` extension.
+* #979, #1266: Fix exclude handling in ``sphinx-apidoc``.
+* #1302: Fix regression in :mod:`sphinx.ext.inheritance_diagram` when
+ documenting classes that can't be pickled.
+* #1316: Remove hard-coded ``font-face`` resources from epub theme.
+* #1329: Fix traceback with empty translation msgstr in .po files.
+* #1300: Fix references not working in translated documents in some instances.
+* #1283: Fix a bug in the detection of changed files that would try to access
+ doctrees of deleted documents.
+* #1330: Fix :confval:`exclude_patterns` behavior with subdirectories in the
+ :confval:`html_static_path`.
+* #1323: Fix emitting empty ``<ul>`` tags in the HTML writer, which is not
+ valid HTML.
+* #1147: Don't emit a sidebar search box in the "singlehtml" builder.
+* PR#211: When checking for existence of the :confval:`html_logo` file, check
+ the full relative path and not the basename.
+* PR#212: Fix traceback with autodoc and ``__init__`` methods without docstring.
Documentation
-------------
@@ -372,6 +376,8 @@ Features added
reference documentation in doc directory provides a ``sphinx.pot`` file with
message strings from ``doc/_templates/*.html`` when using ``make gettext``.
+ - PR#61,#703: Add support for non-ASCII filename handling.
+
* Other builders:
- Added the Docutils-native XML and pseudo-XML builders. See
diff --git a/doc/Makefile b/doc/Makefile
index 831c12c5..55909649 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -4,157 +4,15 @@
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = python ../sphinx-build.py
-PAPER =
-
-PAPEROPT_a4 = -D latex_paper_size=a4
-PAPEROPT_letter = -D latex_paper_size=letter
-ALLSPHINXOPTS = -d _build/doctrees $(PAPEROPT_$(PAPER)) \
- $(SPHINXOPTS) $(O) .
-I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(O) .
-
-.PHONY: help clean html dirhtml singlehtml text man pickle json htmlhelp \
- qthelp devhelp epub latex latexpdf changes linkcheck doctest xml \
- pseudoxml
+SPHINXPROJ = sphinx
+SOURCEDIR = .
+BUILDDIR = _build
+# Has to be explicit, otherwise we don't get "make" without targets right.
help:
- @echo "Please use \`make <target>' where <target> is one of"
- @echo " html to make standalone HTML files"
- @echo " dirhtml to make HTML files called index.html in directories"
- @echo " singlehtml to make one big HTML file"
- @echo " text to make text files"
- @echo " man to make manual pages"
- @echo " pickle to make pickle files"
- @echo " json to make json files"
- @echo " htmlhelp to make HTML files and a HTML help project"
- @echo " qthelp to make Qt help files and project"
- @echo " devhelp to make Devhelp files and project"
- @echo " epub to make an epub file"
- @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
- @echo " latexpdf to make LaTeX files and run pdflatex"
- @echo " texinfo to make Texinfo files"
- @echo " info to make Texinfo files and run them through makeinfo"
- @echo " gettext to make PO message catalogs"
- @echo " changes to make an overview over all changed/added/deprecated items"
- @echo " linkcheck to check all external links for integrity"
-
-clean:
- rm -rf _build/*
-
-html:
- $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) _build/html
- @echo
- @echo "Build finished. The HTML pages are in _build/html."
-
-dirhtml:
- $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) _build/dirhtml
- @echo
- @echo "Build finished. The HTML pages are in _build/dirhtml."
-
-singlehtml:
- $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) _build/singlehtml
- @echo
- @echo "Build finished. The HTML page is in _build/singlehtml."
-
-text:
- $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) _build/text
- @echo
- @echo "Build finished."
-
-man:
- $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) _build/man
- @echo
- @echo "Build finished."
-
-pickle:
- $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) _build/pickle
- @echo
- @echo "Build finished."
-
-json:
- $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) _build/json
- @echo
- @echo "Build finished."
-
-htmlhelp:
- $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) _build/htmlhelp
- @echo
- @echo "Build finished; now you can run HTML Help Workshop with the" \
- ".hhp project file in _build/htmlhelp."
-
-qthelp:
- $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) _build/qthelp
- @echo
- @echo "Build finished; now you can run qcollectiongenerator with the" \
- ".qhcp project file in build/qthelp."
- @echo "# qcollectiongenerator _build/qthelp/Sphinx.qhcp"
- @echo "To view the help collection:"
- @echo "# assistant -collectionFile _build/qthelp/Sphinx.qhc"
-
-devhelp:
- $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) _build/devhelp
- @echo
- @echo "Build finished."
- @echo "To view the help file:"
- @echo "# mkdir -p $$HOME/.local/share/devhelp/sphinx"
- @echo "# ln -s _build/devhelp $$HOME/.local/share/devhelp/sphinx"
- @echo "# devhelp"
-
-epub:
- $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) _build/epub
- @echo
- @echo "Build finished. The epub file is in _build/epub."
-
-latex:
- $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex
- @echo
- @echo "Build finished; the LaTeX files are in _build/latex."
- @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
- "run these through (pdf)latex."
-
-latexpdf:
- $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex
- @echo "Running LaTeX files through pdflatex..."
- make -C _build/latex all-pdf
- @echo "pdflatex finished; the PDF files are in _build/latex."
-
-gettext:
- $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) _build/locale
- @echo
- @echo "Build finished. The message catalogs are in _build/locale."
-
-changes:
- $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) _build/changes
- @echo
- @echo "The overview file is in _build/changes."
-
-linkcheck:
- $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) _build/linkcheck
- @echo
- @echo "Link check complete; look for any errors in the above output " \
- "or in _build/linkcheck/output.txt."
-
-doctest:
- $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) _build/doctest
-
-texinfo:
- $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) _build/texinfo
- @echo
- @echo "Build finished. The Texinfo files are in _build/texinfo."
- @echo "Run \`make' in that directory to run these through makeinfo" \
- "(use \`make info' here to do that automatically)."
-
-info:
- $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) _build/texinfo
- @echo "Running Texinfo files through makeinfo..."
- make -C _build/texinfo info
- @echo "makeinfo finished; the Info files are in _build/texinfo."
-
-xml:
- $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) _build/xml
- @echo
- @echo "Build finished. The XML files are in _build/XML."
+ @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
-pseudoxml:
- $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) _build/pseudoxml
- @echo
- @echo "Build finished. The pseudo-XML files are in _build/pseudoxml."
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
+%:
+ @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/doc/conf.py b/doc/conf.py
index 08149fba..9932c8ed 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -5,6 +5,7 @@
import re
import sphinx
+
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.todo',
'sphinx.ext.autosummary', 'sphinx.ext.extlinks']
diff --git a/doc/config.rst b/doc/config.rst
index 181ef2a4..1b196abb 100644
--- a/doc/config.rst
+++ b/doc/config.rst
@@ -45,7 +45,8 @@ Important points to note:
* There is a special object named ``tags`` available in the config file.
It can be used to query and change the tags (see :ref:`tags`). Use
``tags.has('tag')`` to query, ``tags.add('tag')`` and ``tags.remove('tag')``
- to change.
+ to change. Only tags set via the ``-t`` command-line option or via
+ ``tags.add('tag')`` can be queried using ``tags.has('tag')``.
General configuration
@@ -113,38 +114,6 @@ General configuration
.. versionadded:: 1.0
-.. confval:: unused_docs
-
- A list of document names that are present, but not currently included in the
- toctree. Use this setting to suppress the warning that is normally emitted
- in that case.
-
- .. deprecated:: 1.0
- Use :confval:`exclude_patterns` or :ref:`metadata` instead.
-
-.. confval:: exclude_trees
-
- A list of directory paths, relative to the source directory, that are to be
- recursively excluded from the search for source files, that is, their
- subdirectories won't be searched too. The default is ``[]``.
-
- .. versionadded:: 0.4
-
- .. deprecated:: 1.0
- Use :confval:`exclude_patterns` instead.
-
-.. confval:: exclude_dirnames
-
- A list of directory names that are to be excluded from any recursive
- operation Sphinx performs (e.g. searching for source files or copying static
- files). This is useful, for example, to exclude version-control-specific
- directories like ``'CVS'``. The default is ``[]``.
-
- .. versionadded:: 0.5
-
- .. deprecated:: 1.0
- Use :confval:`exclude_patterns` instead.
-
.. confval:: templates_path
A list of paths that contain extra templates (or templates that overwrite
@@ -496,22 +465,25 @@ that use Sphinx' HTMLWriter class.
.. confval:: html_logo
- If given, this must be the name of an image file that is the logo of the
- docs. It is placed at the top of the sidebar; its width should therefore not
- exceed 200 pixels. Default: ``None``.
+ If given, this must be the name of an image file (path relative to the
+ :term:`configuration directory`) that is the logo of the docs. It is placed
+ at the top of the sidebar; its width should therefore not exceed 200 pixels.
+ Default: ``None``.
.. versionadded:: 0.4.1
The image file will be copied to the ``_static`` directory of the output
- HTML, so an already existing file with that name will be overwritten.
+ HTML, but only if the file does not already exist there.
.. confval:: html_favicon
- If given, this must be the name of an image file (within the static path, see
- below) that is the favicon of the docs. Modern browsers use this as icon for
- tabs, windows and bookmarks. It should be a Windows-style icon file
- (``.ico``), which is 16x16 or 32x32 pixels large. Default: ``None``.
+ If given, this must be the name of an image file (path relative to the
+ :term:`configuration directory`) that is the favicon of the docs. Modern browsers use this
+ as icon for tabs, windows and bookmarks. It should be a Windows-style icon
+ file (``.ico``), which is 16x16 or 32x32 pixels large. Default: ``None``.
.. versionadded:: 0.4
+ The image file will be copied to the ``_static`` directory of the output
+ HTML, but only if the file does not already exist there.
.. confval:: html_static_path
diff --git a/doc/contents.rst b/doc/contents.rst
index 5e0ae6c1..d3fd3c8e 100644
--- a/doc/contents.rst
+++ b/doc/contents.rst
@@ -18,6 +18,7 @@ Sphinx documentation contents
theming
templating
extensions
+ extdev/index
websupport
faq
diff --git a/doc/devguide.rst b/doc/devguide.rst
index b23acb1c..fccdd3fa 100644
--- a/doc/devguide.rst
+++ b/doc/devguide.rst
@@ -80,7 +80,7 @@ These are the basic steps needed to start developing on Sphinx.
For new features or other substantial changes that should wait until the
next major release, use the ``default`` branch.
-#. Setup your Python environment. ::
+#. Optional: setup a virtual environment. ::
virtualenv ~/sphinxenv
. ~/sphinxenv/bin/activate
@@ -90,32 +90,33 @@ These are the basic steps needed to start developing on Sphinx.
For tips on working with the code, see the `Coding Guide`_.
-#. Test, test, test.
+#. Test, test, test. Possible steps:
- Run the unit tests::
+ * Run the unit tests::
- pip install nose
+ pip install nose mock
make test
- Build the documentation and check the output for different builders::
+ * Build the documentation and check the output for different builders::
- cd docs
- make clean html text man info latexpdf
+ cd doc
+ make clean html latexpdf
- Run the unit tests under different Python environments using
- :program:`tox`::
+ * Run the unit tests under different Python environments using
+ :program:`tox`::
pip install tox
tox -v
- Add a new unit test in the ``tests`` directory if you can.
+ * Add a new unit test in the ``tests`` directory if you can.
- For bug fixes, first add a test that fails without your changes and passes
- after they are applied.
+ * For bug fixes, first add a test that fails without your changes and passes
+ after they are applied.
-#. Commit your changes. ::
+#. Please add a bullet point to :file:`CHANGES` if the fix or feature is not trivial
+ (small doc updates, typo fixes). Then commit::
- hg commit -m 'Add useful new feature that does this.'
+ hg commit -m '#42: Add useful new feature that does this.'
BitBucket recognizes `certain phrases`__ that can be used to automatically
update the issue tracker.
@@ -214,7 +215,7 @@ Coding Guide
generated output.
* When adding a new configuration variable, be sure to document it and update
- :file:`sphinx/quickstart.py`.
+ :file:`sphinx/quickstart.py` if it's important enough.
* Use the included :program:`utils/check_sources.py` script to check for
common formatting issues (trailing whitespace, lengthy lines, etc).
diff --git a/doc/ext/autodoc.rst b/doc/ext/autodoc.rst
index c92fe0c4..38d7700e 100644
--- a/doc/ext/autodoc.rst
+++ b/doc/ext/autodoc.rst
@@ -26,6 +26,16 @@ hand-written documentation, this technique eases the pain of having to maintain
two locations for documentation, while at the same time avoiding
auto-generated-looking pure API documentation.
+If you prefer `NumPy`_ or `Google`_ style docstrings over reStructuredText,
+you can also enable the :mod:`napoleon <sphinx.ext.napoleon>` extension.
+:mod:`napoleon <sphinx.ext.napoleon>` is a preprocessor that converts your
+docstrings to correct reStructuredText before :mod:`autodoc` processes them.
+
+.. _Google:
+ http://google-styleguide.googlecode.com/svn/trunk/pyguide.html#Comments
+.. _NumPy:
+ https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt
+
:mod:`autodoc` provides several directives that are versions of the usual
:rst:dir:`py:module`, :rst:dir:`py:class` and so forth. On parsing time, they
import the corresponding module and extract the docstring of the given objects,
@@ -195,6 +205,12 @@ inserting them into the page source under a suitable :rst:dir:`py:module`,
.. versionadded:: 1.2
+ * Add a list of modules in the :confval:`autodoc_mock_imports` to prevent
+ import errors to halt the building process when some external dependencies
+ are not importable at build time.
+
+ .. versionadded:: 1.3
+
.. rst:directive:: autofunction
autodata
@@ -335,6 +351,14 @@ There are also new config values that you can set:
.. versionadded:: 1.1
+.. confval:: autodoc_mock_imports
+
+ This value contains a list of modules to be mocked up. This is useful when
+ some external dependencies are not met at build time and break the building
+ process.
+
+ .. versionadded:: 1.3
+
Docstring preprocessing
-----------------------
diff --git a/doc/ext/example_google.py b/doc/ext/example_google.py
new file mode 100644
index 00000000..c94dcdf1
--- /dev/null
+++ b/doc/ext/example_google.py
@@ -0,0 +1,223 @@
+# -*- coding: utf-8 -*-
+"""Example Google style docstrings.
+
+This module demonstrates documentation as specified by the `Google Python
+Style Guide`_. Docstrings may extend over multiple lines. Sections are created
+with a section header and a colon followed by a block of indented text.
+
+Example:
+ Examples can be given using either the ``Example`` or ``Examples``
+ sections. Sections support any reStructuredText formatting, including
+ literal blocks::
+
+ $ python example_google.py
+
+Section breaks are created by simply resuming unindented text. Section breaks
+are also implicitly created anytime a new section starts.
+
+Attributes:
+ module_level_variable (int): Module level variables may be documented in
+ either the ``Attributes`` section of the module docstring, or in an
+ inline docstring immediately following the variable.
+
+ Either form is acceptable, but the two should not be mixed. Choose
+ one convention to document module level variables and be consistent
+ with it.
+
+.. _Google Python Style Guide:
+ http://google-styleguide.googlecode.com/svn/trunk/pyguide.html
+
+"""
+
+module_level_variable = 12345
+
+
+def module_level_function(param1, param2=None, *args, **kwargs):
+ """This is an example of a module level function.
+
+ Function parameters should be documented in the ``Args`` section. The name
+ of each parameter is required. The type and description of each parameter
+ is optional, but should be included if not obvious.
+
+ If the parameter itself is optional, it should be noted by adding
+ ", optional" to the type. If \*args or \*\*kwargs are accepted, they
+ should be listed as \*args and \*\*kwargs.
+
+ The format for a parameter is::
+
+ name (type): description
+ The description may span multiple lines. Following
+ lines should be indented.
+
+ Multiple paragraphs are supported in parameter
+ descriptions.
+
+ Args:
+ param1 (int): The first parameter.
+ param2 (str, optional): The second parameter. Defaults to None.
+ Second line of description should be indented.
+ *args: Variable length argument list.
+ **kwargs: Arbitrary keyword arguments.
+
+ Returns:
+ bool: True if successful, False otherwise.
+
+ The return type is optional and may be specified at the beginning of
+ the ``Returns`` section followed by a colon.
+
+ The ``Returns`` section may span multiple lines and paragraphs.
+ Following lines should be indented to match the first line.
+
+ The ``Returns`` section supports any reStructuredText formatting,
+ including literal blocks::
+
+ {
+ 'param1': param1,
+ 'param2': param2
+ }
+
+ Raises:
+ AttributeError: The ``Raises`` section is a list of all exceptions
+ that are relevant to the interface.
+ ValueError: If `param2` is equal to `param1`.
+
+ """
+ if param1 == param2:
+ raise ValueError('param1 may not be equal to param2')
+ return True
+
+
+def example_generator(n):
+ """Generators have a ``Yields`` section instead of a ``Returns`` section.
+
+ Args:
+ n (int): The upper limit of the range to generate, from 0 to `n` - 1
+
+ Yields:
+ int: The next number in the range of 0 to `n` - 1
+
+ Examples:
+ Examples should be written in doctest format, and should illustrate how
+ to use the function.
+
+ >>> print [i for i in example_generator(4)]
+ [0, 1, 2, 3]
+
+ """
+ for i in range(n):
+ yield i
+
+
+class ExampleError(Exception):
+ """Exceptions are documented in the same way as classes.
+
+ The __init__ method may be documented in either the class level
+ docstring, or as a docstring on the __init__ method itself.
+
+ Either form is acceptable, but the two should not be mixed. Choose one
+ convention to document the __init__ method and be consistent with it.
+
+ Note:
+ Do not include the `self` parameter in the ``Args`` section.
+
+ Args:
+ msg (str): Human readable string describing the exception.
+ code (int, optional): Error code, defaults to 2.
+
+ Attributes:
+ msg (str): Human readable string describing the exception.
+ code (int): Exception error code.
+
+ """
+ def __init__(self, msg, code=2):
+ self.msg = msg
+ self.code = code
+
+
+class ExampleClass(object):
+ """The summary line for a class docstring should fit on one line.
+
+ If the class has public attributes, they should be documented here
+ in an ``Attributes`` section and follow the same formatting as a
+ function's ``Args`` section.
+
+ Attributes:
+ attr1 (str): Description of `attr1`.
+ attr2 (list of str): Description of `attr2`.
+ attr3 (int): Description of `attr3`.
+
+ """
+ def __init__(self, param1, param2, param3=0):
+ """Example of docstring on the __init__ method.
+
+ The __init__ method may be documented in either the class level
+ docstring, or as a docstring on the __init__ method itself.
+
+ Either form is acceptable, but the two should not be mixed. Choose one
+ convention to document the __init__ method and be consistent with it.
+
+ Note:
+ Do not include the `self` parameter in the ``Args`` section.
+
+ Args:
+ param1 (str): Description of `param1`.
+ param2 (list of str): Description of `param2`. Multiple
+ lines are supported.
+ param3 (int, optional): Description of `param3`, defaults to 0.
+
+ """
+ self.attr1 = param1
+ self.attr2 = param2
+ self.attr3 = param3
+
+ def example_method(self, param1, param2):
+ """Class methods are similar to regular functions.
+
+ Note:
+ Do not include the `self` parameter in the ``Args`` section.
+
+ Args:
+ param1: The first parameter.
+ param2: The second parameter.
+
+ Returns:
+ True if successful, False otherwise.
+
+ """
+ return True
+
+ def __special__(self):
+ """By default special members with docstrings are included.
+
+ Special members are any methods or attributes that start with and
+ end with a double underscore. Any special member with a docstring
+ will be included in the output.
+
+ This behavior can be disabled by changing the following setting in
+ Sphinx's conf.py::
+
+ napoleon_include_special_with_doc = False
+
+ """
+ pass
+
+ def __special_without_docstring__(self):
+ pass
+
+ def _private(self):
+ """By default private members are not included.
+
+ Private members are any methods or attributes that start with an
+ underscore and are *not* special. By default they are not included
+ in the output.
+
+ This behavior can be changed such that private members *are* included
+ by changing the following setting in Sphinx's conf.py::
+
+ napoleon_include_private_with_doc = True
+
+ """
+ pass
+
+ def _private_without_docstring(self):
+ pass
diff --git a/doc/ext/example_google.rst b/doc/ext/example_google.rst
new file mode 100644
index 00000000..06508082
--- /dev/null
+++ b/doc/ext/example_google.rst
@@ -0,0 +1,15 @@
+:orphan:
+
+.. _example_google:
+
+Example Google Style Python Docstrings
+======================================
+
+.. seealso::
+
+ :ref:`example_numpy`
+
+Download: :download:`example_google.py <example_google.py>`
+
+.. literalinclude:: example_google.py
+ :language: python
diff --git a/doc/ext/example_numpy.py b/doc/ext/example_numpy.py
new file mode 100644
index 00000000..df1d20e6
--- /dev/null
+++ b/doc/ext/example_numpy.py
@@ -0,0 +1,272 @@
+# -*- coding: utf-8 -*-
+"""Example NumPy style docstrings.
+
+This module demonstrates documentation as specified by the `NumPy
+Documentation HOWTO`_. Docstrings may extend over multiple lines. Sections
+are created with a section header followed by an underline of equal length.
+
+Example
+-------
+Examples can be given using either the ``Example`` or ``Examples``
+sections. Sections support any reStructuredText formatting, including
+literal blocks::
+
+ $ python example_numpy.py
+
+
+Section breaks are created with two blank lines. Section breaks are also
+implicitly created anytime a new section starts. Section bodies *may* be
+indented:
+
+Notes
+-----
+ This is an example of an indented section. It's like any other section,
+ but the body is indented to help it stand out from surrounding text.
+
+If a section is indented, then a section break is created simply by
+resuming unindented text.
+
+Attributes
+----------
+module_level_variable : int
+ Module level variables may be documented in either the ``Attributes``
+ section of the module docstring, or in an inline docstring immediately
+ following the variable.
+
+ Either form is acceptable, but the two should not be mixed. Choose
+ one convention to document module level variables and be consistent
+ with it.
+
+.. _NumPy Documentation HOWTO:
+ https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt
+
+"""
+
+module_level_variable = 12345
+
+
+def module_level_function(param1, param2=None, *args, **kwargs):
+ """This is an example of a module level function.
+
+ Function parameters should be documented in the ``Parameters`` section.
+ The name of each parameter is required. The type and description of each
+ parameter is optional, but should be included if not obvious.
+
+ If the parameter itself is optional, it should be noted by adding
+ ", optional" to the type. If \*args or \*\*kwargs are accepted, they
+ should be listed as \*args and \*\*kwargs.
+
+ The format for a parameter is::
+
+ name : type
+ description
+
+ The description may span multiple lines. Following lines
+ should be indented to match the first line of the description.
+
+ Multiple paragraphs are supported in parameter
+ descriptions.
+
+ Parameters
+ ----------
+ param1 : int
+ The first parameter.
+ param2 : str, optional
+ The second parameter, defaults to None.
+ *args
+ Variable length argument list.
+ **kwargs
+ Arbitrary keyword arguments.
+
+ Returns
+ -------
+ bool
+ True if successful, False otherwise.
+
+ The return type is not optional. The ``Returns`` section may span
+ multiple lines and paragraphs. Following lines should be indented to
+ match the first line of the description.
+
+ The ``Returns`` section supports any reStructuredText formatting,
+ including literal blocks::
+
+ {
+ 'param1': param1,
+ 'param2': param2
+ }
+
+ Raises
+ ------
+ AttributeError
+ The ``Raises`` section is a list of all exceptions
+ that are relevant to the interface.
+ ValueError
+ If `param2` is equal to `param1`.
+
+ """
+ if param1 == param2:
+ raise ValueError('param1 may not be equal to param2')
+ return True
+
+
+def example_generator(n):
+ """Generators have a ``Yields`` section instead of a ``Returns`` section.
+
+ Parameters
+ ----------
+ n : int
+ The upper limit of the range to generate, from 0 to `n` - 1
+
+ Yields
+ ------
+ int
+ The next number in the range of 0 to `n` - 1
+
+ Examples
+ --------
+ Examples should be written in doctest format, and should illustrate how
+ to use the function.
+
+ >>> print [i for i in example_generator(4)]
+ [0, 1, 2, 3]
+
+ """
+ for i in range(n):
+ yield i
+
+
+class ExampleError(Exception):
+ """Exceptions are documented in the same way as classes.
+
+ The __init__ method may be documented in either the class level
+ docstring, or as a docstring on the __init__ method itself.
+
+ Either form is acceptable, but the two should not be mixed. Choose one
+ convention to document the __init__ method and be consistent with it.
+
+ Note
+ ----
+ Do not include the `self` parameter in the ``Parameters`` section.
+
+ Parameters
+ ----------
+ msg : str
+ Human readable string describing the exception.
+ code : int, optional
+ Error code, defaults to 2.
+
+ Attributes
+ ----------
+ msg : str
+ Human readable string describing the exception.
+ code : int
+ Exception error code.
+
+ """
+ def __init__(self, msg, code=2):
+ self.msg = msg
+ self.code = code
+
+
+class ExampleClass(object):
+ """The summary line for a class docstring should fit on one line.
+
+ If the class has public attributes, they should be documented here
+ in an ``Attributes`` section and follow the same formatting as a
+ function's ``Parameters`` section.
+
+ Attributes
+ ----------
+ attr1 : str
+ Description of `attr1`.
+ attr2 : list of str
+ Description of `attr2`.
+ attr3 : int
+ Description of `attr3`.
+
+ """
+ def __init__(self, param1, param2, param3=0):
+ """Example of docstring on the __init__ method.
+
+ The __init__ method may be documented in either the class level
+ docstring, or as a docstring on the __init__ method itself.
+
+ Either form is acceptable, but the two should not be mixed. Choose one
+ convention to document the __init__ method and be consistent with it.
+
+ Note
+ ----
+ Do not include the `self` parameter in the ``Parameters`` section.
+
+ Parameters
+ ----------
+ param1 : str
+ Description of `param1`.
+ param2 : list of str
+ Description of `param2`. Multiple
+ lines are supported.
+ param3 : int, optional
+ Description of `param3`, defaults to 0.
+
+ """
+ self.attr1 = param1
+ self.attr2 = param2
+ self.attr3 = param3
+
+ def example_method(self, param1, param2):
+ """Class methods are similar to regular functions.
+
+ Note
+ ----
+ Do not include the `self` parameter in the ``Parameters`` section.
+
+ Parameters
+ ----------
+ param1
+ The first parameter.
+ param2
+ The second parameter.
+
+ Returns
+ -------
+ bool
+ True if successful, False otherwise.
+
+ """
+ return True
+
+ def __special__(self):
+ """By default special members with docstrings are included.
+
+ Special members are any methods or attributes that start with and
+ end with a double underscore. Any special member with a docstring
+ will be included in the output.
+
+ This behavior can be disabled by changing the following setting in
+ Sphinx's conf.py::
+
+ napoleon_include_special_with_doc = False
+
+ """
+ pass
+
+ def __special_without_docstring__(self):
+ pass
+
+ def _private(self):
+ """By default private members are not included.
+
+ Private members are any methods or attributes that start with an
+ underscore and are *not* special. By default they are not included
+ in the output.
+
+ This behavior can be changed such that private members *are* included
+ by changing the following setting in Sphinx's conf.py::
+
+ napoleon_include_private_with_doc = True
+
+ """
+ pass
+
+ def _private_without_docstring(self):
+ pass
diff --git a/doc/ext/example_numpy.rst b/doc/ext/example_numpy.rst
new file mode 100644
index 00000000..a3b41613
--- /dev/null
+++ b/doc/ext/example_numpy.rst
@@ -0,0 +1,15 @@
+:orphan:
+
+.. _example_numpy:
+
+Example NumPy Style Python Docstrings
+======================================
+
+.. seealso::
+
+ :ref:`example_google`
+
+Download: :download:`example_numpy.py <example_numpy.py>`
+
+.. literalinclude:: example_numpy.py
+ :language: python
diff --git a/doc/ext/napoleon.rst b/doc/ext/napoleon.rst
new file mode 100644
index 00000000..32deecb8
--- /dev/null
+++ b/doc/ext/napoleon.rst
@@ -0,0 +1,375 @@
+:mod:`sphinx.ext.napoleon` -- Support for NumPy and Google style docstrings
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. module:: sphinx.ext.napoleon
+ :synopsis: Support for NumPy and Google style docstrings
+
+.. moduleauthor:: Rob Ruana
+
+.. versionadded:: 1.3
+
+Napoleon - *Marching toward legible docstrings*
+===============================================
+
+Are you tired of writing docstrings that look like this::
+
+ :param path: The path of the file to wrap
+ :type path: str
+ :param field_storage: The :class:`FileStorage` instance to wrap
+ :type field_storage: FileStorage
+ :param temporary: Whether or not to delete the file when the File
+ instance is destructed
+ :type temporary: bool
+ :returns: A buffered writable file descriptor
+ :rtype: BufferedFileStorage
+
+`ReStructuredText`_ is great, but it creates visually dense, hard to read
+`docstrings`_. Compare the jumble above to the same thing rewritten
+according to the `Google Python Style Guide`_::
+
+ Args:
+ path (str): The path of the file to wrap
+ field_storage (FileStorage): The :class:`FileStorage` instance to wrap
+ temporary (bool): Whether or not to delete the file when the File
+ instance is destructed
+
+ Returns:
+ BufferedFileStorage: A buffered writable file descriptor
+
+Much more legible, no?
+
+Napoleon is a Sphinx extension that allows you to write readable API
+documentation in your source code. Napoleon understands both `NumPy`_ and
+`Google`_ style docstrings - the style recommended by `Khan Academy`_.
+
+.. _ReStructuredText: http://docutils.sourceforge.net/rst.html
+.. _docstrings: http://www.python.org/dev/peps/pep-0287/
+.. _Google Python Style Guide:
+ http://google-styleguide.googlecode.com/svn/trunk/pyguide.html
+.. _Google:
+ http://google-styleguide.googlecode.com/svn/trunk/pyguide.html#Comments
+.. _NumPy:
+ https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt
+.. _Khan Academy:
+ https://sites.google.com/a/khanacademy.org/forge/for-developers/styleguide/python#TOC-Docstrings
+
+Getting Started
+---------------
+
+1. After `setting up Sphinx`_ to build your docs, enable napoleon in the
+ Sphinx `conf.py` file::
+
+ # conf.py
+
+ # Add autodoc and napoleon to the extensions list
+ extensions = ['sphinx.ext.autodoc', 'sphinx.ext.napoleon']
+
+2. Use `sphinx-apidoc` to build your API documentation::
+
+ $ sphinx-apidoc -f -o docs/source projectdir
+
+.. _setting up Sphinx: http://sphinx-doc.org/tutorial.html
+
+Docstrings
+----------
+
+Napoleon interprets every docstring that :mod:`autodoc <sphinx.ext.autodoc>`
+can find, including docstrings on: ``modules``, ``classes``, ``attributes``,
+``methods``, ``functions``, and ``variables``. Inside each docstring,
+specially formatted `Sections`_ are parsed and converted to
+reStructuredText.
+
+All standard reStructuredText formatting still works as expected.
+
+
+.. _Sections:
+
+Docstring Sections
+------------------
+
+All of the following section headers are supported:
+
+ * ``Args`` *(alias of Parameters)*
+ * ``Arguments`` *(alias of Parameters)*
+ * ``Attributes``
+ * ``Example``
+ * ``Examples``
+ * ``Keyword Args`` *(alias of Keyword Arguments)*
+ * ``Keyword Arguments``
+ * ``Methods``
+ * ``Note``
+ * ``Notes``
+ * ``Other Parameters``
+ * ``Parameters``
+ * ``Return`` *(alias of Returns)*
+ * ``Returns``
+ * ``Raises``
+ * ``References``
+ * ``See Also``
+ * ``Warning``
+ * ``Warnings`` *(alias of Warning)*
+ * ``Warns``
+ * ``Yields``
+
+Google vs NumPy
+---------------
+
+Napoleon supports two styles of docstrings: `Google`_ and `NumPy`_. The
+main difference between the two styles is that Google uses indention to
+separate sections, whereas NumPy uses underlines.
+
+Google style::
+
+ def func(arg1, arg2):
+ """Summary line.
+
+ Extended description of function.
+
+ Args:
+ arg1 (int): Description of arg1
+ arg2 (str): Description of arg2
+
+ Returns:
+ bool: Description of return value
+
+ """
+ return True
+
+NumPy style::
+
+ def func(arg1, arg2):
+ """Summary line.
+
+ Extended description of function.
+
+ Parameters
+ ----------
+ arg1 : int
+ Description of arg1
+ arg2 : str
+ Description of arg2
+
+ Returns
+ -------
+ bool
+ Description of return value
+
+ """
+ return True
+
+NumPy style tends to require more vertical space, whereas Google style
+tends to use more horizontal space. Google style tends to be easier to
+read for short and simple docstrings, whereas NumPy style tends be easier
+to read for long and in-depth docstrings.
+
+The `Khan Academy`_ recommends using Google style.
+
+The choice between styles is largely aesthetic, but the two styles should
+not be mixed. Choose one style for your project and be consistent with it.
+
+.. seealso::
+
+ For complete examples:
+
+ * :ref:`example_google`
+ * :ref:`example_numpy`
+
+
+Configuration
+=============
+
+Listed below are all the settings used by napoleon and their default
+values. These settings can be changed in the Sphinx `conf.py` file. Make
+sure that both "sphinx.ext.autodoc" and "sphinx.ext.napoleon" are
+enabled in `conf.py`::
+
+ # conf.py
+
+ # Add any Sphinx extension module names here, as strings
+ extensions = ['sphinx.ext.autodoc', 'sphinx.ext.napoleon']
+
+ # Napoleon settings
+ napoleon_google_docstring = True
+ napoleon_numpy_docstring = True
+ napoleon_include_private_with_doc = False
+ napoleon_include_special_with_doc = True
+ napoleon_use_admonition_for_examples = False
+ napoleon_use_admonition_for_notes = False
+ napoleon_use_admonition_for_references = False
+ napoleon_use_ivar = False
+ napoleon_use_param = False
+ napoleon_use_rtype = False
+
+.. _Google style:
+ http://google-styleguide.googlecode.com/svn/trunk/pyguide.html
+.. _NumPy style:
+ https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt
+
+
+
+.. confval:: napoleon_google_docstring
+
+ True to parse `Google style`_ docstrings. False to disable support
+ for Google style docstrings. *Defaults to True.*
+
+.. confval:: napoleon_numpy_docstring
+
+ True to parse `NumPy style`_ docstrings. False to disable support
+ for NumPy style docstrings. *Defaults to True.*
+
+.. confval:: napoleon_include_private_with_doc
+
+ True to include private members (like ``_membername``) with docstrings
+ in the documentation. False to fall back to Sphinx's default behavior.
+ *Defaults to False.*
+
+ **If True**::
+
+ def _included(self):
+ """
+ This will be included in the docs because it has a docstring
+ """
+ pass
+
+ def _skipped(self):
+ # This will NOT be included in the docs
+ pass
+
+.. confval:: napoleon_include_special_with_doc
+
+ True to include special members (like ``__membername__``) with
+ docstrings in the documentation. False to fall back to Sphinx's
+ default behavior. *Defaults to True.*
+
+ **If True**::
+
+ def __str__(self):
+ """
+ This will be included in the docs because it has a docstring
+ """
+ return unicode(self).encode('utf-8')
+
+ def __unicode__(self):
+ # This will NOT be included in the docs
+ return unicode(self.__class__.__name__)
+
+.. confval:: napoleon_use_admonition_for_examples
+
+ True to use the ``.. admonition::`` directive for the **Example** and
+ **Examples** sections. False to use the ``.. rubric::`` directive
+ instead. One may look better than the other depending on what HTML
+ theme is used. *Defaults to False.*
+
+ This `NumPy style`_ snippet will be converted as follows::
+
+ Example
+ -------
+ This is just a quick example
+
+ **If True**::
+
+ .. admonition:: Example
+
+ This is just a quick example
+
+ **If False**::
+
+ .. rubric:: Example
+
+ This is just a quick example
+
+.. confval:: napoleon_use_admonition_for_notes
+
+ True to use the ``.. admonition::`` directive for **Notes** sections.
+ False to use the ``.. rubric::`` directive instead. *Defaults to False.*
+
+ .. note:: The singular **Note** section will always be converted to a
+ ``.. note::`` directive.
+
+ .. seealso::
+
+ :attr:`napoleon_use_admonition_for_examples`
+
+.. confval:: napoleon_use_admonition_for_references
+
+ True to use the ``.. admonition::`` directive for **References**
+ sections. False to use the ``.. rubric::`` directive instead.
+ *Defaults to False.*
+
+ .. seealso::
+
+ :attr:`napoleon_use_admonition_for_examples`
+
+.. confval:: napoleon_use_ivar
+
+ True to use the ``:ivar:`` role for instance variables. False to use
+ the ``.. attribute::`` directive instead. *Defaults to False.*
+
+ This `NumPy style`_ snippet will be converted as follows::
+
+ Attributes
+ ----------
+ attr1 : int
+ Description of `attr1`
+
+ **If True**::
+
+ :ivar attr1: Description of `attr1`
+ :vartype attr1: int
+
+ **If False**::
+
+ .. attribute:: attr1
+ :annotation: int
+
+ Description of `attr1`
+
+.. confval:: napoleon_use_param
+
+ True to use a ``:param:`` role for each function parameter. False to
+ use a single ``:parameters:`` role for all the parameters.
+ *Defaults to False.*
+
+ This `NumPy style`_ snippet will be converted as follows::
+
+ Parameters
+ ----------
+ arg1 : str
+ Description of `arg1`
+ arg2 : int, optional
+ Description of `arg2`, defaults to 0
+
+ **If True**::
+
+ :param arg1: Description of `arg1`
+ :type arg1: str
+ :param arg2: Description of `arg2`, defaults to 0
+ :type arg2: int, optional
+
+ **If False**::
+
+ :parameters: * **arg1** (*str*) --
+ Description of `arg1`
+ * **arg2** (*int, optional*) --
+ Description of `arg2`, defaults to 0
+
+.. confval:: napoleon_use_rtype
+
+ True to use the ``:rtype:`` role for the return type. False to output
+ the return type inline with the description. *Defaults to False.*
+
+ This `NumPy style`_ snippet will be converted as follows::
+
+ Returns
+ -------
+ bool
+ True if successful, False otherwise
+
+ **If True**::
+
+ :returns: True if successful, False otherwise
+ :rtype: bool
+
+ **If False**::
+
+ :returns: *bool* -- True if successful, False otherwise
diff --git a/doc/ext/appapi.rst b/doc/extdev/appapi.rst
index 156dede8..eca191bb 100644
--- a/doc/ext/appapi.rst
+++ b/doc/extdev/appapi.rst
@@ -1,14 +1,27 @@
.. highlight:: rest
-Extension API
-=============
+Application API
+===============
+
+.. module:: sphinx.application
+ :synopsis: Application class and extensibility interface.
-.. currentmodule:: sphinx.application
Each Sphinx extension is a Python module with at least a :func:`setup` function.
This function is called at initialization time with one argument, the
-application object representing the Sphinx process. This application object has
-the following public API:
+application object representing the Sphinx process.
+
+.. class:: Sphinx
+
+ This application object has the public API described in the following.
+
+Extension setup
+---------------
+
+These methods are usually called in an extension's ``setup()`` function.
+
+Examples of using the Sphinx extension API can be seen in the :mod:`sphinx.ext`
+package.
.. method:: Sphinx.setup_extension(name)
@@ -296,6 +309,14 @@ the following public API:
.. versionadded:: 1.1
+.. method:: Sphinx.require_sphinx(version)
+
+ Compare *version* (which must be a ``major.minor`` version string,
+ e.g. ``'1.1'``) with the version of the running Sphinx, and abort the build
+ when it is too old.
+
+ .. versionadded:: 1.0
+
.. method:: Sphinx.connect(event, callback)
Register *callback* to be called when *event* is emitted. For details on
@@ -309,6 +330,16 @@ the following public API:
Unregister callback *listener_id*.
+
+.. exception:: ExtensionError
+
+ All these methods raise this exception if something went wrong with the
+ extension API.
+
+
+Emitting events
+---------------
+
.. method:: Sphinx.emit(event, *arguments)
Emit *event* and pass *arguments* to the callback functions. Return the
@@ -322,22 +353,21 @@ the following public API:
.. versionadded:: 0.5
-.. method:: Sphinx.require_sphinx(version)
- Compare *version* (which must be a ``major.minor`` version string,
- e.g. ``'1.1'``) with the version of the running Sphinx, and abort the build
- when it is too old.
+Producing messages / logging
+----------------------------
- .. versionadded:: 1.0
+The application object also provides support for emitting leveled messages.
+.. automethod:: Sphinx.warn
-.. exception:: ExtensionError
+.. automethod:: Sphinx.info
- All these functions raise this exception if something went wrong with the
- extension API.
+.. automethod:: Sphinx.verbose
-Examples of using the Sphinx extension API can be seen in the :mod:`sphinx.ext`
-package.
+.. automethod:: Sphinx.debug
+
+.. automethod:: Sphinx.debug2
.. _events:
@@ -481,28 +511,27 @@ Use this to adapt your extension to API changes in Sphinx.
Before version 1.2, check the string ``sphinx.__version__``.
-.. _template-bridge:
+The Config object
+-----------------
-The template bridge
--------------------
-
-.. currentmodule:: sphinx.application
+.. module:: sphinx.config
-.. autoclass:: TemplateBridge
- :members:
+.. class:: Config
+ The config object makes the values of all config values available as
+ attributes.
-.. _domain-api:
+ It is available as the ``config`` attribute on the application and
+ environment objects. For example, to get the value of :confval:`language`,
+ use either ``app.config.language`` or ``env.config.language``.
-Domain API
-----------
-.. module:: sphinx.domains
+.. _template-bridge:
-.. autoclass:: Domain
- :members:
+The template bridge
+-------------------
-.. autoclass:: ObjType
+.. currentmodule:: sphinx.application
-.. autoclass:: Index
+.. autoclass:: TemplateBridge
:members:
diff --git a/doc/ext/builderapi.rst b/doc/extdev/builderapi.rst
index 3ace2687..cd8688a3 100644
--- a/doc/ext/builderapi.rst
+++ b/doc/extdev/builderapi.rst
@@ -1,7 +1,7 @@
.. _writing-builders:
-Writing new builders
-====================
+Builder API
+===========
.. todo:: Expand this.
diff --git a/doc/extdev/domainapi.rst b/doc/extdev/domainapi.rst
new file mode 100644
index 00000000..d6ecf063
--- /dev/null
+++ b/doc/extdev/domainapi.rst
@@ -0,0 +1,14 @@
+.. _domain-api:
+
+Domain API
+----------
+
+.. module:: sphinx.domains
+
+.. autoclass:: Domain
+ :members:
+
+.. autoclass:: ObjType
+
+.. autoclass:: Index
+ :members:
diff --git a/doc/extdev/envapi.rst b/doc/extdev/envapi.rst
new file mode 100644
index 00000000..56771f73
--- /dev/null
+++ b/doc/extdev/envapi.rst
@@ -0,0 +1,54 @@
+Build environment API
+=====================
+
+.. module:: sphinx.environment
+
+.. class:: BuildEnvironment
+
+ **Attributes**
+
+ .. attribute:: app
+
+ Reference to the application object.
+
+ .. attribute:: config
+
+ Reference to the :class:`.Config` object.
+
+ .. attribute:: srcdir
+
+ Source directory (the directory containing ``conf.py``).
+
+ .. attribute:: doctreedir
+
+ Directory for storing pickled doctrees.
+
+ .. attribute:: found_docs
+
+ A set of all existing docnames.
+
+ .. attribute:: metadata
+
+ Dictionary mapping docnames to "metadata" (see :ref:`metadata`).
+
+ .. attribute:: titles
+
+ Dictionary mapping docnames to the docutils node for their main title.
+
+ .. autoattribute:: docname
+
+ **Utility methods**
+
+ .. automethod:: warn
+
+ .. automethod:: warn_node
+
+ .. automethod:: doc2path
+
+ .. automethod:: relfn2path
+
+ .. automethod:: note_dependency
+
+ .. automethod:: new_serialno
+
+ .. automethod:: note_reread
diff --git a/doc/extdev/index.rst b/doc/extdev/index.rst
new file mode 100644
index 00000000..b76928c0
--- /dev/null
+++ b/doc/extdev/index.rst
@@ -0,0 +1,33 @@
+.. _dev-extensions:
+
+Developing extensions for Sphinx
+================================
+
+Since many projects will need special features in their documentation, Sphinx is
+designed to be extensible on several levels.
+
+This is what you can do in an extension: First, you can add new
+:term:`builder`\s to support new output formats or actions on the parsed
+documents. Then, it is possible to register custom reStructuredText roles and
+directives, extending the markup. And finally, there are so-called "hook
+points" at strategic places throughout the build process, where an extension can
+register a hook and run specialized code.
+
+An extension is simply a Python module. When an extension is loaded, Sphinx
+imports this module and executes its ``setup()`` function, which in turn
+notifies Sphinx of everything the extension offers -- see the extension tutorial
+for examples.
+
+The configuration file itself can be treated as an extension if it contains a
+``setup()`` function. All other extensions to load must be listed in the
+:confval:`extensions` configuration value.
+
+.. toctree::
+
+ tutorial
+ appapi
+ envapi
+ builderapi
+ markupapi
+ domainapi
+ nodes
diff --git a/doc/extdev/markupapi.rst b/doc/extdev/markupapi.rst
new file mode 100644
index 00000000..8f6c29b1
--- /dev/null
+++ b/doc/extdev/markupapi.rst
@@ -0,0 +1,11 @@
+Docutils markup API
+===================
+
+Roles
+-----
+
+
+Directives
+----------
+
+TODO.
diff --git a/doc/extdev/nodes.rst b/doc/extdev/nodes.rst
new file mode 100644
index 00000000..e67fa3da
--- /dev/null
+++ b/doc/extdev/nodes.rst
@@ -0,0 +1,57 @@
+.. _nodes:
+
+Doctree node classes added by Sphinx
+====================================
+
+.. module:: sphinx.addnodes
+
+Nodes for domain-specific object descriptions
+---------------------------------------------
+
+.. autoclass:: desc
+.. autoclass:: desc_signature
+.. autoclass:: desc_addname
+.. autoclass:: desc_type
+.. autoclass:: desc_returns
+.. autoclass:: desc_name
+.. autoclass:: desc_parameterlist
+.. autoclass:: desc_parameter
+.. autoclass:: desc_optional
+.. autoclass:: desc_annotation
+.. autoclass:: desc_content
+
+New admonition-like constructs
+------------------------------
+
+.. autoclass:: versionmodified
+.. autoclass:: seealso
+
+Other paragraph-level nodes
+-------------------------------
+
+.. autoclass:: compact_paragraph
+
+New inline nodes
+----------------
+
+.. autoclass:: index
+.. autoclass:: pending_xref
+.. autoclass:: literal_emphasis
+.. autoclass:: abbreviation
+.. autoclass:: download_reference
+
+Special nodes
+-------------
+
+.. autoclass:: only
+.. autoclass:: meta
+.. autoclass:: highlightlang
+
+You should not need to generate the nodes below in extensions.
+
+.. autoclass:: glossary
+.. autoclass:: toctree
+.. autoclass:: start_of_file
+.. autoclass:: productionlist
+.. autoclass:: production
+.. autoclass:: termsep
diff --git a/doc/ext/tutorial.rst b/doc/extdev/tutorial.rst
index c09be29a..38264190 100644
--- a/doc/ext/tutorial.rst
+++ b/doc/extdev/tutorial.rst
@@ -12,6 +12,50 @@ include todo entries in the documentation, and collecting these in a central
place. (A similar "todo" extension is distributed with Sphinx.)
+Important objects
+-----------------
+
+There are several key objects whose API you will use while writing an
+extension. These are:
+
+**Application**
+ The application object (usually called ``app``) is an instance of
+ :class:`.Sphinx`. It controls the most high-level functionality, such as the
+ setup of extensions, event dispatching and producing output (logging).
+
+ If you have the environment object, the application is available as
+ ``env.app``.
+
+**Environment**
+ The build environment object (usually called ``env``) is an instance of
+ :class:`.BuildEnvironment`. It is responsible for parsing the source
+ documents stores all metadata about the document collection and is serialized
+ after each build.
+
+ Its API provides methods to do with access to metadata, resolving references,
+ etc. It can also be used by extensions to cache information that should
+ persist for incremental rebuilds.
+
+ If you have the application or builder object, the environment is available
+ as ``app.env`` or ``builder.env``.
+
+**Builder**
+ The builder object (usually called ``builder``) is an instance of a specific
+ subclass of :class:`.Builder`. Each builder class knows how to convert the
+ parsed documents into an output format, or otherwise process them (e.g. check
+ external links).
+
+ If you have the application object, the environment is available as
+ ``app.builder``.
+
+**Config**
+ The config object (usually called ``config``) provides the values of
+ configuration values set in :file:`conf.py` as attributes. It is an instance
+ of :class:`.Config`.
+
+ The config is available as ``app.config`` or ``env.config``.
+
+
Build Phases
------------
@@ -29,11 +73,15 @@ in which a Sphinx project is built: this works in several phases.
In Phase 1, all source files (and on subsequent builds, those that are new or
changed) are read and parsed. This is the phase where directives and roles
- are encountered by the docutils, and the corresponding functions are called.
- The output of this phase is a *doctree* for each source files, that is a tree
- of docutils nodes. For document elements that aren't fully known until all
+ are encountered by the docutils, and the corresponding code is executed. The
+ output of this phase is a *doctree* for each source files, that is a tree of
+ docutils nodes. For document elements that aren't fully known until all
existing files are read, temporary nodes are created.
+ There are nodes provided by docutils, which are documented `in the docutils
+ documentation <http://docutils.sourceforge.net/docs/ref/doctree.html>`__.
+ Additional nodes are provided by Sphinx and :ref:`documented here <nodes>`.
+
During reading, the build environment is updated with all meta- and cross
reference data of the read documents, such as labels, the names of headings,
described Python objects and index entries. This will later be used to
@@ -165,21 +213,26 @@ docutils classes defined in :mod:`docutils.nodes`. ``todo`` inherits from
``Admonition`` because it should be handled like a note or warning, ``todolist``
is just a "general" node.
+.. note::
+
+ Many extensions will not have to create their own node classes and work fine
+ with the nodes already provided by `docutils
+ <http://docutils.sourceforge.net/docs/ref/doctree.html>`__ and :ref:`Sphinx
+ <nodes>`.
+
The Directive Classes
---------------------
A directive class is a class deriving usually from
-``docutils.parsers.rst.Directive``. Since the class-based directive interface
-doesn't exist yet in Docutils 0.4, Sphinx has another base class called
-``sphinx.util.compat.Directive`` that you can derive your directive from, and it
-will work with both Docutils 0.4 and 0.5 upwards. The directive interface is
-covered in detail in the `docutils documentation`_; the important thing is that the
-class has a method ``run`` that returns a list of nodes.
+``docutils.parsers.rst.Directive``. The directive interface is covered in
+detail in the `docutils documentation`_; the important thing is that the class
+has attributes that configure the allowed markup and a method ``run`` that
+returns a list of nodes.
The ``todolist`` directive is quite simple::
- from sphinx.util.compat import Directive
+ from docutils.parsers.rst import Directive
class TodolistDirective(Directive):
@@ -224,9 +277,9 @@ to the build environment instance using ``self.state.document.settings.env``.
Then, to act as a link target (from the todolist), the todo directive needs to
return a target node in addition to the todo node. The target ID (in HTML, this
-will be the anchor name) is generated by using ``env.new_serialno`` which is
-returns a new integer directive on each call and therefore leads to unique
-target names. The target node is instantiated without any text (the first two
+will be the anchor name) is generated by using ``env.new_serialno`` which
+returns a new unique integer on each call and therefore leads to unique target
+names. The target node is instantiated without any text (the first two
arguments).
An admonition is created using a standard docutils function (wrapped in Sphinx
diff --git a/doc/extensions.rst b/doc/extensions.rst
index 7597f281..b0d98e38 100644
--- a/doc/extensions.rst
+++ b/doc/extensions.rst
@@ -3,34 +3,12 @@
Sphinx Extensions
=================
-.. module:: sphinx.application
- :synopsis: Application class and extensibility interface.
-
-Since many projects will need special features in their documentation, Sphinx is
-designed to be extensible on several levels.
-
-This is what you can do in an extension: First, you can add new
-:term:`builder`\s to support new output formats or actions on the parsed
-documents. Then, it is possible to register custom reStructuredText roles and
-directives, extending the markup. And finally, there are so-called "hook
-points" at strategic places throughout the build process, where an extension can
-register a hook and run specialized code.
-
-An extension is simply a Python module. When an extension is loaded, Sphinx
-imports this module and executes its ``setup()`` function, which in turn
-notifies Sphinx of everything the extension offers -- see the extension tutorial
-for examples.
-
-The configuration file itself can be treated as an extension if it contains a
-``setup()`` function. All other extensions to load must be listed in the
-:confval:`extensions` configuration value.
-
-.. toctree::
-
- ext/tutorial
- ext/appapi
- ext/builderapi
+Since many projects will need special features in their documentation, Sphinx
+allows to add "extensions" to the build process, each of which can modify almost
+any aspect of document processing.
+This chapter describes the extensions bundled with Sphinx. For the API
+documentation on writing your own extension, see :ref:`dev-extensions`.
Builtin Sphinx extensions
-------------------------
@@ -53,6 +31,7 @@ These extensions are built in and can be activated by respective entries in the
ext/extlinks
ext/viewcode
ext/linkcode
+ ext/napoleon
Third-party extensions
diff --git a/doc/install.rst b/doc/install.rst
index bf653872..71e37e9c 100644
--- a/doc/install.rst
+++ b/doc/install.rst
@@ -4,7 +4,7 @@ Installing Sphinx
=================
Since Sphinx is written in the Python language, you need to install Python
-(the required version is at least 2.5) and Sphinx.
+(the required version is at least 2.6) and Sphinx.
Sphinx packages are available on the `Python Package Index
<https://pypi.python.org/pypi/Sphinx>`_.
@@ -79,8 +79,8 @@ sidebar and under "Quick Links", click "Windows Installer" to download.
.. note::
- Currently, Python offers two major versions, 2.x and 3.x. Sphinx 1.2 can run
- under Python 2.5 to 2.7 and 3.1 to 3.3, with the recommended version being
+ Currently, Python offers two major versions, 2.x and 3.x. Sphinx 1.3 can run
+ under Python 2.6, 2.7, 3.2, 3.3, with the recommended version being
2.7. This chapter assumes you have installed Python 2.7.
Follow the Windows installer for Python.
diff --git a/doc/intro.rst b/doc/intro.rst
index 66d0c58d..dd541bf1 100644
--- a/doc/intro.rst
+++ b/doc/intro.rst
@@ -54,7 +54,7 @@ See the :ref:`pertinent section in the FAQ list <usingwith>`.
Prerequisites
-------------
-Sphinx needs at least **Python 2.5** or **Python 3.1** to run, as well as the
+Sphinx needs at least **Python 2.6** or **Python 3.2** to run, as well as the
docutils_ and Jinja2_ libraries. Sphinx should work with docutils version 0.7
or some (not broken) SVN trunk snapshot. If you like to have source code
highlighting support, you must also install the Pygments_ library.
diff --git a/doc/invocation.rst b/doc/invocation.rst
index f831dc0a..dca02793 100644
--- a/doc/invocation.rst
+++ b/doc/invocation.rst
@@ -159,8 +159,8 @@ The :program:`sphinx-build` script has several options:
.. option:: -v
- Increase verbosity. This option can be given up to three times to get more
- debug output. It implies :option:`-T`.
+ Increase verbosity (loglevel). This option can be given up to three times
+ to get more debug logging output. It implies :option:`-T`.
.. versionadded:: 1.2
diff --git a/doc/markup/code.rst b/doc/markup/code.rst
index 6e707e00..e9f8f1da 100644
--- a/doc/markup/code.rst
+++ b/doc/markup/code.rst
@@ -188,6 +188,25 @@ Includes
The ``prepend`` and ``append`` options, as well as ``tab-width``.
+Showing a file name
+^^^^^^^^^^^^^^^^^^^
+
+.. versionadded:: 1.3
+
+A ``filename`` option can be given to show that name before the code block. For
+example::
+
+ .. code-block:: python
+ :filename: this.py
+
+ print 'Explicit is better than implicit.'
+
+
+:rst:dir:`literalinclude` also supports the ``filename`` option, with the
+additional feature that if you leave the value empty, the shown filename will be
+exactly the one given as an argument.
+
+
.. rubric:: Footnotes
.. [1] There is a standard ``.. include`` directive, but it raises errors if the
diff --git a/doc/markup/toctree.rst b/doc/markup/toctree.rst
index fdecc37d..1d5d667a 100644
--- a/doc/markup/toctree.rst
+++ b/doc/markup/toctree.rst
@@ -141,9 +141,12 @@ tables of contents. The ``toctree`` directive is the central element.
In the end, all documents in the :term:`source directory` (or subdirectories)
must occur in some ``toctree`` directive; Sphinx will emit a warning if it
finds a file that is not included, because that means that this file will not
- be reachable through standard navigation. Use :ref:`metadata` to remove the
- warning, and :confval:`exclude_patterns` to explicitly exclude documents or
- directories from building.
+ be reachable through standard navigation.
+
+ Use :confval:`exclude_patterns` to explicitly exclude documents or
+ directories from building completely. Use :ref:`the "orphan" metadata
+ <metadata>` to let a document be built, but notify Sphinx that it is not
+ reachable via a toctree.
The "master document" (selected by :confval:`master_doc`) is the "root" of
the TOC tree hierarchy. It can be used as the documentation's main page, or
diff --git a/setup.py b/setup.py
index f13c5047..6cf59834 100644
--- a/setup.py
+++ b/setup.py
@@ -44,20 +44,20 @@ A development egg can be found `here
<http://bitbucket.org/birkenfeld/sphinx/get/tip.gz#egg=Sphinx-dev>`_.
'''
+if sys.version_info < (2, 6) or (3, 0) <= sys.version_info < (3, 2):
+ print('ERROR: Sphinx requires at least Python 2.6 or 3.2 to run.')
+ sys.exit(1)
+
requires = ['Pygments>=1.2', 'docutils>=0.7', 'snowballstemmer>=1.1']
if sys.version_info[:3] >= (3, 3, 0):
requires[1] = 'docutils>=0.10'
-if sys.version_info < (2, 6) or (3, 0) <= sys.version_info < (3, 3):
+if (3, 0) <= sys.version_info < (3, 3):
requires.append('Jinja2>=2.3,<2.7')
-else:
+else: # 2.6, 2.7, 3.3 or later
requires.append('Jinja2>=2.3')
-if sys.version_info < (2, 5):
- print('ERROR: Sphinx requires at least Python 2.5 to run.')
- sys.exit(1)
-
# tell distribute to use 2to3 with our own fixers
extra = {}
if sys.version_info >= (3, 0):
diff --git a/sphinx-build.py b/sphinx-build.py
index 6737d072..54bdb529 100755
--- a/sphinx-build.py
+++ b/sphinx-build.py
@@ -11,5 +11,8 @@
import sys
if __name__ == '__main__':
- from sphinx import main
- sys.exit(main(sys.argv))
+ from sphinx import main, make_main
+ if sys.argv[1:2] == ['-M']:
+ sys.exit(make_main(sys.argv))
+ else:
+ sys.exit(main(sys.argv))
diff --git a/sphinx/__init__.py b/sphinx/__init__.py
index f93e57a1..c1e9e38d 100644
--- a/sphinx/__init__.py
+++ b/sphinx/__init__.py
@@ -42,8 +42,9 @@ if '+' in __version__ or 'pre' in __version__:
def main(argv=sys.argv):
"""Sphinx build "main" command-line entry."""
- if sys.version_info[:3] < (2, 5, 0):
- sys.stderr.write('Error: Sphinx requires at least Python 2.5 to run.\n')
+ if (sys.version_info[:3] < (2, 6, 0) or
+ (3, 0, 0) <= sys.version_info[:3] < (3, 2, 0)):
+ sys.stderr.write('Error: Sphinx requires at least Python 2.6 to run.\n')
return 1
try:
from sphinx import cmdline
@@ -80,5 +81,11 @@ def main(argv=sys.argv):
return cmdline.main(argv)
+def make_main(argv=sys.argv):
+ """Sphinx build "make mode" entry."""
+ from sphinx import make_mode
+ return make_mode.run_make_mode(argv[2:])
+
+
if __name__ == '__main__':
sys.exit(main(sys.argv))
diff --git a/sphinx/apidoc.py b/sphinx/apidoc.py
index c3054662..7e95cdae 100644
--- a/sphinx/apidoc.py
+++ b/sphinx/apidoc.py
@@ -65,7 +65,7 @@ def write_file(name, text, opts):
def format_heading(level, text):
"""Create a heading of <level> [1, 2 or 3 supported]."""
- underlining = ['=', '-', '~', ][level-1] * len(text)
+ underlining = ['=', '-', '~', ][level - 1] * len(text)
return '%s\n%s\n\n' % (text, underlining)
@@ -173,9 +173,6 @@ def recurse_tree(rootpath, excludes, opts):
Look for every file in the directory tree and create the corresponding
ReST files.
"""
- # use absolute path for root, as relative paths like '../../foo' cause
- # 'if "/." in root ...' to filter out *all* modules otherwise
- rootpath = path.normpath(path.abspath(rootpath))
# check if the base directory is a package and get its name
if INITPY in os.listdir(rootpath):
root_package = rootpath.split(path.sep)[-1]
@@ -185,13 +182,12 @@ def recurse_tree(rootpath, excludes, opts):
toplevels = []
followlinks = getattr(opts, 'followlinks', False)
+ includeprivate = getattr(opts, 'includeprivate', False)
for root, subs, files in os.walk(rootpath, followlinks=followlinks):
- if is_excluded(root, excludes):
- del subs[:]
- continue
- # document only Python module files
+ # document only Python module files (that aren't excluded)
py_files = sorted(f for f in files
- if path.splitext(f)[1] in PY_SUFFIXES)
+ if path.splitext(f)[1] in PY_SUFFIXES and
+ not is_excluded(path.join(root, f), excludes))
is_pkg = INITPY in py_files
if is_pkg:
py_files.remove(INITPY)
@@ -200,8 +196,14 @@ def recurse_tree(rootpath, excludes, opts):
# only accept non-package at toplevel
del subs[:]
continue
- # remove hidden ('.') and private ('_') directories
- subs[:] = sorted(sub for sub in subs if sub[0] not in ['.', '_'])
+ # remove hidden ('.') and private ('_') directories, as well as
+ # excluded dirs
+ if includeprivate:
+ exclude_prefixes = ('.',)
+ else:
+ exclude_prefixes = ('.', '_')
+ subs[:] = sorted(sub for sub in subs if not sub.startswith(exclude_prefixes)
+ and not is_excluded(path.join(root, sub), excludes))
if is_pkg:
# we are in a package with something to document
@@ -225,47 +227,35 @@ def recurse_tree(rootpath, excludes, opts):
def normalize_excludes(rootpath, excludes):
- """
- Normalize the excluded directory list:
- * must be either an absolute path or start with rootpath,
- * otherwise it is joined with rootpath
- * with trailing slash
- """
- f_excludes = []
- for exclude in excludes:
- if not path.isabs(exclude) and not exclude.startswith(rootpath):
- exclude = path.join(rootpath, exclude)
- f_excludes.append(path.normpath(exclude) + path.sep)
- return f_excludes
+ """Normalize the excluded directory list."""
+ return [path.normpath(path.abspath(exclude)) for exclude in excludes]
def is_excluded(root, excludes):
- """
- Check if the directory is in the exclude list.
+ """Check if the directory is in the exclude list.
Note: by having trailing slashes, we avoid common prefix issues, like
e.g. an exlude "foo" also accidentally excluding "foobar".
"""
- sep = path.sep
- if not root.endswith(sep):
- root += sep
+ root = path.normpath(root)
for exclude in excludes:
- if root.startswith(exclude):
+ if root == exclude:
return True
return False
def main(argv=sys.argv):
- """
- Parse and check the command line arguments.
- """
+ """Parse and check the command line arguments."""
parser = optparse.OptionParser(
usage="""\
-usage: %prog [options] -o <output_path> <module_path> [exclude_paths, ...]
+usage: %prog [options] -o <output_path> <module_path> [exclude_path, ...]
Look recursively in <module_path> for Python modules and packages and create
one reST file with automodule directives per package in the <output_path>.
+The <exclude_path>s can be files and/or directories that will be excluded
+from generation.
+
Note: By default this script will not overwrite already created files.""")
parser.add_option('-o', '--output-dir', action='store', dest='destdir',
@@ -327,6 +317,7 @@ Note: By default this script will not overwrite already created files.""")
if not path.isdir(opts.destdir):
if not opts.dryrun:
os.makedirs(opts.destdir)
+ rootpath = path.normpath(path.abspath(rootpath))
excludes = normalize_excludes(rootpath, excludes)
modules = recurse_tree(rootpath, excludes, opts)
if opts.full:
diff --git a/sphinx/application.py b/sphinx/application.py
index 6b86b181..c9626b3b 100644
--- a/sphinx/application.py
+++ b/sphinx/application.py
@@ -234,6 +234,17 @@ class Sphinx(object):
wfile.flush()
def warn(self, message, location=None, prefix='WARNING: '):
+ """Emit a warning.
+
+ If *location* is given, it should either be a tuple of (docname, lineno)
+ or a string describing the location of the warning as well as possible.
+
+ *prefix* usually should not be changed.
+
+ .. note:: For warnings emitted during parsing, you should use
+ :meth:`.BuildEnvironment.warn` since that will collect all
+ warnings during parsing for later output.
+ """
if isinstance(location, tuple):
docname, lineno = location
if docname:
@@ -248,9 +259,22 @@ class Sphinx(object):
self._log(warntext, self._warning, True)
def info(self, message='', nonl=False):
+ """Emit an informational message.
+
+ If *nonl* is true, don't emit a newline at the end (which implies that
+ more info output will follow soon.)
+ """
self._log(message, self._status, nonl)
def verbose(self, message, *args, **kwargs):
+ """Emit a verbose informational message.
+
+ The message will only be emitted for verbosity levels >= 1 (i.e. at
+ least one ``-v`` option was given).
+
+ The message can contain %-style interpolation placeholders, which is
+ formatted with either the ``*args`` or ``**kwargs`` when output.
+ """
if self.verbosity < 1:
return
if args or kwargs:
@@ -258,6 +282,14 @@ class Sphinx(object):
self._log(message, self._status)
def debug(self, message, *args, **kwargs):
+ """Emit a debug-level informational message.
+
+ The message will only be emitted for verbosity levels >= 2 (i.e. at
+ least two ``-v`` options were given).
+
+ The message can contain %-style interpolation placeholders, which is
+ formatted with either the ``*args`` or ``**kwargs`` when output.
+ """
if self.verbosity < 2:
return
if args or kwargs:
@@ -265,6 +297,14 @@ class Sphinx(object):
self._log(darkgray(message), self._status)
def debug2(self, message, *args, **kwargs):
+ """Emit a lowlevel debug-level informational message.
+
+ The message will only be emitted for verbosity level 3 (i.e. three
+ ``-v`` options were given).
+
+ The message can contain %-style interpolation placeholders, which is
+ formatted with either the ``*args`` or ``**kwargs`` when output.
+ """
if self.verbosity < 3:
return
if args or kwargs:
diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py
index c153d121..17c9f51d 100644
--- a/sphinx/builders/__init__.py
+++ b/sphinx/builders/__init__.py
@@ -265,6 +265,12 @@ class Builder(object):
self.info(bold('no targets are out of date.'))
return
+ # filter "docnames" (list of outdated files) by the updated
+ # found_docs of the environment; this will remove docs that
+ # have since been removed
+ if docnames and docnames != ['__all__']:
+ docnames = set(docnames) & self.env.found_docs
+
# another indirection to support builders that don't build
# files individually
self.write(docnames, list(updated_docnames), method)
@@ -289,6 +295,7 @@ class Builder(object):
docnames = set(build_docnames) | set(updated_docnames)
else:
docnames = set(build_docnames)
+ self.app.debug('docnames to write: %s', ', '.join(sorted(docnames)))
# add all toctree-containing files that may have changed
for docname in list(docnames):
diff --git a/sphinx/builders/epub.py b/sphinx/builders/epub.py
index 1dfcc704..35061b79 100644
--- a/sphinx/builders/epub.py
+++ b/sphinx/builders/epub.py
@@ -12,7 +12,6 @@
import os
import re
-import sys
import time
import codecs
import zipfile
@@ -750,12 +749,5 @@ class EpubBuilder(StandaloneHTMLBuilder):
zipfile.ZIP_STORED)
for file in projectfiles:
fp = path.join(outdir, file)
- if sys.version_info < (2, 6):
- # When zipile.ZipFile.write call with unicode filename, ZipFile
- # encode filename to 'utf-8' (only after Python-2.6).
- if isinstance(file, unicode):
- # OEBPS Container Format (OCF) 2.0.1 specification require
- # "File Names MUST be UTF-8 encoded".
- file = file.encode('utf-8')
epub.write(fp, file, zipfile.ZIP_DEFLATED)
epub.close()
diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py
index 55e46ba4..0a5d1c88 100644
--- a/sphinx/builders/html.py
+++ b/sphinx/builders/html.py
@@ -16,11 +16,7 @@ import codecs
import posixpath
import cPickle as pickle
from os import path
-try:
- from hashlib import md5
-except ImportError:
- # 2.4 compatibility
- from md5 import md5
+from hashlib import md5
from docutils import nodes
from docutils.io import DocTreeInput, StringOutput
@@ -35,7 +31,7 @@ from sphinx.util.osutil import SEP, os_path, relative_uri, ensuredir, \
movefile, ustrftime, copyfile
from sphinx.util.nodes import inline_all_toctrees
from sphinx.util.matching import patmatch, compile_matchers
-from sphinx.util.pycompat import any, b
+from sphinx.util.pycompat import b
from sphinx.errors import SphinxError
from sphinx.locale import _
from sphinx.search import js_index
@@ -583,10 +579,7 @@ class StandaloneHTMLBuilder(Builder):
# then, copy over all user-supplied static files
staticentries = [path.join(self.confdir, spath)
for spath in self.config.html_static_path]
- matchers = compile_matchers(
- self.config.exclude_patterns +
- ['**/' + d for d in self.config.exclude_dirnames]
- )
+ matchers = compile_matchers(self.config.exclude_patterns)
for entry in staticentries:
if not path.exists(entry):
self.warn('html_static_path entry %r does not exist' % entry)
@@ -597,15 +590,17 @@ class StandaloneHTMLBuilder(Builder):
if self.config.html_logo:
logobase = path.basename(self.config.html_logo)
logotarget = path.join(self.outdir, '_static', logobase)
- if not path.isfile(logobase):
- self.warn('logo file %r does not exist' % logobase)
+ if not path.isfile(path.join(self.confdir, self.config.html_logo)):
+ self.warn('logo file %r does not exist' % self.config.html_logo)
elif not path.isfile(logotarget):
copyfile(path.join(self.confdir, self.config.html_logo),
logotarget)
if self.config.html_favicon:
iconbase = path.basename(self.config.html_favicon)
icontarget = path.join(self.outdir, '_static', iconbase)
- if not path.isfile(icontarget):
+ if not path.isfile(path.join(self.confdir, self.config.html_favicon)):
+ self.warn('favicon file %r does not exist' % self.config.html_favicon)
+ elif not path.isfile(icontarget):
copyfile(path.join(self.confdir, self.config.html_favicon),
icontarget)
self.info('done')
@@ -1098,8 +1093,4 @@ class JSONHTMLBuilder(SerializingHTMLBuilder):
searchindex_filename = 'searchindex.json'
def init(self):
- if jsonimpl.json is None:
- 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/linkcheck.py b/sphinx/builders/linkcheck.py
index 6cbe361b..171e68d5 100644
--- a/sphinx/builders/linkcheck.py
+++ b/sphinx/builders/linkcheck.py
@@ -10,7 +10,6 @@
"""
import re
-import sys
import Queue
import socket
import threading
@@ -110,7 +109,7 @@ class CheckExternalLinksBuilder(Builder):
def check_thread(self):
kwargs = {}
- if sys.version_info > (2, 5) and self.app.config.linkcheck_timeout:
+ if self.app.config.linkcheck_timeout:
kwargs['timeout'] = self.app.config.linkcheck_timeout
def check():
diff --git a/sphinx/cmdline.py b/sphinx/cmdline.py
index 2d7146c3..97f9718b 100644
--- a/sphinx/cmdline.py
+++ b/sphinx/cmdline.py
@@ -42,6 +42,7 @@ General options
-d <path> path for the cached environment and doctree files
(default: outdir/.doctrees)
-j <N> build in parallel with N processes where possible
+-M <builder> "make" mode -- used by Makefile, like "sphinx-build -M html"
Build configuration options
^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -82,19 +83,28 @@ def main(argv):
# Windows' poor cmd box doesn't understand ANSI sequences
nocolor()
+ # parse options
try:
opts, args = getopt.getopt(argv[1:], 'ab:t:d:c:CD:A:nNEqQWw:PThvj:',
['help', 'version'])
- allopts = set(opt[0] for opt in opts)
- if '-h' in allopts or '--help' in allopts:
- usage(argv)
- print >>sys.stderr
- print >>sys.stderr, 'For more information, see '\
- '<http://sphinx-doc.org/>.'
- return 0
- if '--version' in allopts:
- print 'Sphinx (sphinx-build) %s' % __version__
- return 0
+ except getopt.error, err:
+ usage(argv, 'Error: %s' % err)
+ return 1
+
+ # handle basic options
+ allopts = set(opt[0] for opt in opts)
+ # help and version options
+ if '-h' in allopts or '--help' in allopts:
+ usage(argv)
+ print >>sys.stderr
+ print >>sys.stderr, 'For more information, see <http://sphinx-doc.org/>.'
+ return 0
+ if '--version' in allopts:
+ print 'Sphinx (sphinx-build) %s' % __version__
+ return 0
+
+ # get paths (first and second positional argument)
+ try:
srcdir = confdir = abspath(args[0])
if not path.isdir(srcdir):
print >>sys.stderr, 'Error: Cannot find source directory `%s\'.' % (
@@ -103,12 +113,9 @@ def main(argv):
if not path.isfile(path.join(srcdir, 'conf.py')) and \
'-c' not in allopts and '-C' not in allopts:
print >>sys.stderr, ('Error: Source directory doesn\'t '
- 'contain conf.py file.')
+ 'contain a conf.py file.')
return 1
outdir = abspath(args[1])
- except getopt.error, err:
- usage(argv, 'Error: %s' % err)
- return 1
except IndexError:
usage(argv, 'Error: Insufficient arguments.')
return 1
@@ -118,6 +125,7 @@ def main(argv):
'encoding (%r).' % fs_encoding)
return 1
+ # handle remaining filename arguments
filenames = args[2:]
err = 0
for filename in filenames:
@@ -262,6 +270,13 @@ def main(argv):
elif isinstance(err, SphinxError):
print >>error, red('%s:' % err.category)
print >>error, terminal_safe(unicode(err))
+ elif isinstance(err, UnicodeError):
+ print >>error, red('Encoding error:')
+ print >>error, terminal_safe(unicode(err))
+ tbpath = save_traceback(app)
+ print >>error, red('The full traceback has been saved '
+ 'in %s, if you want to report the '
+ 'issue to the developers.' % tbpath)
else:
print >>error, red('Exception occurred:')
print >>error, format_exception_cut_frames().rstrip()
diff --git a/sphinx/config.py b/sphinx/config.py
index df74e914..ace5f2a6 100644
--- a/sphinx/config.py
+++ b/sphinx/config.py
@@ -51,10 +51,6 @@ class Config(object):
source_suffix = ('.rst', 'env'),
source_encoding = ('utf-8-sig', 'env'),
exclude_patterns = ([], 'env'),
- # the next three are all deprecated now
- unused_docs = ([], 'env'),
- exclude_trees = ([], 'env'),
- exclude_dirnames = ([], 'env'),
default_role = (None, 'env'),
add_function_parentheses = (True, 'env'),
add_module_names = (True, 'env'),
diff --git a/sphinx/directives/code.py b/sphinx/directives/code.py
index 7b1acfd5..6900ea6b 100644
--- a/sphinx/directives/code.py
+++ b/sphinx/directives/code.py
@@ -58,6 +58,7 @@ class CodeBlock(Directive):
'linenos': directives.flag,
'lineno-start': int,
'emphasize-lines': directives.unchanged_required,
+ 'filename': directives.unchanged_required,
}
def run(self):
@@ -76,6 +77,9 @@ class CodeBlock(Directive):
literal = nodes.literal_block(code, code)
literal['language'] = self.arguments[0]
+ filename = self.options.get('filename')
+ if filename:
+ literal['filename'] = filename
literal['linenos'] = 'linenos' in self.options or \
'lineno-start' in self.options
extra_args = literal['highlight_args'] = {}
@@ -111,6 +115,7 @@ class LiteralInclude(Directive):
'prepend': directives.unchanged_required,
'append': directives.unchanged_required,
'emphasize-lines': directives.unchanged_required,
+ 'filename': directives.unchanged,
}
def run(self):
@@ -212,6 +217,11 @@ class LiteralInclude(Directive):
retnode['language'] = self.options['language']
retnode['linenos'] = 'linenos' in self.options or \
'lineno-start' in self.options
+ filename = self.options.get('filename')
+ if filename is not None:
+ if not filename:
+ filename = self.arguments[0]
+ retnode['filename'] = filename
extra_args = retnode['highlight_args'] = {}
if hl_lines is not None:
extra_args['hl_lines'] = hl_lines
diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py
index 377130b8..d44c9cd7 100644
--- a/sphinx/domains/cpp.py
+++ b/sphinx/domains/cpp.py
@@ -537,6 +537,7 @@ class DefinitionParser(object):
'mutable': None,
'const': None,
'typename': None,
+ 'struct': None,
'unsigned': set(('char', 'short', 'int', 'long')),
'signed': set(('char', 'short', 'int', 'long')),
'short': set(('int',)),
diff --git a/sphinx/environment.py b/sphinx/environment.py
index a319ef3c..ca9a213e 100644
--- a/sphinx/environment.py
+++ b/sphinx/environment.py
@@ -246,10 +246,17 @@ class BuildEnvironment:
self.versioning_condition = condition
def warn(self, docname, msg, lineno=None):
+ """Emit a warning.
+
+ This differs from using ``app.warn()`` in that the warning may not
+ be emitted instantly, but collected for emitting all warnings after
+ the update of the environment.
+ """
# strange argument order is due to backwards compatibility
self._warnfunc(msg, (docname, lineno))
def warn_node(self, msg, node):
+ """Like :meth:`warn`, but with source information taken from *node*."""
self._warnfunc(msg, '%s:%s' % get_source_line(node))
def clear_doc(self, docname):
@@ -333,9 +340,6 @@ class BuildEnvironment:
matchers = compile_matchers(
config.exclude_patterns[:] +
config.html_extra_path +
- config.exclude_trees +
- [d + config.source_suffix for d in config.unused_docs] +
- ['**/' + d for d in config.exclude_dirnames] +
['**/_sources', '.#*']
)
self.found_docs = set(get_matching_docs(
@@ -614,11 +618,8 @@ class BuildEnvironment:
pub.process_programmatic_settings(None, self.settings, None)
pub.set_source(None, src_path.encode(fs_encoding))
pub.set_destination(None, None)
- try:
- pub.publish()
- doctree = pub.document
- except UnicodeError, err:
- raise SphinxError(str(err))
+ pub.publish()
+ doctree = pub.document
# post-processing
self.filter_messages(doctree)
@@ -696,7 +697,7 @@ class BuildEnvironment:
@property
def docname(self):
- """Backwards compatible alias."""
+ """Returns the docname of the document currently being parsed."""
return self.temp_data['docname']
@property
@@ -710,16 +711,28 @@ class BuildEnvironment:
return self.temp_data.get('py:class')
def new_serialno(self, category=''):
- """Return a serial number, e.g. for index entry targets."""
+ """Return a serial number, e.g. for index entry targets.
+
+ The number is guaranteed to be unique in the current document.
+ """
key = category + 'serialno'
cur = self.temp_data.get(key, 0)
self.temp_data[key] = cur + 1
return cur
def note_dependency(self, filename):
+ """Add *filename* as a dependency of the current document.
+
+ This means that the document will be rebuilt if this file changes.
+
+ *filename* should be absolute or relative to the source directory.
+ """
self.dependencies.setdefault(self.docname, set()).add(filename)
def note_reread(self):
+ """Add the current document to the list of documents that will
+ automatically be re-read at the next build.
+ """
self.reread_always.add(self.docname)
def note_versionchange(self, type, version, node, lineno):
@@ -738,7 +751,6 @@ class BuildEnvironment:
self.app.debug('%s [filtered system message]', node.astext())
node.parent.remove(node)
-
def process_dependencies(self, docname, doctree):
"""Process docutils-generated dependency info."""
cwd = os.getcwd()
diff --git a/sphinx/ext/autodoc.py b/sphinx/ext/autodoc.py
index c454641b..77862ff2 100644
--- a/sphinx/ext/autodoc.py
+++ b/sphinx/ext/autodoc.py
@@ -29,7 +29,7 @@ from sphinx.util.nodes import nested_parse_with_titles
from sphinx.util.compat import Directive
from sphinx.util.inspect import getargspec, isdescriptor, safe_getmembers, \
safe_getattr, safe_repr, is_builtin_class_method
-from sphinx.util.pycompat import base_exception, class_types
+from sphinx.util.pycompat import class_types
from sphinx.util.docstrings import prepare_docstring
@@ -70,6 +70,35 @@ class Options(dict):
return None
+class _MockModule(object):
+ """Used by autodoc_mock_imports."""
+ def __init__(self, *args, **kwargs):
+ pass
+
+ def __call__(self, *args, **kwargs):
+ return _MockModule()
+
+ @classmethod
+ def __getattr__(cls, name):
+ if name in ('__file__', '__path__'):
+ return '/dev/null'
+ elif name[0] == name[0].upper():
+ # Not very good, we assume Uppercase names are classes...
+ mocktype = type(name, (), {})
+ mocktype.__module__ = __name__
+ return mocktype
+ else:
+ return _MockModule()
+
+def mock_import(modname):
+ if '.' in modname:
+ pkg, _n, mods = modname.rpartition('.')
+ mock_import(pkg)
+ mod = _MockModule()
+ sys.modules[modname] = mod
+ return mod
+
+
ALL = object()
INSTANCEATTR = object()
@@ -332,6 +361,9 @@ class Documenter(object):
self.modname, '.'.join(self.objpath))
try:
dbg('[autodoc] import %s', self.modname)
+ for modname in self.env.config.autodoc_mock_imports:
+ dbg('[autodoc] adding a mock module %s!', self.modname)
+ mock_import(modname)
__import__(self.modname)
parent = None
obj = self.module = sys.modules[self.modname]
@@ -1085,8 +1117,9 @@ class ClassDocumenter(ModuleLevelDocumenter):
initdocstring = self.get_attr(
self.get_attr(self.object, '__init__', None), '__doc__')
# for new-style classes, no __init__ means default __init__
- if (initdocstring == object.__init__.__doc__ or # for pypy
- initdocstring.strip() == object.__init__.__doc__): #for !pypy
+ if (initdocstring is not None and
+ (initdocstring == object.__init__.__doc__ or # for pypy
+ initdocstring.strip() == object.__init__.__doc__)): #for !pypy
initdocstring = None
if initdocstring:
if content == 'init':
@@ -1130,7 +1163,7 @@ class ExceptionDocumenter(ClassDocumenter):
@classmethod
def can_document_member(cls, member, membername, isattr, parent):
return isinstance(member, class_types) and \
- issubclass(member, base_exception)
+ issubclass(member, BaseException)
class DataDocumenter(ModuleLevelDocumenter):
@@ -1453,6 +1486,7 @@ def setup(app):
app.add_config_value('autodoc_member_order', 'alphabetic', True)
app.add_config_value('autodoc_default_flags', [], True)
app.add_config_value('autodoc_docstring_signature', True, True)
+ app.add_config_value('autodoc_mock_imports', [], True)
app.add_event('autodoc-process-docstring')
app.add_event('autodoc-process-signature')
app.add_event('autodoc-skip-member')
diff --git a/sphinx/ext/graphviz.py b/sphinx/ext/graphviz.py
index 800e9ca8..32bb96d3 100644
--- a/sphinx/ext/graphviz.py
+++ b/sphinx/ext/graphviz.py
@@ -178,6 +178,9 @@ def render_dot(self, code, options, format, prefix='graphviz'):
if p.returncode != 0:
raise GraphvizError('dot exited with error:\n[stderr]\n%s\n'
'[stdout]\n%s' % (stderr, stdout))
+ if not path.isfile(outfn):
+ raise GraphvizError('dot did not produce an output file:\n[stderr]\n%s\n'
+ '[stdout]\n%s' % (stderr, stdout))
return relfn, outfn
diff --git a/sphinx/ext/inheritance_diagram.py b/sphinx/ext/inheritance_diagram.py
index 2d1d6e30..fd973544 100644
--- a/sphinx/ext/inheritance_diagram.py
+++ b/sphinx/ext/inheritance_diagram.py
@@ -154,8 +154,18 @@ class InheritanceGraph(object):
nodename = self.class_name(cls, parts)
fullname = self.class_name(cls, 0)
+ # Use first line of docstring as tooltip, if available
+ tooltip = None
+ try:
+ if cls.__doc__:
+ doc = cls.__doc__.strip().split("\n")[0]
+ if doc:
+ tooltip = '"%s"' % doc.replace('"', '\\"')
+ except Exception: # might raise AttributeError for strange classes
+ pass
+
baselist = []
- all_classes[cls] = (nodename, fullname, baselist)
+ all_classes[cls] = (nodename, fullname, baselist, tooltip)
for base in cls.__bases__:
if not show_builtins and base in builtins:
continue
@@ -168,7 +178,7 @@ class InheritanceGraph(object):
for cls in classes:
recurse(cls)
- return all_classes
+ return all_classes.values()
def class_name(self, cls, parts=0):
"""Given a class object, return a fully-qualified name.
@@ -188,7 +198,7 @@ class InheritanceGraph(object):
def get_all_class_names(self):
"""Get all of the class names involved in the graph."""
- return [fullname for (_, fullname, _) in self.class_info.values()]
+ return [fullname for (_, fullname, _, _) in self.class_info]
# These are the default attrs for graphviz
default_graph_attrs = {
@@ -241,17 +251,13 @@ class InheritanceGraph(object):
res.append('digraph %s {\n' % name)
res.append(self._format_graph_attrs(g_attrs))
- for cls, (name, fullname, bases) in sorted(self.class_info.items()):
+ for name, fullname, bases, tooltip in sorted(self.class_info):
# Write the node
this_node_attrs = n_attrs.copy()
if fullname in urls:
this_node_attrs['URL'] = '"%s"' % urls[fullname]
- # Use first line of docstring as tooltip, if available
- if cls.__doc__:
- doc = cls.__doc__.strip().split("\n")[0]
- if doc:
- doc = doc.replace('"', '\\"')
- this_node_attrs['tooltip'] = '"%s"' % doc
+ if tooltip:
+ this_node_attrs['tooltip'] = tooltip
res.append(' "%s" [%s];\n' %
(name, self._format_node_attrs(this_node_attrs)))
diff --git a/sphinx/ext/napoleon/__init__.py b/sphinx/ext/napoleon/__init__.py
new file mode 100644
index 00000000..d3b6f754
--- /dev/null
+++ b/sphinx/ext/napoleon/__init__.py
@@ -0,0 +1,375 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.ext.napoleon
+ ~~~~~~~~~~~~~~~~~~~
+
+ Support for NumPy and Google style docstrings.
+
+ :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import sys
+from sphinx.ext.napoleon.docstring import GoogleDocstring, NumpyDocstring
+
+
+class Config(object):
+ """Sphinx napoleon extension settings in `conf.py`.
+
+ Listed below are all the settings used by napoleon and their default
+ values. These settings can be changed in the Sphinx `conf.py` file. Make
+ sure that both "sphinx.ext.autodoc" and "sphinx.ext.napoleon" are
+ enabled in `conf.py`::
+
+ # conf.py
+
+ # Add any Sphinx extension module names here, as strings
+ extensions = ['sphinx.ext.autodoc', 'sphinx.ext.napoleon']
+
+ # Napoleon settings
+ napoleon_google_docstring = True
+ napoleon_numpy_docstring = True
+ napoleon_include_private_with_doc = False
+ napoleon_include_special_with_doc = True
+ napoleon_use_admonition_for_examples = False
+ napoleon_use_admonition_for_notes = False
+ napoleon_use_admonition_for_references = False
+ napoleon_use_ivar = False
+ napoleon_use_param = False
+ napoleon_use_rtype = False
+
+ .. _Google style:
+ http://google-styleguide.googlecode.com/svn/trunk/pyguide.html
+ .. _NumPy style:
+ https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt
+
+ Attributes
+ ----------
+ napoleon_google_docstring : bool, defaults to True
+ True to parse `Google style`_ docstrings. False to disable support
+ for Google style docstrings.
+ napoleon_numpy_docstring : bool, defaults to True
+ True to parse `NumPy style`_ docstrings. False to disable support
+ for NumPy style docstrings.
+ napoleon_include_private_with_doc : bool, defaults to False
+ True to include private members (like ``_membername``) with docstrings
+ in the documentation. False to fall back to Sphinx's default behavior.
+
+ **If True**::
+
+ def _included(self):
+ \"\"\"
+ This will be included in the docs because it has a docstring
+ \"\"\"
+ pass
+
+ def _skipped(self):
+ # This will NOT be included in the docs
+ pass
+
+ napoleon_include_special_with_doc : bool, defaults to True
+ True to include special members (like ``__membername__``) with
+ docstrings in the documentation. False to fall back to Sphinx's
+ default behavior.
+
+ **If True**::
+
+ def __str__(self):
+ \"\"\"
+ This will be included in the docs because it has a docstring
+ \"\"\"
+ return unicode(self).encode('utf-8')
+
+ def __unicode__(self):
+ # This will NOT be included in the docs
+ return unicode(self.__class__.__name__)
+
+ napoleon_use_admonition_for_examples : bool, defaults to False
+ True to use the ``.. admonition::`` directive for the **Example** and
+ **Examples** sections. False to use the ``.. rubric::`` directive
+ instead. One may look better than the other depending on what HTML
+ theme is used.
+
+ This `NumPy style`_ snippet will be converted as follows::
+
+ Example
+ -------
+ This is just a quick example
+
+ **If True**::
+
+ .. admonition:: Example
+
+ This is just a quick example
+
+ **If False**::
+
+ .. rubric:: Example
+
+ This is just a quick example
+
+ napoleon_use_admonition_for_notes : bool, defaults to False
+ True to use the ``.. admonition::`` directive for **Notes** sections.
+ False to use the ``.. rubric::`` directive instead.
+
+ Note
+ ----
+ The singular **Note** section will always be converted to a
+ ``.. note::`` directive.
+
+ See Also
+ --------
+ :attr:`napoleon_use_admonition_for_examples`
+
+ napoleon_use_admonition_for_references : bool, defaults to False
+ True to use the ``.. admonition::`` directive for **References**
+ sections. False to use the ``.. rubric::`` directive instead.
+
+ See Also
+ --------
+ :attr:`napoleon_use_admonition_for_examples`
+
+ napoleon_use_ivar : bool, defaults to False
+ True to use the ``:ivar:`` role for instance variables. False to use
+ the ``.. attribute::`` directive instead.
+
+ This `NumPy style`_ snippet will be converted as follows::
+
+ Attributes
+ ----------
+ attr1 : int
+ Description of `attr1`
+
+ **If True**::
+
+ :ivar attr1: Description of `attr1`
+ :vartype attr1: int
+
+ **If False**::
+
+ .. attribute:: attr1
+ :annotation: int
+
+ Description of `attr1`
+
+ napoleon_use_param : bool, defaults to False
+ True to use a ``:param:`` role for each function parameter. False to
+ use a single ``:parameters:`` role for all the parameters.
+
+ This `NumPy style`_ snippet will be converted as follows::
+
+ Parameters
+ ----------
+ arg1 : str
+ Description of `arg1`
+ arg2 : int, optional
+ Description of `arg2`, defaults to 0
+
+ **If True**::
+
+ :param arg1: Description of `arg1`
+ :type arg1: str
+ :param arg2: Description of `arg2`, defaults to 0
+ :type arg2: int, optional
+
+ **If False**::
+
+ :parameters: * **arg1** (*str*) --
+ Description of `arg1`
+ * **arg2** (*int, optional*) --
+ Description of `arg2`, defaults to 0
+
+ napoleon_use_rtype : bool, defaults to False
+ True to use the ``:rtype:`` role for the return type. False to output
+ the return type inline with the description.
+
+ This `NumPy style`_ snippet will be converted as follows::
+
+ Returns
+ -------
+ bool
+ True if successful, False otherwise
+
+ **If True**::
+
+ :returns: True if successful, False otherwise
+ :rtype: bool
+
+ **If False**::
+
+ :returns: *bool* -- True if successful, False otherwise
+
+ """
+ _config_values = {
+ 'napoleon_google_docstring': (True, 'env'),
+ 'napoleon_numpy_docstring': (True, 'env'),
+ 'napoleon_include_private_with_doc': (False, 'env'),
+ 'napoleon_include_special_with_doc': (True, 'env'),
+ 'napoleon_use_admonition_for_examples': (False, 'env'),
+ 'napoleon_use_admonition_for_notes': (False, 'env'),
+ 'napoleon_use_admonition_for_references': (False, 'env'),
+ 'napoleon_use_ivar': (False, 'env'),
+ 'napoleon_use_param': (False, 'env'),
+ 'napoleon_use_rtype': (False, 'env'),
+ }
+
+ def __init__(self, **settings):
+ for name, (default, rebuild) in self._config_values.iteritems():
+ setattr(self, name, default)
+ for name, value in settings.iteritems():
+ setattr(self, name, value)
+
+
+def setup(app):
+ """Sphinx extension setup function.
+
+ When the extension is loaded, Sphinx imports this module and executes
+ the ``setup()`` function, which in turn notifies Sphinx of everything
+ the extension offers.
+
+ Parameters
+ ----------
+ app : sphinx.application.Sphinx
+ Application object representing the Sphinx process
+
+ See Also
+ --------
+ The Sphinx documentation on `Extensions`_, the `Extension Tutorial`_, and
+ the `Extension API`_.
+
+ .. _Extensions: http://sphinx-doc.org/extensions.html
+ .. _Extension Tutorial: http://sphinx-doc.org/ext/tutorial.html
+ .. _Extension API: http://sphinx-doc.org/ext/appapi.html
+
+ """
+ from sphinx.application import Sphinx
+ if not isinstance(app, Sphinx):
+ return # probably called by tests
+
+ app.connect('autodoc-process-docstring', _process_docstring)
+ app.connect('autodoc-skip-member', _skip_member)
+
+ for name, (default, rebuild) in Config._config_values.iteritems():
+ app.add_config_value(name, default, rebuild)
+
+
+def _process_docstring(app, what, name, obj, options, lines):
+ """Process the docstring for a given python object.
+
+ Called when autodoc has read and processed a docstring. `lines` is a list
+ of docstring lines that `_process_docstring` modifies in place to change
+ what Sphinx outputs.
+
+ The following settings in conf.py control what styles of docstrings will
+ be parsed:
+
+ * ``napoleon_google_docstring`` -- parse Google style docstrings
+ * ``napoleon_numpy_docstring`` -- parse NumPy style docstrings
+
+ Parameters
+ ----------
+ app : sphinx.application.Sphinx
+ Application object representing the Sphinx process.
+ what : str
+ A string specifying the type of the object to which the docstring
+ belongs. Valid values: "module", "class", "exception", "function",
+ "method", "attribute".
+ name : str
+ The fully qualified name of the object.
+ obj : module, class, exception, function, method, or attribute
+ The object to which the docstring belongs.
+ options : sphinx.ext.autodoc.Options
+ The options given to the directive: an object with attributes
+ inherited_members, undoc_members, show_inheritance and noindex that
+ are True if the flag option of same name was given to the auto
+ directive.
+ lines : list of str
+ The lines of the docstring, see above.
+
+ .. note:: `lines` is modified *in place*
+
+ """
+ result_lines = lines
+ if app.config.napoleon_numpy_docstring:
+ docstring = NumpyDocstring(result_lines, app.config, app, what, name,
+ obj, options)
+ result_lines = docstring.lines()
+ if app.config.napoleon_google_docstring:
+ docstring = GoogleDocstring(result_lines, app.config, app, what, name,
+ obj, options)
+ result_lines = docstring.lines()
+ lines[:] = result_lines[:]
+
+
+def _skip_member(app, what, name, obj, skip, options):
+ """Determine if private and special class members are included in docs.
+
+ The following settings in conf.py determine if private and special class
+ members are included in the generated documentation:
+
+ * ``napoleon_include_private_with_doc`` --
+ include private members if they have docstrings
+ * ``napoleon_include_special_with_doc`` --
+ include special members if they have docstrings
+
+ Parameters
+ ----------
+ app : sphinx.application.Sphinx
+ Application object representing the Sphinx process
+ what : str
+ A string specifying the type of the object to which the member
+ belongs. Valid values: "module", "class", "exception", "function",
+ "method", "attribute".
+ name : str
+ The name of the member.
+ obj : module, class, exception, function, method, or attribute.
+ For example, if the member is the __init__ method of class A, then
+ `obj` will be `A.__init__`.
+ skip : bool
+ A boolean indicating if autodoc will skip this member if `_skip_member`
+ does not override the decision
+ options : sphinx.ext.autodoc.Options
+ The options given to the directive: an object with attributes
+ inherited_members, undoc_members, show_inheritance and noindex that
+ are True if the flag option of same name was given to the auto
+ directive.
+
+ Returns
+ -------
+ bool
+ True if the member should be skipped during creation of the docs,
+ False if it should be included in the docs.
+
+ """
+ has_doc = getattr(obj, '__doc__', False)
+ is_member = (what == 'class' or what == 'exception' or what == 'module')
+ if name != '__weakref__' and name != '__init__' and has_doc and is_member:
+ if what == 'class' or what == 'exception':
+ if sys.version_info[0] < 3:
+ cls = getattr(obj, 'im_class', getattr(obj, '__objclass__',
+ None))
+ cls_is_owner = (cls and hasattr(cls, name) and
+ name in cls.__dict__)
+ elif sys.version_info[1] >= 3 and hasattr(obj, '__qualname__'):
+ cls_path, _, _ = obj.__qualname__.rpartition('.')
+ if cls_path:
+ import importlib
+ import functools
+
+ mod = importlib.import_module(obj.__module__)
+ cls = functools.reduce(getattr, cls_path.split('.'), mod)
+ cls_is_owner = (cls and hasattr(cls, name) and
+ name in cls.__dict__)
+ else:
+ cls_is_owner = False
+ else:
+ cls_is_owner = True
+
+ if what == 'module' or cls_is_owner:
+ is_special = name.startswith('__') and name.endswith('__')
+ is_private = not is_special and name.startswith('_')
+ inc_special = app.config.napoleon_include_special_with_doc
+ inc_private = app.config.napoleon_include_private_with_doc
+ if (is_special and inc_special) or (is_private and inc_private):
+ return False
+ return skip
diff --git a/sphinx/ext/napoleon/docstring.py b/sphinx/ext/napoleon/docstring.py
new file mode 100644
index 00000000..d6c73db8
--- /dev/null
+++ b/sphinx/ext/napoleon/docstring.py
@@ -0,0 +1,714 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.ext.napoleon.docstring
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+ Classes for docstring parsing and formatting.
+
+
+ :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import re
+import sys
+from sphinx.ext.napoleon.iterators import modify_iter
+
+
+if sys.version_info[0] >= 3:
+ basestring = str
+ xrange = range
+
+
+_directive_regex = re.compile(r'\.\. \S+::')
+_field_parens_regex = re.compile(r'\s*(\w+)\s*\(\s*(.+?)\s*\)')
+
+
+class GoogleDocstring(object):
+ """Parse Google style docstrings.
+
+ Convert Google style docstrings to reStructuredText.
+
+ Parameters
+ ----------
+ docstring : str or list of str
+ The docstring to parse, given either as a string or split into
+ individual lines.
+ config : sphinx.ext.napoleon.Config or sphinx.config.Config, optional
+ The configuration settings to use. If not given, defaults to the
+ config object on `app`; or if `app` is not given defaults to the
+ a new `sphinx.ext.napoleon.Config` object.
+
+ See Also
+ --------
+ :class:`sphinx.ext.napoleon.Config`
+
+ Other Parameters
+ ----------------
+ app : sphinx.application.Sphinx, optional
+ Application object representing the Sphinx process.
+ what : str, optional
+ A string specifying the type of the object to which the docstring
+ belongs. Valid values: "module", "class", "exception", "function",
+ "method", "attribute".
+ name : str, optional
+ The fully qualified name of the object.
+ obj : module, class, exception, function, method, or attribute
+ The object to which the docstring belongs.
+ options : sphinx.ext.autodoc.Options, optional
+ The options given to the directive: an object with attributes
+ inherited_members, undoc_members, show_inheritance and noindex that
+ are True if the flag option of same name was given to the auto
+ directive.
+
+ Example
+ -------
+ >>> from sphinx.ext.napoleon import Config
+ >>> config = Config(napoleon_use_param=True, napoleon_use_rtype=True)
+ >>> docstring = '''One line summary.
+ ...
+ ... Extended description.
+ ...
+ ... Args:
+ ... arg1(int): Description of `arg1`
+ ... arg2(str): Description of `arg2`
+ ... Returns:
+ ... str: Description of return value.
+ ... '''
+ >>> print(GoogleDocstring(docstring, config))
+ One line summary.
+ <BLANKLINE>
+ Extended description.
+ <BLANKLINE>
+ :param arg1: Description of `arg1`
+ :type arg1: int
+ :param arg2: Description of `arg2`
+ :type arg2: str
+ <BLANKLINE>
+ :returns: Description of return value.
+ :rtype: str
+
+ """
+ def __init__(self, docstring, config=None, app=None, what='', name='',
+ obj=None, options=None):
+ self._config = config
+ self._app = app
+ if not self._config:
+ from sphinx.ext.napoleon import Config
+ self._config = self._app and self._app.config or Config()
+ self._what = what
+ self._name = name
+ self._obj = obj
+ self._opt = options
+ if isinstance(docstring, basestring):
+ docstring = docstring.splitlines()
+ self._lines = docstring
+ self._line_iter = modify_iter(docstring, modifier=lambda s: s.rstrip())
+ self._parsed_lines = []
+ self._is_in_section = False
+ self._section_indent = 0
+ if not hasattr(self, '_directive_sections'):
+ self._directive_sections = []
+ if not hasattr(self, '_sections'):
+ self._sections = {
+ 'args': self._parse_parameters_section,
+ 'arguments': self._parse_parameters_section,
+ 'attributes': self._parse_attributes_section,
+ 'example': self._parse_examples_section,
+ 'examples': self._parse_examples_section,
+ 'keyword args': self._parse_keyword_arguments_section,
+ 'keyword arguments': self._parse_keyword_arguments_section,
+ 'methods': self._parse_methods_section,
+ 'note': self._parse_note_section,
+ 'notes': self._parse_notes_section,
+ 'other parameters': self._parse_other_parameters_section,
+ 'parameters': self._parse_parameters_section,
+ 'return': self._parse_returns_section,
+ 'returns': self._parse_returns_section,
+ 'raises': self._parse_raises_section,
+ 'references': self._parse_references_section,
+ 'see also': self._parse_see_also_section,
+ 'warning': self._parse_warning_section,
+ 'warnings': self._parse_warning_section,
+ 'warns': self._parse_warns_section,
+ 'yields': self._parse_yields_section,
+ }
+ self._parse()
+
+ def __str__(self):
+ """Return the parsed docstring in reStructuredText format.
+
+ Returns
+ -------
+ str
+ UTF-8 encoded version of the docstring.
+
+ """
+ if sys.version_info[0] >= 3:
+ return self.__unicode__()
+ else:
+ return self.__unicode__().encode('utf8')
+
+ def __unicode__(self):
+ """Return the parsed docstring in reStructuredText format.
+
+ Returns
+ -------
+ unicode
+ Unicode version of the docstring.
+
+ """
+ return u'\n'.join(self.lines())
+
+ def lines(self):
+ """Return the parsed lines of the docstring in reStructuredText format.
+
+ Returns
+ -------
+ list of str
+ The lines of the docstring in a list.
+
+ """
+ return self._parsed_lines
+
+ def _consume_indented_block(self, indent=1):
+ lines = []
+ line = self._line_iter.peek()
+ while(not self._is_section_break()
+ and (not line or self._is_indented(line, indent))):
+ lines.append(self._line_iter.next())
+ line = self._line_iter.peek()
+ return lines
+
+ def _consume_contiguous(self):
+ lines = []
+ while (self._line_iter.has_next()
+ and self._line_iter.peek()
+ and not self._is_section_header()):
+ lines.append(self._line_iter.next())
+ return lines
+
+ def _consume_empty(self):
+ lines = []
+ line = self._line_iter.peek()
+ while self._line_iter.has_next() and not line:
+ lines.append(self._line_iter.next())
+ line = self._line_iter.peek()
+ return lines
+
+ def _consume_field(self, parse_type=True, prefer_type=False):
+ line = self._line_iter.next()
+ _name, _, _desc = line.partition(':')
+ _name, _type, _desc = _name.strip(), '', _desc.strip()
+ match = _field_parens_regex.match(_name)
+ if parse_type and match:
+ _name = match.group(1)
+ _type = match.group(2)
+ if prefer_type and not _type:
+ _type, _name = _name, _type
+ indent = self._get_indent(line) + 1
+ _desc = [_desc] + self._dedent(self._consume_indented_block(indent))
+ _desc = self.__class__(_desc, self._config).lines()
+ return _name, _type, _desc
+
+ def _consume_fields(self, parse_type=True, prefer_type=False):
+ self._consume_empty()
+ fields = []
+ while not self._is_section_break():
+ _name, _type, _desc = self._consume_field(parse_type, prefer_type)
+ if _name or _type or _desc:
+ fields.append((_name, _type, _desc,))
+ return fields
+
+ def _consume_returns_section(self):
+ lines = self._dedent(self._consume_to_next_section())
+ if lines:
+ if ':' in lines[0]:
+ _type, _, _desc = lines[0].partition(':')
+ _name, _type, _desc = '', _type.strip(), _desc.strip()
+ match = _field_parens_regex.match(_type)
+ if match:
+ _name = match.group(1)
+ _type = match.group(2)
+ lines[0] = _desc
+ _desc = lines
+ else:
+ _name, _type, _desc = '', '', lines
+ _desc = self.__class__(_desc, self._config).lines()
+ return [(_name, _type, _desc,)]
+ else:
+ return []
+
+ def _consume_section_header(self):
+ section = self._line_iter.next()
+ stripped_section = section.strip(':')
+ if stripped_section.lower() in self._sections:
+ section = stripped_section
+ return section
+
+ def _consume_to_next_section(self):
+ self._consume_empty()
+ lines = []
+ while not self._is_section_break():
+ lines.append(self._line_iter.next())
+ return lines + self._consume_empty()
+
+ def _dedent(self, lines, full=False):
+ if full:
+ return [line.lstrip() for line in lines]
+ else:
+ min_indent = self._get_min_indent(lines)
+ return [line[min_indent:] for line in lines]
+
+ def _format_admonition(self, admonition, lines):
+ lines = self._strip_empty(lines)
+ if len(lines) == 1:
+ return ['.. %s:: %s' % (admonition, lines[0].strip()), '']
+ elif lines:
+ lines = self._indent(self._dedent(lines), 3)
+ return ['.. %s::' % admonition, ''] + lines + ['']
+ else:
+ return ['.. %s::' % admonition, '']
+
+ def _format_block(self, prefix, lines, padding=None):
+ if lines:
+ if padding is None:
+ padding = ' ' * len(prefix)
+ result_lines = []
+ for i, line in enumerate(lines):
+ if line:
+ if i == 0:
+ result_lines.append(prefix + line)
+ else:
+ result_lines.append(padding + line)
+ else:
+ result_lines.append('')
+ return result_lines
+ else:
+ return [prefix]
+
+ def _format_field(self, _name, _type, _desc):
+ separator = any([s for s in _desc]) and ' --' or ''
+ if _name:
+ if _type:
+ field = ['**%s** (*%s*)%s' % (_name, _type, separator)]
+ else:
+ field = ['**%s**%s' % (_name, separator)]
+ elif _type:
+ field = ['*%s*%s' % (_type, separator)]
+ else:
+ field = []
+ return field + _desc
+
+ def _format_fields(self, field_type, fields):
+ field_type = ':%s:' % field_type.strip()
+ padding = ' ' * len(field_type)
+ multi = len(fields) > 1
+ lines = []
+ for _name, _type, _desc in fields:
+ field = self._format_field(_name, _type, _desc)
+ if multi:
+ if lines:
+ lines.extend(self._format_block(padding + ' * ', field))
+ else:
+ lines.extend(self._format_block(field_type + ' * ', field))
+ else:
+ lines.extend(self._format_block(field_type + ' ', field))
+ return lines
+
+ def _get_current_indent(self, peek_ahead=0):
+ line = self._line_iter.peek(peek_ahead + 1)[peek_ahead]
+ while line != self._line_iter.sentinel:
+ if line:
+ return self._get_indent(line)
+ peek_ahead += 1
+ line = self._line_iter.peek(peek_ahead + 1)[peek_ahead]
+ return 0
+
+ def _get_indent(self, line):
+ for i, s in enumerate(line):
+ if not s.isspace():
+ return i
+ return len(line)
+
+ def _get_min_indent(self, lines):
+ min_indent = None
+ for line in lines:
+ if line:
+ indent = self._get_indent(line)
+ if min_indent is None:
+ min_indent = indent
+ elif indent < min_indent:
+ min_indent = indent
+ return min_indent or 0
+
+ def _indent(self, lines, n=4):
+ return [(' ' * n) + line for line in lines]
+
+ def _is_indented(self, line, indent=1):
+ for i, s in enumerate(line):
+ if i >= indent:
+ return True
+ elif not s.isspace():
+ return False
+ return False
+
+ def _is_section_header(self):
+ section = self._line_iter.peek().lower()
+ if section.strip(':') in self._sections:
+ header_indent = self._get_indent(section)
+ section_indent = self._get_current_indent(peek_ahead=1)
+ return section_indent > header_indent
+ elif self._directive_sections:
+ if _directive_regex.match(section):
+ for directive_section in self._directive_sections:
+ if section.startswith(directive_section):
+ return True
+ return False
+
+ def _is_section_break(self):
+ line = self._line_iter.peek()
+ return (not self._line_iter.has_next()
+ or self._is_section_header()
+ or (self._is_in_section
+ and line
+ and not self._is_indented(line, self._section_indent)))
+
+ def _parse(self):
+ self._parsed_lines = self._consume_empty()
+ while self._line_iter.has_next():
+ if self._is_section_header():
+ try:
+ section = self._consume_section_header()
+ self._is_in_section = True
+ self._section_indent = self._get_current_indent()
+ if _directive_regex.match(section):
+ lines = [section] + self._consume_to_next_section()
+ else:
+ lines = self._sections[section.lower()](section)
+ finally:
+ self._is_in_section = False
+ self._section_indent = 0
+ else:
+ if not self._parsed_lines:
+ lines = self._consume_contiguous() + self._consume_empty()
+ else:
+ lines = self._consume_to_next_section()
+ self._parsed_lines.extend(lines)
+
+ def _parse_attributes_section(self, section):
+ lines = []
+ for _name, _type, _desc in self._consume_fields():
+ if self._config.napoleon_use_ivar:
+ field = ':ivar %s: ' % _name
+ lines.extend(self._format_block(field, _desc))
+ if _type:
+ lines.append(':vartype %s: %s' % (_name, _type))
+ else:
+ lines.append('.. attribute:: ' + _name)
+ if _type:
+ lines.append(' :annotation: ' + _type)
+ if _desc:
+ lines.extend([''] + self._indent(_desc, 3))
+ lines.append('')
+ if self._config.napoleon_use_ivar:
+ lines.append('')
+ return lines
+
+ def _parse_examples_section(self, section):
+ use_admonition = self._config.napoleon_use_admonition_for_examples
+ return self._parse_generic_section(section, use_admonition)
+
+ def _parse_generic_section(self, section, use_admonition):
+ lines = self._strip_empty(self._consume_to_next_section())
+ lines = self._dedent(lines)
+ if use_admonition:
+ header = '.. admonition:: %s' % section
+ lines = self._indent(lines, 3)
+ else:
+ header = '.. rubric:: %s' % section
+ if lines:
+ return [header, ''] + lines + ['']
+ else:
+ return [header, '']
+
+ def _parse_keyword_arguments_section(self, section):
+ return self._format_fields('Keyword Arguments', self._consume_fields())
+
+ def _parse_methods_section(self, section):
+ lines = []
+ for _name, _, _desc in self._consume_fields(parse_type=False):
+ lines.append('.. method:: %s' % _name)
+ if _desc:
+ lines.extend([''] + self._indent(_desc, 3))
+ lines.append('')
+ return lines
+
+ def _parse_note_section(self, section):
+ lines = self._consume_to_next_section()
+ return self._format_admonition('note', lines)
+
+ def _parse_notes_section(self, section):
+ use_admonition = self._config.napoleon_use_admonition_for_notes
+ return self._parse_generic_section('Notes', use_admonition)
+
+ def _parse_other_parameters_section(self, section):
+ return self._format_fields('Other Parameters', self._consume_fields())
+
+ def _parse_parameters_section(self, section):
+ fields = self._consume_fields()
+ if self._config.napoleon_use_param:
+ lines = []
+ for _name, _type, _desc in fields:
+ field = ':param %s: ' % _name
+ lines.extend(self._format_block(field, _desc))
+ if _type:
+ lines.append(':type %s: %s' % (_name, _type))
+ return lines + ['']
+ else:
+ return self._format_fields('Parameters', fields)
+
+ def _parse_raises_section(self, section):
+ fields = self._consume_fields()
+ field_type = ':raises:'
+ padding = ' ' * len(field_type)
+ multi = len(fields) > 1
+ lines = []
+ for _name, _type, _desc in fields:
+ sep = _desc and ' -- ' or ''
+ if _name:
+ if ' ' in _name:
+ _name = '**%s**' % _name
+ else:
+ _name = ':exc:`%s`' % _name
+ if _type:
+ field = ['%s (*%s*)%s' % (_name, _type, sep)]
+ else:
+ field = ['%s%s' % (_name, sep)]
+ elif _type:
+ field = ['*%s*%s' % (_type, sep)]
+ else:
+ field = []
+ field = field + _desc
+ if multi:
+ if lines:
+ lines.extend(self._format_block(padding + ' * ', field))
+ else:
+ lines.extend(self._format_block(field_type + ' * ', field))
+ else:
+ lines.extend(self._format_block(field_type + ' ', field))
+ return lines
+
+ def _parse_references_section(self, section):
+ use_admonition = self._config.napoleon_use_admonition_for_references
+ return self._parse_generic_section('References', use_admonition)
+
+ def _parse_returns_section(self, section):
+ fields = self._consume_returns_section()
+ multi = len(fields) > 1
+ if multi:
+ use_rtype = False
+ else:
+ use_rtype = self._config.napoleon_use_rtype
+
+ lines = []
+ for _name, _type, _desc in fields:
+ if use_rtype:
+ field = self._format_field(_name, '', _desc)
+ else:
+ field = self._format_field(_name, _type, _desc)
+
+ if multi:
+ if lines:
+ lines.extend(self._format_block(' * ', field))
+ else:
+ lines.extend(self._format_block(':returns: * ', field))
+ else:
+ lines.extend(self._format_block(':returns: ', field))
+ if _type and use_rtype:
+ lines.append(':rtype: %s' % _type)
+ return lines
+
+ def _parse_see_also_section(self, section):
+ lines = self._consume_to_next_section()
+ return self._format_admonition('seealso', lines)
+
+ def _parse_warning_section(self, section):
+ lines = self._consume_to_next_section()
+ return self._format_admonition('warning', lines)
+
+ def _parse_warns_section(self, section):
+ return self._format_fields('Warns', self._consume_fields())
+
+ def _parse_yields_section(self, section):
+ fields = self._consume_fields(prefer_type=True)
+ return self._format_fields('Yields', fields)
+
+ def _strip_empty(self, lines):
+ if lines:
+ start = -1
+ for i, line in enumerate(lines):
+ if line:
+ start = i
+ break
+ if start == -1:
+ lines = []
+ end = -1
+ for i in reversed(xrange(len(lines))):
+ line = lines[i]
+ if line:
+ end = i
+ break
+ if start > 0 or end + 1 < len(lines):
+ lines = lines[start:end + 1]
+ return lines
+
+
+class NumpyDocstring(GoogleDocstring):
+ """Parse NumPy style docstrings.
+
+ Convert NumPy style docstrings to reStructuredText.
+
+ Parameters
+ ----------
+ docstring : str or list of str
+ The docstring to parse, given either as a string or split into
+ individual lines.
+ config : sphinx.ext.napoleon.Config or sphinx.config.Config, optional
+ The configuration settings to use. If not given, defaults to the
+ config object on `app`; or if `app` is not given defaults to the
+ a new `sphinx.ext.napoleon.Config` object.
+
+ See Also
+ --------
+ :class:`sphinx.ext.napoleon.Config`
+
+ Other Parameters
+ ----------------
+ app : sphinx.application.Sphinx, optional
+ Application object representing the Sphinx process.
+ what : str, optional
+ A string specifying the type of the object to which the docstring
+ belongs. Valid values: "module", "class", "exception", "function",
+ "method", "attribute".
+ name : str, optional
+ The fully qualified name of the object.
+ obj : module, class, exception, function, method, or attribute
+ The object to which the docstring belongs.
+ options : sphinx.ext.autodoc.Options, optional
+ The options given to the directive: an object with attributes
+ inherited_members, undoc_members, show_inheritance and noindex that
+ are True if the flag option of same name was given to the auto
+ directive.
+
+ Example
+ -------
+ >>> from sphinx.ext.napoleon import Config
+ >>> config = Config(napoleon_use_param=True, napoleon_use_rtype=True)
+ >>> docstring = '''One line summary.
+ ...
+ ... Extended description.
+ ...
+ ... Parameters
+ ... ----------
+ ... arg1 : int
+ ... Description of `arg1`
+ ... arg2 : str
+ ... Description of `arg2`
+ ... Returns
+ ... -------
+ ... str
+ ... Description of return value.
+ ... '''
+ >>> print(NumpyDocstring(docstring, config))
+ One line summary.
+ <BLANKLINE>
+ Extended description.
+ <BLANKLINE>
+ :param arg1: Description of `arg1`
+ :type arg1: int
+ :param arg2: Description of `arg2`
+ :type arg2: str
+ <BLANKLINE>
+ :returns: Description of return value.
+ :rtype: str
+
+ Methods
+ -------
+ __str__()
+ Return the parsed docstring in reStructuredText format.
+
+ Returns
+ -------
+ str
+ UTF-8 encoded version of the docstring.
+
+ __unicode__()
+ Return the parsed docstring in reStructuredText format.
+
+ Returns
+ -------
+ unicode
+ Unicode version of the docstring.
+
+ lines()
+ Return the parsed lines of the docstring in reStructuredText format.
+
+ Returns
+ -------
+ list of str
+ The lines of the docstring in a list.
+
+ """
+ def __init__(self, docstring, config=None, app=None, what='', name='',
+ obj=None, options=None):
+ self._directive_sections = ['.. index::']
+ super(NumpyDocstring, self).__init__(docstring, config, app, what,
+ name, obj, options)
+
+ def _consume_field(self, parse_type=True, prefer_type=False):
+ line = self._line_iter.next()
+ if parse_type:
+ _name, _, _type = line.partition(':')
+ else:
+ _name, _type = line, ''
+ _name, _type = _name.strip(), _type.strip()
+ if prefer_type and not _type:
+ _type, _name = _name, _type
+ indent = self._get_indent(line)
+ _desc = self._dedent(self._consume_indented_block(indent + 1))
+ _desc = self.__class__(_desc, self._config).lines()
+ return _name, _type, _desc
+
+ def _consume_returns_section(self):
+ return self._consume_fields(prefer_type=True)
+
+ def _consume_section_header(self):
+ section = self._line_iter.next()
+ if not _directive_regex.match(section):
+ # Consume the header underline
+ self._line_iter.next()
+ return section
+
+ def _is_section_break(self):
+ line1, line2 = self._line_iter.peek(2)
+ return (not self._line_iter.has_next()
+ or self._is_section_header()
+ or ['', ''] == [line1, line2]
+ or (self._is_in_section
+ and line1
+ and not self._is_indented(line1, self._section_indent)))
+
+ def _is_section_header(self):
+ section, underline = self._line_iter.peek(2)
+ section = section.lower()
+ if section in self._sections and isinstance(underline, basestring):
+ pattern = r'[=\-`:\'"~^_*+#<>]{' + str(len(section)) + r'}$'
+ return bool(re.match(pattern, underline))
+ elif self._directive_sections:
+ if _directive_regex.match(section):
+ for directive_section in self._directive_sections:
+ if section.startswith(directive_section):
+ return True
+ return False
diff --git a/sphinx/ext/napoleon/iterators.py b/sphinx/ext/napoleon/iterators.py
new file mode 100644
index 00000000..2f1904da
--- /dev/null
+++ b/sphinx/ext/napoleon/iterators.py
@@ -0,0 +1,244 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.ext.napoleon.iterators
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+ A collection of helpful iterators.
+
+
+ :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import collections
+import sys
+
+
+if sys.version_info[0] >= 3:
+ callable = lambda o: hasattr(o, '__call__')
+
+
+class peek_iter(object):
+ """An iterator object that supports peeking ahead.
+
+ Parameters
+ ----------
+ o : iterable or callable
+ `o` is interpreted very differently depending on the presence of
+ `sentinel`.
+
+ If `sentinel` is not given, then `o` must be a collection object
+ which supports either the iteration protocol or the sequence protocol.
+
+ If `sentinel` is given, then `o` must be a callable object.
+
+ sentinel : any value, optional
+ If given, the iterator will call `o` with no arguments for each
+ call to its `next` method; if the value returned is equal to
+ `sentinel`, :exc:`StopIteration` will be raised, otherwise the
+ value will be returned.
+
+ See Also
+ --------
+ `peek_iter` can operate as a drop in replacement for the built-in
+ `iter <http://docs.python.org/2/library/functions.html#iter>`_ function.
+
+ Attributes
+ ----------
+ sentinel
+ The value used to indicate the iterator is exhausted. If `sentinel`
+ was not given when the `peek_iter` was instantiated, then it will
+ be set to a new object instance: ``object()``.
+
+ """
+ def __init__(self, *args):
+ """__init__(o, sentinel=None)"""
+ self._iterable = iter(*args)
+ self._cache = collections.deque()
+ if len(args) == 2:
+ self.sentinel = args[1]
+ else:
+ self.sentinel = object()
+
+ def __iter__(self):
+ return self
+
+ def __next__(self, n=None):
+ # note: prevent 2to3 to transform self.next() in next(self) which
+ # causes an infinite loop !
+ return getattr(self, 'next')(n)
+
+ def _fillcache(self, n):
+ """Cache `n` items. If `n` is 0 or None, then 1 item is cached."""
+ if not n:
+ n = 1
+ try:
+ while len(self._cache) < n:
+ self._cache.append(self._iterable.next())
+ except StopIteration:
+ while len(self._cache) < n:
+ self._cache.append(self.sentinel)
+
+ def has_next(self):
+ """Determine if iterator is exhausted.
+
+ Returns
+ -------
+ bool
+ True if iterator has more items, False otherwise.
+
+ Note
+ ----
+ Will never raise :exc:`StopIteration`.
+
+ """
+ return self.peek() != self.sentinel
+
+ def next(self, n=None):
+ """Get the next item or `n` items of the iterator.
+
+ Parameters
+ ----------
+ n : int or None
+ The number of items to retrieve. Defaults to None.
+
+ Returns
+ -------
+ item or list of items
+ The next item or `n` items of the iterator. If `n` is None, the
+ item itself is returned. If `n` is an int, the items will be
+ returned in a list. If `n` is 0, an empty list is returned.
+
+ Raises
+ ------
+ StopIteration
+ Raised if the iterator is exhausted, even if `n` is 0.
+
+ """
+ self._fillcache(n)
+ if not n:
+ if self._cache[0] == self.sentinel:
+ raise StopIteration
+ if n is None:
+ result = self._cache.popleft()
+ else:
+ result = []
+ else:
+ if self._cache[n - 1] == self.sentinel:
+ raise StopIteration
+ result = [self._cache.popleft() for i in range(n)]
+ return result
+
+ def peek(self, n=None):
+ """Preview the next item or `n` items of the iterator.
+
+ The iterator is not advanced when peek is called.
+
+ Returns
+ -------
+ item or list of items
+ The next item or `n` items of the iterator. If `n` is None, the
+ item itself is returned. If `n` is an int, the items will be
+ returned in a list. If `n` is 0, an empty list is returned.
+
+ If the iterator is exhausted, `peek_iter.sentinel` is returned,
+ or placed as the last item in the returned list.
+
+ Note
+ ----
+ Will never raise :exc:`StopIteration`.
+
+ """
+ self._fillcache(n)
+ if n is None:
+ result = self._cache[0]
+ else:
+ result = [self._cache[i] for i in range(n)]
+ return result
+
+
+class modify_iter(peek_iter):
+ """An iterator object that supports modifying items as they are returned.
+
+ Parameters
+ ----------
+ o : iterable or callable
+ `o` is interpreted very differently depending on the presence of
+ `sentinel`.
+
+ If `sentinel` is not given, then `o` must be a collection object
+ which supports either the iteration protocol or the sequence protocol.
+
+ If `sentinel` is given, then `o` must be a callable object.
+
+ sentinel : any value, optional
+ If given, the iterator will call `o` with no arguments for each
+ call to its `next` method; if the value returned is equal to
+ `sentinel`, :exc:`StopIteration` will be raised, otherwise the
+ value will be returned.
+
+ modifier : callable, optional
+ The function that will be used to modify each item returned by the
+ iterator. `modifier` should take a single argument and return a
+ single value. Defaults to ``lambda x: x``.
+
+ If `sentinel` is not given, `modifier` must be passed as a keyword
+ argument.
+
+ Attributes
+ ----------
+ modifier : callable
+ `modifier` is called with each item in `o` as it is iterated. The
+ return value of `modifier` is returned in lieu of the item.
+
+ Values returned by `peek` as well as `next` are affected by
+ `modifier`. However, `modify_iter.sentinel` is never passed through
+ `modifier`; it will always be returned from `peek` unmodified.
+
+ Example
+ -------
+ >>> a = [" A list ",
+ ... " of strings ",
+ ... " with ",
+ ... " extra ",
+ ... " whitespace. "]
+ >>> modifier = lambda s: s.strip().replace('with', 'without')
+ >>> for s in modify_iter(a, modifier=modifier):
+ ... print('"%s"' % s)
+ "A list"
+ "of strings"
+ "without"
+ "extra"
+ "whitespace."
+
+ """
+ def __init__(self, *args, **kwargs):
+ """__init__(o, sentinel=None, modifier=lambda x: x)"""
+ if 'modifier' in kwargs:
+ self.modifier = kwargs['modifier']
+ elif len(args) > 2:
+ self.modifier = args[2]
+ args = args[:2]
+ else:
+ self.modifier = lambda x: x
+ if not callable(self.modifier):
+ raise TypeError('modify_iter(o, modifier): '
+ 'modifier must be callable')
+ super(modify_iter, self).__init__(*args)
+
+ def _fillcache(self, n):
+ """Cache `n` modified items. If `n` is 0 or None, 1 item is cached.
+
+ Each item returned by the iterator is passed through the
+ `modify_iter.modified` function before being cached.
+
+ """
+ if not n:
+ n = 1
+ try:
+ while len(self._cache) < n:
+ self._cache.append(self.modifier(self._iterable.next()))
+ except StopIteration:
+ while len(self._cache) < n:
+ self._cache.append(self.sentinel)
diff --git a/sphinx/ext/viewcode.py b/sphinx/ext/viewcode.py
index 962b543b..36fb47d2 100644
--- a/sphinx/ext/viewcode.py
+++ b/sphinx/ext/viewcode.py
@@ -24,17 +24,17 @@ def doctree_read(app, doctree):
def has_tag(modname, fullname, docname):
entry = env._viewcode_modules.get(modname, None)
- if entry is None:
- try:
- analyzer = ModuleAnalyzer.for_module(modname)
- except Exception:
- env._viewcode_modules[modname] = False
- return
+ try:
+ analyzer = ModuleAnalyzer.for_module(modname)
+ except Exception:
+ env._viewcode_modules[modname] = False
+ return
+ if not isinstance(analyzer.code, unicode):
+ code = analyzer.code.decode(analyzer.encoding)
+ else:
+ code = analyzer.code
+ if entry is None or entry[0] != code:
analyzer.find_tags()
- if not isinstance(analyzer.code, unicode):
- code = analyzer.code.decode(analyzer.encoding)
- else:
- code = analyzer.code
entry = code, analyzer.tags, {}
env._viewcode_modules[modname] = entry
elif entry is False:
@@ -142,7 +142,7 @@ def collect_pages(app):
if not modnames:
return
- app.builder.info(' _modules/index')
+ app.builder.info(' _modules/index', nonl=True)
html = ['\n']
# the stack logic is needed for using nested lists for submodules
stack = ['']
diff --git a/sphinx/highlighting.py b/sphinx/highlighting.py
index 2c95dec0..c30c9ef3 100644
--- a/sphinx/highlighting.py
+++ b/sphinx/highlighting.py
@@ -63,12 +63,6 @@ _LATEX_STYLES = r'''
\newcommand\PYGZcb{\char`\}}
'''
-parsing_exceptions = (SyntaxError, UnicodeEncodeError)
-if sys.version_info < (2, 5):
- # Python <= 2.4 raises MemoryError when parsing an
- # invalid encoding cookie
- parsing_exceptions += MemoryError,
-
class PygmentsBridge(object):
# Set these attributes if you want to have different Pygments formatters
@@ -131,10 +125,6 @@ class PygmentsBridge(object):
# lines beginning with "..." are probably placeholders for suite
src = re.sub(r"(?m)^(\s*)" + mark + "(.)", r"\1"+ mark + r"# \2", src)
- # if we're using 2.5, use the with statement
- if sys.version_info >= (2, 5):
- src = 'from __future__ import with_statement\n' + src
-
if sys.version_info < (3, 0) and isinstance(src, unicode):
# Non-ASCII chars will only occur in string literals
# and comments. If we wanted to give them to the parser
@@ -143,18 +133,12 @@ class PygmentsBridge(object):
# just replace all non-ASCII characters.
src = src.encode('ascii', 'replace')
- if (3, 0) <= sys.version_info < (3, 2):
- # Python 3.1 can't process '\r' as linesep.
- # `parser.suite("print('hello')\r\n")` cause error.
- if '\r\n' in src:
- src = src.replace('\r\n', '\n')
-
if parser is None:
return True
try:
parser.suite(src)
- except parsing_exceptions:
+ except (SyntaxError, UnicodeEncodeError):
return False
else:
return True
diff --git a/sphinx/make_mode.py b/sphinx/make_mode.py
new file mode 100644
index 00000000..24de6b28
--- /dev/null
+++ b/sphinx/make_mode.py
@@ -0,0 +1,252 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.make_mode
+ ~~~~~~~~~~~~~~~~
+
+ sphinx-build -M command-line handling.
+
+ This replaces the old, platform-dependent and once-generated content
+ of Makefile / make.bat.
+
+ This is in its own module so that importing it is fast. It should not
+ import the main Sphinx modules (like sphinx.applications, sphinx.builders).
+
+ :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import os
+import sys
+import shutil
+from os import path
+from subprocess import call
+
+import sphinx
+from sphinx.util.console import bold, blue
+
+proj_name = os.getenv('SPHINXPROJ', '<project>')
+
+
+BUILDERS = [
+ ("", "html", "to make standalone HTML files"),
+ ("", "dirhtml", "to make HTML files named index.html in directories"),
+ ("", "singlehtml","to make a single large HTML file"),
+ ("", "pickle", "to make pickle files"),
+ ("", "json", "to make JSON files"),
+ ("", "htmlhelp", "to make HTML files and a HTML help project"),
+ ("", "qthelp", "to make HTML files and a qthelp project"),
+ ("", "devhelp", "to make HTML files and a Devhelp project"),
+ ("", "epub", "to make an epub"),
+ ("", "latex", "to make LaTeX files, you can set PAPER=a4 or PAPER=letter"),
+ ("posix", "latexpdf", "to make LaTeX files and run them through pdflatex"),
+ ("posix", "latexpdfja","to make LaTeX files and run them through platex/dvipdfmx"),
+ ("", "text", "to make text files"),
+ ("", "man", "to make manual pages"),
+ ("", "texinfo", "to make Texinfo files"),
+ ("posix", "info", "to make Texinfo files and run them through makeinfo"),
+ ("", "gettext", "to make PO message catalogs"),
+ ("", "changes", "to make an overview of all changed/added/deprecated items"),
+ ("", "xml", "to make Docutils-native XML files"),
+ ("", "pseudoxml", "to make pseudoxml-XML files for display purposes"),
+ ("", "linkcheck", "to check all external links for integrity"),
+ ("", "doctest", "to run all doctests embedded in the documentation (if enabled)"),
+ ("", "coverage", "to run coverage check of the documentation (if enabled)"),
+]
+
+
+class Make(object):
+
+ def __init__(self, srcdir, builddir, opts):
+ self.srcdir = srcdir
+ self.builddir = builddir
+ self.opts = opts
+
+ def builddir_join(self, *comps):
+ return path.join(self.builddir, *comps)
+
+ def build_clean(self):
+ if not path.exists(self.builddir):
+ return
+ elif not path.isdir(self.builddir):
+ print "Error: %r is not a directory!" % self.builddir
+ return 1
+ print "Removing everything under %r..." % self.builddir
+ for item in os.listdir(self.builddir):
+ shutil.rmtree(self.builddir_join(item))
+
+ def build_help(self):
+ print bold("Sphinx v%s" % sphinx.__version__)
+ print "Please use `make %s' where %s is one of" % ((blue('target'),)*2)
+ for osname, bname, description in BUILDERS:
+ if not osname or os.name == osname:
+ print ' %s %s' % (blue(bname.ljust(10)), description)
+
+ def build_html(self):
+ if self.run_generic_build('html') > 0:
+ return 1
+ print
+ print 'Build finished. The HTML pages are in %s.' % self.builddir_join('html')
+
+ def build_dirhtml(self):
+ if self.run_generic_build('dirhtml') > 0:
+ return 1
+ print
+ print 'Build finished. The HTML pages are in %s.' % self.builddir_join('dirhtml')
+
+ def build_singlehtml(self):
+ if self.run_generic_build('singlehtml') > 0:
+ return 1
+ print
+ print 'Build finished. The HTML page is in %s.' % self.builddir_join('singlehtml')
+
+ def build_pickle(self):
+ if self.run_generic_build('pickle') > 0:
+ return 1
+ print
+ print 'Build finished; now you can process the pickle files.'
+
+ def build_json(self):
+ if self.run_generic_build('json') > 0:
+ return 1
+ print
+ print 'Build finished; now you can process the JSON files.'
+
+ def build_htmlhelp(self):
+ if self.run_generic_build('htmlhelp') > 0:
+ return 1
+ print
+ print ('Build finished; now you can run HTML Help Workshop with the '
+ '.hhp project file in %s.') % self.builddir_join('htmlhelp')
+
+ def build_qthelp(self):
+ if self.run_generic_build('qthelp') > 0:
+ return 1
+ print
+ print ('Build finished; now you can run "qcollectiongenerator" with the '
+ '.qhcp project file in %s, like this:') % self.builddir_join('qthelp')
+ print '$ qcollectiongenerator %s.qhcp' % self.builddir_join('qthelp', proj_name)
+ print 'To view the help file:'
+ print '$ assistant -collectionFile %s.qhc' % self.builddir_join('qthelp', proj_name)
+
+ def build_devhelp(self):
+ if self.run_generic_build('devhelp') > 0:
+ return 1
+ print
+ print "Build finished."
+ print "To view the help file:"
+ print "$ mkdir -p $HOME/.local/share/devhelp/" + proj_name
+ print "$ ln -s %s $HOME/.local/share/devhelp/%s" % \
+ (self.builddir_join('devhelp'), proj_name)
+ print "$ devhelp"
+
+ def build_epub(self):
+ if self.run_generic_build('epub') > 0:
+ return 1
+ print
+ print 'Build finished. The ePub file is in %s.' % self.builddir_join('epub')
+
+ def build_latex(self):
+ if self.run_generic_build('latex') > 0:
+ return 1
+ print "Build finished; the LaTeX files are in %s." % self.builddir_join('latex')
+ if os.name == 'posix':
+ print "Run `make' in that directory to run these through (pdf)latex"
+ print "(use `make latexpdf' here to do that automatically)."
+
+ def build_latexpdf(self):
+ if self.run_generic_build('latex') > 0:
+ return 1
+ os.system('make -C %s all-pdf' % self.builddir_join('latex'))
+
+ def build_latexpdfja(self):
+ if self.run_generic_build('latex') > 0:
+ return 1
+ os.system('make -C %s all-pdf-ja' % self.builddir_join('latex'))
+
+ def build_text(self):
+ if self.run_generic_build('text') > 0:
+ return 1
+ print
+ print 'Build finished. The text files are in %s.' % self.builddir_join('text')
+
+ def build_texinfo(self):
+ if self.run_generic_build('texinfo') > 0:
+ return 1
+ print "Build finished; the Texinfo files are in %s." % self.builddir_join('texinfo')
+ if os.name == 'posix':
+ print "Run `make' in that directory to run these through makeinfo"
+ print "(use `make info' here to do that automatically)."
+
+ def build_info(self):
+ if self.run_generic_build('texinfo') > 0:
+ return 1
+ os.system('make -C %s info' % self.builddir_join('texinfo'))
+
+ def build_gettext(self):
+ dtdir = self.builddir_join('gettext', '.doctrees')
+ if self.run_generic_build('gettext', doctreedir=dtdir) > 0:
+ return 1
+ print
+ print 'Build finished. The message catalogs are in %s.' % self.builddir_join('gettext')
+
+ def build_changes(self):
+ if self.run_generic_build('changes') > 0:
+ return 1
+ print
+ print 'Build finished. The overview file is in %s.' % self.builddir_join('changes')
+
+ def build_linkcheck(self):
+ res = self.run_generic_build('linkcheck')
+ print
+ print ('Link check complete; look for any errors in the above output '
+ 'or in %s.') % self.builddir_join('linkcheck', 'output.txt')
+ return res
+
+ def build_doctest(self):
+ res = self.run_generic_build('doctest')
+ print ("Testing of doctests in the sources finished, look at the "
+ "results in %s." % self.builddir_join('doctest', 'output.txt'))
+ return res
+
+ def build_coverage(self):
+ if self.run_generic_build('coverage') > 0:
+ print "Has the coverage extension been enabled?"
+ return 1
+ print
+ print ("Testing of coverage in the sources finished, look at the "
+ "results in %s." % self.builddir_join('coverage'))
+
+ def build_xml(self):
+ if self.run_generic_build('xml') > 0:
+ return 1
+ print
+ print 'Build finished. The XML files are in %s.' % self.builddir_join('xml')
+
+ def build_pseudoxml(self):
+ if self.run_generic_build('pseudoxml') > 0:
+ return 1
+ print
+ print 'Build finished. The pseudo-XML files are in %s.' % self.builddir_join('pseudoxml')
+
+ def run_generic_build(self, builder, doctreedir=None):
+ # compatibility with old Makefile
+ papersize = os.getenv('PAPER', '')
+ opts = self.opts
+ if papersize in ('a4', 'letter'):
+ opts.extend(['-D', 'latex_paper_size=' + papersize])
+ if doctreedir is None:
+ doctreedir = self.builddir_join('doctrees')
+ return call([sys.executable, sys.argv[0], '-b', builder,
+ '-d', doctreedir, self.srcdir, self.builddir_join(builder)] + opts)
+
+
+def run_make_mode(args):
+ if len(args) < 3:
+ print >>sys.stderr, ('Error: at least 3 arguments (builder, source '
+ 'dir, build dir) are required.')
+ return 1
+ make = Make(args[1], args[2], args[3:])
+ run_method = 'build_' + args[0]
+ if hasattr(make, run_method):
+ return getattr(make, run_method)()
+ return make.run_generic_build(args[0])
diff --git a/sphinx/pycode/__init__.py b/sphinx/pycode/__init__.py
index 2c0708f9..54e79da6 100644
--- a/sphinx/pycode/__init__.py
+++ b/sphinx/pycode/__init__.py
@@ -17,7 +17,7 @@ from sphinx.errors import PycodeError
from sphinx.pycode import nodes
from sphinx.pycode.pgen2 import driver, token, tokenize, parse, literals
from sphinx.util import get_module_source, detect_encoding
-from sphinx.util.pycompat import next, StringIO, BytesIO, TextIOWrapper
+from sphinx.util.pycompat import StringIO, BytesIO, TextIOWrapper
from sphinx.util.docstrings import prepare_docstring, prepare_commentdoc
diff --git a/sphinx/pycode/pgen2/parse.c b/sphinx/pycode/pgen2/parse.c
index e09f5058..96fa6c8b 100644
--- a/sphinx/pycode/pgen2/parse.c
+++ b/sphinx/pycode/pgen2/parse.c
@@ -353,95 +353,6 @@ static void __Pyx_RaiseArgtupleInvalid(const char* func_name, int exact,
static int __Pyx_ParseOptionalKeywords(PyObject *kwds, PyObject **argnames[], PyObject *kwds2, PyObject *values[], Py_ssize_t num_pos_args, const char* function_name); /*proto*/
-#if PY_VERSION_HEX < 0x02050000
-#ifndef PyAnySet_CheckExact
-
-#define PyAnySet_CheckExact(ob) \
- ((ob)->ob_type == &PySet_Type || \
- (ob)->ob_type == &PyFrozenSet_Type)
-
-#define PySet_New(iterable) \
- PyObject_CallFunctionObjArgs((PyObject *)&PySet_Type, (iterable), NULL)
-
-#define Pyx_PyFrozenSet_New(iterable) \
- PyObject_CallFunctionObjArgs((PyObject *)&PyFrozenSet_Type, (iterable), NULL)
-
-#define PySet_Size(anyset) \
- PyObject_Size((anyset))
-
-#define PySet_Contains(anyset, key) \
- PySequence_Contains((anyset), (key))
-
-#define PySet_Pop(set) \
- PyObject_CallMethod(set, (char *)"pop", NULL)
-
-static INLINE int PySet_Clear(PyObject *set) {
- PyObject *ret = PyObject_CallMethod(set, (char *)"clear", NULL);
- if (!ret) return -1;
- Py_DECREF(ret); return 0;
-}
-
-static INLINE int PySet_Discard(PyObject *set, PyObject *key) {
- PyObject *ret = PyObject_CallMethod(set, (char *)"discard", (char *)"O", key);
- if (!ret) return -1;
- Py_DECREF(ret); return 0;
-}
-
-static INLINE int PySet_Add(PyObject *set, PyObject *key) {
- PyObject *ret = PyObject_CallMethod(set, (char *)"add", (char *)"O", key);
- if (!ret) return -1;
- Py_DECREF(ret); return 0;
-}
-
-#endif /* PyAnySet_CheckExact (<= Py2.4) */
-
-#if PY_VERSION_HEX < 0x02040000
-#ifndef Py_SETOBJECT_H
-#define Py_SETOBJECT_H
-
-static PyTypeObject *__Pyx_PySet_Type = NULL;
-static PyTypeObject *__Pyx_PyFrozenSet_Type = NULL;
-
-#define PySet_Type (*__Pyx_PySet_Type)
-#define PyFrozenSet_Type (*__Pyx_PyFrozenSet_Type)
-
-#define PyAnySet_Check(ob) \
- (PyAnySet_CheckExact(ob) || \
- PyType_IsSubtype((ob)->ob_type, &PySet_Type) || \
- PyType_IsSubtype((ob)->ob_type, &PyFrozenSet_Type))
-
-#define PyFrozenSet_CheckExact(ob) ((ob)->ob_type == &PyFrozenSet_Type)
-
-static int __Pyx_Py23SetsImport(void) {
- PyObject *sets=0, *Set=0, *ImmutableSet=0;
-
- sets = PyImport_ImportModule((char *)"sets");
- if (!sets) goto bad;
- Set = PyObject_GetAttrString(sets, (char *)"Set");
- if (!Set) goto bad;
- ImmutableSet = PyObject_GetAttrString(sets, (char *)"ImmutableSet");
- if (!ImmutableSet) goto bad;
- Py_DECREF(sets);
-
- __Pyx_PySet_Type = (PyTypeObject*) Set;
- __Pyx_PyFrozenSet_Type = (PyTypeObject*) ImmutableSet;
-
- return 0;
-
- bad:
- Py_XDECREF(sets);
- Py_XDECREF(Set);
- Py_XDECREF(ImmutableSet);
- return -1;
-}
-
-#else
-static int __Pyx_Py23SetsImport(void) { return 0; }
-#endif /* !Py_SETOBJECT_H */
-#endif /* < Py2.4 */
-#endif /* < Py2.5 */
-
-
static INLINE PyObject *__Pyx_GetItemInt_Generic(PyObject *o, PyObject* j) {
PyObject *r;
if (!j) return NULL;
diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py
index a9d92cdf..9939f565 100644
--- a/sphinx/quickstart.py
+++ b/sphinx/quickstart.py
@@ -11,6 +11,7 @@
import sys, os, time, re
from os import path
+from io import open
TERM_ENCODING = getattr(sys.stdin, 'encoding', None)
@@ -21,7 +22,6 @@ from sphinx.util.osutil import make_filename
from sphinx.util.console import purple, bold, red, turquoise, \
nocolor, color_terminal
from sphinx.util import texescape
-from sphinx.util.pycompat import open
# function to get input from terminal -- overridden by the test suite
try:
@@ -220,9 +220,6 @@ html_static_path = ['%(dot)sstatic']
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
-# This is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = None
-
# Language to be used for generating the HTML full-text search index.
# Sphinx supports the following languages:
# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja'
@@ -856,6 +853,76 @@ if "%%1" == "pseudoxml" (
:end
'''
+# This will become the Makefile template for Sphinx 1.5.
+MAKEFILE_NEW = u'''\
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+SPHINXPROJ = %(project_fn)s
+SOURCEDIR = %(rsrcdir)s
+BUILDDIR = %(rbuilddir)s
+
+# User-friendly check for sphinx-build.
+ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
+$(error \
+The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx \
+installed, then set the SPHINXBUILD environment variable to point \
+to the full path of the '$(SPHINXBUILD)' executable. Alternatively you \
+can add the directory with the executable to your PATH. \
+If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
+endif
+
+# Has to be explicit, otherwise we don't get "make" without targets right.
+help:
+\t@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+# You can add custom targets here.
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
+%:
+\t@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+'''
+
+# This will become the make.bat template for Sphinx 1.5.
+BATCHFILE_NEW = u'''\
+@ECHO OFF
+
+REM Command file for Sphinx documentation
+
+if "%%SPHINXBUILD%%" == "" (
+\tset SPHINXBUILD=sphinx-build
+)
+set BUILDDIR=%(rbuilddir)s
+set SPHINXPROJ=%(project_fn)s
+
+if "%%1" == "" goto help
+
+%%SPHINXBUILD%% 2> nul
+if errorlevel 9009 (
+\techo.
+\techo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+\techo.installed, then set the SPHINXBUILD environment variable to point
+\techo.to the full path of the 'sphinx-build' executable. Alternatively you
+\techo.may add the Sphinx directory to PATH.
+\techo.
+\techo.If you don't have Sphinx installed, grab it from
+\techo.http://sphinx-doc.org/
+\texit /b 1
+)
+
+%%SPHINXBUILD%% -M %%1 %%BUILDDIR%% %%SPHINXOPTS%%
+goto end
+
+:help
+%%SPHINXBUILD%% -M help %%BUILDDIR%% %%SPHINXOPTS%%
+
+:end
+'''
+
def mkdir_p(dir):
if path.isdir(dir):
diff --git a/sphinx/setup_command.py b/sphinx/setup_command.py
index 07cd520c..7999bf59 100644
--- a/sphinx/setup_command.py
+++ b/sphinx/setup_command.py
@@ -14,6 +14,7 @@
import sys
import os
+import types
from StringIO import StringIO
from distutils.cmd import Command
@@ -98,6 +99,19 @@ class BuildDoc(Command):
return root
return None
+ # Overriding distutils' Command._ensure_stringlike which doesn't support
+ # unicode, causing finalize_options to fail if invoked again. Workaround
+ # for http://bugs.python.org/issue19570
+ def _ensure_stringlike(self, option, what, default=None):
+ val = getattr(self, option)
+ if val is None:
+ setattr(self, option, default)
+ return default
+ elif not isinstance(val, types.StringTypes):
+ raise DistutilsOptionError("'%s' must be a %s (got `%s`)"
+ % (option, what, val))
+ return val
+
def finalize_options(self):
if self.source_dir is None:
self.source_dir = self._guess_source_dir()
diff --git a/sphinx/texinputs/Makefile b/sphinx/texinputs/Makefile
index 6b87ad88..5e6030c0 100644
--- a/sphinx/texinputs/Makefile
+++ b/sphinx/texinputs/Makefile
@@ -9,6 +9,10 @@ ARCHIVEPRREFIX =
# Additional LaTeX options
LATEXOPTS =
+LATEX = latex
+PDFLATEX = pdflatex
+MAKEINDEX = makeindex
+
all: $(ALLPDF)
all-pdf: $(ALLPDF)
all-dvi: $(ALLDVI)
@@ -43,20 +47,20 @@ bz2: tar
# The number of LaTeX runs is quite conservative, but I don't expect it
# to get run often, so the little extra time won't hurt.
%.dvi: %.tex
- latex $(LATEXOPTS) '$<'
- latex $(LATEXOPTS) '$<'
- latex $(LATEXOPTS) '$<'
- -makeindex -s python.ist '$(basename $<).idx'
- latex $(LATEXOPTS) '$<'
- latex $(LATEXOPTS) '$<'
+ $(LATEX) $(LATEXOPTS) '$<'
+ $(LATEX) $(LATEXOPTS) '$<'
+ $(LATEX) $(LATEXOPTS) '$<'
+ -$(MAKEINDEX) -s python.ist '$(basename $<).idx'
+ $(LATEX) $(LATEXOPTS) '$<'
+ $(LATEX) $(LATEXOPTS) '$<'
%.pdf: %.tex
- pdflatex $(LATEXOPTS) '$<'
- pdflatex $(LATEXOPTS) '$<'
- pdflatex $(LATEXOPTS) '$<'
- -makeindex -s python.ist '$(basename $<).idx'
- pdflatex $(LATEXOPTS) '$<'
- pdflatex $(LATEXOPTS) '$<'
+ $(PDFLATEX) $(LATEXOPTS) '$<'
+ $(PDFLATEX) $(LATEXOPTS) '$<'
+ $(PDFLATEX) $(LATEXOPTS) '$<'
+ -$(MAKEINDEX) -s python.ist '$(basename $<).idx'
+ $(PDFLATEX) $(LATEXOPTS) '$<'
+ $(PDFLATEX) $(LATEXOPTS) '$<'
clean:
rm -f *.dvi *.log *.ind *.aux *.toc *.syn *.idx *.out *.ilg *.pla
diff --git a/sphinx/themes/agogo/static/agogo.css_t b/sphinx/themes/agogo/static/agogo.css_t
index 3fb81178..4c7eb378 100644
--- a/sphinx/themes/agogo/static/agogo.css_t
+++ b/sphinx/themes/agogo/static/agogo.css_t
@@ -462,3 +462,10 @@ div.viewcode-block:target {
border-top: 1px solid #ac9;
border-bottom: 1px solid #ac9;
}
+
+div.code-block-filename {
+ background-color: #ddd;
+ color: #333;
+ padding: 2px 5px;
+ font-size: small;
+}
diff --git a/sphinx/themes/basic/searchbox.html b/sphinx/themes/basic/searchbox.html
index 0a573c32..420e0121 100644
--- a/sphinx/themes/basic/searchbox.html
+++ b/sphinx/themes/basic/searchbox.html
@@ -7,7 +7,7 @@
:copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
-{%- if pagename != "search" %}
+{%- if pagename != "search" and builder != "singlehtml" %}
<div id="searchbox" style="display: none" role="search">
<h3>{{ _('Quick search') }}</h3>
<form class="search" action="{{ pathto('search') }}" method="get">
diff --git a/sphinx/themes/basic/static/basic.css_t b/sphinx/themes/basic/static/basic.css_t
index d54e7f4e..a3255ebd 100644
--- a/sphinx/themes/basic/static/basic.css_t
+++ b/sphinx/themes/basic/static/basic.css_t
@@ -471,6 +471,20 @@ table.highlighttable td {
padding: 0 0.5em 0 0.5em;
}
+div.code-block-filename {
+ padding: 2px 5px;
+ font-size: small;
+}
+
+div.code-block-filename tt {
+ background-color: transparent;
+}
+
+div.code-block-filename + pre,
+div.code-block-filename + div.highlight > pre {
+ margin-top: 0;
+}
+
tt.descname {
background-color: transparent;
font-weight: bold;
diff --git a/sphinx/themes/basic/static/doctools.js b/sphinx/themes/basic/static/doctools.js
index 8614442e..2036e5f5 100644
--- a/sphinx/themes/basic/static/doctools.js
+++ b/sphinx/themes/basic/static/doctools.js
@@ -168,6 +168,9 @@ var Documentation = {
var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : [];
if (terms.length) {
var body = $('div.body');
+ if (!body.length) {
+ body = $('body');
+ }
window.setTimeout(function() {
$.each(terms, function() {
body.highlightText(this.toLowerCase(), 'highlighted');
diff --git a/sphinx/themes/default/static/default.css_t b/sphinx/themes/default/static/default.css_t
index 5db77108..cdc1b782 100644
--- a/sphinx/themes/default/static/default.css_t
+++ b/sphinx/themes/default/static/default.css_t
@@ -308,3 +308,8 @@ div.viewcode-block:target {
border-top: 1px solid #ac9;
border-bottom: 1px solid #ac9;
}
+
+div.code-block-filename {
+ color: #efefef;
+ background-color: #1c4e63;
+}
diff --git a/sphinx/themes/epub/static/epub.css b/sphinx/themes/epub/static/epub.css
index 3f4664f6..6c8ff5e7 100644
--- a/sphinx/themes/epub/static/epub.css
+++ b/sphinx/themes/epub/static/epub.css
@@ -339,7 +339,7 @@ dl.glossary dt {
/* -- code displays --------------------------------------------------------- */
pre {
- font-family: "LiberationNarrow", monospace;
+ font-family: monospace;
overflow: auto;
overflow-y: hidden;
}
@@ -360,7 +360,7 @@ table.highlighttable td {
}
tt {
- font-family: "LiberationNarrow", monospace;
+ font-family: monospace;
}
tt.descname {
@@ -432,6 +432,7 @@ table .link-target {
/* -- font-face ------------------------------------------------------------- */
+/*
@font-face {
font-family: "LiberationNarrow";
font-style: normal;
@@ -460,4 +461,4 @@ table .link-target {
src: url("res:///Data/fonts/LiberationNarrow-BoldItalic.otf")
format("opentype");
}
-
+*/ \ No newline at end of file
diff --git a/sphinx/themes/nature/static/nature.css_t b/sphinx/themes/nature/static/nature.css_t
index 3c492034..e4f4d2ed 100644
--- a/sphinx/themes/nature/static/nature.css_t
+++ b/sphinx/themes/nature/static/nature.css_t
@@ -243,3 +243,9 @@ div.viewcode-block:target {
border-top: 1px solid #ac9;
border-bottom: 1px solid #ac9;
}
+
+div.code-block-filename {
+ background-color: #ddd;
+ color: #222;
+ border: 1px solid #C6C9CB;
+}
diff --git a/sphinx/themes/pyramid/static/pyramid.css_t b/sphinx/themes/pyramid/static/pyramid.css_t
index c4e94908..c724a493 100644
--- a/sphinx/themes/pyramid/static/pyramid.css_t
+++ b/sphinx/themes/pyramid/static/pyramid.css_t
@@ -340,3 +340,8 @@ tt.xref {
font-weight: normal;
font-style: normal;
}
+
+div.code-block-filename {
+ background-color: #ddd;
+ color: #222;
+}
diff --git a/sphinx/themes/sphinxdoc/static/sphinxdoc.css_t b/sphinx/themes/sphinxdoc/static/sphinxdoc.css_t
index af498257..1d7c5796 100644
--- a/sphinx/themes/sphinxdoc/static/sphinxdoc.css_t
+++ b/sphinx/themes/sphinxdoc/static/sphinxdoc.css_t
@@ -337,3 +337,9 @@ div.viewcode-block:target {
border-top: 1px solid #ac9;
border-bottom: 1px solid #ac9;
}
+
+div.code-block-filename {
+ background-color: #ddd;
+ color: #222;
+ border: 1px solid #ccc;
+}
diff --git a/sphinx/themes/traditional/static/traditional.css_t b/sphinx/themes/traditional/static/traditional.css_t
index fff411ef..cbea798c 100644
--- a/sphinx/themes/traditional/static/traditional.css_t
+++ b/sphinx/themes/traditional/static/traditional.css_t
@@ -698,3 +698,7 @@ div.viewcode-block:target {
margin: -1px -10px;
padding: 0 10px;
}
+
+div.code-block-filename {
+ background-color: #cceeff;
+}
diff --git a/sphinx/transforms.py b/sphinx/transforms.py
index 338d4739..e44a3d3e 100644
--- a/sphinx/transforms.py
+++ b/sphinx/transforms.py
@@ -23,7 +23,6 @@ from sphinx.util import split_index_msg
from sphinx.util.nodes import traverse_translatable_index, extract_messages
from sphinx.util.osutil import ustrftime, find_catalog
from sphinx.util.compat import docutils_version
-from sphinx.util.pycompat import all
from sphinx.domains.std import (
make_term_from_paragraph_node,
make_termnodes_from_paragraph_node,
@@ -196,7 +195,10 @@ class Locale(Transform):
patch = new_document(source, settings)
CustomLocaleReporter(node.source, node.line).set_reporter(patch)
parser.parse(msgstr, patch)
- patch = patch[0]
+ try:
+ patch = patch[0]
+ except IndexError: # empty node
+ pass
# XXX doctest and other block markup
if not isinstance(patch, nodes.paragraph):
continue # skip for now
@@ -239,8 +241,7 @@ class Locale(Transform):
self.document.ids.pop(_id, None)
# re-entry with new named section node.
- self.document.note_implicit_target(
- section_node, section_node)
+ self.document.note_implicit_target(section_node)
# replace target's refname to new target name
def is_named_target(node):
@@ -299,7 +300,10 @@ class Locale(Transform):
patch = new_document(source, settings)
CustomLocaleReporter(node.source, node.line).set_reporter(patch)
parser.parse(msgstr, patch)
- patch = patch[0]
+ try:
+ patch = patch[0]
+ except IndexError: # empty node
+ pass
# XXX doctest and other block markup
if not isinstance(patch, nodes.paragraph):
continue # skip for now
diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py
index 5cbbb61b..a20fc19b 100644
--- a/sphinx/util/__init__.py
+++ b/sphinx/util/__init__.py
@@ -158,18 +158,17 @@ def copy_static_entry(source, targetdir, builder, context={},
else:
copyfile(source, target)
elif path.isdir(source):
- if level == 0:
- for entry in os.listdir(source):
- if entry.startswith('.'):
- continue
- copy_static_entry(path.join(source, entry), targetdir,
- builder, context, level=1,
- exclude_matchers=exclude_matchers)
- else:
- target = path.join(targetdir, path.basename(source))
- if path.exists(target):
- shutil.rmtree(target)
- shutil.copytree(source, target)
+ if not path.isdir(targetdir):
+ os.mkdir(targetdir)
+ for entry in os.listdir(source):
+ if entry.startswith('.'):
+ continue
+ newtarget = targetdir
+ if path.isdir(path.join(source, entry)):
+ newtarget = path.join(targetdir, entry)
+ copy_static_entry(path.join(source, entry), newtarget,
+ builder, context, level=level+1,
+ exclude_matchers=exclude_matchers)
_DEBUG_HEADER = '''\
diff --git a/sphinx/util/inspect.py b/sphinx/util/inspect.py
index 61061a9a..c7556d05 100644
--- a/sphinx/util/inspect.py
+++ b/sphinx/util/inspect.py
@@ -55,7 +55,7 @@ if sys.version_info >= (3, 0):
raise TypeError('%r is not a Python function' % func)
return inspect.getfullargspec(func)
-elif sys.version_info >= (2, 5):
+else: # 2.6, 2.7
from functools import partial
def getargspec(func):
"""Like inspect.getargspec but supports functools.partial as well."""
@@ -86,12 +86,7 @@ elif sys.version_info >= (2, 5):
del func_defaults[i]
except IndexError:
pass
- if sys.version_info >= (2, 6):
- return inspect.ArgSpec(args, varargs, varkw, func_defaults)
- else:
- return (args, varargs, varkw, func_defaults)
-else:
- getargspec = inspect.getargspec
+ return inspect.ArgSpec(args, varargs, varkw, func_defaults)
def isdescriptor(x):
diff --git a/sphinx/util/jsonimpl.py b/sphinx/util/jsonimpl.py
index aa0ea825..de846b24 100644
--- a/sphinx/util/jsonimpl.py
+++ b/sphinx/util/jsonimpl.py
@@ -10,27 +10,15 @@
"""
import UserString
+import json
-try:
- import json
- # json-py's json module has no JSONEncoder; this will raise AttributeError
- # if json-py is imported instead of the built-in json module
- JSONEncoder = json.JSONEncoder
-except (ImportError, AttributeError):
- try:
- import simplejson as json
- JSONEncoder = json.JSONEncoder
- except ImportError:
- json = None
- JSONEncoder = object
-
-
-class SphinxJSONEncoder(JSONEncoder):
+
+class SphinxJSONEncoder(json.JSONEncoder):
"""JSONEncoder subclass that forces translation proxies."""
def default(self, obj):
if isinstance(obj, UserString.UserString):
return unicode(obj)
- return JSONEncoder.default(self, obj)
+ return json.JSONEncoder.default(self, obj)
def dump(obj, fp, *args, **kwds):
diff --git a/sphinx/util/osutil.py b/sphinx/util/osutil.py
index 87717771..a5c461a6 100644
--- a/sphinx/util/osutil.py
+++ b/sphinx/util/osutil.py
@@ -69,6 +69,9 @@ def ensuredir(path):
raise
+# This function is same as os.walk of Python2.6, 2.7, 3.2, 3.3 except a
+# customization that check UnicodeError.
+# The customization obstacle to replace the function with the os.walk.
def walk(top, topdown=True, followlinks=False):
"""Backport of os.walk from 2.6, where the *followlinks* argument was
added.
@@ -155,9 +158,8 @@ else:
def safe_relpath(path, start=None):
- from sphinx.util.pycompat import relpath
try:
- return relpath(path, start)
+ return os.path.relpath(path, start)
except ValueError:
return path
@@ -171,14 +173,13 @@ def find_catalog(docname, compaction):
def find_catalog_files(docname, srcdir, locale_dirs, lang, compaction):
- from sphinx.util.pycompat import relpath
if not(lang and locale_dirs):
return []
domain = find_catalog(docname, compaction)
files = [gettext.find(domain, path.join(srcdir, dir_), [lang])
for dir_ in locale_dirs]
- files = [relpath(f, srcdir) for f in files if f]
+ files = [path.relpath(f, srcdir) for f in files if f]
return files
diff --git a/sphinx/util/pycompat.py b/sphinx/util/pycompat.py
index 1e5ea314..9941dc0c 100644
--- a/sphinx/util/pycompat.py
+++ b/sphinx/util/pycompat.py
@@ -11,7 +11,6 @@
import sys
import codecs
-import encodings
# ------------------------------------------------------------------------------
# Python 2/3 compatibility
@@ -48,6 +47,7 @@ if sys.version_info >= (3, 0):
# try to match ParseError details with SyntaxError details
raise SyntaxError(err.msg, (filepath, lineno, offset, err.value))
return unicode(tree)
+ from itertools import zip_longest # Python 3 name
else:
# Python 2
@@ -69,6 +69,8 @@ else:
# error handler
import locale
sys_encoding = locale.getpreferredencoding()
+ # use Python 3 name
+ from itertools import izip_longest as zip_longest
def execfile_(filepath, _globals):
@@ -81,8 +83,8 @@ def execfile_(filepath, _globals):
finally:
f.close()
- # py25,py26,py31 accept only LF eol instead of CRLF
- if sys.version_info[:2] in ((2, 5), (2, 6), (3, 1)):
+ # py26 accept only LF eol instead of CRLF
+ if sys.version_info[:2] == (2, 6):
source = source.replace(b('\r\n'), b('\n'))
# compile to a code object, handle syntax errors
@@ -100,178 +102,7 @@ def execfile_(filepath, _globals):
exec code in _globals
-try:
- from html import escape as htmlescape
-except ImportError:
+if sys.version_info >= (3, 2):
+ from html import escape as htmlescape # >= Python 3.2
+else: # 2.6, 2.7, 3.1
from cgi import escape as htmlescape
-
-# ------------------------------------------------------------------------------
-# Missing builtins and itertools in Python < 2.6
-
-if sys.version_info >= (2, 6):
- # Python >= 2.6
- next = next
-
- from itertools import product
- try:
- from itertools import zip_longest # Python 3 name
- except ImportError:
- from itertools import izip_longest as zip_longest
-
- import os
- relpath = os.path.relpath
- del os
-
- import io
- open = io.open
-
-else:
- # Python < 2.6
- from itertools import izip, repeat, chain
-
- # this is on Python 2, where the method is called "next" (it is refactored
- # to __next__ by 2to3, but in that case never executed)
- def next(iterator):
- return iterator.next()
-
- # These replacement functions have been taken from the Python 2.6
- # itertools documentation.
- def product(*args, **kwargs):
- pools = map(tuple, args) * kwargs.get('repeat', 1)
- result = [[]]
- for pool in pools:
- result = [x + [y] for x in result for y in pool]
- for prod in result:
- yield tuple(prod)
-
- def zip_longest(*args, **kwds):
- # zip_longest('ABCD', 'xy', fillvalue='-') --> Ax By C- D-
- fillvalue = kwds.get('fillvalue')
- def sentinel(counter = ([fillvalue]*(len(args)-1)).pop):
- yield counter() # yields the fillvalue, or raises IndexError
- fillers = repeat(fillvalue)
- iters = [chain(it, sentinel(), fillers) for it in args]
- try:
- for tup in izip(*iters):
- yield tup
- except IndexError:
- pass
-
- from os.path import curdir
- def relpath(path, start=curdir):
- """Return a relative version of a path"""
- from os.path import sep, abspath, commonprefix, join, pardir
-
- if not path:
- raise ValueError("no path specified")
-
- start_list = abspath(start).split(sep)
- path_list = abspath(path).split(sep)
-
- # Work out how much of the filepath is shared by start and path.
- i = len(commonprefix([start_list, path_list]))
-
- rel_list = [pardir] * (len(start_list)-i) + path_list[i:]
- if not rel_list:
- return start
- return join(*rel_list)
- del curdir
-
- from types import MethodType
- def open(filename, mode='r', *args, **kw):
- newline = kw.pop('newline', None)
- mode = mode.replace('t', '')
- f = codecs.open(filename, mode, *args, **kw)
- if newline is not None:
- f._write = f.write
- def write(self, text):
- text = text.replace(u'\r\n', u'\n').replace(u'\n', newline)
- self._write(text)
- f.write = MethodType(write, f)
- return f
-
-
-# ------------------------------------------------------------------------------
-# Missing builtins and codecs in Python < 2.5
-
-if sys.version_info >= (2, 5):
- # Python >= 2.5
- base_exception = BaseException
- any = any
- all = all
-
-else:
- # Python 2.4
- base_exception = Exception
-
- def all(gen):
- for i in gen:
- if not i:
- return False
- return True
-
- def any(gen):
- for i in gen:
- if i:
- return True
- return False
-
- # Python 2.4 doesn't know the utf-8-sig encoding, so deliver it here
-
- def my_search_function(encoding):
- norm_encoding = encodings.normalize_encoding(encoding)
- if norm_encoding != 'utf_8_sig':
- return None
- return (encode, decode, StreamReader, StreamWriter)
-
- codecs.register(my_search_function)
-
- # begin code copied from utf_8_sig.py in Python 2.6
-
- def encode(input, errors='strict'):
- return (codecs.BOM_UTF8 +
- codecs.utf_8_encode(input, errors)[0], len(input))
-
- def decode(input, errors='strict'):
- prefix = 0
- if input[:3] == codecs.BOM_UTF8:
- input = input[3:]
- prefix = 3
- (output, consumed) = codecs.utf_8_decode(input, errors, True)
- return (output, consumed+prefix)
-
- class StreamWriter(codecs.StreamWriter):
- def reset(self):
- codecs.StreamWriter.reset(self)
- try:
- del self.encode
- except AttributeError:
- pass
-
- def encode(self, input, errors='strict'):
- self.encode = codecs.utf_8_encode
- return encode(input, errors)
-
- class StreamReader(codecs.StreamReader):
- def reset(self):
- codecs.StreamReader.reset(self)
- try:
- del self.decode
- except AttributeError:
- pass
-
- def decode(self, input, errors='strict'):
- if len(input) < 3:
- if codecs.BOM_UTF8.startswith(input):
- # not enough data to decide if this is a BOM
- # => try again on the next call
- return (u"", 0)
- elif input[:3] == codecs.BOM_UTF8:
- self.decode = codecs.utf_8_decode
- (output, consumed) = codecs.utf_8_decode(input[3:],errors)
- return (output, consumed+3)
- # (else) no BOM present
- self.decode = codecs.utf_8_decode
- return codecs.utf_8_decode(input, errors)
-
- # end code copied from utf_8_sig.py
diff --git a/sphinx/versioning.py b/sphinx/versioning.py
index a16751bb..ccab41d4 100644
--- a/sphinx/versioning.py
+++ b/sphinx/versioning.py
@@ -11,8 +11,9 @@
"""
from uuid import uuid4
from operator import itemgetter
+from itertools import product
-from sphinx.util.pycompat import product, zip_longest, all
+from sphinx.util.pycompat import zip_longest
# anything below that ratio is considered equal/changed
diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py
index 308e1f5b..1e0c9f77 100644
--- a/sphinx/writers/html.py
+++ b/sphinx/writers/html.py
@@ -240,6 +240,12 @@ class HTMLTranslator(BaseTranslator):
self.body.append('.'.join(map(str, numbers)) +
self.secnumber_suffix)
+ # overwritten to avoid emitting empty <ul></ul>
+ def visit_bullet_list(self, node):
+ if len(node) == 1 and node[0].tagname == 'toctree':
+ raise nodes.SkipNode
+ BaseTranslator.visit_bullet_list(self, node)
+
# overwritten
def visit_title(self, node):
BaseTranslator.visit_title(self, node)
@@ -267,6 +273,9 @@ class HTMLTranslator(BaseTranslator):
**highlight_args)
starttag = self.starttag(node, 'div', suffix='',
CLASS='highlight-%s' % lang)
+ if node.has_key('filename'):
+ starttag += '<div class="code-block-filename"><tt>%s</tt></div>' % (
+ node['filename'],)
self.body.append(starttag + highlighted + '</div>\n')
raise nodes.SkipNode
diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py
index b55adda0..61aa5828 100644
--- a/sphinx/writers/latex.py
+++ b/sphinx/writers/latex.py
@@ -25,7 +25,6 @@ from sphinx.errors import SphinxError
from sphinx.locale import admonitionlabels, _
from sphinx.util import split_into
from sphinx.util.osutil import ustrftime
-from sphinx.util.pycompat import any
from sphinx.util.texescape import tex_escape_map, tex_replace_map
from sphinx.util.smartypants import educate_quotes_latex
@@ -1338,6 +1337,11 @@ class LaTeXTranslator(nodes.NodeVisitor):
highlight_args['force'] = True
if 'linenos' in node:
linenos = node['linenos']
+ filename = node.get('filename')
+ if filename:
+ self.body.append('\n{\\colorbox[rgb]{0.9,0.9,0.9}'
+ '{\\makebox[\\textwidth][l]'
+ '{\\small\\texttt{%s}}}}\n' % (filename,))
def warner(msg):
self.builder.warn(msg, (self.curfilestack[-1], node.line))
hlcode = self.highlighter.highlight_block(code, lang, warn=warner,
diff --git a/tests/root/autodoc.txt b/tests/root/autodoc.txt
index d4b3404c..aa0dffba 100644
--- a/tests/root/autodoc.txt
+++ b/tests/root/autodoc.txt
@@ -45,3 +45,5 @@ Just testing a few autodoc possibilities...
:members: ca1, ia1
Specific members (2 total)
+
+.. automodule:: autodoc_missing_imports
diff --git a/tests/root/autodoc_missing_imports.py b/tests/root/autodoc_missing_imports.py
new file mode 100644
index 00000000..7a717345
--- /dev/null
+++ b/tests/root/autodoc_missing_imports.py
@@ -0,0 +1,9 @@
+
+import missing_module
+from missing_module import missing_name
+import missing_package1.missing_module1
+from missing_package2 import missing_module2
+from missing_package3.missing_module3 import missing_name
+
+class TestAutodoc(object):
+ """TestAutodoc docstring."""
diff --git a/tests/root/conf.py b/tests/root/conf.py
index 3399043f..f0d40148 100644
--- a/tests/root/conf.py
+++ b/tests/root/conf.py
@@ -23,7 +23,6 @@ copyright = '2010, Georg Brandl & Team'
version = '0.6'
release = '0.6alpha1'
today_fmt = '%B %d, %Y'
-# unused_docs = []
exclude_patterns = ['_build', '**/excluded.*']
keep_warnings = True
pygments_style = 'sphinx'
@@ -71,6 +70,13 @@ autosummary_generate = ['autosummary']
extlinks = {'issue': ('http://bugs.python.org/issue%s', 'issue '),
'pyurl': ('http://python.org/%s', None)}
+autodoc_mock_imports = [
+ 'missing_module',
+ 'missing_package1.missing_module1',
+ 'missing_package2.missing_module2',
+ 'missing_package3.missing_module3',
+]
+
# modify tags from conf.py
tags.add('confpytag')
diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py
index 3735a7b6..3d30d0dd 100644
--- a/tests/test_autodoc.py
+++ b/tests/test_autodoc.py
@@ -348,6 +348,22 @@ def test_get_doc():
directive.env.config.autoclass_content = 'both'
assert getdocl('class', F) == ['Class docstring']
+ # class has __init__ method with no docstring
+ class G(object):
+ """Class docstring"""
+ def __init__(self):
+ pass
+
+ # docstring in the __init__ method of base class will not be used
+ for f in (False, True):
+ directive.env.config.autodoc_docstring_signature = f
+ directive.env.config.autoclass_content = 'class'
+ assert getdocl('class', G) == ['Class docstring']
+ directive.env.config.autoclass_content = 'init'
+ assert getdocl('class', G) == ['Class docstring']
+ directive.env.config.autoclass_content = 'both'
+ assert getdocl('class', G) == ['Class docstring']
+
@with_setup(setup_test)
def test_docstring_processing():
@@ -751,12 +767,8 @@ def _funky_classmethod(name, b, c, d, docstring=None):
some arguments."""
def template(cls, a, b, c, d=4, e=5, f=6):
return a, b, c, d, e, f
- if sys.version_info >= (2, 5):
- from functools import partial
- function = partial(template, b=b, c=c, d=d)
- else:
- def function(cls, a, e=5, f=6):
- return template(a, b, c, d, e, f)
+ from functools import partial
+ function = partial(template, b=b, c=c, d=d)
function.__name__ = name
function.__doc__ = docstring
return classmethod(function)
@@ -788,10 +800,9 @@ class Class(Base):
#: should be documented -- süß
attr = 'bar'
+ @property
def prop(self):
"""Property."""
- # stay 2.4 compatible (docstring!)
- prop = property(prop, doc="Property.")
docattr = 'baz'
"""should likewise be documented -- süß"""
diff --git a/tests/test_intl.py b/tests/test_intl.py
index c9822089..0119d677 100644
--- a/tests/test_intl.py
+++ b/tests/test_intl.py
@@ -16,8 +16,6 @@ from StringIO import StringIO
from subprocess import Popen, PIPE
from xml.etree import ElementTree
-from sphinx.util.pycompat import relpath
-
from util import test_roots, path, with_app, SkipTest
@@ -49,7 +47,7 @@ def setup_module():
for f in [f for f in files if f.endswith('.po')]:
po = dirpath / f
mo = root / 'xx' / 'LC_MESSAGES' / (
- relpath(po[:-3], root) + '.mo')
+ os.path.relpath(po[:-3], root) + '.mo')
if not mo.parent.exists():
mo.parent.makedirs()
try:
@@ -75,7 +73,7 @@ def teardown_module():
def elem_gettexts(elem):
def itertext(self):
# this function copied from Python-2.7 'ElementTree.itertext'.
- # for compatibility to Python-2.5, 2.6, 3.1
+ # for compatibility to Python-2.6
tag = self.tag
if not isinstance(tag, basestring) and tag is not None:
return
diff --git a/tests/test_napoleon.py b/tests/test_napoleon.py
new file mode 100644
index 00000000..d8c71960
--- /dev/null
+++ b/tests/test_napoleon.py
@@ -0,0 +1,190 @@
+# -*- coding: utf-8 -*-
+"""
+ test_napoleon
+ ~~~~~~~~~~~~~
+
+ Tests for :mod:`sphinx.ext.napoleon.__init__` module.
+
+
+ :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+from mock import Mock
+from sphinx.application import Sphinx
+from sphinx.ext.napoleon import (_process_docstring, _skip_member, Config,
+ setup)
+from unittest import TestCase
+
+
+def _private_doc():
+ """module._private_doc.DOCSTRING"""
+ pass
+
+
+def _private_undoc():
+ pass
+
+
+def __special_doc__():
+ """module.__special_doc__.DOCSTRING"""
+ pass
+
+
+def __special_undoc__():
+ pass
+
+
+class SampleClass(object):
+ def _private_doc(self):
+ """SampleClass._private_doc.DOCSTRING"""
+ pass
+
+ def _private_undoc(self):
+ pass
+
+ def __special_doc__(self):
+ """SampleClass.__special_doc__.DOCSTRING"""
+ pass
+
+ def __special_undoc__(self):
+ pass
+
+
+class SampleError(Exception):
+ def _private_doc(self):
+ """SampleError._private_doc.DOCSTRING"""
+ pass
+
+ def _private_undoc(self):
+ pass
+
+ def __special_doc__(self):
+ """SampleError.__special_doc__.DOCSTRING"""
+ pass
+
+ def __special_undoc__(self):
+ pass
+
+
+class ProcessDocstringTest(TestCase):
+ def test_modify_in_place(self):
+ lines = ['Summary line.',
+ '',
+ 'Args:',
+ ' arg1: arg1 description']
+ app = Mock()
+ app.config = Config()
+ _process_docstring(app, 'class', 'SampleClass', SampleClass, Mock(),
+ lines)
+
+ expected = ['Summary line.',
+ '',
+ ':Parameters: **arg1** --',
+ ' arg1 description']
+ self.assertEqual(expected, lines)
+
+
+class SetupTest(TestCase):
+ def test_unknown_app_type(self):
+ setup(object())
+
+ def test_add_config_values(self):
+ app = Mock(Sphinx)
+ setup(app)
+ for name, (default, rebuild) in Config._config_values.items():
+ has_config = False
+ for method_name, args, kwargs in app.method_calls:
+ if(method_name == 'add_config_value' and
+ args[0] == name):
+ has_config = True
+ if not has_config:
+ self.fail('Config value was not added to app %s' % name)
+
+ has_process_docstring = False
+ has_skip_member = False
+ for method_name, args, kwargs in app.method_calls:
+ if method_name == 'connect':
+ if(args[0] == 'autodoc-process-docstring' and
+ args[1] == _process_docstring):
+ has_process_docstring = True
+ elif(args[0] == 'autodoc-skip-member' and
+ args[1] == _skip_member):
+ has_skip_member = True
+ if not has_process_docstring:
+ self.fail('autodoc-process-docstring never connected')
+ if not has_skip_member:
+ self.fail('autodoc-skip-member never connected')
+
+
+class SkipMemberTest(TestCase):
+ def assertSkip(self, what, member, obj, expect_skip, config_name):
+ skip = 'default skip'
+ app = Mock()
+ app.config = Config()
+ setattr(app.config, config_name, True)
+ if expect_skip:
+ self.assertEqual(skip, _skip_member(app, what, member, obj, skip,
+ Mock()))
+ else:
+ self.assertFalse(_skip_member(app, what, member, obj, skip,
+ Mock()))
+ setattr(app.config, config_name, False)
+ self.assertEqual(skip, _skip_member(app, what, member, obj, skip,
+ Mock()))
+
+ def test_class_private_doc(self):
+ self.assertSkip('class', '_private_doc',
+ SampleClass._private_doc, False,
+ 'napoleon_include_private_with_doc')
+
+ def test_class_private_undoc(self):
+ self.assertSkip('class', '_private_undoc',
+ SampleClass._private_undoc, True,
+ 'napoleon_include_private_with_doc')
+
+ def test_class_special_doc(self):
+ self.assertSkip('class', '__special_doc__',
+ SampleClass.__special_doc__, False,
+ 'napoleon_include_special_with_doc')
+
+ def test_class_special_undoc(self):
+ self.assertSkip('class', '__special_undoc__',
+ SampleClass.__special_undoc__, True,
+ 'napoleon_include_special_with_doc')
+
+ def test_exception_private_doc(self):
+ self.assertSkip('exception', '_private_doc',
+ SampleError._private_doc, False,
+ 'napoleon_include_private_with_doc')
+
+ def test_exception_private_undoc(self):
+ self.assertSkip('exception', '_private_undoc',
+ SampleError._private_undoc, True,
+ 'napoleon_include_private_with_doc')
+
+ def test_exception_special_doc(self):
+ self.assertSkip('exception', '__special_doc__',
+ SampleError.__special_doc__, False,
+ 'napoleon_include_special_with_doc')
+
+ def test_exception_special_undoc(self):
+ self.assertSkip('exception', '__special_undoc__',
+ SampleError.__special_undoc__, True,
+ 'napoleon_include_special_with_doc')
+
+ def test_module_private_doc(self):
+ self.assertSkip('module', '_private_doc', _private_doc, False,
+ 'napoleon_include_private_with_doc')
+
+ def test_module_private_undoc(self):
+ self.assertSkip('module', '_private_undoc', _private_undoc, True,
+ 'napoleon_include_private_with_doc')
+
+ def test_module_special_doc(self):
+ self.assertSkip('module', '__special_doc__', __special_doc__, False,
+ 'napoleon_include_special_with_doc')
+
+ def test_module_special_undoc(self):
+ self.assertSkip('module', '__special_undoc__', __special_undoc__, True,
+ 'napoleon_include_special_with_doc')
diff --git a/tests/test_napoleon_docstring.py b/tests/test_napoleon_docstring.py
new file mode 100644
index 00000000..aa52aebe
--- /dev/null
+++ b/tests/test_napoleon_docstring.py
@@ -0,0 +1,259 @@
+# -*- coding: utf-8 -*-
+"""
+ test_napoleon_docstring
+ ~~~~~~~~~~~~~~~~~~~~~~~
+
+ Tests for :mod:`sphinx.ext.napoleon.docstring` module.
+
+
+ :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import textwrap
+from sphinx.ext.napoleon.docstring import GoogleDocstring, NumpyDocstring
+from unittest import TestCase
+
+
+class BaseDocstringTest(TestCase):
+ pass
+
+
+class GoogleDocstringTest(BaseDocstringTest):
+ docstrings = [(
+ """Single line summary""",
+ """Single line summary"""
+ ), (
+ """
+ Single line summary
+
+ Extended description
+
+ """,
+ """
+ Single line summary
+
+ Extended description
+ """
+ ), (
+ """
+ Single line summary
+
+ Args:
+ arg1(str):Extended
+ description of arg1
+ """,
+ """
+ Single line summary
+
+ :Parameters: **arg1** (*str*) --
+ Extended
+ description of arg1"""
+ ), (
+ """
+ Single line summary
+
+ Args:
+ arg1(str):Extended
+ description of arg1
+ arg2 ( int ) : Extended
+ description of arg2
+
+ Keyword Args:
+ kwarg1(str):Extended
+ description of kwarg1
+ kwarg2 ( int ) : Extended
+ description of kwarg2""",
+ """
+ Single line summary
+
+ :Parameters: * **arg1** (*str*) --
+ Extended
+ description of arg1
+ * **arg2** (*int*) --
+ Extended
+ description of arg2
+
+ :Keyword Arguments: * **kwarg1** (*str*) --
+ Extended
+ description of kwarg1
+ * **kwarg2** (*int*) --
+ Extended
+ description of kwarg2"""
+ ), (
+ """
+ Single line summary
+
+ Arguments:
+ arg1(str):Extended
+ description of arg1
+ arg2 ( int ) : Extended
+ description of arg2
+
+ Keyword Arguments:
+ kwarg1(str):Extended
+ description of kwarg1
+ kwarg2 ( int ) : Extended
+ description of kwarg2""",
+ """
+ Single line summary
+
+ :Parameters: * **arg1** (*str*) --
+ Extended
+ description of arg1
+ * **arg2** (*int*) --
+ Extended
+ description of arg2
+
+ :Keyword Arguments: * **kwarg1** (*str*) --
+ Extended
+ description of kwarg1
+ * **kwarg2** (*int*) --
+ Extended
+ description of kwarg2"""
+ ), (
+ """
+ Single line summary
+
+ Return:
+ str:Extended
+ description of return value
+ """,
+ """
+ Single line summary
+
+ :returns: *str* --
+ Extended
+ description of return value"""
+ ), (
+ """
+ Single line summary
+
+ Returns:
+ str:Extended
+ description of return value
+ """,
+ """
+ Single line summary
+
+ :returns: *str* --
+ Extended
+ description of return value"""
+ )]
+
+ def test_docstrings(self):
+ for docstring, expected in self.docstrings:
+ actual = str(GoogleDocstring(textwrap.dedent(docstring)))
+ expected = textwrap.dedent(expected)
+ self.assertEqual(expected, actual)
+
+
+class NumpyDocstringTest(BaseDocstringTest):
+ docstrings = [(
+ """Single line summary""",
+ """Single line summary"""
+ ), (
+ """
+ Single line summary
+
+ Extended description
+
+ """,
+ """
+ Single line summary
+
+ Extended description
+ """
+ ), (
+ """
+ Single line summary
+
+ Parameters
+ ----------
+ arg1:str
+ Extended
+ description of arg1
+ """,
+ """
+ Single line summary
+
+ :Parameters: **arg1** (*str*) --
+ Extended
+ description of arg1"""
+ ), (
+ """
+ Single line summary
+
+ Parameters
+ ----------
+ arg1:str
+ Extended
+ description of arg1
+ arg2 : int
+ Extended
+ description of arg2
+
+ Keyword Arguments
+ -----------------
+ kwarg1:str
+ Extended
+ description of kwarg1
+ kwarg2 : int
+ Extended
+ description of kwarg2
+ """,
+ """
+ Single line summary
+
+ :Parameters: * **arg1** (*str*) --
+ Extended
+ description of arg1
+ * **arg2** (*int*) --
+ Extended
+ description of arg2
+
+ :Keyword Arguments: * **kwarg1** (*str*) --
+ Extended
+ description of kwarg1
+ * **kwarg2** (*int*) --
+ Extended
+ description of kwarg2"""
+ ), (
+ """
+ Single line summary
+
+ Return
+ ------
+ str
+ Extended
+ description of return value
+ """,
+ """
+ Single line summary
+
+ :returns: *str* --
+ Extended
+ description of return value"""
+ ), (
+ """
+ Single line summary
+
+ Returns
+ -------
+ str
+ Extended
+ description of return value
+ """,
+ """
+ Single line summary
+
+ :returns: *str* --
+ Extended
+ description of return value"""
+ )]
+
+ def test_docstrings(self):
+ for docstring, expected in self.docstrings:
+ actual = str(NumpyDocstring(textwrap.dedent(docstring)))
+ expected = textwrap.dedent(expected)
+ self.assertEqual(expected, actual)
diff --git a/tests/test_napoleon_iterators.py b/tests/test_napoleon_iterators.py
new file mode 100644
index 00000000..db0be32c
--- /dev/null
+++ b/tests/test_napoleon_iterators.py
@@ -0,0 +1,346 @@
+# -*- coding: utf-8 -*-
+"""
+ test_napoleon_iterators
+ ~~~~~~~~~~~~~~~~~~~~~~~
+
+ Tests for :mod:`sphinx.ext.napoleon.iterators` module.
+
+
+ :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+from sphinx.ext.napoleon.iterators import peek_iter, modify_iter
+from unittest import TestCase
+
+
+class BaseIteratorsTest(TestCase):
+ def assertEqualTwice(self, expected, func, *args):
+ self.assertEqual(expected, func(*args))
+ self.assertEqual(expected, func(*args))
+
+ def assertFalseTwice(self, func, *args):
+ self.assertFalse(func(*args))
+ self.assertFalse(func(*args))
+
+ def assertNext(self, it, expected, is_last):
+ self.assertTrueTwice(it.has_next)
+ self.assertEqualTwice(expected, it.peek)
+ self.assertTrueTwice(it.has_next)
+ self.assertEqualTwice(expected, it.peek)
+ self.assertTrueTwice(it.has_next)
+ self.assertEqual(expected, it.next())
+ if is_last:
+ self.assertFalseTwice(it.has_next)
+ self.assertRaisesTwice(StopIteration, it.next)
+ else:
+ self.assertTrueTwice(it.has_next)
+
+ def assertRaisesTwice(self, exc, func, *args):
+ self.assertRaises(exc, func, *args)
+ self.assertRaises(exc, func, *args)
+
+ def assertTrueTwice(self, func, *args):
+ self.assertTrue(func(*args))
+ self.assertTrue(func(*args))
+
+
+class PeekIterTest(BaseIteratorsTest):
+ def test_init_with_sentinel(self):
+ a = iter(['1', '2', 'DONE'])
+ sentinel = 'DONE'
+ self.assertRaises(TypeError, peek_iter, a, sentinel)
+
+ def get_next():
+ return next(a)
+ it = peek_iter(get_next, sentinel)
+ self.assertEqual(it.sentinel, sentinel)
+ self.assertNext(it, '1', is_last=False)
+ self.assertNext(it, '2', is_last=True)
+
+ def test_iter(self):
+ a = ['1', '2', '3']
+ it = peek_iter(a)
+ self.assertTrue(it is it.__iter__())
+
+ a = []
+ b = [i for i in peek_iter(a)]
+ self.assertEqual([], b)
+
+ a = ['1']
+ b = [i for i in peek_iter(a)]
+ self.assertEqual(['1'], b)
+
+ a = ['1', '2']
+ b = [i for i in peek_iter(a)]
+ self.assertEqual(['1', '2'], b)
+
+ a = ['1', '2', '3']
+ b = [i for i in peek_iter(a)]
+ self.assertEqual(['1', '2', '3'], b)
+
+ def test_next_with_multi(self):
+ a = []
+ it = peek_iter(a)
+ self.assertFalseTwice(it.has_next)
+ self.assertRaisesTwice(StopIteration, it.next, 2)
+
+ a = ['1']
+ it = peek_iter(a)
+ self.assertTrueTwice(it.has_next)
+ self.assertRaisesTwice(StopIteration, it.next, 2)
+ self.assertTrueTwice(it.has_next)
+
+ a = ['1', '2']
+ it = peek_iter(a)
+ self.assertTrueTwice(it.has_next)
+ self.assertEqual(['1', '2'], it.next(2))
+ self.assertFalseTwice(it.has_next)
+
+ a = ['1', '2', '3']
+ it = peek_iter(a)
+ self.assertTrueTwice(it.has_next)
+ self.assertEqual(['1', '2'], it.next(2))
+ self.assertTrueTwice(it.has_next)
+ self.assertRaisesTwice(StopIteration, it.next, 2)
+ self.assertTrueTwice(it.has_next)
+
+ a = ['1', '2', '3', '4']
+ it = peek_iter(a)
+ self.assertTrueTwice(it.has_next)
+ self.assertEqual(['1', '2'], it.next(2))
+ self.assertTrueTwice(it.has_next)
+ self.assertEqual(['3', '4'], it.next(2))
+ self.assertFalseTwice(it.has_next)
+ self.assertRaisesTwice(StopIteration, it.next, 2)
+ self.assertFalseTwice(it.has_next)
+
+ def test_next_with_none(self):
+ a = []
+ it = peek_iter(a)
+ self.assertFalseTwice(it.has_next)
+ self.assertRaisesTwice(StopIteration, it.next)
+ self.assertFalseTwice(it.has_next)
+
+ a = ['1']
+ it = peek_iter(a)
+ self.assertEqual('1', it.__next__())
+
+ a = ['1']
+ it = peek_iter(a)
+ self.assertNext(it, '1', is_last=True)
+
+ a = ['1', '2']
+ it = peek_iter(a)
+ self.assertNext(it, '1', is_last=False)
+ self.assertNext(it, '2', is_last=True)
+
+ a = ['1', '2', '3']
+ it = peek_iter(a)
+ self.assertNext(it, '1', is_last=False)
+ self.assertNext(it, '2', is_last=False)
+ self.assertNext(it, '3', is_last=True)
+
+ def test_next_with_one(self):
+ a = []
+ it = peek_iter(a)
+ self.assertFalseTwice(it.has_next)
+ self.assertRaisesTwice(StopIteration, it.next, 1)
+
+ a = ['1']
+ it = peek_iter(a)
+ self.assertTrueTwice(it.has_next)
+ self.assertEqual(['1'], it.next(1))
+ self.assertFalseTwice(it.has_next)
+ self.assertRaisesTwice(StopIteration, it.next, 1)
+
+ a = ['1', '2']
+ it = peek_iter(a)
+ self.assertTrueTwice(it.has_next)
+ self.assertEqual(['1'], it.next(1))
+ self.assertTrueTwice(it.has_next)
+ self.assertEqual(['2'], it.next(1))
+ self.assertFalseTwice(it.has_next)
+ self.assertRaisesTwice(StopIteration, it.next, 1)
+
+ def test_next_with_zero(self):
+ a = []
+ it = peek_iter(a)
+ self.assertFalseTwice(it.has_next)
+ self.assertRaisesTwice(StopIteration, it.next, 0)
+
+ a = ['1']
+ it = peek_iter(a)
+ self.assertTrueTwice(it.has_next)
+ self.assertEqualTwice([], it.next, 0)
+ self.assertTrueTwice(it.has_next)
+ self.assertEqualTwice([], it.next, 0)
+
+ a = ['1', '2']
+ it = peek_iter(a)
+ self.assertTrueTwice(it.has_next)
+ self.assertEqualTwice([], it.next, 0)
+ self.assertTrueTwice(it.has_next)
+ self.assertEqualTwice([], it.next, 0)
+
+ def test_peek_with_multi(self):
+ a = []
+ it = peek_iter(a)
+ self.assertFalseTwice(it.has_next)
+ self.assertEqualTwice([it.sentinel, it.sentinel], it.peek, 2)
+ self.assertFalseTwice(it.has_next)
+
+ a = ['1']
+ it = peek_iter(a)
+ self.assertTrueTwice(it.has_next)
+ self.assertEqualTwice(['1', it.sentinel], it.peek, 2)
+ self.assertTrueTwice(it.has_next)
+ self.assertEqualTwice(['1', it.sentinel, it.sentinel], it.peek, 3)
+ self.assertTrueTwice(it.has_next)
+
+ a = ['1', '2']
+ it = peek_iter(a)
+ self.assertTrueTwice(it.has_next)
+ self.assertEqualTwice(['1', '2'], it.peek, 2)
+ self.assertTrueTwice(it.has_next)
+ self.assertEqualTwice(['1', '2', it.sentinel], it.peek, 3)
+ self.assertTrueTwice(it.has_next)
+ self.assertEqualTwice(['1', '2', it.sentinel, it.sentinel], it.peek, 4)
+ self.assertTrueTwice(it.has_next)
+
+ a = ['1', '2', '3']
+ it = peek_iter(a)
+ self.assertTrueTwice(it.has_next)
+ self.assertEqualTwice(['1', '2'], it.peek, 2)
+ self.assertTrueTwice(it.has_next)
+ self.assertEqualTwice(['1', '2', '3'], it.peek, 3)
+ self.assertTrueTwice(it.has_next)
+ self.assertEqualTwice(['1', '2', '3', it.sentinel], it.peek, 4)
+ self.assertTrueTwice(it.has_next)
+ self.assertEqual('1', it.next())
+ self.assertTrueTwice(it.has_next)
+ self.assertEqualTwice(['2', '3'], it.peek, 2)
+ self.assertTrueTwice(it.has_next)
+ self.assertEqualTwice(['2', '3', it.sentinel], it.peek, 3)
+ self.assertTrueTwice(it.has_next)
+ self.assertEqualTwice(['2', '3', it.sentinel, it.sentinel], it.peek, 4)
+ self.assertTrueTwice(it.has_next)
+
+ def test_peek_with_none(self):
+ a = []
+ it = peek_iter(a)
+ self.assertFalseTwice(it.has_next)
+ self.assertEqualTwice(it.sentinel, it.peek)
+ self.assertFalseTwice(it.has_next)
+
+ a = ['1']
+ it = peek_iter(a)
+ self.assertTrueTwice(it.has_next)
+ self.assertEqualTwice('1', it.peek)
+ self.assertEqual('1', it.next())
+ self.assertFalseTwice(it.has_next)
+ self.assertEqualTwice(it.sentinel, it.peek)
+ self.assertFalseTwice(it.has_next)
+
+ a = ['1', '2']
+ it = peek_iter(a)
+ self.assertTrueTwice(it.has_next)
+ self.assertEqualTwice('1', it.peek)
+ self.assertEqual('1', it.next())
+ self.assertTrueTwice(it.has_next)
+ self.assertEqualTwice('2', it.peek)
+ self.assertEqual('2', it.next())
+ self.assertFalseTwice(it.has_next)
+ self.assertEqualTwice(it.sentinel, it.peek)
+ self.assertFalseTwice(it.has_next)
+
+ def test_peek_with_one(self):
+ a = []
+ it = peek_iter(a)
+ self.assertFalseTwice(it.has_next)
+ self.assertEqualTwice([it.sentinel], it.peek, 1)
+ self.assertFalseTwice(it.has_next)
+
+ a = ['1']
+ it = peek_iter(a)
+ self.assertTrueTwice(it.has_next)
+ self.assertEqualTwice(['1'], it.peek, 1)
+ self.assertEqual('1', it.next())
+ self.assertFalseTwice(it.has_next)
+ self.assertEqualTwice([it.sentinel], it.peek, 1)
+ self.assertFalseTwice(it.has_next)
+
+ a = ['1', '2']
+ it = peek_iter(a)
+ self.assertTrueTwice(it.has_next)
+ self.assertEqualTwice(['1'], it.peek, 1)
+ self.assertEqual('1', it.next())
+ self.assertTrueTwice(it.has_next)
+ self.assertEqualTwice(['2'], it.peek, 1)
+ self.assertEqual('2', it.next())
+ self.assertFalseTwice(it.has_next)
+ self.assertEqualTwice([it.sentinel], it.peek, 1)
+ self.assertFalseTwice(it.has_next)
+
+ def test_peek_with_zero(self):
+ a = []
+ it = peek_iter(a)
+ self.assertFalseTwice(it.has_next)
+ self.assertEqualTwice([], it.peek, 0)
+
+ a = ['1']
+ it = peek_iter(a)
+ self.assertTrueTwice(it.has_next)
+ self.assertEqualTwice([], it.peek, 0)
+ self.assertTrueTwice(it.has_next)
+ self.assertEqualTwice([], it.peek, 0)
+
+ a = ['1', '2']
+ it = peek_iter(a)
+ self.assertTrueTwice(it.has_next)
+ self.assertEqualTwice([], it.peek, 0)
+ self.assertTrueTwice(it.has_next)
+ self.assertEqualTwice([], it.peek, 0)
+
+
+class ModifyIterTest(BaseIteratorsTest):
+ def test_init_with_sentinel_args(self):
+ a = iter(['1', '2', '3', 'DONE'])
+ sentinel = 'DONE'
+
+ def get_next():
+ return next(a)
+ it = modify_iter(get_next, sentinel, int)
+ expected = [1, 2, 3]
+ self.assertEqual(expected, [i for i in it])
+
+ def test_init_with_sentinel_kwargs(self):
+ a = iter([1, 2, 3, 4])
+ sentinel = 4
+
+ def get_next():
+ return next(a)
+ it = modify_iter(get_next, sentinel, modifier=str)
+ expected = ['1', '2', '3']
+ self.assertEqual(expected, [i for i in it])
+
+ def test_modifier_default(self):
+ a = ['', ' ', ' a ', 'b ', ' c', ' ', '']
+ it = modify_iter(a)
+ expected = ['', ' ', ' a ', 'b ', ' c', ' ', '']
+ self.assertEqual(expected, [i for i in it])
+
+ def test_modifier_not_callable(self):
+ self.assertRaises(TypeError, modify_iter, [1], modifier='not_callable')
+
+ def test_modifier_rstrip(self):
+ a = ['', ' ', ' a ', 'b ', ' c', ' ', '']
+ it = modify_iter(a, modifier=lambda s: s.rstrip())
+ expected = ['', '', ' a', 'b', ' c', '', '']
+ self.assertEqual(expected, [i for i in it])
+
+ def test_modifier_rstrip_unicode(self):
+ a = [u'', u' ', u' a ', u'b ', u' c', u' ', u'']
+ it = modify_iter(a, modifier=lambda s: s.rstrip())
+ expected = [u'', u'', u' a', u'b', u' c', u'', u'']
+ self.assertEqual(expected, [i for i in it])
diff --git a/tests/test_versioning.py b/tests/test_versioning.py
index 6469a33c..5cff92ff 100644
--- a/tests/test_versioning.py
+++ b/tests/test_versioning.py
@@ -15,7 +15,6 @@ from docutils.parsers.rst.directives.html import MetaBody
from sphinx import addnodes
from sphinx.versioning import add_uids, merge_doctrees, get_ratio
-from sphinx.util.pycompat import all
from util import test_root, TestApp
diff --git a/tests/test_websupport.py b/tests/test_websupport.py
index 611a131a..d950a36c 100644
--- a/tests/test_websupport.py
+++ b/tests/test_websupport.py
@@ -11,12 +11,7 @@
import os
from StringIO import StringIO
-
-try:
- from functools import wraps
-except ImportError:
- # functools is new in 2.5
- wraps = lambda f: (lambda w: w)
+from functools import wraps
from sphinx.websupport import WebSupport
from sphinx.websupport.errors import DocumentNotFoundError, \
diff --git a/tests/util.py b/tests/util.py
index 4ba89030..a2f345bf 100644
--- a/tests/util.py
+++ b/tests/util.py
@@ -13,12 +13,7 @@ import tempfile
import shutil
import re
from codecs import open
-
-try:
- from functools import wraps
-except ImportError:
- # functools is new in 2.4
- wraps = lambda f: (lambda w: w)
+from functools import wraps
from sphinx import application
from sphinx.theming import Theme
diff --git a/tox.ini b/tox.ini
index f1675e90..ee8ce791 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,8 +1,9 @@
[tox]
-envlist=py25,py26,py27,py31,py32,py33,pypy,du11,du10,du09,du08,du07
+envlist=py26,py27,py32,py33,pypy,du11,du10,du09,du08,du07
[testenv]
deps=
+ mock
nose
sqlalchemy
whoosh
@@ -12,14 +13,6 @@ commands=
{envpython} tests/run.py {posargs}
sphinx-build -W -b html -d {envtmpdir}/doctrees doc {envtmpdir}/html
-[testenv:py25]
-deps=
- simplejson==2.5.0
- {[testenv]deps}
-setenv=
- PIP_INSECURE = 1
- {[testenv]setenv}
-
[testenv:py33]
deps=
docutils>=0.10.0