Merge branch 'master' into feature-text-secnumbers

This commit is contained in:
Takeshi KOMIYA 2018-01-15 00:21:50 +09:00 committed by GitHub
commit b688941b09
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
382 changed files with 5516 additions and 3381 deletions

View File

@ -6,19 +6,12 @@ environment:
matrix: matrix:
- PYTHON: 27 - PYTHON: 27
DOCUTILS: 0.13.1
TEST_IGNORE: --ignore py35
- PYTHON: 27
DOCUTILS: 0.14
TEST_IGNORE: --ignore py35 TEST_IGNORE: --ignore py35
- PYTHON: 36 - PYTHON: 36
DOCUTILS: 0.14
- PYTHON: 36-x64 - PYTHON: 36-x64
DOCUTILS: 0.14
install: install:
- C:\Python%PYTHON%\python.exe -m pip install -U pip setuptools - C:\Python%PYTHON%\python.exe -m pip install -U pip setuptools
- C:\Python%PYTHON%\python.exe -m pip install docutils==%DOCUTILS% mock
- C:\Python%PYTHON%\python.exe -m pip install .[test,websupport] - C:\Python%PYTHON%\python.exe -m pip install .[test,websupport]
# No automatic build, just run python tests # No automatic build, just run python tests
@ -39,7 +32,7 @@ test_script:
if (-not $test_ignore) { $test_ignore = '' } if (-not $test_ignore) { $test_ignore = '' }
$tests = $env:TEST $tests = $env:TEST
if (-not $tests) { $tests = '' } if (-not $tests) { $tests = '' }
& "C:\Python$($env:PYTHON)\python.exe" run.py $test_ignore.Split(' ') --junitxml .junit.xml $tests.Split(' ') & "C:\Python$($env:PYTHON)\python.exe" -m pytest $test_ignore.Split(' ') --junitxml .junit.xml $tests.Split(' ')
Pop-Location Pop-Location
if ($LastExitCode -eq 1) { Write-Host "Test Failures Occurred, leaving for test result parsing" } if ($LastExitCode -eq 1) { Write-Host "Test Failures Occurred, leaving for test result parsing" }
elseif ($LastExitCode -ne 0) { Write-Host "Other Error Occurred, aborting"; exit $LastExitCode } elseif ($LastExitCode -ne 0) { Write-Host "Other Error Occurred, aborting"; exit $LastExitCode }

View File

@ -6,4 +6,6 @@ jobs:
working_directory: /sphinx working_directory: /sphinx
steps: steps:
- checkout - checkout
- run: /python3.4/bin/pip install -U pip setuptools
- run: /python3.4/bin/pip install -U .[test,websupport]
- run: make test PYTHON=/python3.4/bin/python - run: make test PYTHON=/python3.4/bin/python

1
.gitignore vendored
View File

@ -13,6 +13,7 @@ TAGS
.tox .tox
.venv .venv
.coverage .coverage
htmlcov
.DS_Store .DS_Store
sphinx/pycode/Grammar*pickle sphinx/pycode/Grammar*pickle
distribute-* distribute-*

View File

@ -2,46 +2,48 @@ language: python
sudo: false sudo: false
dist: trusty dist: trusty
cache: pip cache: pip
python:
- "pypy-5.4.1"
- "3.6"
- "3.5"
- "3.4"
- "2.7"
- "nightly"
env: env:
global: global:
- TEST='-v --durations 25'
- PYTHONFAULTHANDLER=x - PYTHONFAULTHANDLER=x
- PYTHONWARNINGS=all
- SKIP_LATEX_BUILD=1 - SKIP_LATEX_BUILD=1
matrix:
- DOCUTILS=0.13.1
- DOCUTILS=0.14
matrix: matrix:
exclude: include:
- python: "3.4" - python: 'pypy'
env: DOCUTILS=0.13.1 env: TOXENV=pypy
- python: "3.5" - python: '2.7'
env: DOCUTILS=0.13.1 env:
- python: "3.6" - TOXENV=du13
env: DOCUTILS=0.13.1 - PYTEST_ADDOPTS = --cov sphinx --cov-append --cov-config setup.cfg
- python: nightly - python: '3.4'
env: DOCUTILS=0.13.1 env: TOXENV=py34
- python: "pypy-5.4.1" - python: '3.5'
env: DOCUTILS=0.13.1 env: TOXENV=py35
- python: '3.6'
env:
- TOXENV=py36
- PYTEST_ADDOPTS = --cov sphinx --cov-append --cov-config setup.cfg
- python: 'nightly'
env: TOXENV=py37
- python: '3.6'
env: TOXENV=docs
- python: '3.6'
env: TOXENV=mypy
- python: '2.7'
env: TOXENV=flake8
addons: addons:
apt: apt:
packages: packages:
- graphviz - graphviz
- imagemagick - imagemagick
install: install:
- pip install -U pip setuptools - pip install -U tox codecov
- pip install docutils==$DOCUTILS
- pip install .[test,websupport]
- pip install flake8
- if [[ $TRAVIS_PYTHON_VERSION == '3.6' ]]; then python3.6 -m pip install mypy typed-ast; fi
script: script:
- flake8 - tox -- -v
- if [[ $TRAVIS_PYTHON_VERSION == '3.6' ]]; then make type-check test-async; fi
- if [[ $TRAVIS_PYTHON_VERSION != '3.6' ]]; then make test; fi after_success:
- codecov

View File

@ -18,6 +18,7 @@ Other co-maintainers:
Other contributors, listed alphabetically, are: Other contributors, listed alphabetically, are:
* Alastair Houghton -- Apple Help builder * Alastair Houghton -- Apple Help builder
* Alexander Todorov -- inheritance_diagram tests and improvements
* Andi Albrecht -- agogo theme * Andi Albrecht -- agogo theme
* Jakob Lykke Andersen -- Rewritten C++ domain * Jakob Lykke Andersen -- Rewritten C++ domain
* Henrique Bastos -- SVG support for graphviz extension * Henrique Bastos -- SVG support for graphviz extension

108
CHANGES
View File

@ -1,6 +1,11 @@
Release 1.7 (in development) Release 1.7 (in development)
============================ ============================
Dependencies
------------
* Add ``packaging`` package
Incompatible changes Incompatible changes
-------------------- --------------------
@ -11,12 +16,31 @@ Incompatible changes
package when ``--implicit-namespaces`` option given, not subdirectories of package when ``--implicit-namespaces`` option given, not subdirectories of
given directory. given directory.
* #3929: apidoc: Move sphinx.apidoc to sphinx.ext.apidoc * #3929: apidoc: Move sphinx.apidoc to sphinx.ext.apidoc
* #4226: apidoc: Generate new style makefile (make-mode)
* #4274: sphinx-build returns 2 as an exit code on argument error
* #4389: output directory will be created after loading extensions
* autodoc does not generate warnings messages to the generated document even if
:confval:`keep_warnings` is True. They are only emitted to stderr.
* shebang line is removed from generated conf.py
* #2557: autodoc: :confval:`autodoc_mock_imports` only mocks specified modules
with their descendants. It does not mock their ancestors. If you want to
mock them, please specify the name of ancestors implicitly.
* #3620: html theme: move DOCUMENTATION_OPTIONS to independent JavaScript file
(refs: #4295)
Deprecated Deprecated
---------- ----------
* using a string value for :confval:`html_sidebars` is deprecated and only list * using a string value for :confval:`html_sidebars` is deprecated and only list
values will be accepted at 2.0. values will be accepted at 2.0.
* ``format_annotation()`` and ``formatargspec()`` is deprecated. Please use
``sphinx.util.inspect.Signature`` instead.
* ``sphinx.ext.autodoc.AutodocReporter`` is replaced by ``sphinx.util.docutils.
switch_source_input()`` and now deprecated. It will be removed in Sphinx-2.0.
* ``sphinx.ext.autodoc.add_documenter()`` and ``AutoDirective._register`` is now
deprecated. Please use ``app.add_autodocumenter()`` instead.
* ``AutoDirective._special_attrgetters`` is now deprecated. Please use
``app.add_autodoc_attrgetter()`` instead.
Features added Features added
-------------- --------------
@ -24,7 +48,8 @@ Features added
* C++, handle ``decltype(auto)``. * C++, handle ``decltype(auto)``.
* #2406: C++, add proper parsing of expressions, including linking of identifiers. * #2406: C++, add proper parsing of expressions, including linking of identifiers.
* C++, add a ``cpp:expr`` role for inserting inline C++ expressions or types. * C++, add a ``cpp:expr`` role for inserting inline C++ expressions or types.
* #4094: C++, allow empty template argument lists. * C++, support explicit member instantiations with shorthand ``template`` prefix.
* C++, make function parameters linkable, like template params.
* #3638: Allow to change a label of reference to equation using * #3638: Allow to change a label of reference to equation using
``math_eqref_format`` ``math_eqref_format``
@ -43,10 +68,29 @@ Features added
* #4168: improve zh search with jieba * #4168: improve zh search with jieba
* HTML themes can set up default sidebars through ``theme.conf`` * HTML themes can set up default sidebars through ``theme.conf``
* #3160: html: Use ``<kdb>`` to represent ``:kbd:`` role * #3160: html: Use ``<kdb>`` to represent ``:kbd:`` role
* #4212: autosummary: catch all exceptions when importing modules
* #4166: Add :confval:`math_numfig` for equation numbering by section (refs:
#3991, #4080). Thanks to Oliver Jahn.
* #4311: Let LaTeX obey :confval:`numfig_secnum_depth` for figures, tables, and
code-blocks
* #947: autodoc now supports ignore-module-all to ignore a module's ``__all__``
* #4332: Let LaTeX obey :confval:`math_numfig` for equation numbering
* #4093: sphinx-build creates empty directories for unknown targets/builders
* Add ``top-classes`` option for the ``sphinx.ext.inheritance_diagram``
extension to limit the scope of inheritance graphs.
* #4183: doctest: ``:pyversion:`` option also follows PEP-440 specification
* #4235: html: Add :confval:`manpages_url` to make manpage roles to hyperlinks
* #3570: autodoc: Do not display 'typing.' module for type hints
* #4354: sphinx-build now emits finish message. Builders can modify it through
``Builder.epilog`` attribute
* #4245: html themes: Add ``language`` to javascript vars list
* #4079: html: Add ``notranslate`` class to each code-blocks, literals and maths
to let Google Translate know they are not translatable
* #4137: doctest: doctest block is always highlighted as python console (pycon)
* #4137: doctest: testcode block is always highlighted as python
* #3998: text: Add new config values :confval:`text_add_secnumbers` and * #3998: text: Add new config values :confval:`text_add_secnumbers` and
:confval:`text_secnumber_suffix` :confval:`text_secnumber_suffix`
Features removed Features removed
---------------- ----------------
@ -75,19 +119,36 @@ Features removed
* ``sphinx.util.nodes.process_only_nodes()`` * ``sphinx.util.nodes.process_only_nodes()``
* LaTeX environment ``notice``, use ``sphinxadmonition`` instead * LaTeX environment ``notice``, use ``sphinxadmonition`` instead
* LaTeX ``\sphinxstylethead``, use ``\sphinxstyletheadfamily`` * LaTeX ``\sphinxstylethead``, use ``\sphinxstyletheadfamily``
* C++, support of function concepts. Thanks to mickk-on-cpp.
* Not used and previously not documented LaTeX macros ``\shortversion``
and ``\setshortversion``
Bugs fixed Bugs fixed
---------- ----------
* #3882: Update the order of files for HTMLHelp and QTHelp * #3882: Update the order of files for HTMLHelp and QTHelp
* #3962: sphinx-apidoc does not recognize implicit namespace packages correctly * #3962: sphinx-apidoc does not recognize implicit namespace packages correctly
* #4094: C++, allow empty template argument lists.
* C++, also hyperlink types in the name of declarations with qualified names.
* C++, do not add index entries for declarations inside concepts.
* C++, support the template disambiguator for dependent names.
* #4314: For PDF 'howto' documents, numbering of code-blocks differs from the
one of figures and tables
* #4330: PDF 'howto' documents have an incoherent default LaTeX tocdepth counter
setting
* #4198: autosummary emits multiple 'autodoc-process-docstring' event. Thanks
to Joel Nothman.
* #4081: Warnings and errors colored the same when building
* latex: Do not display 'Release' label if :confval:`release` is not set
Testing Testing
-------- --------
* Add support for docutils 0.14 * Add support for docutils 0.14
* Add tests for the ``sphinx.ext.inheritance_diagram`` extension.
Release 1.6.6 (in development) Release 1.6.7 (in development)
============================== ==============================
Dependencies Dependencies
@ -102,15 +163,50 @@ Deprecated
Features added Features added
-------------- --------------
Bugs fixed
----------
Testing
--------
Release 1.6.6 (released Jan 08, 2018)
=====================================
Features added
--------------
* #4181: autodoc: Sort dictionary keys when possible * #4181: autodoc: Sort dictionary keys when possible
* ``VerbatimHighlightColor`` is a new
:ref:`LaTeX 'sphinxsetup' <latexsphinxsetup>` key (refs: #4285)
* Easier customizability of LaTeX macros involved in rendering of code-blocks
* Show traceback if conf.py raises an exception (refs: #4369)
* Add :confval:`smartquotes` to disable smart quotes through ``conf.py``
(refs: #3967)
* Add :confval:`smartquotes_action` and :confval:`smartquotes_excludes`
(refs: #4142, #4357)
Bugs fixed Bugs fixed
---------- ----------
* #4334: sphinx-apidoc: Don't generate references to non-existing files in TOC
* #4206: latex: reST label between paragraphs loses paragraph break * #4206: latex: reST label between paragraphs loses paragraph break
* #4231: html: Apply fixFirefoxAnchorBug only under Firefox
Testing * #4221: napoleon depends on autodoc, but users need to load it manually
-------- * #2298: automodule fails to document a class attribute
* #4099: C++: properly link class reference to class from inside constructor
* #4267: PDF build broken by Unicode U+2116 NUMERO SIGN character
* #4249: PDF output: Pygments error highlighting increases line spacing in
code blocks
* #1238: Support ``:emphasize-lines:`` in PDF output
* #4279: Sphinx crashes with pickling error when run with multiple processes and
remote image
* #1421: Respect the quiet flag in sphinx-quickstart
* #4281: Race conditions when creating output directory
* #4315: For PDF 'howto' documents, ``latex_toplevel_sectioning='part'`` generates
``\chapter`` commands
* #4214: Two todolist directives break sphinx-1.6.5
* Fix links to external option docs with intersphinx (refs: #3769)
* #4091: Private members not documented without :undoc-members:
Release 1.6.5 (released Oct 23, 2017) Release 1.6.5 (released Oct 23, 2017)
===================================== =====================================

View File

@ -33,10 +33,10 @@ Bug Reports and Feature Requests
If you have encountered a problem with Sphinx or have an idea for a new If you have encountered a problem with Sphinx or have an idea for a new
feature, please submit it to the `issue tracker`_ on GitHub or discuss it feature, please submit it to the `issue tracker`_ on GitHub or discuss it
on the sphinx-dev mailing list. on the `sphinx-dev`_ mailing list.
For bug reports, please include the output produced during the build process For bug reports, please include the output produced during the build process
and also the log file Sphinx creates after it encounters an un-handled and also the log file Sphinx creates after it encounters an unhandled
exception. The location of this file should be shown towards the end of the exception. The location of this file should be shown towards the end of the
error message. error message.
@ -45,6 +45,7 @@ issue. If possible, try to create a minimal project that produces the error
and post that instead. and post that instead.
.. _`issue tracker`: https://github.com/sphinx-doc/sphinx/issues .. _`issue tracker`: https://github.com/sphinx-doc/sphinx/issues
.. _`sphinx-dev`: mailto:sphinx-dev@googlegroups.com
Contributing to Sphinx Contributing to Sphinx
@ -58,7 +59,7 @@ of the core developers before it is merged into the main repository.
#. Check for open issues or open a fresh issue to start a discussion around a #. Check for open issues or open a fresh issue to start a discussion around a
feature idea or a bug. feature idea or a bug.
#. If you feel uncomfortable or uncertain about an issue or your changes, feel #. If you feel uncomfortable or uncertain about an issue or your changes, feel
free to email sphinx-dev@googlegroups.com. free to email the *sphinx-dev* mailing list.
#. Fork `the repository`_ on GitHub to start making your changes to the #. Fork `the repository`_ on GitHub to start making your changes to the
**master** branch for next major version, or **stable** branch for next **master** branch for next major version, or **stable** branch for next
minor version. minor version.
@ -98,10 +99,14 @@ These are the basic steps needed to start developing on Sphinx.
For new features or other substantial changes that should wait until the For new features or other substantial changes that should wait until the
next major release, use the ``master`` branch. next major release, use the ``master`` branch.
#. Optional: setup a virtual environment. :: #. Setup a virtual environment.
virtualenv ~/sphinxenv This is not necessary for unit testing, thanks to ``tox``, but it is
. ~/sphinxenv/bin/activate necessary if you wish to run ``sphinx-build`` locally or run unit tests
without the help of ``tox``. ::
virtualenv ~/.venv
. ~/.venv/bin/activate
pip install -e . pip install -e .
#. Create a new working branch. Choose any name you like. :: #. Create a new working branch. Choose any name you like. ::
@ -112,44 +117,53 @@ These are the basic steps needed to start developing on Sphinx.
For tips on working with the code, see the `Coding Guide`_. For tips on working with the code, see the `Coding Guide`_.
#. Test, test, test. Possible steps: #. Test, test, test.
* Run the unit tests:: Testing is best done through ``tox``, which provides a number of targets and
allows testing against multiple different Python environments:
pip install .[test,websupport] * To list all possible targets::
make test
* Again, it's useful to turn on deprecation warnings on so they're shown in tox -av
the test output::
PYTHONWARNINGS=all make test * To run unit tests for a specific Python version, such as 3.6::
* Arguments to pytest can be passed via tox, e.g. in order to run a tox -e py36
* To run unit tests for a specific Python version and turn on deprecation
warnings on so they're shown in the test output::
PYTHONWARNINGS=all tox -e py36
* To run code style and type checks::
tox -e mypy
tox -e flake8
* Arguments to ``pytest`` can be passed via ``tox``, e.g. in order to run a
particular test:: particular test::
tox -e py27 tests/test_module.py::test_new_feature tox -e py36 tests/test_module.py::test_new_feature
* Build the documentation and check the output for different builders:: * To build the documentation::
make docs target="clean html latexpdf" tox -e docs
* Run code style checks and type checks (type checks require mypy):: * To build the documentation in multiple formats::
make style-check tox -e docs -- -b html,latexpdf
make type-check
* Run the unit tests under different Python environments using You can also test by installing dependencies in your local environment. ::
:program:`tox`::
pip install tox pip install .[test]
tox -v
* Add a new unit test in the ``tests`` directory if you can. New unit tests should be included in the ``tests`` directory where
necessary:
* For bug fixes, first add a test that fails without your changes and passes * For bug fixes, first add a test that fails without your changes and passes
after they are applied. after they are applied.
* Tests that need a sphinx-build run should be integrated in one of the * Tests that need a ``sphinx-build`` run should be integrated in one of the
existing test modules if possible. New tests that to ``@with_app`` and existing test modules if possible. New tests that to ``@with_app`` and
then ``build_all`` for a few assertions are not good since *the test suite then ``build_all`` for a few assertions are not good since *the test suite
should not take more than a minute to run*. should not take more than a minute to run*.
@ -266,7 +280,7 @@ Debugging Tips
code by running the command ``make clean`` or using the code by running the command ``make clean`` or using the
:option:`sphinx-build -E` option. :option:`sphinx-build -E` option.
* Use the :option:`sphinx-build -P` option to run Pdb on exceptions. * Use the :option:`sphinx-build -P` option to run ``pdb`` on exceptions.
* Use ``node.pformat()`` and ``node.asdom().toxml()`` to generate a printable * Use ``node.pformat()`` and ``node.asdom().toxml()`` to generate a printable
representation of the document structure. representation of the document structure.
@ -303,14 +317,17 @@ There are a couple reasons that code in Sphinx might be deprecated:
no longer needs to support the older version of Python that doesn't include no longer needs to support the older version of Python that doesn't include
the library, the library will be deprecated in Sphinx. the library, the library will be deprecated in Sphinx.
As the :ref:`deprecation-policy` describes, As the :ref:`deprecation-policy` describes, the first release of Sphinx that
the first release of Sphinx that deprecates a feature (``A.B``) should raise a deprecates a feature (``A.B``) should raise a ``RemovedInSphinxXXWarning``
``RemovedInSphinxXXWarning`` (where XX is the Sphinx version where the feature (where ``XX`` is the Sphinx version where the feature will be removed) when the
will be removed) when the deprecated feature is invoked. Assuming we have good deprecated feature is invoked. Assuming we have good test coverage, these
test coverage, these warnings are converted to errors when running the test warnings are converted to errors when running the test suite with warnings
suite with warnings enabled: ``python -Wall tests/run.py``. Thus, when adding enabled::
a ``RemovedInSphinxXXWarning`` you need to eliminate or silence any warnings
generated when running the tests. pytest -Wall
Thus, when adding a ``RemovedInSphinxXXWarning`` you need to eliminate or
silence any warnings generated when running the tests.
.. _deprecation-policy: .. _deprecation-policy:

View File

@ -34,6 +34,8 @@ Documentation using the alabaster theme
* pytest: https://docs.pytest.org/ (customized) * pytest: https://docs.pytest.org/ (customized)
* python-apt: https://apt.alioth.debian.org/python-apt-doc/ * python-apt: https://apt.alioth.debian.org/python-apt-doc/
* PyVisfile: https://documen.tician.de/pyvisfile/ * PyVisfile: https://documen.tician.de/pyvisfile/
* Requests: http://www.python-requests.org/
* searx: https://asciimoo.github.io/searx/
* Tablib: http://docs.python-tablib.org/ * Tablib: http://docs.python-tablib.org/
* urllib3: https://urllib3.readthedocs.io/ (customized) * urllib3: https://urllib3.readthedocs.io/ (customized)
* Werkzeug: http://werkzeug.pocoo.org/docs/ (customized) * Werkzeug: http://werkzeug.pocoo.org/docs/ (customized)
@ -46,6 +48,7 @@ Documentation using the classic theme
* APSW: https://rogerbinns.github.io/apsw/ * APSW: https://rogerbinns.github.io/apsw/
* Arb: http://arblib.org/ * Arb: http://arblib.org/
* Bazaar: http://doc.bazaar.canonical.com/ (customized) * Bazaar: http://doc.bazaar.canonical.com/ (customized)
* Beautiful Soup: https://www.crummy.com/software/BeautifulSoup/bs4/doc/
* Blender: https://docs.blender.org/api/current/ * Blender: https://docs.blender.org/api/current/
* Bugzilla: https://bugzilla.readthedocs.io/ * Bugzilla: https://bugzilla.readthedocs.io/
* Buildbot: https://docs.buildbot.net/latest/ * Buildbot: https://docs.buildbot.net/latest/
@ -79,6 +82,8 @@ Documentation using the classic theme
* Pyevolve: http://pyevolve.sourceforge.net/ * Pyevolve: http://pyevolve.sourceforge.net/
* Pygame: https://www.pygame.org/docs/ (customized) * Pygame: https://www.pygame.org/docs/ (customized)
* PyMQI: https://pythonhosted.org/pymqi/ * PyMQI: https://pythonhosted.org/pymqi/
* PyQt4: http://pyqt.sourceforge.net/Docs/PyQt4/ (customized)
* PyQt5: http://pyqt.sourceforge.net/Docs/PyQt5/ (customized)
* Python 2: https://docs.python.org/2/ * Python 2: https://docs.python.org/2/
* Python 3: https://docs.python.org/3/ (customized) * Python 3: https://docs.python.org/3/ (customized)
* Python Packaging Authority: https://www.pypa.io/ (customized) * Python Packaging Authority: https://www.pypa.io/ (customized)
@ -88,7 +93,7 @@ Documentation using the classic theme
* simuPOP: http://simupop.sourceforge.net/manual_release/build/userGuide.html (customized) * simuPOP: http://simupop.sourceforge.net/manual_release/build/userGuide.html (customized)
* Sprox: http://sprox.org/ (customized) * Sprox: http://sprox.org/ (customized)
* SymPy: http://docs.sympy.org/ * SymPy: http://docs.sympy.org/
* TurboGears: https://turbogears.readthedocs.org/ (customized) * TurboGears: https://turbogears.readthedocs.io/ (customized)
* tvtk: http://docs.enthought.com/mayavi/tvtk/ * tvtk: http://docs.enthought.com/mayavi/tvtk/
* Varnish: https://www.varnish-cache.org/docs/ (customized, alabaster for index) * Varnish: https://www.varnish-cache.org/docs/ (customized, alabaster for index)
* Waf: https://waf.io/apidocs/ * Waf: https://waf.io/apidocs/
@ -121,18 +126,25 @@ Documentation using the nature theme
* Alembic: http://alembic.zzzcomputing.com/ * Alembic: http://alembic.zzzcomputing.com/
* Cython: http://docs.cython.org/ * Cython: http://docs.cython.org/
* easybuild: https://easybuild.readthedocs.io/
* jsFiddle: http://doc.jsfiddle.net/ * jsFiddle: http://doc.jsfiddle.net/
* libLAS: https://www.liblas.org/ (customized) * libLAS: https://www.liblas.org/ (customized)
* Lmod: https://lmod.readthedocs.io/
* MapServer: http://mapserver.org/ (customized) * MapServer: http://mapserver.org/ (customized)
* Pandas: https://pandas.pydata.org/pandas-docs/stable/
* pyglet: https://pyglet.readthedocs.io/ (customized)
* Setuptools: https://setuptools.readthedocs.io/ * Setuptools: https://setuptools.readthedocs.io/
* Spring Python: https://docs.spring.io/spring-python/1.2.x/sphinx/html/ * Spring Python: https://docs.spring.io/spring-python/1.2.x/sphinx/html/
* StatsModels: http://www.statsmodels.org/ (customized)
* Sylli: http://sylli.sourceforge.net/ * Sylli: http://sylli.sourceforge.net/
Documentation using another builtin theme Documentation using another builtin theme
----------------------------------------- -----------------------------------------
* Arcade: http://arcade.academy/ (sphinx_rtd_theme)
* Breathe: https://breathe.readthedocs.io/ (haiku) * Breathe: https://breathe.readthedocs.io/ (haiku)
* MPipe: https://vmlaker.github.io/mpipe/ (sphinx13) * MPipe: https://vmlaker.github.io/mpipe/ (sphinx13)
* NLTK: http://www.nltk.org/ (agogo)
* Programmieren mit PyGTK und Glade (German): * Programmieren mit PyGTK und Glade (German):
http://www.florian-diesch.de/doc/python-und-glade/online/ (agogo, customized) http://www.florian-diesch.de/doc/python-und-glade/online/ (agogo, customized)
* PyPubSub: https://pypubsub.readthedocs.io/ (bizstyle) * PyPubSub: https://pypubsub.readthedocs.io/ (bizstyle)
@ -150,8 +162,10 @@ Documentation using sphinx_rtd_theme
* ASE: https://wiki.fysik.dtu.dk/ase/ * ASE: https://wiki.fysik.dtu.dk/ase/
* Autofac: http://docs.autofac.org/ * Autofac: http://docs.autofac.org/
* BigchainDB: https://docs.bigchaindb.com/ * BigchainDB: https://docs.bigchaindb.com/
* Blocks: https://blocks.readthedocs.io/
* bootstrap-datepicker: https://bootstrap-datepicker.readthedocs.io/ * bootstrap-datepicker: https://bootstrap-datepicker.readthedocs.io/
* Certbot: https://letsencrypt.readthedocs.io/ * Certbot: https://letsencrypt.readthedocs.io/
* Chainer: https://docs.chainer.org/ (customized)
* CherryPy: http://docs.cherrypy.org/ * CherryPy: http://docs.cherrypy.org/
* Chainer: https://docs.chainer.org/ * Chainer: https://docs.chainer.org/
* CodeIgniter: https://www.codeigniter.com/user_guide/ * CodeIgniter: https://www.codeigniter.com/user_guide/
@ -178,14 +192,18 @@ Documentation using sphinx_rtd_theme
* Idris: http://docs.idris-lang.org/ * Idris: http://docs.idris-lang.org/
* javasphinx: https://bronto-javasphinx.readthedocs.io/ * javasphinx: https://bronto-javasphinx.readthedocs.io/
* Julia: https://julia.readthedocs.io/ * Julia: https://julia.readthedocs.io/
* Jupyter Notebook: https://jupyter-notebook.readthedocs.io/
* Lasagne: https://lasagne.readthedocs.io/
* Linguistica: https://linguistica-uchicago.github.io/lxa5/ * Linguistica: https://linguistica-uchicago.github.io/lxa5/
* Linux kernel: https://www.kernel.org/doc/html/latest/index.html * Linux kernel: https://www.kernel.org/doc/html/latest/index.html
* MathJax: https://docs.mathjax.org/ * MathJax: https://docs.mathjax.org/
* MDTraj: http://mdtraj.org/latest/ (customized) * MDTraj: http://mdtraj.org/latest/ (customized)
* MICrobial Community Analysis (micca): http://micca.org/docs/latest/ * MICrobial Community Analysis (micca): http://micca.org/docs/latest/
* MicroPython: https://docs.micropython.org/ * MicroPython: https://docs.micropython.org/
* Minds: https://www.minds.org/docs/ (customized)
* Mink: http://mink.behat.org/ * Mink: http://mink.behat.org/
* Mockery: http://docs.mockery.io/ * Mockery: http://docs.mockery.io/
* mod_wsgi: https://modwsgi.readthedocs.io/
* MoinMoin: https://moin-20.readthedocs.io/ * MoinMoin: https://moin-20.readthedocs.io/
* Mopidy: https://docs.mopidy.com/ * Mopidy: https://docs.mopidy.com/
* MyHDL: http://docs.myhdl.org/ * MyHDL: http://docs.myhdl.org/
@ -224,13 +242,16 @@ Documentation using sphinx_rtd_theme
* Sylius: http://docs.sylius.org/ * Sylius: http://docs.sylius.org/
* Tango Controls: https://tango-controls.readthedocs.io/ (customized) * Tango Controls: https://tango-controls.readthedocs.io/ (customized)
* Topshelf: http://docs.topshelf-project.com/ * Topshelf: http://docs.topshelf-project.com/
* Theano: http://www.deeplearning.net/software/theano/
* ThreatConnect: https://docs.threatconnect.com/ * ThreatConnect: https://docs.threatconnect.com/
* Tuleap: https://tuleap.net/doc/en/ * Tuleap: https://tuleap.net/doc/en/
* TYPO3: https://docs.typo3.org/ (customized) * TYPO3: https://docs.typo3.org/ (customized)
* uWSGI: https://uwsgi-docs.readthedocs.io/
* Wagtail: http://docs.wagtail.io/ * Wagtail: http://docs.wagtail.io/
* Web Application Attack and Audit Framework (w3af): http://docs.w3af.org/ * Web Application Attack and Audit Framework (w3af): http://docs.w3af.org/
* Weblate: https://docs.weblate.org/ * Weblate: https://docs.weblate.org/
* x265: https://x265.readthedocs.io/ * x265: https://x265.readthedocs.io/
* ZeroNet: https://zeronet.readthedocs.io/
Documentation using sphinx_bootstrap_theme Documentation using sphinx_bootstrap_theme
------------------------------------------ ------------------------------------------
@ -238,19 +259,21 @@ Documentation using sphinx_bootstrap_theme
* Bootstrap Theme: https://ryan-roemer.github.io/sphinx-bootstrap-theme/ * Bootstrap Theme: https://ryan-roemer.github.io/sphinx-bootstrap-theme/
* C/C++ Software Development with Eclipse: http://eclipsebook.in/ * C/C++ Software Development with Eclipse: http://eclipsebook.in/
* Dataverse: http://guides.dataverse.org/ * Dataverse: http://guides.dataverse.org/
* e-cidadania: http://e-cidadania.readthedocs.org/ * e-cidadania: https://e-cidadania.readthedocs.io/
* Hangfire: http://docs.hangfire.io/ * Hangfire: http://docs.hangfire.io/
* Hedge: https://documen.tician.de/hedge/ * Hedge: https://documen.tician.de/hedge/
* ObsPy: https://docs.obspy.org/ * ObsPy: https://docs.obspy.org/
* Open Dylan: https://opendylan.org/documentation/ * Open Dylan: https://opendylan.org/documentation/
* Pootle: http://docs.translatehouse.org/projects/pootle/ * Pootle: http://docs.translatehouse.org/projects/pootle/
* PyUblas: https://documen.tician.de/pyublas/ * PyUblas: https://documen.tician.de/pyublas/
* seaborn: https://seaborn.pydata.org/
Documentation using a custom theme or integrated in a website Documentation using a custom theme or integrated in a website
------------------------------------------------------------- -------------------------------------------------------------
* Apache Cassandra: https://cassandra.apache.org/doc/ * Apache Cassandra: https://cassandra.apache.org/doc/
* Astropy: http://docs.astropy.org/ * Astropy: http://docs.astropy.org/
* Bokeh: https://bokeh.pydata.org/
* Boto 3: https://boto3.readthedocs.io/ * Boto 3: https://boto3.readthedocs.io/
* CakePHP: https://book.cakephp.org/ * CakePHP: https://book.cakephp.org/
* CasperJS: http://docs.casperjs.org/ * CasperJS: http://docs.casperjs.org/
@ -263,6 +286,7 @@ Documentation using a custom theme or integrated in a website
* Enterprise Toolkit for Acrobat products: * Enterprise Toolkit for Acrobat products:
https://www.adobe.com/devnet-docs/acrobatetk/ https://www.adobe.com/devnet-docs/acrobatetk/
* Gameduino: http://excamera.com/sphinx/gameduino/ * Gameduino: http://excamera.com/sphinx/gameduino/
* gensim: https://radimrehurek.com/gensim/
* GeoServer: http://docs.geoserver.org/ * GeoServer: http://docs.geoserver.org/
* gevent: http://www.gevent.org/ * gevent: http://www.gevent.org/
* GHC - Glasgow Haskell Compiler: http://downloads.haskell.org/~ghc/master/users-guide/ * GHC - Glasgow Haskell Compiler: http://downloads.haskell.org/~ghc/master/users-guide/
@ -307,6 +331,7 @@ Documentation using a custom theme or integrated in a website
* Sulu: http://docs.sulu.io/ * Sulu: http://docs.sulu.io/
* SQLAlchemy: https://docs.sqlalchemy.org/ * SQLAlchemy: https://docs.sqlalchemy.org/
* tinyTiM: http://tinytim.sourceforge.net/docs/2.0/ * tinyTiM: http://tinytim.sourceforge.net/docs/2.0/
* Twisted: http://twistedmatrix.com/documents/current/
* Ubuntu Packaging Guide: http://packaging.ubuntu.com/html/ * Ubuntu Packaging Guide: http://packaging.ubuntu.com/html/
* WebFaction: https://docs.webfaction.com/ * WebFaction: https://docs.webfaction.com/
* WTForms: https://wtforms.readthedocs.io/ * WTForms: https://wtforms.readthedocs.io/
@ -320,8 +345,10 @@ Homepages and other non-documentation sites
* Benoit Boissinot: https://bboissin.appspot.com/ (classic, customized) * Benoit Boissinot: https://bboissin.appspot.com/ (classic, customized)
* Computer Networks, Parallelization, and Simulation Laboratory (CNPSLab): * Computer Networks, Parallelization, and Simulation Laboratory (CNPSLab):
https://lab.miletic.net/ (sphinx_rtd_theme) https://lab.miletic.net/ (sphinx_rtd_theme)
* Deep Learning Tutorials: http://www.deeplearning.net/tutorial/ (sphinxdoc)
* Loyola University Chicago COMP 339-439 Distributed Systems course: * Loyola University Chicago COMP 339-439 Distributed Systems course:
http://books.cs.luc.edu/distributedsystems/ (sphinx_bootstrap_theme) http://books.cs.luc.edu/distributedsystems/ (sphinx_bootstrap_theme)
* Pylearn2: http://www.deeplearning.net/software/pylearn2/ (sphinxdoc, customized)
* SciPy Cookbook: https://scipy-cookbook.readthedocs.io/ (sphinx_rtd_theme) * SciPy Cookbook: https://scipy-cookbook.readthedocs.io/ (sphinx_rtd_theme)
* The Wine Cellar Book: https://www.thewinecellarbook.com/doc/en/ (sphinxdoc) * The Wine Cellar Book: https://www.thewinecellarbook.com/doc/en/ (sphinxdoc)
* Thomas Cokelaer's Python, Sphinx and reStructuredText tutorials: * Thomas Cokelaer's Python, Sphinx and reStructuredText tutorials:

View File

@ -1,7 +1,7 @@
License for Sphinx License for Sphinx
================== ==================
Copyright (c) 2007-2017 by the Sphinx team (see AUTHORS file). Copyright (c) 2007-2018 by the Sphinx team (see AUTHORS file).
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without

View File

@ -69,15 +69,15 @@ reindent:
.PHONY: test .PHONY: test
test: test:
@cd tests; $(PYTHON) run.py --ignore py35 -v $(TEST) @$(PYTHON) -m pytest -v $(TEST)
.PHONY: test-async .PHONY: test-async
test-async: test-async:
@cd tests; $(PYTHON) run.py -v $(TEST) @echo "This target no longer does anything and will be removed imminently"
.PHONY: covertest .PHONY: covertest
covertest: covertest:
@cd tests; $(PYTHON) run.py -v --cov=sphinx --junitxml=.junit.xml $(TEST) @$(PYTHON) -m pytest -v --cov=sphinx --junitxml=.junit.xml $(TEST)
.PHONY: build .PHONY: build
build: build:

View File

@ -1,45 +1,106 @@
========
Sphinx
========
.. image:: https://img.shields.io/pypi/v/sphinx.svg .. image:: https://img.shields.io/pypi/v/sphinx.svg
:target: https://pypi.python.org/pypi/Sphinx :target: https://pypi.python.org/pypi/Sphinx
:alt: Package on PyPi
.. image:: https://readthedocs.org/projects/sphinx/badge/ .. image:: https://readthedocs.org/projects/sphinx/badge/
:target: http://www.sphinx-doc.org/ :target: http://www.sphinx-doc.org/
:alt: Documentation Status :alt: Documentation Status
.. image:: https://travis-ci.org/sphinx-doc/sphinx.svg?branch=master .. image:: https://travis-ci.org/sphinx-doc/sphinx.svg?branch=master
:target: https://travis-ci.org/sphinx-doc/sphinx :target: https://travis-ci.org/sphinx-doc/sphinx
:alt: Build Status (Travis CI)
================= .. image:: https://ci.appveyor.com/api/projects/status/github/sphinx-doc/sphinx?branch=master&svg=true
README for Sphinx :target: https://ci.appveyor.com/project/sphinxdoc/sphinx
================= :alt: Build Status (AppVeyor)
This is the Sphinx documentation generator, see http://www.sphinx-doc.org/. .. image:: https://circleci.com/gh/sphinx-doc/sphinx.svg?style=shield
:target: https://circleci.com/gh/sphinx-doc/sphinx
:alt: Build Status (CircleCI)
.. image:: https://codecov.io/gh/sphinx-doc/sphinx/branch/master/graph/badge.svg
:target: https://codecov.io/gh/sphinx-doc/sphinx
:alt: Code Coverage Status (Codecov)
Installing Sphinx is a tool that makes it easy to create intelligent and beautiful
========== documentation for Python projects (or other documents consisting of multiple
reStructuredText sources), written by Georg Brandl. It was originally created
for the new Python documentation, and has excellent facilities for Python
project documentation, but C/C++ is supported as well, and more languages are
planned.
Install from PyPI to use stable version:: Sphinx uses reStructuredText as its markup language, and many of its strengths
come from the power and straightforwardness of reStructuredText and its parsing
and translating suite, the Docutils.
Among its features are the following:
* Output formats: HTML (including derivative formats such as HTML Help, Epub
and Qt Help), plain text, manual pages and LaTeX or direct PDF output
using rst2pdf
* Extensive cross-references: semantic markup and automatic links
for functions, classes, glossary terms and similar pieces of information
* Hierarchical structure: easy definition of a document tree, with automatic
links to siblings, parents and children
* Automatic indices: general index as well as a module index
* Code handling: automatic highlighting using the Pygments highlighter
* Flexible HTML output using the Jinja 2 templating engine
* Various extensions are available, e.g. for automatic testing of snippets
and inclusion of appropriately formatted docstrings
* Setuptools integration
For more information, refer to the `the documentation`__.
.. __: http://www.sphinx-doc.org/
Installation
============
Sphinx is published on `PyPI`__ and can be installed from there::
pip install -U sphinx pip install -U sphinx
Install from PyPI to use beta version:: We also publish beta releases::
pip install -U --pre sphinx pip install -U --pre sphinx
Install from newest dev version in stable branch:: If you wish to install `Sphinx` for development purposes, refer to `the
contributors guide`__.
pip install git+https://github.com/sphinx-doc/sphinx@stable __ https://pypi.python.org/pypi/Sphinx
__ CONTRIBUTING.rst
Install from newest dev version in master branch:: Documentation
=============
pip install git+https://github.com/sphinx-doc/sphinx Documentation is available from `sphinx-doc.org`__.
Install from cloned source:: __ http://www.sphinx-doc.org/
pip install . Testing
=======
Install from cloned source as editable:: Continuous testing is provided by `Travis`__ (for unit tests and style checks
on Linux), `AppVeyor`__ (for unit tests on Windows), and `CircleCI`__ (for
large processes like TeX compilation).
pip install -e . For information on running tests locally, refer to `the contributors guide`__.
__ https://travis-ci.org/sphinx-doc/sphinx
__ https://ci.appveyor.com/project/sphinxdoc/sphinx
__ https://circleci.com/gh/sphinx-doc/sphinx
__ CONTRIBUTING.rst
Contributing
============
Refer to `the contributors guide`__.
__ CONTRIBUTING.rst
Release signatures Release signatures
================== ==================
@ -48,37 +109,3 @@ Releases are signed with following keys:
* `498D6B9E <https://pgp.mit.edu/pks/lookup?op=vindex&search=0x102C2C17498D6B9E>`_ * `498D6B9E <https://pgp.mit.edu/pks/lookup?op=vindex&search=0x102C2C17498D6B9E>`_
* `5EBA0E07 <https://pgp.mit.edu/pks/lookup?op=vindex&search=0x1425F8CE5EBA0E07>`_ * `5EBA0E07 <https://pgp.mit.edu/pks/lookup?op=vindex&search=0x1425F8CE5EBA0E07>`_
Reading the docs
================
You can read them online at <http://www.sphinx-doc.org/>.
Or, after installing::
cd doc
make html
Then, direct your browser to ``_build/html/index.html``.
Testing
=======
To run the tests with the interpreter available as ``python``, use::
make test
If you want to use a different interpreter, e.g. ``python3``, use::
PYTHON=python3 make test
Continuous testing runs on travis: https://travis-ci.org/sphinx-doc/sphinx
Contributing
============
See `CONTRIBUTING.rst`__
.. __: CONTRIBUTING.rst

View File

@ -74,9 +74,9 @@
<p>{%trans%} <p>{%trans%}
You can also download PDF/EPUB versions of the Sphinx documentation: You can also download PDF/EPUB versions of the Sphinx documentation:
a <a href="http://readthedocs.org/projects/sphinx/downloads/pdf/stable/">PDF version</a> generated from a <a href="https://media.readthedocs.org/pdf/sphinx/stable/sphinx.pdf">PDF version</a> generated from
the LaTeX Sphinx produces, and the LaTeX Sphinx produces, and
a <a href="http://readthedocs.org/projects/sphinx/downloads/epub/stable/">EPUB version</a>. a <a href="https://media.readthedocs.org/epub/sphinx/stable/sphinx.epub">EPUB version</a>.
{%endtrans%} {%endtrans%}
</p> </p>
@ -106,7 +106,7 @@
<h2>{%trans%}Hosting{%endtrans%}</h2> <h2>{%trans%}Hosting{%endtrans%}</h2>
<p>{%trans%}Need a place to host your Sphinx docs? <p>{%trans%}Need a place to host your Sphinx docs?
<a href="http://readthedocs.org">readthedocs.org</a> hosts a lot of Sphinx docs <a href="https://readthedocs.org/">readthedocs.org</a> hosts a lot of Sphinx docs
already, and integrates well with projects' source control. It also features a already, and integrates well with projects' source control. It also features a
powerful built-in search that exceeds the possibilities of Sphinx' JavaScript-based powerful built-in search that exceeds the possibilities of Sphinx' JavaScript-based
offline search.{%endtrans%}</p> offline search.{%endtrans%}</p>

View File

@ -20,12 +20,14 @@ Index</a>, or install it with:{%endtrans%}</p>
<h3>{%trans%}Questions? Suggestions?{%endtrans%}</h3> <h3>{%trans%}Questions? Suggestions?{%endtrans%}</h3>
<p>{%trans%}Join the <a href="http://groups.google.com/group/sphinx-users">sphinx-users</a> mailing list on Google Groups:{%endtrans%}</p> <p>{%trans%}Join the <a href="http://groups.google.com/group/sphinx-users">sphinx-users</a> mailing list on Google Groups:{%endtrans%}</p>
<div class="subscribeformwrapper">
<form action="http://groups.google.com/group/sphinx-users/boxsubscribe" <form action="http://groups.google.com/group/sphinx-users/boxsubscribe"
style="padding-left: 0.5em"> class="subscribeform">
<input type="text" name="email" value="your@email" style="font-size: 90%; width: 120px" <input type="text" name="email" value="your@email"
onfocus="$(this).val('');"/> onfocus="$(this).val('');" />
<input type="submit" name="sub" value="Subscribe" style="font-size: 90%; width: 70px"/> <input type="submit" name="sub" value="Subscribe" />
</form> </form>
</div>
<p>{%trans%}or come to the <tt>#sphinx-doc</tt> channel on FreeNode.{%endtrans%}</p> <p>{%trans%}or come to the <tt>#sphinx-doc</tt> channel on FreeNode.{%endtrans%}</p>
<p>{%trans%}You can also open an issue at the <p>{%trans%}You can also open an issue at the
<a href="https://github.com/sphinx-doc/sphinx/issues">tracker</a>.{%endtrans%}</p> <a href="https://github.com/sphinx-doc/sphinx/issues">tracker</a>.{%endtrans%}</p>

View File

@ -4,7 +4,7 @@
Sphinx layout template for the sphinxdoc theme. Sphinx layout template for the sphinxdoc theme.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
#} #}
{%- extends "basic/layout.html" %} {%- extends "basic/layout.html" %}

View File

@ -4,7 +4,7 @@
* *
* Sphinx stylesheet -- sphinx13 theme. * Sphinx stylesheet -- sphinx13 theme.
* *
* :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. * :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details. * :license: BSD, see LICENSE for details.
* *
*/ */
@ -140,11 +140,37 @@ div.sphinxsidebar .logo img {
vertical-align: middle; vertical-align: middle;
} }
div.subscribeformwrapper {
display: block;
overflow: auto;
margin-bottom: 1.2em;
}
div.sphinxsidebar input { div.sphinxsidebar input {
border: 1px solid #aaa; border: 1px solid #aaa;
font-family: 'Open Sans', 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva', font-family: 'Open Sans', 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva',
'Verdana', sans-serif; 'Verdana', sans-serif;
font-size: 1em; }
div.sphinxsidebar .subscribeform {
margin-top: 0;
}
div.sphinxsidebar .subscribeform input {
border: 1px solid #aaa;
font-size: 0.9em;
float: left;
padding: 0.25em 0.5em;
box-sizing: border-box;
}
div.sphinxsidebar .subscribeform input[type="text"] {
width: 60%;
}
div.sphinxsidebar .subscribeform input[type="submit"] {
width: 40%;
border-left: none;
} }
div.sphinxsidebar h3 { div.sphinxsidebar h3 {
@ -281,7 +307,7 @@ tt {
border: 1px solid #ddd; border: 1px solid #ddd;
border-radius: 2px; border-radius: 2px;
color: #333; color: #333;
padding: 1px; padding: 1px 0.2em;
} }
tt.descname, tt.descclassname, tt.xref { tt.descname, tt.descclassname, tt.xref {

View File

@ -15,7 +15,7 @@ templates_path = ['_templates']
exclude_patterns = ['_build'] exclude_patterns = ['_build']
project = 'Sphinx' project = 'Sphinx'
copyright = '2007-2017, Georg Brandl and the Sphinx team' copyright = '2007-2018, Georg Brandl and the Sphinx team'
version = sphinx.__display_version__ version = sphinx.__display_version__
release = version release = version
show_authors = True show_authors = True

View File

@ -138,12 +138,10 @@ General configuration
- ``'library/xml.rst'`` -- ignores the ``library/xml.rst`` file (replaces - ``'library/xml.rst'`` -- ignores the ``library/xml.rst`` file (replaces
entry in :confval:`unused_docs`) entry in :confval:`unused_docs`)
- ``'library/xml'`` -- ignores the ``library/xml`` directory (replaces entry - ``'library/xml'`` -- ignores the ``library/xml`` directory
in :confval:`exclude_trees`)
- ``'library/xml*'`` -- ignores all files and directories starting with - ``'library/xml*'`` -- ignores all files and directories starting with
``library/xml`` ``library/xml``
- ``'**/.svn'`` -- ignores all ``.svn`` directories (replaces entry in - ``'**/.svn'`` -- ignores all ``.svn`` directories
:confval:`exclude_dirnames`)
:confval:`exclude_patterns` is also consulted when looking for static files :confval:`exclude_patterns` is also consulted when looking for static files
in :confval:`html_static_path` and :confval:`html_extra_path`. in :confval:`html_static_path` and :confval:`html_extra_path`.
@ -295,6 +293,24 @@ General configuration
.. versionadded:: 1.3 .. versionadded:: 1.3
.. confval:: manpages_url
A URL to cross-reference :rst:role:`manpage` directives. If this is
defined to ``https://manpages.debian.org/{path}``, the
:literal:`:manpage:`man(1)`` role will like to
<https://manpages.debian.org/man(1)>. The patterns available are:
* ``page`` - the manual page (``man``)
* ``section`` - the manual section (``1``)
* ``path`` - the original manual page and section specified (``man(1)``)
This also supports manpages specified as ``man.1``.
.. note:: This currently affects only HTML writers but could be
expanded in the future.
.. versionadded:: 1.7
.. confval:: nitpicky .. confval:: nitpicky
If true, Sphinx will warn about *all* references where the target cannot be If true, Sphinx will warn about *all* references where the target cannot be
@ -315,8 +331,8 @@ General configuration
.. confval:: numfig .. confval:: numfig
If true, figures, tables and code-blocks are automatically numbered if they If true, figures, tables and code-blocks are automatically numbered if they
have a caption. At same time, the `numref` role is enabled. For now, it have a caption. The :rst:role:`numref` role is enabled.
works only with the HTML builder and LaTeX builder. Default is ``False``. Obeyed so far only by HTML and LaTeX builders. Default is ``False``.
.. note:: .. note::
@ -339,13 +355,80 @@ General configuration
.. confval:: numfig_secnum_depth .. confval:: numfig_secnum_depth
The scope of figure numbers, that is, the numfig feature numbers figures - if set to ``0``, figures, tables and code-blocks are continuously numbered
in which scope. ``0`` means "whole document". ``1`` means "in a section". starting at ``1``.
Sphinx numbers like x.1, x.2, x.3... ``2`` means "in a subsection". Sphinx - if ``1`` (default) numbers will be ``x.1``, ``x.2``, ... with ``x``
numbers like x.x.1, x.x.2, x.x.3..., and so on. Default is ``1``. the section number (top level sectioning; no ``x.`` if no section).
This naturally applies only if section numbering has been activated via
the ``:numbered:`` option of the :rst:dir:`toctree` directive.
- ``2`` means that numbers will be ``x.y.1``, ``x.y.2``, ... if located in
a sub-section (but still ``x.1``, ``x.2``, ... if located directly under a
section and ``1``, ``2``, ... if not in any top level section.)
- etc...
.. versionadded:: 1.3 .. versionadded:: 1.3
.. versionchanged:: 1.7
The LaTeX builder obeys this setting (if :confval:`numfig` is set to
``True``).
.. confval:: smartquotes
If true, the `Docutils Smart Quotes transform`__, originally based on
`SmartyPants`__ (limited to English) and currently applying to many
languages, will be used to convert quotes and dashes to typographically
correct entities. Default: ``True``.
__ http://docutils.sourceforge.net/docs/user/smartquotes.html
__ https://daringfireball.net/projects/smartypants/
.. versionadded:: 1.6.6
It replaces deprecated :confval:`html_use_smartypants`.
It applies by default to all builders except ``man`` and ``text``
(see :confval:`smartquotes_excludes`.)
A `docutils.conf`__ file located in the configuration directory (or a
global :file:`~/.docutils` file) is obeyed unconditionally if it
*deactivates* smart quotes via the corresponding `Docutils option`__. But
if it *activates* them, then :confval:`smartquotes` does prevail.
__ http://docutils.sourceforge.net/docs/user/config.html
__ http://docutils.sourceforge.net/docs/user/config.html#smart-quotes
.. confval:: smartquotes_action
This string, for use with Docutils ``0.14`` or later, customizes the Smart
Quotes transform. See the file :file:`smartquotes.py` at the `Docutils
repository`__ for details. The default ``'qDe'`` educates normal **q**\
uote characters ``"``, ``'``, em- and en-**D**\ ashes ``---``, ``--``, and
**e**\ llipses ``...``.
.. versionadded:: 1.6.6
__ https://sourceforge.net/p/docutils/code/HEAD/tree/trunk/docutils/
.. confval:: smartquotes_excludes
This is a ``dict`` whose default is::
{'languages': ['ja'], 'builders': ['man', 'text']}
Each entry gives a sufficient condition to ignore the
:confval:`smartquotes` setting and deactivate the Smart Quotes transform.
Accepted keys are as above ``'builders'`` or ``'languages'``.
The values are lists.
.. note:: Currently, in case of invocation of :program:`make` with multiple
targets, the first target name is the only one which is tested against
the ``'builders'`` entry and it decides for all. Also, a ``make text``
following ``make html`` needs to be issued in the form ``make text
O="-E"`` to force re-parsing of source files, as the cached ones are
already transformed. On the other hand the issue does not arise with
direct usage of :program:`sphinx-build` as it caches
(in its default usage) the parsed source files in per builder locations.
.. versionadded:: 1.6.6
.. confval:: tls_verify .. confval:: tls_verify
If true, Sphinx verifies server certifications. Default is ``True``. If true, Sphinx verifies server certifications. Default is ``True``.
@ -777,15 +860,11 @@ that use Sphinx's HTMLWriter class.
.. confval:: html_use_smartypants .. confval:: html_use_smartypants
If true, `SmartyPants <https://daringfireball.net/projects/smartypants/>`_ If true, quotes and dashes are converted to typographically correct
will be used to convert quotes and dashes to typographically correct
entities. Default: ``True``. entities. Default: ``True``.
.. deprecated:: 1.6 .. deprecated:: 1.6
To disable or customize smart quotes, use the Docutils configuration file To disable smart quotes, use rather :confval:`smartquotes`.
(``docutils.conf``) instead to set there its `smart_quotes option`_.
.. _`smart_quotes option`: http://docutils.sourceforge.net/docs/user/config.html#smart-quotes
.. confval:: html_add_permalinks .. confval:: html_add_permalinks
@ -1450,10 +1529,6 @@ the `Dublin Core metadata <http://dublincore.org/>`_.
a chapter, but can be confusing because it mixes entries of different a chapter, but can be confusing because it mixes entries of different
depth in one list. The default value is ``True``. depth in one list. The default value is ``True``.
.. note::
``epub3`` builder ignores ``epub_tocdup`` option(always ``False``)
.. confval:: epub_tocscope .. confval:: epub_tocscope
This setting control the scope of the epub table of contents. The setting This setting control the scope of the epub table of contents. The setting
@ -1615,10 +1690,15 @@ These options influence LaTeX output. See further :doc:`latex`.
.. confval:: latex_toplevel_sectioning .. confval:: latex_toplevel_sectioning
This value determines the topmost sectioning unit. It should be chosen from This value determines the topmost sectioning unit. It should be chosen from
``part``, ``chapter`` or ``section``. The default is ``None``; the topmost ``'part'``, ``'chapter'`` or ``'section'``. The default is ``None``;
sectioning unit is switched by documentclass. ``section`` is used if the topmost
sectioning unit is switched by documentclass: ``section`` is used if
documentclass will be ``howto``, otherwise ``chapter`` will be used. documentclass will be ``howto``, otherwise ``chapter`` will be used.
Note that if LaTeX uses ``\part`` command, then the numbering of sectioning
units one level deep gets off-sync with HTML numbering, because LaTeX
numbers continuously ``\chapter`` (or ``\section`` for ``howto``.)
.. versionadded:: 1.4 .. versionadded:: 1.4
.. confval:: latex_appendices .. confval:: latex_appendices

View File

@ -138,7 +138,7 @@ own extensions.
.. _cmakedomain: https://bitbucket.org/klorenz/sphinxcontrib-cmakedomain .. _cmakedomain: https://bitbucket.org/klorenz/sphinxcontrib-cmakedomain
.. _GNU Make: http://www.gnu.org/software/make/ .. _GNU Make: http://www.gnu.org/software/make/
.. _makedomain: https://bitbucket.org/klorenz/sphinxcontrib-makedomain .. _makedomain: https://bitbucket.org/klorenz/sphinxcontrib-makedomain
.. _inlinesyntaxhighlight: http://sphinxcontrib-inlinesyntaxhighlight.readthedocs.org .. _inlinesyntaxhighlight: https://sphinxcontrib-inlinesyntaxhighlight.readthedocs.io/
.. _CMake: https://cmake.org .. _CMake: https://cmake.org
.. _domaintools: https://bitbucket.org/klorenz/sphinxcontrib-domaintools .. _domaintools: https://bitbucket.org/klorenz/sphinxcontrib-domaintools
.. _restbuilder: https://pypi.python.org/pypi/sphinxcontrib-restbuilder .. _restbuilder: https://pypi.python.org/pypi/sphinxcontrib-restbuilder

View File

@ -720,13 +720,13 @@ a visibility statement (``public``, ``private`` or ``protected``).
.. rst:directive:: .. cpp:concept:: template-parameter-list name .. rst:directive:: .. cpp:concept:: template-parameter-list name
.. cpp:concept:: template-parameter-list name()
.. warning:: The support for concepts is experimental. It is based on the .. warning:: The support for concepts is experimental. It is based on the
Concepts Technical Specification, and the features may change as the TS evolves. current draft standard and the Concepts Technical Specification.
The features may change as they evolve.
Describe a variable concept or a function concept. Both must have exactly 1 Describe a concept. It must have exactly 1 template parameter list. The name may be a
template parameter list. The name may be a nested name. Examples:: nested name. Example::
.. cpp:concept:: template<typename It> std::Iterator .. cpp:concept:: template<typename It> std::Iterator
@ -744,12 +744,7 @@ a visibility statement (``public``, ``private`` or ``protected``).
- :cpp:expr:`*r`, when :cpp:expr:`r` is dereferenceable. - :cpp:expr:`*r`, when :cpp:expr:`r` is dereferenceable.
- :cpp:expr:`++r`, with return type :cpp:expr:`It&`, when :cpp:expr:`r` is incrementable. - :cpp:expr:`++r`, with return type :cpp:expr:`It&`, when :cpp:expr:`r` is incrementable.
.. cpp:concept:: template<typename Cont> std::Container() This will render as follows:
Holder of elements, to which it can provide access via
:cpp:concept:`Iterator` s.
They will render as follows:
.. cpp:concept:: template<typename It> std::Iterator .. cpp:concept:: template<typename It> std::Iterator
@ -767,11 +762,6 @@ a visibility statement (``public``, ``private`` or ``protected``).
- :cpp:expr:`*r`, when :cpp:expr:`r` is dereferenceable. - :cpp:expr:`*r`, when :cpp:expr:`r` is dereferenceable.
- :cpp:expr:`++r`, with return type :cpp:expr:`It&`, when :cpp:expr:`r` is incrementable. - :cpp:expr:`++r`, with return type :cpp:expr:`It&`, when :cpp:expr:`r` is incrementable.
.. cpp:concept:: template<typename Cont> std::Container()
Holder of elements, to which it can provide access via
:cpp:concept:`Iterator` s.
Options Options
....... .......
@ -785,8 +775,9 @@ Some directives support options:
Constrained Templates Constrained Templates
~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~
.. warning:: The support for constrained templates is experimental. It is based on the .. warning:: The support for concepts is experimental. It is based on the
Concepts Technical Specification, and the features may change as the TS evolves. current draft standard and the Concepts Technical Specification.
The features may change as they evolve.
.. note:: Sphinx does not currently support ``requires`` clauses. .. note:: Sphinx does not currently support ``requires`` clauses.

View File

@ -103,8 +103,10 @@ inserting them into the page source under a suitable :rst:dir:`py:module`,
will document all non-private member functions and properties (that is, will document all non-private member functions and properties (that is,
those whose name doesn't start with ``_``). those whose name doesn't start with ``_``).
For modules, ``__all__`` will be respected when looking for members; the For modules, ``__all__`` will be respected when looking for members unless
order of the members will also be the order in ``__all__``. you give the ``ignore-module-all`` flag option. Without
``ignore-module-all``, the order of the members will also be the order in
``__all__``.
You can also give an explicit list of members; only these will then be You can also give an explicit list of members; only these will then be
documented:: documented::
@ -339,7 +341,7 @@ There are also new config values that you can set:
This value is a list of autodoc directive flags that should be automatically This value is a list of autodoc directive flags that should be automatically
applied to all autodoc directives. The supported flags are ``'members'``, applied to all autodoc directives. The supported flags are ``'members'``,
``'undoc-members'``, ``'private-members'``, ``'special-members'``, ``'undoc-members'``, ``'private-members'``, ``'special-members'``,
``'inherited-members'`` and ``'show-inheritance'``. ``'inherited-members'``, ``'show-inheritance'`` and ``'ignore-module-all'``.
If you set one of these flags in this config value, you can use a negated If you set one of these flags in this config value, you can use a negated
form, :samp:`'no-{flag}'`, in an autodoc directive, to disable it once. form, :samp:`'no-{flag}'`, in an autodoc directive, to disable it once.

View File

@ -80,12 +80,24 @@ a comma-separated list of group names.
.. doctest:: .. doctest::
:pyversion: > 3.3 :pyversion: > 3.3
The supported operands are ``<``, ``<=``, ``==``, ``>=``, ``>``, and The following operands are supported:
comparison is performed by `distutils.version.LooseVersion
<https://www.python.org/dev/peps/pep-0386/#distutils>`__. * ``~=``: Compatible release clause
* ``==``: Version matching clause
* ``!=``: Version exclusion clause
* ``<=``, ``>=``: Inclusive ordered comparison clause
* ``<``, ``>``: Exclusive ordered comparison clause
* ``===``: Arbitrary equality clause.
``pyversion`` option is followed `PEP-440: Version Specifiers
<https://www.python.org/dev/peps/pep-0440/#version-specifiers>`__.
.. versionadded:: 1.6 .. versionadded:: 1.6
.. versionchanged:: 1.7
Supported PEP-440 operands and notations
Note that like with standard doctests, you have to use ``<BLANKLINE>`` to Note that like with standard doctests, you have to use ``<BLANKLINE>`` to
signal a blank line in the expected output. The ``<BLANKLINE>`` is removed signal a blank line in the expected output. The ``<BLANKLINE>`` is removed
when building presentation output (HTML, LaTeX etc.). when building presentation output (HTML, LaTeX etc.).

View File

@ -42,6 +42,54 @@ It adds this directive:
.. versionchanged:: 1.5 .. versionchanged:: 1.5
Added ``caption`` option Added ``caption`` option
It also supports a ``top-classes`` option which requires one or more class
names separated by comma. If specified inheritance traversal will stop at the
specified class names. Given the following Python module::
"""
A
/ \
B C
/ \ / \
E D F
"""
class A(object):
pass
class B(A):
pass
class C(A):
pass
class D(B, C):
pass
class E(B):
pass
class F(C):
pass
If you have specified a module in the inheritance diagram like this::
.. inheritance-diagram:: dummy.test
:top-classes: dummy.test.B, dummy.test.C
any base classes which are ancestors to ``top-classes`` and are also defined
in the same module will be rendered as stand alone nodes. In this example
class A will be rendered as stand alone node in the graph. This is a known
issue due to how this extension works internally.
If you don't want class A (or any other ancestors) to be visible then specify
only the classes you would like to generate the diagram for like this::
.. inheritance-diagram:: dummy.test.D dummy.test.E dummy.test.F
:top-classes: dummy.test.B, dummy.test.C
.. versionchanged:: 1.7
Added ``top-classes`` option to limit the scope of inheritance graphs.
New config values are: New config values are:

View File

@ -44,6 +44,15 @@ or use Python raw strings (``r"raw"``).
Example: ``'Eq.{number}'`` is rendered as ``Eq.10`` Example: ``'Eq.{number}'`` is rendered as ``Eq.10``
.. confval:: math_numfig
If ``True``, displayed math equations are numbered across pages when
:confval:`numfig` is enabled. The :confval:`numfig_secnum_depth` setting
is respected. The :rst:role:`eq`, not :rst:role:`numref`, role
must be used to reference equation numbers. Default is ``True``.
.. versionadded:: 1.7
:mod:`.mathbase` defines these new markup elements: :mod:`.mathbase` defines these new markup elements:
.. rst:role:: math .. rst:role:: math
@ -85,7 +94,7 @@ or use Python raw strings (``r"raw"``).
Normally, equations are not numbered. If you want your equation to get a Normally, equations are not numbered. If you want your equation to get a
number, use the ``label`` option. When given, it selects an internal label number, use the ``label`` option. When given, it selects an internal label
for the equation, by which it can be cross-referenced, and causes an equation for the equation, by which it can be cross-referenced, and causes an equation
number to be issued. See :rst:role:`eqref` for an example. The numbering number to be issued. See :rst:role:`eq` for an example. The numbering
style depends on the output format. style depends on the output format.
There is also an option ``nowrap`` that prevents any wrapping of the given There is also an option ``nowrap`` that prevents any wrapping of the given
@ -102,8 +111,7 @@ or use Python raw strings (``r"raw"``).
.. rst:role:: eq .. rst:role:: eq
Role for cross-referencing equations via their label. This currently works Role for cross-referencing equations via their label. Example::
only within the same document. Example::
.. math:: e^{i\pi} + 1 = 0 .. math:: e^{i\pi} + 1 = 0
:label: euler :label: euler

View File

@ -68,8 +68,8 @@ Getting Started
# conf.py # conf.py
# Add autodoc and napoleon to the extensions list # Add napoleon to the extensions list
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.napoleon'] extensions = ['sphinx.ext.napoleon']
2. Use `sphinx-apidoc` to build your API documentation:: 2. Use `sphinx-apidoc` to build your API documentation::
@ -246,13 +246,12 @@ Configuration
Listed below are all the settings used by napoleon and their default 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 values. These settings can be changed in the Sphinx `conf.py` file. Make
sure that both "sphinx.ext.autodoc" and "sphinx.ext.napoleon" are sure that "sphinx.ext.napoleon" is enabled in `conf.py`::
enabled in `conf.py`::
# conf.py # conf.py
# Add any Sphinx extension module names here, as strings # Add any Sphinx extension module names here, as strings
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.napoleon'] extensions = ['sphinx.ext.napoleon']
# Napoleon settings # Napoleon settings
napoleon_google_docstring = True napoleon_google_docstring = True

View File

@ -6,7 +6,7 @@ repository. It is open for anyone who wants to maintain an extension
publicly; just send a short message asking for write permissions. publicly; just send a short message asking for write permissions.
There are also several extensions hosted elsewhere. The `Sphinx extension There are also several extensions hosted elsewhere. The `Sphinx extension
survey <http://sphinxext-survey.readthedocs.org/en/latest/>`__ contains a survey <https://sphinxext-survey.readthedocs.io/>`__ contains a
comprehensive list. comprehensive list.
If you write an extension that you think others will find useful or you think If you write an extension that you think others will find useful or you think

View File

@ -15,6 +15,7 @@ Builder API
.. autoattribute:: name .. autoattribute:: name
.. autoattribute:: format .. autoattribute:: format
.. autoattribute:: epilog
.. autoattribute:: supported_image_types .. autoattribute:: supported_image_types
These methods are predefined and will be called from the application: These methods are predefined and will be called from the application:

View File

@ -117,12 +117,30 @@ Both APIs parse the content into a given node. They are used like this::
node = docutils.nodes.paragraph() node = docutils.nodes.paragraph()
# either # either
from sphinx.ext.autodoc import AutodocReporter
self.state.memo.reporter = AutodocReporter(self.result, self.state.memo.reporter) # override reporter to avoid errors from "include" directive
nested_parse_with_titles(self.state, self.result, node) nested_parse_with_titles(self.state, self.result, node)
# or # or
self.state.nested_parse(self.result, 0, node) self.state.nested_parse(self.result, 0, node)
.. note::
``sphinx.util.docutils.switch_source_input()`` allows to change a target file
during nested_parse. It is useful to mixed contents. For example, ``sphinx.
ext.autodoc`` uses it to parse docstrings::
from sphinx.util.docutils import switch_source_input
# Switch source_input between parsing content.
# Inside this context, all parsing errors and warnings are reported as
# happened in new source_input (in this case, ``self.result``).
with switch_source_input(self.state, self.result):
node = docutils.nodes.paragraph()
self.state.nested_parse(self.result, 0, node)
.. deprecated:: 1.7
Until Sphinx-1.6, ``sphinx.ext.autodoc.AutodocReporter`` is used for this purpose.
For now, it is replaced by ``switch_source_input()``.
If you don't need the wrapping node, you can use any concrete node type and If you don't need the wrapping node, you can use any concrete node type and
return ``node.children`` from the Directive. return ``node.children`` from the Directive.

View File

@ -58,7 +58,7 @@ Read the Docs
Sphinx. They will host sphinx documentation, along with supporting a number Sphinx. They will host sphinx documentation, along with supporting a number
of other features including version support, PDF generation, and more. The of other features including version support, PDF generation, and more. The
`Getting Started `Getting Started
<http://read-the-docs.readthedocs.org/en/latest/getting_started.html>`_ <https://read-the-docs.readthedocs.io/en/latest/getting_started.html>`_
guide is a good place to start. guide is a good place to start.
Epydoc Epydoc

View File

@ -3,7 +3,7 @@ Introduction
This is the documentation for the Sphinx documentation builder. Sphinx is a This is the documentation for the Sphinx documentation builder. Sphinx is a
tool that translates a set of reStructuredText_ source files into various output tool that translates a set of reStructuredText_ source files into various output
formats, automatically producing cross-references, indices etc. That is, if formats, automatically producing cross-references, indices, etc. That is, if
you have a directory containing a bunch of reST-formatted documents (and you have a directory containing a bunch of reST-formatted documents (and
possibly subdirectories of docs in there as well), Sphinx can generate a possibly subdirectories of docs in there as well), Sphinx can generate a
nicely-organized arrangement of HTML files (in some other directory) for easy nicely-organized arrangement of HTML files (in some other directory) for easy
@ -17,7 +17,7 @@ docs have a look at `Epydoc <http://epydoc.sourceforge.net/>`_, which also
understands reST. understands reST.
For a great "introduction" to writing docs in general -- the whys and hows, see For a great "introduction" to writing docs in general -- the whys and hows, see
also `Write the docs <http://write-the-docs.readthedocs.org/>`_, written by Eric also `Write the docs <https://write-the-docs.readthedocs.io/>`_, written by Eric
Holscher. Holscher.
.. _rinohtype: https://github.com/brechtm/rinohtype .. _rinohtype: https://github.com/brechtm/rinohtype
@ -38,7 +38,7 @@ to reStructuredText/Sphinx from other documentation systems.
code to convert Python-doc-style LaTeX markup to Sphinx reST. code to convert Python-doc-style LaTeX markup to Sphinx reST.
* Marcin Wojdyr has written a script to convert Docbook to reST with Sphinx * Marcin Wojdyr has written a script to convert Docbook to reST with Sphinx
markup; it is at `Google Code <https://github.com/wojdyr/db2rst>`_. markup; it is at `GitHub <https://github.com/wojdyr/db2rst>`_.
* Christophe de Vienne wrote a tool to convert from Open/LibreOffice documents * Christophe de Vienne wrote a tool to convert from Open/LibreOffice documents
to Sphinx: `odt2sphinx <https://pypi.python.org/pypi/odt2sphinx/>`_. to Sphinx: `odt2sphinx <https://pypi.python.org/pypi/odt2sphinx/>`_.

View File

@ -267,6 +267,16 @@ The available styling options
``VerbatimBorderColor`` ``VerbatimBorderColor``
default ``{rgb}{0,0,0}``. The frame color, defaults to black. default ``{rgb}{0,0,0}``. The frame color, defaults to black.
``VerbatimHighlightColor``
default ``{rgb}{0.878,1,1}``. The color for highlighted lines.
.. versionadded:: 1.6.6
.. note::
Starting with this colour key, and for all others coming next, the actual
names declared to "color" or "xcolor" are prefixed with "sphinx".
``verbatimsep`` ``verbatimsep``
default ``\fboxsep``. The separation between code lines and the frame. default ``\fboxsep``. The separation between code lines and the frame.
@ -288,11 +298,6 @@ The available styling options
default ``{rgb}{0,0,0}`` (black). The colour for the two horizontal rules default ``{rgb}{0,0,0}`` (black). The colour for the two horizontal rules
used by Sphinx in LaTeX for styling a :dudir:`note` type admonition. used by Sphinx in LaTeX for styling a :dudir:`note` type admonition.
.. note::
The actual colour names declared to "color" or "xcolor" are prefixed with
"sphinx".
``noteborder``, ``hintborder``, ``importantborder``, ``tipborder`` ``noteborder``, ``hintborder``, ``importantborder``, ``tipborder``
default ``0.5pt``. The width of the two horizontal rules. default ``0.5pt``. The width of the two horizontal rules.
@ -441,6 +446,11 @@ Environments
.. versionadded:: 1.5 .. versionadded:: 1.5
options ``verbatimwithframe``, ``verbatimwrapslines``, options ``verbatimwithframe``, ``verbatimwrapslines``,
``verbatimsep``, ``verbatimborder``. ``verbatimsep``, ``verbatimborder``.
.. versionadded:: 1.6.6
support for ``:emphasize-lines:`` option
.. versionadded:: 1.6.6
easier customizability of the formatting via exposed to user LaTeX macros
such as ``\sphinxVerbatimHighlightLine``.
- the bibliography uses ``sphinxthebibliography`` and the Python Module index - the bibliography uses ``sphinxthebibliography`` and the Python Module index
as well as the general index both use ``sphinxtheindex``; these environments as well as the general index both use ``sphinxtheindex``; these environments
are wrappers of the ``thebibliography`` and respectively ``theindex`` are wrappers of the ``thebibliography`` and respectively ``theindex``

View File

@ -91,7 +91,7 @@ Options
Interpret paths recursively according to PEP-0420. Interpret paths recursively according to PEP-0420.
.. option:: -M .. option:: -M, --module-first
Put module documentation before submodule documentation. Put module documentation before submodule documentation.
@ -118,6 +118,14 @@ These options are used when :option:`--full` is specified:
Sets the project release to put in generated files (see :confval:`release`). Sets the project release to put in generated files (see :confval:`release`).
Environment
-----------
.. envvar:: SPHINX_APIDOC_OPTIONS
A comma-separated list of option to append to generated ``automodule``
directives. Defaults to ``members,undoc-members,show-inheritance``.
See also See also
-------- --------

View File

@ -99,17 +99,16 @@ Options
:ref:`builders <builders>`, the following build pipelines are available: :ref:`builders <builders>`, the following build pipelines are available:
**latexpdf** **latexpdf**
Build LaTeX files and run them through :program:`pdflatex`. Build LaTeX files and run them through :program:`pdflatex`, or as per
:confval:`latex_engine` setting.
**latexpdfja** If :confval:`language` is set to ``'ja'``, will use automatically
Build LaTeX files and run them through :program:`platex/dvipdfmx`. the :program:`platex/dvipdfmx` latex to PDF pipeline.
We recommend using ``latexpdf`` instead.
**info** **info**
Build Texinfo files and run them through :program:`makeinfo`. Build Texinfo files and run them through :program:`makeinfo`.
.. important:: .. important::
Sphinx only recognizes the ``-M`` option if it is placed first. Sphinx only recognizes the ``-M`` option if it is placed first.
.. versionadded:: 1.2.1 .. versionadded:: 1.2.1

View File

@ -121,6 +121,8 @@ emphasize particular lines::
.. versionchanged:: 1.3 .. versionchanged:: 1.3
``lineno-start`` has been added. ``lineno-start`` has been added.
.. versionchanged:: 1.6.6
LaTeX supports the ``emphasize-lines`` option.
Includes Includes
^^^^^^^^ ^^^^^^^^
@ -188,8 +190,8 @@ Includes
``lines``, the first allowed line having by convention the line number ``1``. ``lines``, the first allowed line having by convention the line number ``1``.
When lines have been selected in any of the ways described above, the When lines have been selected in any of the ways described above, the
line numbers in ``emphasize-lines`` also refer to the selection, with the line numbers in ``emphasize-lines`` refer to those selected lines, counted
first selected line having number ``1``. consecutively starting at ``1``.
When specifying particular parts of a file to display, it can be useful to When specifying particular parts of a file to display, it can be useful to
display the original line numbers. This can be done using the display the original line numbers. This can be done using the

View File

@ -63,7 +63,7 @@ Cross-referencing anything
by :rst:role:`doc`, :rst:role:`ref` or :rst:role:`option`. by :rst:role:`doc`, :rst:role:`ref` or :rst:role:`option`.
Custom objects added to the standard domain by extensions (see Custom objects added to the standard domain by extensions (see
:meth:`.add_object_type`) are also searched. :meth:`Sphinx.add_object_type`) are also searched.
* Then, it looks for objects (targets) in all loaded domains. It is up to * Then, it looks for objects (targets) in all loaded domains. It is up to
the domains how specific a match must be. For example, in the Python the domains how specific a match must be. For example, in the Python
@ -227,15 +227,15 @@ Cross-referencing figures by figure number
reST labels are used. When you use this role, it will insert a reference to reST labels are used. When you use this role, it will insert a reference to
the figure with link text by its figure number like "Fig. 1.1". the figure with link text by its figure number like "Fig. 1.1".
If an explicit link text is given (like usual: ``:numref:`Image of Sphinx (Fig. If an explicit link text is given (as usual: ``:numref:`Image of Sphinx (Fig.
%s) <my-figure>```), the link caption will be the title of the reference. %s) <my-figure>```), the link caption will serve as title of the reference.
As a special character, `%s` and `{number}` will be replaced to figure As placeholders, `%s` and `{number}` get replaced by the figure
number. `{name}` will be replaced to figure caption. number and `{name}` by the figure caption.
If no explicit link text is given, the value of :confval:`numfig_format` is If no explicit link text is given, the :confval:`numfig_format` setting is
used to default value of link text. used as fall-back default.
If :confval:`numfig` is ``False``, figures are not numbered. If :confval:`numfig` is ``False``, figures are not numbered,
so this role inserts not a reference but labels or link text. so this role inserts not a reference but the label or the link text.
Cross-referencing other items of interest Cross-referencing other items of interest
----------------------------------------- -----------------------------------------
@ -355,7 +355,8 @@ in a different style:
.. rst:role:: manpage .. rst:role:: manpage
A reference to a Unix manual page including the section, A reference to a Unix manual page including the section,
e.g. ``:manpage:`ls(1)```. e.g. ``:manpage:`ls(1)```. Creates a hyperlink to an external site
rendering the manpage if :confval:`manpages_url` is defined.
.. rst:role:: menuselection .. rst:role:: menuselection

View File

@ -323,6 +323,11 @@ following directive exists:
Sphinx's merged cells interact well with ``p{width}``, ``\X{a}{b}``, ``Y{f}`` Sphinx's merged cells interact well with ``p{width}``, ``\X{a}{b}``, ``Y{f}``
and tabulary's columns. and tabulary's columns.
.. note::
:rst:dir:`tabularcolumns` conflicts with ``:widths:`` option of table
directives. If both are specified, ``:widths:`` option will be ignored.
Math Math
---- ----

View File

@ -1,11 +1,20 @@
[metadata]
license_file = LICENSE
[egg_info] [egg_info]
tag_build = .dev tag_build = .dev
tag_date = true tag_date = true
[bdist_wheel]
universal = 1
[aliases] [aliases]
release = egg_info -Db '' release = egg_info -Db ''
upload = upload --sign --identity=36580288 upload = upload --sign --identity=36580288
[build_sphinx]
warning-is-error = 1
[extract_messages] [extract_messages]
mapping_file = babel.cfg mapping_file = babel.cfg
output_file = sphinx/locale/sphinx.pot output_file = sphinx/locale/sphinx.pot
@ -20,12 +29,6 @@ output_dir = sphinx/locale/
domain = sphinx domain = sphinx
directory = sphinx/locale/ directory = sphinx/locale/
[bdist_wheel]
universal = 1
[metadata]
license_file = LICENSE
[flake8] [flake8]
max-line-length = 95 max-line-length = 95
ignore = E116,E241,E251,E741 ignore = E116,E241,E251,E741
@ -41,5 +44,20 @@ incremental = True
check_untyped_defs = True check_untyped_defs = True
warn_unused_ignores = True warn_unused_ignores = True
[build_sphinx] [tool:pytest]
warning-is-error = 1 filterwarnings =
ignore::DeprecationWarning:docutils.io
[coverage:run]
branch = True
source = sphinx
[coverage:report]
exclude_lines =
# Have to re-enable the standard pragma
pragma: no cover
# Don't complain if tests don't hit defensive assertion code:
raise NotImplementedError
# Don't complain if non-runnable code isn't run:
if __name__ == .__main__.:
ignore_errors = True

View File

@ -8,34 +8,8 @@ from distutils.cmd import Command
import sphinx import sphinx
long_desc = ''' with open('README.rst') as f:
Sphinx is a tool that makes it easy to create intelligent and beautiful long_desc = f.read()
documentation for Python projects (or other documents consisting of multiple
reStructuredText sources), written by Georg Brandl. It was originally created
for the new Python documentation, and has excellent facilities for Python
project documentation, but C/C++ is supported as well, and more languages are
planned.
Sphinx uses reStructuredText as its markup language, and many of its strengths
come from the power and straightforwardness of reStructuredText and its parsing
and translating suite, the Docutils.
Among its features are the following:
* Output formats: HTML (including derivative formats such as HTML Help, Epub
and Qt Help), plain text, manual pages and LaTeX or direct PDF output
using rst2pdf
* Extensive cross-references: semantic markup and automatic links
for functions, classes, glossary terms and similar pieces of information
* Hierarchical structure: easy definition of a document tree, with automatic
links to siblings, parents and children
* Automatic indices: general index as well as a module index
* Code handling: automatic highlighting using the Pygments highlighter
* Flexible HTML output using the Jinja 2 templating engine
* Various extensions are available, e.g. for automatic testing of snippets
and inclusion of appropriately formatted docstrings
* Setuptools integration
'''
if sys.version_info < (2, 7) or (3, 0) <= sys.version_info < (3, 4): if sys.version_info < (2, 7) or (3, 0) <= sys.version_info < (3, 4):
print('ERROR: Sphinx requires at least Python 2.7 or 3.4 to run.') print('ERROR: Sphinx requires at least Python 2.7 or 3.4 to run.')
@ -52,6 +26,7 @@ requires = [
'imagesize', 'imagesize',
'requests>=2.0.0', 'requests>=2.0.0',
'setuptools', 'setuptools',
'packaging',
'sphinxcontrib-websupport', 'sphinxcontrib-websupport',
] ]
@ -68,13 +43,14 @@ extras_require = {
'whoosh>=2.0', 'whoosh>=2.0',
], ],
'test': [ 'test': [
'mock',
'pytest', 'pytest',
'pytest-cov', 'pytest-cov',
'html5lib', 'html5lib',
'flake8',
], ],
'test:python_version<"3"': [ 'test:python_version<"3"': [
'enum34', 'enum34',
'mock',
], ],
'test:python_version>="3"': [ 'test:python_version>="3"': [
'mypy', 'mypy',

View File

@ -5,7 +5,7 @@
The Sphinx documentation toolchain. The Sphinx documentation toolchain.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """

View File

@ -5,7 +5,7 @@
The Sphinx documentation toolchain. The Sphinx documentation toolchain.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
import sys import sys

View File

@ -5,7 +5,7 @@
Additional docutils nodes. Additional docutils nodes.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """

View File

@ -5,7 +5,7 @@
This file has moved to :py:mod:`sphinx.ext.apidoc`. This file has moved to :py:mod:`sphinx.ext.apidoc`.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """

View File

@ -7,7 +7,7 @@
Gracefully adapted from the TextPress system by Armin. Gracefully adapted from the TextPress system by Armin.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
from __future__ import print_function from __future__ import print_function
@ -19,7 +19,7 @@ import posixpath
from os import path from os import path
from collections import deque from collections import deque
from six import iteritems from six import iteritems, itervalues
from six.moves import cStringIO from six.moves import cStringIO
from docutils import nodes from docutils import nodes
@ -33,14 +33,13 @@ from sphinx.deprecation import RemovedInSphinx20Warning
from sphinx.environment import BuildEnvironment from sphinx.environment import BuildEnvironment
from sphinx.events import EventManager from sphinx.events import EventManager
from sphinx.extension import verify_required_extensions from sphinx.extension import verify_required_extensions
from sphinx.io import SphinxStandaloneReader
from sphinx.locale import __ from sphinx.locale import __
from sphinx.registry import SphinxComponentRegistry from sphinx.registry import SphinxComponentRegistry
from sphinx.util import pycompat # noqa: F401 from sphinx.util import pycompat # noqa: F401
from sphinx.util import import_object from sphinx.util import import_object
from sphinx.util import logging from sphinx.util import logging
from sphinx.util.tags import Tags from sphinx.util.tags import Tags
from sphinx.util.osutil import ENOENT from sphinx.util.osutil import ENOENT, ensuredir
from sphinx.util.console import bold # type: ignore from sphinx.util.console import bold # type: ignore
from sphinx.util.docutils import is_html5_writer_available, directive_helper from sphinx.util.docutils import is_html5_writer_available, directive_helper
from sphinx.util.i18n import find_catalog_source_files from sphinx.util.i18n import find_catalog_source_files
@ -54,7 +53,9 @@ if False:
from sphinx.domains import Domain, Index # NOQA from sphinx.domains import Domain, Index # NOQA
from sphinx.environment.collectors import EnvironmentCollector # NOQA from sphinx.environment.collectors import EnvironmentCollector # NOQA
from sphinx.extension import Extension # NOQA from sphinx.extension import Extension # NOQA
from sphinx.roles import XRefRole # NOQA
from sphinx.theming import Theme # NOQA from sphinx.theming import Theme # NOQA
from sphinx.util.typing import RoleFunction # NOQA
builtin_extensions = ( builtin_extensions = (
'sphinx.builders.applehelp', 'sphinx.builders.applehelp',
@ -83,6 +84,7 @@ builtin_extensions = (
'sphinx.directives.code', 'sphinx.directives.code',
'sphinx.directives.other', 'sphinx.directives.other',
'sphinx.directives.patches', 'sphinx.directives.patches',
'sphinx.io',
'sphinx.parsers', 'sphinx.parsers',
'sphinx.roles', 'sphinx.roles',
'sphinx.transforms.post_transforms', 'sphinx.transforms.post_transforms',
@ -119,7 +121,6 @@ class Sphinx(object):
self.env = None # type: BuildEnvironment self.env = None # type: BuildEnvironment
self.registry = SphinxComponentRegistry() self.registry = SphinxComponentRegistry()
self.enumerable_nodes = {} # type: Dict[nodes.Node, Tuple[unicode, Callable]] # NOQA self.enumerable_nodes = {} # type: Dict[nodes.Node, Tuple[unicode, Callable]] # NOQA
self.post_transforms = [] # type: List[Transform]
self.html_themes = {} # type: Dict[unicode, unicode] self.html_themes = {} # type: Dict[unicode, unicode]
self.srcdir = srcdir self.srcdir = srcdir
@ -156,10 +157,6 @@ class Sphinx(object):
# status code for command-line application # status code for command-line application
self.statuscode = 0 self.statuscode = 0
if not path.isdir(outdir):
logger.info('making output directory...')
os.makedirs(outdir)
# read config # read config
self.tags = Tags(tags) self.tags = Tags(tags)
self.config = Config(confdir, CONFIG_FILENAME, self.config = Config(confdir, CONFIG_FILENAME,
@ -196,6 +193,10 @@ class Sphinx(object):
# preload builder module (before init config values) # preload builder module (before init config values)
self.preload_builder(buildername) self.preload_builder(buildername)
if not path.isdir(outdir):
logger.info('making output directory...')
ensuredir(outdir)
# the config file itself can be an extension # the config file itself can be an extension
if self.config.setup: if self.config.setup:
self._setting_up_extension = ['conf.py'] self._setting_up_extension = ['conf.py']
@ -337,6 +338,13 @@ class Sphinx(object):
(status, self._warncount))) (status, self._warncount)))
else: else:
logger.info(bold(__('build %s.') % status)) logger.info(bold(__('build %s.') % status))
if self.statuscode == 0 and self.builder.epilog:
logger.info('')
logger.info(self.builder.epilog % {
'outdir': path.relpath(self.outdir),
'project': self.config.project
})
except Exception as err: except Exception as err:
# delete the saved env to force a fresh build next time # delete the saved env to force a fresh build next time
envfile = path.join(self.doctreedir, ENV_PICKLE_FILENAME) envfile = path.join(self.doctreedir, ENV_PICKLE_FILENAME)
@ -443,7 +451,6 @@ class Sphinx(object):
def add_builder(self, builder): def add_builder(self, builder):
# type: (Type[Builder]) -> None # type: (Type[Builder]) -> None
logger.debug('[app] adding builder: %r', builder)
self.registry.add_builder(builder) self.registry.add_builder(builder)
def add_config_value(self, name, default, rebuild, types=()): def add_config_value(self, name, default, rebuild, types=()):
@ -463,7 +470,6 @@ class Sphinx(object):
def set_translator(self, name, translator_class): def set_translator(self, name, translator_class):
# type: (unicode, Type[nodes.NodeVisitor]) -> None # type: (unicode, Type[nodes.NodeVisitor]) -> None
logger.info(bold(__('Change of translator for the %s builder.') % name))
self.registry.add_translator(name, translator_class) self.registry.add_translator(name, translator_class)
def add_node(self, node, **kwds): def add_node(self, node, **kwds):
@ -552,39 +558,30 @@ class Sphinx(object):
def add_domain(self, domain): def add_domain(self, domain):
# type: (Type[Domain]) -> None # type: (Type[Domain]) -> None
logger.debug('[app] adding domain: %r', domain)
self.registry.add_domain(domain) self.registry.add_domain(domain)
def override_domain(self, domain): def override_domain(self, domain):
# type: (Type[Domain]) -> None # type: (Type[Domain]) -> None
logger.debug('[app] overriding domain: %r', domain)
self.registry.override_domain(domain) self.registry.override_domain(domain)
def add_directive_to_domain(self, domain, name, obj, def add_directive_to_domain(self, domain, name, obj,
has_content=None, argument_spec=None, **option_spec): has_content=None, argument_spec=None, **option_spec):
# type: (unicode, unicode, Any, bool, Any, Any) -> None # type: (unicode, unicode, Any, bool, Any, Any) -> None
logger.debug('[app] adding directive to domain: %r',
(domain, name, obj, has_content, argument_spec, option_spec))
self.registry.add_directive_to_domain(domain, name, obj, self.registry.add_directive_to_domain(domain, name, obj,
has_content, argument_spec, **option_spec) has_content, argument_spec, **option_spec)
def add_role_to_domain(self, domain, name, role): def add_role_to_domain(self, domain, name, role):
# type: (unicode, unicode, Any) -> None # type: (unicode, unicode, Union[RoleFunction, XRefRole]) -> None
logger.debug('[app] adding role to domain: %r', (domain, name, role))
self.registry.add_role_to_domain(domain, name, role) self.registry.add_role_to_domain(domain, name, role)
def add_index_to_domain(self, domain, index): def add_index_to_domain(self, domain, index):
# type: (unicode, Type[Index]) -> None # type: (unicode, Type[Index]) -> None
logger.debug('[app] adding index to domain: %r', (domain, index))
self.registry.add_index_to_domain(domain, index) self.registry.add_index_to_domain(domain, index)
def add_object_type(self, directivename, rolename, indextemplate='', def add_object_type(self, directivename, rolename, indextemplate='',
parse_node=None, ref_nodeclass=None, objname='', parse_node=None, ref_nodeclass=None, objname='',
doc_field_types=[]): doc_field_types=[]):
# type: (unicode, unicode, unicode, Callable, nodes.Node, unicode, List) -> None # type: (unicode, unicode, unicode, Callable, nodes.Node, unicode, List) -> None
logger.debug('[app] adding object type: %r',
(directivename, rolename, indextemplate, parse_node,
ref_nodeclass, objname, doc_field_types))
self.registry.add_object_type(directivename, rolename, indextemplate, parse_node, self.registry.add_object_type(directivename, rolename, indextemplate, parse_node,
ref_nodeclass, objname, doc_field_types) ref_nodeclass, objname, doc_field_types)
@ -601,21 +598,16 @@ class Sphinx(object):
def add_crossref_type(self, directivename, rolename, indextemplate='', def add_crossref_type(self, directivename, rolename, indextemplate='',
ref_nodeclass=None, objname=''): ref_nodeclass=None, objname=''):
# type: (unicode, unicode, unicode, nodes.Node, unicode) -> None # type: (unicode, unicode, unicode, nodes.Node, unicode) -> None
logger.debug('[app] adding crossref type: %r',
(directivename, rolename, indextemplate, ref_nodeclass,
objname))
self.registry.add_crossref_type(directivename, rolename, self.registry.add_crossref_type(directivename, rolename,
indextemplate, ref_nodeclass, objname) indextemplate, ref_nodeclass, objname)
def add_transform(self, transform): def add_transform(self, transform):
# type: (Type[Transform]) -> None # type: (Type[Transform]) -> None
logger.debug('[app] adding transform: %r', transform) self.registry.add_transform(transform)
SphinxStandaloneReader.transforms.append(transform)
def add_post_transform(self, transform): def add_post_transform(self, transform):
# type: (Type[Transform]) -> None # type: (Type[Transform]) -> None
logger.debug('[app] adding post transform: %r', transform) self.registry.add_post_transform(transform)
self.post_transforms.append(transform)
def add_javascript(self, filename): def add_javascript(self, filename):
# type: (unicode) -> None # type: (unicode) -> None
@ -657,15 +649,14 @@ class Sphinx(object):
def add_autodocumenter(self, cls): def add_autodocumenter(self, cls):
# type: (Any) -> None # type: (Any) -> None
logger.debug('[app] adding autodocumenter: %r', cls) logger.debug('[app] adding autodocumenter: %r', cls)
from sphinx.ext import autodoc from sphinx.ext.autodoc.directive import AutodocDirective
autodoc.add_documenter(cls) self.registry.add_documenter(cls.objtype, cls)
self.add_directive('auto' + cls.objtype, autodoc.AutoDirective) self.add_directive('auto' + cls.objtype, AutodocDirective)
def add_autodoc_attrgetter(self, type, getter): def add_autodoc_attrgetter(self, typ, getter):
# type: (Any, Callable) -> None # type: (Type, Callable[[Any, unicode, Any], Any]) -> None
logger.debug('[app] adding autodoc attrgetter: %r', (type, getter)) logger.debug('[app] adding autodoc attrgetter: %r', (typ, getter))
from sphinx.ext import autodoc self.registry.add_autodoc_attrgetter(typ, getter)
autodoc.AutoDirective._special_attrgetters[type] = getter
def add_search_language(self, cls): def add_search_language(self, cls):
# type: (Any) -> None # type: (Any) -> None
@ -676,7 +667,6 @@ class Sphinx(object):
def add_source_parser(self, suffix, parser): def add_source_parser(self, suffix, parser):
# type: (unicode, Parser) -> None # type: (unicode, Parser) -> None
logger.debug('[app] adding search source_parser: %r, %r', suffix, parser)
self.registry.add_source_parser(suffix, parser) self.registry.add_source_parser(suffix, parser)
def add_env_collector(self, collector): def add_env_collector(self, collector):
@ -689,6 +679,34 @@ class Sphinx(object):
logger.debug('[app] adding HTML theme: %r, %r', name, theme_path) logger.debug('[app] adding HTML theme: %r, %r', name, theme_path)
self.html_themes[name] = theme_path self.html_themes[name] = theme_path
# ---- other methods -------------------------------------------------
def is_parallel_allowed(self, typ):
# type: (unicode) -> bool
"""Check parallel processing is allowed or not.
``typ`` is a type of processing; ``'read'`` or ``'write'``.
"""
if typ == 'read':
attrname = 'parallel_read_safe'
elif typ == 'write':
attrname = 'parallel_write_safe'
else:
raise ValueError('parallel type %s is not supported' % typ)
for ext in itervalues(self.extensions):
allowed = getattr(ext, attrname, None)
if allowed is None:
logger.warning(__("the %s extension does not declare if it is safe "
"for parallel %sing, assuming it isn't - please "
"ask the extension author to check and make it "
"explicit"), ext.name, typ)
logger.warning('doing serial %s', typ)
return False
elif not allowed:
return False
return True
class TemplateBridge(object): class TemplateBridge(object):
""" """

View File

@ -5,11 +5,10 @@
Builder superclass for all builders. Builder superclass for all builders.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
import os
from os import path from os import path
import warnings import warnings
@ -18,13 +17,12 @@ try:
except ImportError: except ImportError:
multiprocessing = None multiprocessing = None
from six import itervalues
from docutils import nodes from docutils import nodes
from sphinx.deprecation import RemovedInSphinx20Warning from sphinx.deprecation import RemovedInSphinx20Warning
from sphinx.environment.adapters.asset import ImageAdapter from sphinx.environment.adapters.asset import ImageAdapter
from sphinx.util import i18n, path_stabilize, logging, status_iterator from sphinx.util import i18n, path_stabilize, logging, status_iterator
from sphinx.util.osutil import SEP, relative_uri from sphinx.util.osutil import SEP, ensuredir, relative_uri
from sphinx.util.i18n import find_catalog from sphinx.util.i18n import find_catalog
from sphinx.util.console import bold # type: ignore from sphinx.util.console import bold # type: ignore
from sphinx.util.parallel import ParallelTasks, SerialTasks, make_chunks, \ from sphinx.util.parallel import ParallelTasks, SerialTasks, make_chunks, \
@ -56,6 +54,11 @@ class Builder(object):
name = '' # type: unicode name = '' # type: unicode
#: The builder's output format, or '' if no document output is produced. #: The builder's output format, or '' if no document output is produced.
format = '' # type: unicode format = '' # type: unicode
#: The message emitted upon successful build completion. This can be a
#: printf-style template string with the following keys: ``outdir``,
#: ``project``
epilog = '' # type: unicode
# default translator class for the builder. This will be overrided by # default translator class for the builder. This will be overrided by
# ``app.set_translator()``. # ``app.set_translator()``.
default_translator_class = None # type: nodes.NodeVisitor default_translator_class = None # type: nodes.NodeVisitor
@ -79,8 +82,7 @@ class Builder(object):
self.confdir = app.confdir self.confdir = app.confdir
self.outdir = app.outdir self.outdir = app.outdir
self.doctreedir = app.doctreedir self.doctreedir = app.doctreedir
if not path.isdir(self.doctreedir): ensuredir(self.doctreedir)
os.makedirs(self.doctreedir)
self.app = app # type: Sphinx self.app = app # type: Sphinx
self.env = None # type: BuildEnvironment self.env = None # type: BuildEnvironment
@ -373,15 +375,10 @@ class Builder(object):
docnames = set(docnames) & self.env.found_docs docnames = set(docnames) & self.env.found_docs
# determine if we can write in parallel # determine if we can write in parallel
self.parallel_ok = False
if parallel_available and self.app.parallel > 1 and self.allow_parallel: if parallel_available and self.app.parallel > 1 and self.allow_parallel:
self.parallel_ok = True self.parallel_ok = self.app.is_parallel_allowed('write')
for extension in itervalues(self.app.extensions): else:
if not extension.parallel_write_safe: self.parallel_ok = False
logger.warning('the %s extension is not safe for parallel '
'writing, doing serial write', extension.name)
self.parallel_ok = False
break
# create a task executor to use for misc. "finish-up" tasks # create a task executor to use for misc. "finish-up" tasks
# if self.parallel_ok: # if self.parallel_ok:

View File

@ -5,7 +5,7 @@
Base class of epub2/epub3 builders. Base class of epub2/epub3 builders.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """

View File

@ -5,7 +5,7 @@
Build Apple help books. Build Apple help books.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
from __future__ import print_function from __future__ import print_function
@ -75,6 +75,10 @@ class AppleHelpBuilder(StandaloneHTMLBuilder):
on the ``hiutil`` command line tool. on the ``hiutil`` command line tool.
""" """
name = 'applehelp' name = 'applehelp'
epilog = ('The help book is in %(outdir)s.\n'
'Note that won\'t be able to view it unless you put it in '
'~/Library/Documentation/Help or install it in your application '
'bundle.')
# don't copy the reST source # don't copy the reST source
copysource = False copysource = False

View File

@ -5,7 +5,7 @@
Changelog builder. Changelog builder.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
@ -38,6 +38,7 @@ class ChangesBuilder(Builder):
Write a summary with all versionadded/changed directives. Write a summary with all versionadded/changed directives.
""" """
name = 'changes' name = 'changes'
epilog = 'The overview file is in %(outdir)s.'
def init(self): def init(self):
# type: () -> None # type: () -> None

View File

@ -7,7 +7,7 @@
.. _Devhelp: http://live.gnome.org/devhelp .. _Devhelp: http://live.gnome.org/devhelp
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
from __future__ import absolute_import from __future__ import absolute_import
@ -43,6 +43,10 @@ class DevhelpBuilder(StandaloneHTMLBuilder):
Builder that also outputs GNOME Devhelp file. Builder that also outputs GNOME Devhelp file.
""" """
name = 'devhelp' name = 'devhelp'
epilog = ('To view the help file:\n'
'$ mkdir -p $HOME/.local/share/devhelp/%(project)s\n'
'$ ln -s %(outdir)s $HOME/.local/share/devhelp/%(project)s\n'
'$ devhelp')
# don't copy the reST source # don't copy the reST source
copysource = False copysource = False

View File

@ -21,6 +21,8 @@ if False:
class DummyBuilder(Builder): class DummyBuilder(Builder):
name = 'dummy' name = 'dummy'
epilog = 'The dummy builder generates no files.'
allow_parallel = True allow_parallel = True
def init(self): def init(self):

View File

@ -63,6 +63,7 @@ class Epub3Builder(_epub_base.EpubBuilder):
an epub file. an epub file.
""" """
name = 'epub' name = 'epub'
epilog = 'The ePub file is in %(outdir)s.'
supported_remote_images = False supported_remote_images = False
template_dir = path.join(package_dir, 'templates', 'epub3') template_dir = path.join(package_dir, 'templates', 'epub3')

View File

@ -5,7 +5,7 @@
The MessageCatalogBuilder class. The MessageCatalogBuilder class.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
@ -214,6 +214,7 @@ class MessageCatalogBuilder(I18nBuilder):
Builds gettext-style message catalogs (.pot files). Builds gettext-style message catalogs (.pot files).
""" """
name = 'gettext' name = 'gettext'
epilog = 'The message catalogs are in %(outdir)s.'
def init(self): def init(self):
# type: () -> None # type: () -> None

View File

@ -5,7 +5,7 @@
Several HTML builders. Several HTML builders.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
@ -153,6 +153,8 @@ class StandaloneHTMLBuilder(Builder):
""" """
name = 'html' name = 'html'
format = 'html' format = 'html'
epilog = 'The HTML pages are in %(outdir)s.'
copysource = True copysource = True
allow_parallel = True allow_parallel = True
out_suffix = '.html' out_suffix = '.html'
@ -274,7 +276,7 @@ class StandaloneHTMLBuilder(Builder):
# type: () -> Iterator[unicode] # type: () -> Iterator[unicode]
cfgdict = dict((confval.name, confval.value) for confval in self.config.filter('html')) cfgdict = dict((confval.name, confval.value) for confval in self.config.filter('html'))
self.config_hash = get_stable_hash(cfgdict) self.config_hash = get_stable_hash(cfgdict)
self.tags_hash = get_stable_hash(sorted(self.tags)) # type: ignore self.tags_hash = get_stable_hash(sorted(self.tags))
old_config_hash = old_tags_hash = '' old_config_hash = old_tags_hash = ''
try: try:
with open(path.join(self.outdir, '.buildinfo')) as fp: with open(path.join(self.outdir, '.buildinfo')) as fp:
@ -1066,6 +1068,8 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
HTML page. HTML page.
""" """
name = 'singlehtml' name = 'singlehtml'
epilog = 'The HTML page is in %(outdir)s.'
copysource = False copysource = False
def get_outdated_docs(self): # type: ignore def get_outdated_docs(self): # type: ignore
@ -1328,12 +1332,14 @@ class PickleHTMLBuilder(SerializingHTMLBuilder):
""" """
A Builder that dumps the generated HTML into pickle files. A Builder that dumps the generated HTML into pickle files.
""" """
name = 'pickle'
epilog = 'You can now process the pickle files in %(outdir)s.'
implementation = pickle implementation = pickle
implementation_dumps_unicode = False implementation_dumps_unicode = False
additional_dump_args = (pickle.HIGHEST_PROTOCOL,) additional_dump_args = (pickle.HIGHEST_PROTOCOL,)
indexer_format = pickle indexer_format = pickle
indexer_dumps_unicode = False indexer_dumps_unicode = False
name = 'pickle'
out_suffix = '.fpickle' out_suffix = '.fpickle'
globalcontext_filename = 'globalcontext.pickle' globalcontext_filename = 'globalcontext.pickle'
searchindex_filename = 'searchindex.pickle' searchindex_filename = 'searchindex.pickle'
@ -1347,11 +1353,13 @@ class JSONHTMLBuilder(SerializingHTMLBuilder):
""" """
A builder that dumps the generated HTML into JSON files. A builder that dumps the generated HTML into JSON files.
""" """
name = 'json'
epilog = 'You can now process the JSON files in %(outdir)s.'
implementation = jsonimpl implementation = jsonimpl
implementation_dumps_unicode = True implementation_dumps_unicode = True
indexer_format = jsonimpl indexer_format = jsonimpl
indexer_dumps_unicode = True indexer_dumps_unicode = True
name = 'json'
out_suffix = '.fjson' out_suffix = '.fjson'
globalcontext_filename = 'globalcontext.json' globalcontext_filename = 'globalcontext.json'
searchindex_filename = 'searchindex.json' searchindex_filename = 'searchindex.json'

View File

@ -6,7 +6,7 @@
Build HTML help support files. Build HTML help support files.
Parts adapted from Python's Doc/tools/prechm.py. Parts adapted from Python's Doc/tools/prechm.py.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
from __future__ import print_function from __future__ import print_function
@ -174,6 +174,8 @@ class HTMLHelpBuilder(StandaloneHTMLBuilder):
index files. Adapted from the original Doc/tools/prechm.py. index files. Adapted from the original Doc/tools/prechm.py.
""" """
name = 'htmlhelp' name = 'htmlhelp'
epilog = ('You can now run HTML Help Workshop with the .htp file in '
'%(outdir)s.')
# don't copy the reST source # don't copy the reST source
copysource = False copysource = False

View File

@ -5,7 +5,7 @@
LaTeX builder. LaTeX builder.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
@ -49,6 +49,12 @@ class LaTeXBuilder(Builder):
""" """
name = 'latex' name = 'latex'
format = 'latex' format = 'latex'
epilog = 'The LaTeX files are in %(outdir)s.'
if os.name == 'posix':
epilog += ("\nRun 'make' in that directory to run these through "
"(pdf)latex\n"
"(use `make latexpdf' here to do that automatically).")
supported_image_types = ['application/pdf', 'image/png', 'image/jpeg'] supported_image_types = ['application/pdf', 'image/png', 'image/jpeg']
supported_remote_images = False supported_remote_images = False
default_translator_class = LaTeXTranslator default_translator_class = LaTeXTranslator

View File

@ -5,7 +5,7 @@
The CheckExternalLinksBuilder class. The CheckExternalLinksBuilder class.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
@ -90,6 +90,8 @@ class CheckExternalLinksBuilder(Builder):
Checks for broken external links. Checks for broken external links.
""" """
name = 'linkcheck' name = 'linkcheck'
epilog = ('Look for any errors in the above output or in '
'%(outdir)s/output.txt')
def init(self): def init(self):
# type: () -> None # type: () -> None

View File

@ -5,7 +5,7 @@
Manual pages builder. Manual pages builder.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
@ -40,6 +40,8 @@ class ManualPageBuilder(Builder):
""" """
name = 'man' name = 'man'
format = 'man' format = 'man'
epilog = 'The manual pages are in %(outdir)s.'
default_translator_class = ManualPageTranslator default_translator_class = ManualPageTranslator
supported_image_types = [] # type: List[unicode] supported_image_types = [] # type: List[unicode]

View File

@ -5,7 +5,7 @@
Build input files for the Qt collection generator. Build input files for the Qt collection generator.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
@ -108,6 +108,11 @@ class QtHelpBuilder(StandaloneHTMLBuilder):
Builder that also outputs Qt help project, contents and index files. Builder that also outputs Qt help project, contents and index files.
""" """
name = 'qthelp' name = 'qthelp'
epilog = ('You can now run "qcollectiongenerator" with the .qhcp '
'project file in %(outdir)s, like this:\n'
'$ qcollectiongenerator %(outdir)s/%(project)s.qhcp\n'
'To view the help file:\n'
'$ assistant -collectionFile %(outdir)s/%(project)s.qhc')
# don't copy the reST source # don't copy the reST source
copysource = False copysource = False
@ -269,7 +274,7 @@ class QtHelpBuilder(StandaloneHTMLBuilder):
link = node['refuri'] link = node['refuri']
title = htmlescape(node.astext()).replace('"', '&quot;') title = htmlescape(node.astext()).replace('"', '&quot;')
item = section_template % {'title': title, 'ref': link} item = section_template % {'title': title, 'ref': link}
item = u' ' * 4 * indentlevel + item # type: ignore item = u' ' * 4 * indentlevel + item
parts.append(item.encode('ascii', 'xmlcharrefreplace')) parts.append(item.encode('ascii', 'xmlcharrefreplace'))
elif isinstance(node, nodes.bullet_list): elif isinstance(node, nodes.bullet_list):
for subnode in node: for subnode in node:

View File

@ -5,10 +5,11 @@
Texinfo builder. Texinfo builder.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
import os
from os import path from os import path
from docutils import nodes from docutils import nodes
@ -97,6 +98,12 @@ class TexinfoBuilder(Builder):
""" """
name = 'texinfo' name = 'texinfo'
format = 'texinfo' format = 'texinfo'
epilog = 'The Texinfo files are in %(outdir)s.'
if os.name == 'posix':
epilog += ("\nRun 'make' in that directory to run these through "
"makeinfo\n"
"(use 'make info' here to do that automatically).")
supported_image_types = ['image/png', 'image/jpeg', supported_image_types = ['image/png', 'image/jpeg',
'image/gif'] 'image/gif']
default_translator_class = TexinfoTranslator default_translator_class = TexinfoTranslator

View File

@ -5,7 +5,7 @@
Plain-text Sphinx builder. Plain-text Sphinx builder.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
@ -31,6 +31,8 @@ logger = logging.getLogger(__name__)
class TextBuilder(Builder): class TextBuilder(Builder):
name = 'text' name = 'text'
format = 'text' format = 'text'
epilog = 'The text files are in %(outdir)s.'
out_suffix = '.txt' out_suffix = '.txt'
allow_parallel = True allow_parallel = True
default_translator_class = TextTranslator default_translator_class = TextTranslator

View File

@ -5,7 +5,7 @@
Builder for the web support package. Builder for the web support package.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """

View File

@ -5,7 +5,7 @@
Docutils-native XML and pseudo-XML builders. Docutils-native XML and pseudo-XML builders.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
@ -35,6 +35,8 @@ class XMLBuilder(Builder):
""" """
name = 'xml' name = 'xml'
format = 'xml' format = 'xml'
epilog = 'The XML files are in %(outdir)s.'
out_suffix = '.xml' out_suffix = '.xml'
allow_parallel = True allow_parallel = True
@ -108,6 +110,8 @@ class PseudoXMLBuilder(XMLBuilder):
""" """
name = 'pseudoxml' name = 'pseudoxml'
format = 'pseudoxml' format = 'pseudoxml'
epilog = 'The pseudo-XML files are in %(outdir)s.'
out_suffix = '.pseudoxml' out_suffix = '.pseudoxml'
_writer_class = PseudoXMLWriter _writer_class = PseudoXMLWriter

View File

@ -5,6 +5,6 @@
Modules for command line executables. Modules for command line executables.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """

View File

@ -5,7 +5,7 @@
Build documentation from a provided source. Build documentation from a provided source.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """

View File

@ -5,7 +5,7 @@
Quickly setup documentation source to work with Sphinx. Quickly setup documentation source to work with Sphinx.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
from __future__ import print_function from __future__ import print_function
@ -16,6 +16,7 @@ import os
import re import re
import sys import sys
import time import time
from collections import OrderedDict
from io import open from io import open
from os import path from os import path
@ -35,7 +36,7 @@ from six.moves.urllib.parse import quote as urlquote
from docutils.utils import column_width from docutils.utils import column_width
from sphinx import __display_version__, package_dir from sphinx import __display_version__, package_dir
from sphinx.util.osutil import make_filename from sphinx.util.osutil import ensuredir, make_filename
from sphinx.util.console import ( # type: ignore from sphinx.util.console import ( # type: ignore
purple, bold, red, turquoise, nocolor, color_terminal purple, bold, red, turquoise, nocolor, color_terminal
) )
@ -48,7 +49,22 @@ if False:
TERM_ENCODING = getattr(sys.stdin, 'encoding', None) TERM_ENCODING = getattr(sys.stdin, 'encoding', None)
DEFAULT_VALUE = { EXTENSIONS = OrderedDict([
('autodoc', 'automatically insert docstrings from modules'),
('doctest', 'automatically test code snippets in doctest blocks'),
('intersphinx', 'link between Sphinx documentation of different projects'),
('todo', 'write "todo" entries that can be shown or hidden on build'),
('coverage', 'checks for documentation coverage'),
('imgmath', 'include math, rendered as PNG or SVG images'),
('mathjax', 'include math, rendered in the browser by MathJax'),
('ifconfig', 'conditional inclusion of content based on config values'),
('viewcode',
'include links to the source code of documented Python objects'),
('githubpages',
'create .nojekyll file to publish the document on GitHub pages'),
])
DEFAULTS = {
'path': '.', 'path': '.',
'sep': False, 'sep': False,
'dot': '_', 'dot': '_',
@ -56,26 +72,13 @@ DEFAULT_VALUE = {
'suffix': '.rst', 'suffix': '.rst',
'master': 'index', 'master': 'index',
'epub': False, 'epub': False,
'ext_autodoc': False,
'ext_doctest': False,
'ext_todo': False,
'makefile': True, 'makefile': True,
'batchfile': True, 'batchfile': True,
} }
EXTENSIONS = ('autodoc', 'doctest', 'intersphinx', 'todo', 'coverage',
'imgmath', 'mathjax', 'ifconfig', 'viewcode', 'githubpages')
PROMPT_PREFIX = '> ' PROMPT_PREFIX = '> '
def mkdir_p(dir):
# type: (unicode) -> None
if path.isdir(dir):
return
os.makedirs(dir)
# function to get input from terminal -- overridden by the test suite # function to get input from terminal -- overridden by the test suite
def term_input(prompt): def term_input(prompt):
# type: (unicode) -> unicode # type: (unicode) -> unicode
@ -159,8 +162,8 @@ def term_decode(text):
return text.decode('latin1') return text.decode('latin1')
def do_prompt(d, key, text, default=None, validator=nonempty): def do_prompt(text, default=None, validator=nonempty):
# type: (Dict, unicode, unicode, unicode, Callable[[unicode], Any]) -> None # type: (unicode, unicode, Callable[[unicode], Any]) -> Union[unicode, bool]
while True: while True:
if default is not None: if default is not None:
prompt = PROMPT_PREFIX + '%s [%s]: ' % (text, default) # type: unicode prompt = PROMPT_PREFIX + '%s [%s]: ' % (text, default) # type: unicode
@ -191,7 +194,7 @@ def do_prompt(d, key, text, default=None, validator=nonempty):
print(red('* ' + str(err))) print(red('* ' + str(err)))
continue continue
break break
d[key] = x return x
def convert_python_source(source, rex=re.compile(r"[uU]('.*?')")): def convert_python_source(source, rex=re.compile(r"[uU]('.*?')")):
@ -235,7 +238,7 @@ def ask_user(d):
* suffix: source file suffix * suffix: source file suffix
* master: master document name * master: master document name
* epub: use epub (bool) * epub: use epub (bool)
* ext_*: extensions to use (bools) * extensions: extensions to use (list)
* makefile: make Makefile * makefile: make Makefile
* batchfile: make command file * batchfile: make command file
""" """
@ -251,7 +254,7 @@ Selected root path: %s''' % d['path']))
else: else:
print(''' print('''
Enter the root path for documentation.''') Enter the root path for documentation.''')
do_prompt(d, 'path', 'Root path for the documentation', '.', is_path) d['path'] = do_prompt('Root path for the documentation', '.', is_path)
while path.isfile(path.join(d['path'], 'conf.py')) or \ while path.isfile(path.join(d['path'], 'conf.py')) or \
path.isfile(path.join(d['path'], 'source', 'conf.py')): path.isfile(path.join(d['path'], 'source', 'conf.py')):
@ -260,8 +263,8 @@ Enter the root path for documentation.''')
'selected root path.')) 'selected root path.'))
print('sphinx-quickstart will not overwrite existing Sphinx projects.') print('sphinx-quickstart will not overwrite existing Sphinx projects.')
print() print()
do_prompt(d, 'path', 'Please enter a new root path (or just Enter ' d['path'] = do_prompt('Please enter a new root path (or just Enter '
'to exit)', '', is_path) 'to exit)', '', is_path)
if not d['path']: if not d['path']:
sys.exit(1) sys.exit(1)
@ -270,22 +273,22 @@ Enter the root path for documentation.''')
You have two options for placing the build directory for Sphinx output. You have two options for placing the build directory for Sphinx output.
Either, you use a directory "_build" within the root path, or you separate Either, you use a directory "_build" within the root path, or you separate
"source" and "build" directories within the root path.''') "source" and "build" directories within the root path.''')
do_prompt(d, 'sep', 'Separate source and build directories (y/n)', 'n', d['sep'] = do_prompt('Separate source and build directories (y/n)',
boolean) 'n', boolean)
if 'dot' not in d: if 'dot' not in d:
print(''' print('''
Inside the root directory, two more directories will be created; "_templates" Inside the root directory, two more directories will be created; "_templates"
for custom HTML templates and "_static" for custom stylesheets and other static for custom HTML templates and "_static" for custom stylesheets and other static
files. You can enter another prefix (such as ".") to replace the underscore.''') files. You can enter another prefix (such as ".") to replace the underscore.''')
do_prompt(d, 'dot', 'Name prefix for templates and static dir', '_', ok) d['dot'] = do_prompt('Name prefix for templates and static dir', '_', ok)
if 'project' not in d: if 'project' not in d:
print(''' print('''
The project name will occur in several places in the built documentation.''') The project name will occur in several places in the built documentation.''')
do_prompt(d, 'project', 'Project name') d['project'] = do_prompt('Project name')
if 'author' not in d: if 'author' not in d:
do_prompt(d, 'author', 'Author name(s)') d['author'] = do_prompt('Author name(s)')
if 'version' not in d: if 'version' not in d:
print(''' print('''
@ -294,9 +297,9 @@ software. Each version can have multiple releases. For example, for
Python the version is something like 2.5 or 3.0, while the release is Python the version is something like 2.5 or 3.0, while the release is
something like 2.5.1 or 3.0a1. If you don't need this dual structure, something like 2.5.1 or 3.0a1. If you don't need this dual structure,
just set both to the same value.''') just set both to the same value.''')
do_prompt(d, 'version', 'Project version', '', allow_empty) d['version'] = do_prompt('Project version', '', allow_empty)
if 'release' not in d: if 'release' not in d:
do_prompt(d, 'release', 'Project release', d['version'], allow_empty) d['release'] = do_prompt('Project release', d['version'], allow_empty)
if 'language' not in d: if 'language' not in d:
print(''' print('''
@ -306,7 +309,7 @@ translate text that it generates into that language.
For a list of supported codes, see For a list of supported codes, see
http://sphinx-doc.org/config.html#confval-language.''') http://sphinx-doc.org/config.html#confval-language.''')
do_prompt(d, 'language', 'Project language', 'en') d['language'] = do_prompt('Project language', 'en')
if d['language'] == 'en': if d['language'] == 'en':
d['language'] = None d['language'] = None
@ -314,7 +317,7 @@ http://sphinx-doc.org/config.html#confval-language.''')
print(''' print('''
The file name suffix for source files. Commonly, this is either ".txt" The file name suffix for source files. Commonly, this is either ".txt"
or ".rst". Only files with this suffix are considered documents.''') or ".rst". Only files with this suffix are considered documents.''')
do_prompt(d, 'suffix', 'Source file suffix', '.rst', suffix) d['suffix'] = do_prompt('Source file suffix', '.rst', suffix)
if 'master' not in d: if 'master' not in d:
print(''' print('''
@ -322,8 +325,8 @@ One document is special in that it is considered the top node of the
"contents tree", that is, it is the root of the hierarchical structure "contents tree", that is, it is the root of the hierarchical structure
of the documents. Normally, this is "index", but if your "index" of the documents. Normally, this is "index", but if your "index"
document is a custom template, you can also set this to another filename.''') document is a custom template, you can also set this to another filename.''')
do_prompt(d, 'master', 'Name of your master document (without suffix)', d['master'] = do_prompt('Name of your master document (without suffix)',
'index') 'index')
while path.isfile(path.join(d['path'], d['master'] + d['suffix'])) or \ while path.isfile(path.join(d['path'], d['master'] + d['suffix'])) or \
path.isfile(path.join(d['path'], 'source', d['master'] + d['suffix'])): path.isfile(path.join(d['path'], 'source', d['master'] + d['suffix'])):
@ -332,65 +335,40 @@ document is a custom template, you can also set this to another filename.''')
'selected root path.' % (d['master'] + d['suffix']))) 'selected root path.' % (d['master'] + d['suffix'])))
print('sphinx-quickstart will not overwrite the existing file.') print('sphinx-quickstart will not overwrite the existing file.')
print() print()
do_prompt(d, 'master', 'Please enter a new file name, or rename the ' d['master'] = do_prompt('Please enter a new file name, or rename the '
'existing file and press Enter', d['master']) 'existing file and press Enter', d['master'])
if 'epub' not in d: if 'epub' not in d:
print(''' print('''
Sphinx can also add configuration for epub output:''') Sphinx can also add configuration for epub output:''')
do_prompt(d, 'epub', 'Do you want to use the epub builder (y/n)', d['epub'] = do_prompt('Do you want to use the epub builder (y/n)',
'n', boolean) 'n', boolean)
if 'ext_autodoc' not in d: if 'extensions' not in d:
print(''' print('Indicate which of the following Sphinx extensions should be '
Please indicate if you want to use one of the following Sphinx extensions:''') 'enabled:')
do_prompt(d, 'ext_autodoc', 'autodoc: automatically insert docstrings ' d['extensions'] = []
'from modules (y/n)', 'n', boolean) for name, description in EXTENSIONS.items():
if 'ext_doctest' not in d: if do_prompt('%s: %s (y/n)' % (name, description), 'n', boolean):
do_prompt(d, 'ext_doctest', 'doctest: automatically test code snippets ' d['extensions'].append('sphinx.ext.%s' % name)
'in doctest blocks (y/n)', 'n', boolean)
if 'ext_intersphinx' not in d:
do_prompt(d, 'ext_intersphinx', 'intersphinx: link between Sphinx '
'documentation of different projects (y/n)', 'n', boolean)
if 'ext_todo' not in d:
do_prompt(d, 'ext_todo', 'todo: write "todo" entries '
'that can be shown or hidden on build (y/n)', 'n', boolean)
if 'ext_coverage' not in d:
do_prompt(d, 'ext_coverage', 'coverage: checks for documentation '
'coverage (y/n)', 'n', boolean)
if 'ext_imgmath' not in d:
do_prompt(d, 'ext_imgmath', 'imgmath: include math, rendered '
'as PNG or SVG images (y/n)', 'n', boolean)
if 'ext_mathjax' not in d:
do_prompt(d, 'ext_mathjax', 'mathjax: include math, rendered in the '
'browser by MathJax (y/n)', 'n', boolean)
if d['ext_imgmath'] and d['ext_mathjax']:
print('''Note: imgmath and mathjax cannot be enabled at the same time.
imgmath has been deselected.''')
d['ext_imgmath'] = False
if 'ext_ifconfig' not in d:
do_prompt(d, 'ext_ifconfig', 'ifconfig: conditional inclusion of '
'content based on config values (y/n)', 'n', boolean)
if 'ext_viewcode' not in d:
do_prompt(d, 'ext_viewcode', 'viewcode: include links to the source '
'code of documented Python objects (y/n)', 'n', boolean)
if 'ext_githubpages' not in d:
do_prompt(d, 'ext_githubpages', 'githubpages: create .nojekyll file '
'to publish the document on GitHub pages (y/n)', 'n', boolean)
if 'no_makefile' in d: # Handle conflicting options
d['makefile'] = False if set(['sphinx.ext.imgmath', 'sphinx.ext.mathjax']).issubset(
elif 'makefile' not in d: d['extensions']):
print('Note: imgmath and mathjax cannot be enabled at the same '
'time. imgmath has been deselected.')
d['extensions'].remove('sphinx.ext.imgmath')
if 'makefile' not in d:
print(''' print('''
A Makefile and a Windows command file can be generated for you so that you A Makefile and a Windows command file can be generated for you so that you
only have to run e.g. `make html' instead of invoking sphinx-build only have to run e.g. `make html' instead of invoking sphinx-build
directly.''') directly.''')
do_prompt(d, 'makefile', 'Create Makefile? (y/n)', 'y', boolean) d['makefile'] = do_prompt('Create Makefile? (y/n)', 'y', boolean)
if 'no_batchfile' in d:
d['batchfile'] = False if 'batchfile' not in d:
elif 'batchfile' not in d: d['batchfile'] = do_prompt('Create Windows command file? (y/n)',
do_prompt(d, 'batchfile', 'Create Windows command file? (y/n)', 'y', boolean)
'y', boolean)
print() print()
@ -400,7 +378,6 @@ def generate(d, overwrite=True, silent=False, templatedir=None):
template = QuickstartRenderer(templatedir=templatedir) template = QuickstartRenderer(templatedir=templatedir)
texescape.init() texescape.init()
indent = ' ' * 4
if 'mastertoctree' not in d: if 'mastertoctree' not in d:
d['mastertoctree'] = '' d['mastertoctree'] = ''
@ -414,10 +391,6 @@ def generate(d, overwrite=True, silent=False, templatedir=None):
d['now'] = time.asctime() d['now'] = time.asctime()
d['project_underline'] = column_width(d['project']) * '=' d['project_underline'] = column_width(d['project']) * '='
d.setdefault('extensions', []) d.setdefault('extensions', [])
for name in EXTENSIONS:
if d.get('ext_' + name):
d['extensions'].append('sphinx.ext.' + name)
d['extensions'] = (',\n' + indent).join(repr(name) for name in d['extensions'])
d['copyright'] = time.strftime('%Y') + ', ' + d['author'] d['copyright'] = time.strftime('%Y') + ', ' + d['author']
d['author_texescaped'] = text_type(d['author']).\ d['author_texescaped'] = text_type(d['author']).\
translate(texescape.tex_escape_map) translate(texescape.tex_escape_map)
@ -433,11 +406,11 @@ def generate(d, overwrite=True, silent=False, templatedir=None):
d[key + '_str'] = d[key].replace('\\', '\\\\').replace("'", "\\'") d[key + '_str'] = d[key].replace('\\', '\\\\').replace("'", "\\'")
if not path.isdir(d['path']): if not path.isdir(d['path']):
mkdir_p(d['path']) ensuredir(d['path'])
srcdir = d['sep'] and path.join(d['path'], 'source') or d['path'] srcdir = d['sep'] and path.join(d['path'], 'source') or d['path']
mkdir_p(srcdir) ensuredir(srcdir)
if d['sep']: if d['sep']:
builddir = path.join(d['path'], 'build') builddir = path.join(d['path'], 'build')
d['exclude_patterns'] = '' d['exclude_patterns'] = ''
@ -448,18 +421,20 @@ def generate(d, overwrite=True, silent=False, templatedir=None):
'Thumbs.db', '.DS_Store', 'Thumbs.db', '.DS_Store',
]) ])
d['exclude_patterns'] = ', '.join(exclude_patterns) d['exclude_patterns'] = ', '.join(exclude_patterns)
mkdir_p(builddir) ensuredir(builddir)
mkdir_p(path.join(srcdir, d['dot'] + 'templates')) ensuredir(path.join(srcdir, d['dot'] + 'templates'))
mkdir_p(path.join(srcdir, d['dot'] + 'static')) ensuredir(path.join(srcdir, d['dot'] + 'static'))
def write_file(fpath, content, newline=None): def write_file(fpath, content, newline=None):
# type: (unicode, unicode, unicode) -> None # type: (unicode, unicode, unicode) -> None
if overwrite or not path.isfile(fpath): if overwrite or not path.isfile(fpath):
print('Creating file %s.' % fpath) if 'quiet' not in d:
print('Creating file %s.' % fpath)
with open(fpath, 'wt', encoding='utf-8', newline=newline) as f: with open(fpath, 'wt', encoding='utf-8', newline=newline) as f:
f.write(content) f.write(content)
else: else:
print('File %s already exists, skipping.' % fpath) if 'quiet' not in d:
print('File %s already exists, skipping.' % fpath)
conf_path = os.path.join(templatedir, 'conf.py_t') if templatedir else None conf_path = os.path.join(templatedir, 'conf.py_t') if templatedir else None
if not conf_path or not path.isfile(conf_path): if not conf_path or not path.isfile(conf_path):
@ -587,28 +562,28 @@ Makefile to be used with sphinx-build.
group = parser.add_argument_group('Extension options') group = parser.add_argument_group('Extension options')
for ext in EXTENSIONS: for ext in EXTENSIONS:
group.add_argument('--ext-' + ext, action='store_true', group.add_argument('--ext-%s' % ext, action='append_const',
dest='ext_' + ext, default=False, const='sphinx.ext.%s' % ext, dest='extensions',
help='enable %s extension' % ext) help='enable %s extension' % ext)
group.add_argument('--extensions', metavar='EXTENSIONS', dest='extensions', group.add_argument('--extensions', metavar='EXTENSIONS', dest='extensions',
action='append', help='enable extensions') action='append', help='enable arbitrary extensions')
# TODO(stephenfin): Consider using mutually exclusive groups here
group = parser.add_argument_group('Makefile and Batchfile creation') group = parser.add_argument_group('Makefile and Batchfile creation')
group.add_argument('--makefile', action='store_true', default=False, group.add_argument('--makefile', action='store_true', dest='makefile',
help='create makefile') help='create makefile')
group.add_argument('--no-makefile', action='store_true', default=False, group.add_argument('--no-makefile', action='store_false', dest='makefile',
help='not create makefile') help='do not create makefile')
group.add_argument('--batchfile', action='store_true', default=False, group.add_argument('--batchfile', action='store_true', dest='batchfile',
help='create batchfile') help='create batchfile')
group.add_argument('--no-batchfile', action='store_true', default=False, group.add_argument('--no-batchfile', action='store_false',
help='not create batchfile') dest='batchfile',
group.add_argument('-M', '--no-use-make-mode', action='store_false', help='do not create batchfile')
dest='make_mode', default=False,
help='not use make-mode for Makefile/make.bat')
group.add_argument('-m', '--use-make-mode', action='store_true', group.add_argument('-m', '--use-make-mode', action='store_true',
dest='make_mode', default=True, dest='make_mode', default=True,
help='use make-mode for Makefile/make.bat') help='use make-mode for Makefile/make.bat')
group.add_argument('-M', '--no-use-make-mode', action='store_false',
dest='make_mode',
help='do not use make-mode for Makefile/make.bat')
group = parser.add_argument_group('Project templating') group = parser.add_argument_group('Project templating')
group.add_argument('-t', '--templatedir', metavar='TEMPLATEDIR', group.add_argument('-t', '--templatedir', metavar='TEMPLATEDIR',
@ -648,14 +623,9 @@ def main(argv=sys.argv[1:]):
# quiet mode with all required params satisfied, use default # quiet mode with all required params satisfied, use default
d.setdefault('version', '') d.setdefault('version', '')
d.setdefault('release', d['version']) d.setdefault('release', d['version'])
d2 = DEFAULT_VALUE.copy() d2 = DEFAULTS.copy()
d2.update(dict(("ext_" + ext, False) for ext in EXTENSIONS))
d2.update(d) d2.update(d)
d = d2 d = d2
if 'no_makefile' in d:
d['makefile'] = False
if 'no_batchfile' in d:
d['batchfile'] = False
if not valid_dir(d): if not valid_dir(d):
print() print()
@ -676,13 +646,12 @@ def main(argv=sys.argv[1:]):
if isinstance(value, binary_type): if isinstance(value, binary_type):
d[key] = term_decode(value) d[key] = term_decode(value)
# parse extensions list # handle use of CSV-style extension values
d.setdefault('extensions', []) d.setdefault('extensions', [])
for ext in d['extensions'][:]: for ext in d['extensions'][:]:
if ',' in ext: if ',' in ext:
d['extensions'].remove(ext) d['extensions'].remove(ext)
for modname in ext.split(','): d['extensions'].extend(ext.split(','))
d['extensions'].append(modname)
for variable in d.get('variables', []): for variable in d.get('variables', []):
try: try:

View File

@ -5,7 +5,7 @@
sphinx-build command-line handling. sphinx-build command-line handling.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
from __future__ import print_function from __future__ import print_function
@ -182,11 +182,7 @@ def main(argv=sys.argv[1:]): # type: ignore
# type: (List[unicode]) -> int # type: (List[unicode]) -> int
parser = get_parser() parser = get_parser()
# parse options args = parser.parse_args(argv)
try:
args = parser.parse_args(argv)
except SystemExit as err:
return err.code
# get paths (first and second positional argument) # get paths (first and second positional argument)
try: try:
@ -194,34 +190,28 @@ def main(argv=sys.argv[1:]): # type: ignore
confdir = abspath(args.confdir or srcdir) confdir = abspath(args.confdir or srcdir)
if args.noconfig: if args.noconfig:
confdir = None confdir = None
if not path.isdir(srcdir): if not path.isdir(srcdir):
print('Error: Cannot find source directory `%s\'.' % srcdir, parser.error('cannot find source directory (%s)' % srcdir)
file=sys.stderr)
return 1
if not args.noconfig and not path.isfile(path.join(confdir, 'conf.py')): if not args.noconfig and not path.isfile(path.join(confdir, 'conf.py')):
print('Error: Config directory doesn\'t contain a conf.py file.', parser.error("config directory doesn't contain a conf.py file "
file=sys.stderr) "(%s)" % confdir)
return 1
outdir = abspath(args.outputdir) outdir = abspath(args.outputdir)
if srcdir == outdir: if srcdir == outdir:
print('Error: source directory and destination directory are same.', parser.error('source directory and destination directory are same')
file=sys.stderr)
return 1
except UnicodeError: except UnicodeError:
print( parser.error('multibyte filename not supported on this filesystem '
'Error: Multibyte filename not supported on this filesystem ' 'encoding (%r)' % fs_encoding)
'encoding (%r).' % fs_encoding, file=sys.stderr)
return 1
# handle remaining filename arguments # handle remaining filename arguments
filenames = args.filenames filenames = args.filenames
errored = False missing_files = []
for filename in filenames: for filename in filenames:
if not path.isfile(filename): if not path.isfile(filename):
print('Error: Cannot find file %r.' % filename, file=sys.stderr) missing_files.append(filename)
errored = True if missing_files:
if errored: parser.error('cannot find files %r' % missing_files)
return 1
# likely encoding used for command-line arguments # likely encoding used for command-line arguments
try: try:
@ -231,8 +221,7 @@ def main(argv=sys.argv[1:]): # type: ignore
likely_encoding = None likely_encoding = None
if args.force_all and filenames: if args.force_all and filenames:
print('Error: Cannot combine -a option and filenames.', file=sys.stderr) parser.error('cannot combine -a option and filenames')
return 1
if args.color == 'no' or (args.color == 'auto' and not color_terminal()): if args.color == 'no' or (args.color == 'auto' and not color_terminal()):
nocolor() nocolor()
@ -245,15 +234,16 @@ def main(argv=sys.argv[1:]): # type: ignore
if args.quiet: if args.quiet:
status = None status = None
if args.really_quiet: if args.really_quiet:
status = warning = None status = warning = None
if warning and args.warnfile: if warning and args.warnfile:
try: try:
warnfp = open(args.warnfile, 'w') warnfp = open(args.warnfile, 'w')
except Exception as exc: except Exception as exc:
print('Error: Cannot open warning file %r: %s' % parser.error('cannot open warning file %r: %s' % (
(args.warnfile, exc), file=sys.stderr) args.warnfile, exc))
sys.exit(1)
warning = Tee(warning, warnfp) # type: ignore warning = Tee(warning, warnfp) # type: ignore
error = warning error = warning
@ -262,9 +252,7 @@ def main(argv=sys.argv[1:]): # type: ignore
try: try:
key, val = val.split('=', 1) key, val = val.split('=', 1)
except ValueError: except ValueError:
print('Error: -D option argument must be in the form name=value.', parser.error('-D option argument must be in the form name=value')
file=sys.stderr)
return 1
if likely_encoding and isinstance(val, binary_type): if likely_encoding and isinstance(val, binary_type):
try: try:
val = val.decode(likely_encoding) val = val.decode(likely_encoding)
@ -276,9 +264,7 @@ def main(argv=sys.argv[1:]): # type: ignore
try: try:
key, val = val.split('=') key, val = val.split('=')
except ValueError: except ValueError:
print('Error: -A option argument must be in the form name=value.', parser.error('-A option argument must be in the form name=value')
file=sys.stderr)
return 1
try: try:
val = int(val) val = int(val)
except ValueError: except ValueError:
@ -302,4 +288,4 @@ def main(argv=sys.argv[1:]): # type: ignore
return app.statuscode return app.statuscode
except (Exception, KeyboardInterrupt) as exc: except (Exception, KeyboardInterrupt) as exc:
handle_exception(app, args, exc, error) handle_exception(app, args, exc, error)
return 1 return 2

View File

@ -5,11 +5,12 @@
Build configuration file handling. Build configuration file handling.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
import re import re
import traceback
from os import path, getenv from os import path, getenv
from six import PY2, PY3, iteritems, string_types, binary_type, text_type, integer_types from six import PY2, PY3, iteritems, string_types, binary_type, text_type, integer_types
@ -35,6 +36,7 @@ copyright_year_re = re.compile(r'^((\d{4}-)?)(\d{4})(?=[ ,])')
CONFIG_SYNTAX_ERROR = "There is a syntax error in your configuration file: %s" CONFIG_SYNTAX_ERROR = "There is a syntax error in your configuration file: %s"
if PY3: if PY3:
CONFIG_SYNTAX_ERROR += "\nDid you change the syntax from 2.x to 3.x?" CONFIG_SYNTAX_ERROR += "\nDid you change the syntax from 2.x to 3.x?"
CONFIG_ERROR = "There is a programable error in your configuration file:\n\n%s"
CONFIG_EXIT_ERROR = "The configuration file (or one of the modules it imports) " \ CONFIG_EXIT_ERROR = "The configuration file (or one of the modules it imports) " \
"called sys.exit()" "called sys.exit()"
CONFIG_ENUM_WARNING = "The config value `{name}` has to be a one of {candidates}, " \ CONFIG_ENUM_WARNING = "The config value `{name}` has to be a one of {candidates}, " \
@ -123,6 +125,7 @@ class Config(object):
primary_domain = ('py', 'env', [NoneType]), primary_domain = ('py', 'env', [NoneType]),
needs_sphinx = (None, None, string_classes), needs_sphinx = (None, None, string_classes),
needs_extensions = ({}, None), needs_extensions = ({}, None),
manpages_url = (None, 'env'),
nitpicky = (False, None), nitpicky = (False, None),
nitpick_ignore = ([], None), nitpick_ignore = ([], None),
numfig = (False, 'env'), numfig = (False, 'env'),
@ -135,6 +138,11 @@ class Config(object):
tls_verify = (True, 'env'), tls_verify = (True, 'env'),
tls_cacerts = (None, 'env'), tls_cacerts = (None, 'env'),
smartquotes = (True, 'env'),
smartquotes_action = ('qDe', 'env'),
smartquotes_excludes = ({'languages': ['ja'],
'builders': ['man', 'text']},
'env'),
) # type: Dict[unicode, Tuple] ) # type: Dict[unicode, Tuple]
def __init__(self, dirname, filename, overrides, tags): def __init__(self, dirname, filename, overrides, tags):
@ -155,6 +163,8 @@ class Config(object):
raise ConfigError(CONFIG_SYNTAX_ERROR % err) raise ConfigError(CONFIG_SYNTAX_ERROR % err)
except SystemExit: except SystemExit:
raise ConfigError(CONFIG_EXIT_ERROR) raise ConfigError(CONFIG_EXIT_ERROR)
except Exception:
raise ConfigError(CONFIG_ERROR % traceback.format_exc())
self._raw_config = config self._raw_config = config
# these two must be preinitialized because extensions can add their # these two must be preinitialized because extensions can add their
@ -291,7 +301,7 @@ class Config(object):
logger.warning("%s", exc) logger.warning("%s", exc)
for name in config: for name in config:
if name in self.values: if name in self.values:
self.__dict__[name] = config[name] self.__dict__[name] = config[name] # type: ignore
if isinstance(self.source_suffix, string_types): # type: ignore if isinstance(self.source_suffix, string_types): # type: ignore
self.source_suffix = [self.source_suffix] # type: ignore self.source_suffix = [self.source_suffix] # type: ignore

View File

@ -5,7 +5,7 @@
Sphinx deprecation classes and utilities. Sphinx deprecation classes and utilities.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """

View File

@ -5,7 +5,7 @@
Handlers for additional ReST directives. Handlers for additional ReST directives.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """

View File

@ -3,7 +3,7 @@
sphinx.directives.code sphinx.directives.code
~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """

View File

@ -3,7 +3,7 @@
sphinx.directives.other sphinx.directives.other
~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """

View File

@ -3,7 +3,7 @@
sphinx.directives.patches sphinx.directives.patches
~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """

View File

@ -6,7 +6,7 @@
Support for domains, which are groupings of description directives Support for domains, which are groupings of description directives
and roles describing e.g. constructs of one programming language. and roles describing e.g. constructs of one programming language.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
@ -141,7 +141,7 @@ class Domain(object):
#: domain label: longer, more descriptive (used in messages) #: domain label: longer, more descriptive (used in messages)
label = '' label = ''
#: type (usually directive) name -> ObjType instance #: type (usually directive) name -> ObjType instance
object_types = {} # type: Dict[unicode, Any] object_types = {} # type: Dict[unicode, ObjType]
#: directive name -> directive class #: directive name -> directive class
directives = {} # type: Dict[unicode, Any] directives = {} # type: Dict[unicode, Any]
#: role name -> role callable #: role name -> role callable
@ -161,6 +161,17 @@ class Domain(object):
def __init__(self, env): def __init__(self, env):
# type: (BuildEnvironment) -> None # type: (BuildEnvironment) -> None
self.env = env # type: BuildEnvironment self.env = env # type: BuildEnvironment
self._role_cache = {} # type: Dict[unicode, Callable]
self._directive_cache = {} # type: Dict[unicode, Callable]
self._role2type = {} # type: Dict[unicode, List[unicode]]
self._type2role = {} # type: Dict[unicode, unicode]
# convert class variables to instance one (to enhance through API)
self.object_types = dict(self.object_types)
self.directives = dict(self.directives)
self.roles = dict(self.roles)
self.indices = list(self.indices)
if self.name not in env.domaindata: if self.name not in env.domaindata:
assert isinstance(self.initial_data, dict) assert isinstance(self.initial_data, dict)
new_data = copy.deepcopy(self.initial_data) new_data = copy.deepcopy(self.initial_data)
@ -170,10 +181,6 @@ class Domain(object):
self.data = env.domaindata[self.name] self.data = env.domaindata[self.name]
if self.data['version'] != self.data_version: if self.data['version'] != self.data_version:
raise IOError('data of %r domain out of date' % self.label) raise IOError('data of %r domain out of date' % self.label)
self._role_cache = {} # type: Dict[unicode, Callable]
self._directive_cache = {} # type: Dict[unicode, Callable]
self._role2type = {} # type: Dict[unicode, List[unicode]]
self._type2role = {} # type: Dict[unicode, unicode]
for name, obj in iteritems(self.object_types): for name, obj in iteritems(self.object_types):
for rolename in obj.roles: for rolename in obj.roles:
self._role2type.setdefault(rolename, []).append(name) self._role2type.setdefault(rolename, []).append(name)
@ -181,6 +188,18 @@ class Domain(object):
self.objtypes_for_role = self._role2type.get # type: Callable[[unicode], List[unicode]] # NOQA self.objtypes_for_role = self._role2type.get # type: Callable[[unicode], List[unicode]] # NOQA
self.role_for_objtype = self._type2role.get # type: Callable[[unicode], unicode] self.role_for_objtype = self._type2role.get # type: Callable[[unicode], unicode]
def add_object_type(self, name, objtype):
# type: (unicode, ObjType) -> None
"""Add an object type."""
self.object_types[name] = objtype
if objtype.roles:
self._type2role[name] = objtype.roles[0]
else:
self._type2role[name] = ''
for role in objtype.roles:
self._role2type.setdefault(role, []).append(name)
def role(self, name): def role(self, name):
# type: (unicode) -> Callable # type: (unicode) -> Callable
"""Return a role adapter function that always gives the registered """Return a role adapter function that always gives the registered

View File

@ -5,7 +5,7 @@
The C language domain. The C language domain.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,7 @@
The JavaScript domain. The JavaScript domain.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """

View File

@ -5,7 +5,7 @@
The Python domain. The Python domain.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """

View File

@ -5,7 +5,7 @@
The reStructuredText domain. The reStructuredText domain.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """

View File

@ -5,7 +5,7 @@
The standard domain. The standard domain.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
@ -959,12 +959,18 @@ class StandardDomain(Domain):
def get_full_qualified_name(self, node): def get_full_qualified_name(self, node):
# type: (nodes.Node) -> unicode # type: (nodes.Node) -> unicode
progname = node.get('std:program') if node.get('reftype') == 'option':
target = node.get('reftarget') progname = node.get('std:program')
if progname is None or target is None: command = ws_re.split(node.get('reftarget'))
return None if progname:
command.insert(0, progname)
option = command.pop()
if command:
return '.'.join(['-'.join(command), option])
else:
return None
else: else:
return '.'.join([progname, target]) return None
def setup(app): def setup(app):

View File

@ -5,7 +5,7 @@
Global creation environment. Global creation environment.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
@ -14,27 +14,23 @@ import os
import sys import sys
import time import time
import types import types
import codecs
import fnmatch import fnmatch
import warnings import warnings
from os import path from os import path
from copy import copy from copy import copy
from collections import defaultdict from collections import defaultdict
from contextlib import contextmanager
from six import BytesIO, itervalues, class_types, next from six import BytesIO, itervalues, class_types, next, iteritems
from six.moves import cPickle as pickle from six.moves import cPickle as pickle
from docutils.io import NullOutput
from docutils.core import Publisher
from docutils.utils import Reporter, get_source_line, normalize_language_tag from docutils.utils import Reporter, get_source_line, normalize_language_tag
from docutils.utils.smartquotes import smartchars from docutils.utils.smartquotes import smartchars
from docutils.parsers.rst import roles
from docutils.parsers.rst.languages import en as english
from docutils.frontend import OptionParser from docutils.frontend import OptionParser
from sphinx import addnodes from sphinx import addnodes, versioning
from sphinx.io import SphinxStandaloneReader, SphinxDummyWriter, SphinxFileInput from sphinx.io import read_doc
from sphinx.util import logging from sphinx.util import logging, rst
from sphinx.util import get_matching_docs, FilenameUniqDict, status_iterator from sphinx.util import get_matching_docs, FilenameUniqDict, status_iterator
from sphinx.util.nodes import is_translatable from sphinx.util.nodes import is_translatable
from sphinx.util.osutil import SEP, ensuredir from sphinx.util.osutil import SEP, ensuredir
@ -45,16 +41,14 @@ from sphinx.util.matching import compile_matchers
from sphinx.util.parallel import ParallelTasks, parallel_available, make_chunks from sphinx.util.parallel import ParallelTasks, parallel_available, make_chunks
from sphinx.util.websupport import is_commentable from sphinx.util.websupport import is_commentable
from sphinx.errors import SphinxError, ExtensionError from sphinx.errors import SphinxError, ExtensionError
from sphinx.locale import __ from sphinx.transforms import SphinxTransformer, SphinxSmartQuotes
from sphinx.transforms import SphinxTransformer
from sphinx.versioning import add_uids, merge_doctrees
from sphinx.deprecation import RemovedInSphinx20Warning from sphinx.deprecation import RemovedInSphinx20Warning
from sphinx.environment.adapters.indexentries import IndexEntries from sphinx.environment.adapters.indexentries import IndexEntries
from sphinx.environment.adapters.toctree import TocTree from sphinx.environment.adapters.toctree import TocTree
if False: if False:
# For type annotation # For type annotation
from typing import Any, Callable, Dict, IO, Iterator, List, Pattern, Set, Tuple, Type, Union # NOQA from typing import Any, Callable, Dict, IO, Iterator, List, Pattern, Set, Tuple, Type, Union, Generator # NOQA
from docutils import nodes # NOQA from docutils import nodes # NOQA
from sphinx.application import Sphinx # NOQA from sphinx.application import Sphinx # NOQA
from sphinx.builders import Builder # NOQA from sphinx.builders import Builder # NOQA
@ -73,6 +67,7 @@ default_settings = {
'sectsubtitle_xform': False, 'sectsubtitle_xform': False,
'halt_level': 5, 'halt_level': 5,
'file_insertion_enabled': True, 'file_insertion_enabled': True,
'smartquotes_locales': [],
} }
# This is increased every time an environment attribute is added # This is increased every time an environment attribute is added
@ -82,8 +77,6 @@ default_settings = {
ENV_VERSION = 52 + (sys.version_info[0] - 2) ENV_VERSION = 52 + (sys.version_info[0] - 2)
dummy_reporter = Reporter('', 4, 4)
versioning_conditions = { versioning_conditions = {
'none': False, 'none': False,
'text': is_translatable, 'text': is_translatable,
@ -91,6 +84,22 @@ versioning_conditions = {
} # type: Dict[unicode, Union[bool, Callable]] } # type: Dict[unicode, Union[bool, Callable]]
@contextmanager
def sphinx_smartquotes_action(env):
# type: (BuildEnvironment) -> Generator
if not hasattr(SphinxSmartQuotes, 'smartquotes_action'):
# less than docutils-0.14
yield
else:
# docutils-0.14 or above
try:
original = SphinxSmartQuotes.smartquotes_action
SphinxSmartQuotes.smartquotes_action = env.config.smartquotes_action
yield
finally:
SphinxSmartQuotes.smartquotes_action = original
class NoUri(Exception): class NoUri(Exception):
"""Raised by get_relative_uri if there is no URI available.""" """Raised by get_relative_uri if there is no URI available."""
pass pass
@ -566,21 +575,10 @@ class BuildEnvironment(object):
self.app.emit('env-before-read-docs', self, docnames) self.app.emit('env-before-read-docs', self, docnames)
# check if we should do parallel or serial read # check if we should do parallel or serial read
par_ok = False
if parallel_available and len(docnames) > 5 and self.app.parallel > 1: if parallel_available and len(docnames) > 5 and self.app.parallel > 1:
for ext in itervalues(self.app.extensions): par_ok = self.app.is_parallel_allowed('read')
if ext.parallel_read_safe is None: else:
logger.warning(__('the %s extension does not declare if it is safe ' par_ok = False
'for parallel reading, assuming it isn\'t - please '
'ask the extension author to check and make it '
'explicit'), ext.name)
logger.warning('doing serial read')
break
elif ext.parallel_read_safe is False:
break
else:
# all extensions support parallel-read
par_ok = True
if par_ok: if par_ok:
self._read_parallel(docnames, self.app, nproc=self.app.parallel) self._read_parallel(docnames, self.app, nproc=self.app.parallel)
@ -604,7 +602,8 @@ class BuildEnvironment(object):
# remove all inventory entries for that file # remove all inventory entries for that file
app.emit('env-purge-doc', self, docname) app.emit('env-purge-doc', self, docname)
self.clear_doc(docname) self.clear_doc(docname)
self.read_doc(docname, app) with sphinx_smartquotes_action(self):
self.read_doc(docname, app)
def _read_parallel(self, docnames, app, nproc): def _read_parallel(self, docnames, app, nproc):
# type: (List[unicode], Sphinx, int) -> None # type: (List[unicode], Sphinx, int) -> None
@ -616,8 +615,9 @@ class BuildEnvironment(object):
def read_process(docs): def read_process(docs):
# type: (List[unicode]) -> unicode # type: (List[unicode]) -> unicode
self.app = app self.app = app
for docname in docs: with sphinx_smartquotes_action(self):
self.read_doc(docname, app) for docname in docs:
self.read_doc(docname, app)
# allow pickling self to send it back # allow pickling self to send it back
return BuildEnvironment.dumps(self) return BuildEnvironment.dumps(self)
@ -648,25 +648,9 @@ class BuildEnvironment(object):
# --------- SINGLE FILE READING -------------------------------------------- # --------- SINGLE FILE READING --------------------------------------------
def warn_and_replace(self, error): def prepare_settings(self, docname):
# type: (Any) -> Tuple # type: (unicode) -> None
"""Custom decoding error handler that warns and replaces.""" """Prepare to set up environment for reading."""
linestart = error.object.rfind(b'\n', 0, error.start)
lineend = error.object.find(b'\n', error.start)
if lineend == -1:
lineend = len(error.object)
lineno = error.object.count(b'\n', 0, error.start) + 1
logger.warning('undecodable source characters, replacing with "?": %r',
(error.object[linestart + 1:error.start] + b'>>>' +
error.object[error.start:error.end] + b'<<<' +
error.object[error.end:lineend]),
location=(self.docname, lineno))
return (u'?', error.end)
def read_doc(self, docname, app=None):
# type: (unicode, Sphinx) -> None
"""Parse a file and add/update inventory entries for the doctree."""
self.temp_data['docname'] = docname self.temp_data['docname'] = docname
# defaults to the global default, but can be re-set in a document # defaults to the global default, but can be re-set in a document
self.temp_data['default_role'] = self.config.default_role self.temp_data['default_role'] = self.config.default_role
@ -681,7 +665,19 @@ class BuildEnvironment(object):
language = self.config.language or 'en' language = self.config.language or 'en'
self.settings['language_code'] = language self.settings['language_code'] = language
if 'smart_quotes' not in self.settings: if 'smart_quotes' not in self.settings:
self.settings['smart_quotes'] = True self.settings['smart_quotes'] = self.config.smartquotes
# some conditions exclude smart quotes, overriding smart_quotes
for valname, vallist in iteritems(self.config.smartquotes_excludes):
if valname == 'builders':
# this will work only for checking first build target
if self.app.builder.name in vallist:
self.settings['smart_quotes'] = False
break
elif valname == 'languages':
if self.config.language in vallist:
self.settings['smart_quotes'] = False
break
# confirm selected language supports smart_quotes or not # confirm selected language supports smart_quotes or not
for tag in normalize_language_tag(language): for tag in normalize_language_tag(language):
@ -690,40 +686,19 @@ class BuildEnvironment(object):
else: else:
self.settings['smart_quotes'] = False self.settings['smart_quotes'] = False
def read_doc(self, docname, app=None):
# type: (unicode, Sphinx) -> None
"""Parse a file and add/update inventory entries for the doctree."""
self.prepare_settings(docname)
docutilsconf = path.join(self.srcdir, 'docutils.conf') docutilsconf = path.join(self.srcdir, 'docutils.conf')
# read docutils.conf from source dir, not from current dir # read docutils.conf from source dir, not from current dir
OptionParser.standard_config_files[1] = docutilsconf OptionParser.standard_config_files[1] = docutilsconf
if path.isfile(docutilsconf): if path.isfile(docutilsconf):
self.note_dependency(docutilsconf) self.note_dependency(docutilsconf)
with sphinx_domains(self): with sphinx_domains(self), rst.default_role(docname, self.config.default_role):
if self.config.default_role: doctree = read_doc(self.app, self, self.doc2path(docname))
role_fn, messages = roles.role(self.config.default_role, english,
0, dummy_reporter)
if role_fn:
roles._roles[''] = role_fn
else:
logger.warning('default role %s not found', self.config.default_role,
location=docname)
codecs.register_error('sphinx', self.warn_and_replace) # type: ignore
# publish manually
reader = SphinxStandaloneReader(self.app,
parsers=self.app.registry.get_source_parsers())
pub = Publisher(reader=reader,
writer=SphinxDummyWriter(),
destination_class=NullOutput)
pub.set_components(None, 'restructuredtext', None)
pub.process_programmatic_settings(None, self.settings, None)
src_path = self.doc2path(docname)
source = SphinxFileInput(app, self, source=None, source_path=src_path,
encoding=self.config.source_encoding)
pub.source = source
pub.settings._source = src_path
pub.set_destination(None, None)
pub.publish()
doctree = pub.document
# post-processing # post-processing
for domain in itervalues(self.domains): for domain in itervalues(self.domains):
@ -741,41 +716,14 @@ class BuildEnvironment(object):
time.time(), path.getmtime(self.doc2path(docname))) time.time(), path.getmtime(self.doc2path(docname)))
if self.versioning_condition: if self.versioning_condition:
old_doctree = None
if self.versioning_compare:
# get old doctree
try:
with open(self.doc2path(docname,
self.doctreedir, '.doctree'), 'rb') as f:
old_doctree = pickle.load(f)
except EnvironmentError:
pass
# add uids for versioning # add uids for versioning
if not self.versioning_compare or old_doctree is None: versioning.prepare(doctree)
list(add_uids(doctree, self.versioning_condition))
else:
list(merge_doctrees(
old_doctree, doctree, self.versioning_condition))
# make it picklable
doctree.reporter = None
doctree.transformer = None
doctree.settings.warning_stream = None
doctree.settings.env = None
doctree.settings.record_dependencies = None
# cleanup # cleanup
self.temp_data.clear() self.temp_data.clear()
self.ref_context.clear() self.ref_context.clear()
roles._roles.pop('', None) # if a document has set a local default role
# save the parsed doctree self.write_doctree(docname, doctree)
doctree_filename = self.doc2path(docname, self.doctreedir,
'.doctree')
ensuredir(path.dirname(doctree_filename))
with open(doctree_filename, 'wb') as f:
pickle.dump(doctree, f, pickle.HIGHEST_PROTOCOL)
# utilities to use while reading a document # utilities to use while reading a document
@ -879,6 +827,21 @@ class BuildEnvironment(object):
doctree.reporter = Reporter(self.doc2path(docname), 2, 5, stream=WarningStream()) doctree.reporter = Reporter(self.doc2path(docname), 2, 5, stream=WarningStream())
return doctree return doctree
def write_doctree(self, docname, doctree):
# type: (unicode, nodes.Node) -> None
"""Write the doctree to a file."""
# make it picklable
doctree.reporter = None
doctree.transformer = None
doctree.settings.warning_stream = None
doctree.settings.env = None
doctree.settings.record_dependencies = None
doctree_filename = self.doc2path(docname, self.doctreedir, '.doctree')
ensuredir(path.dirname(doctree_filename))
with open(doctree_filename, 'wb') as f:
pickle.dump(doctree, f, pickle.HIGHEST_PROTOCOL)
def get_and_resolve_doctree(self, docname, builder, doctree=None, def get_and_resolve_doctree(self, docname, builder, doctree=None,
prune_toctrees=True, includehidden=False): prune_toctrees=True, includehidden=False):
# type: (unicode, Builder, nodes.Node, bool, bool) -> nodes.Node # type: (unicode, Builder, nodes.Node, bool, bool) -> nodes.Node
@ -935,7 +898,7 @@ class BuildEnvironment(object):
transformer = SphinxTransformer(doctree) transformer = SphinxTransformer(doctree)
transformer.set_environment(self) transformer.set_environment(self)
transformer.add_transforms(self.app.post_transforms) transformer.add_transforms(self.app.registry.get_post_transforms())
transformer.apply_transforms() transformer.apply_transforms()
finally: finally:
self.temp_data = backup self.temp_data = backup

View File

@ -5,6 +5,6 @@
Sphinx environment adapters Sphinx environment adapters
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """

View File

@ -5,7 +5,7 @@
Assets adapter for sphinx.environment. Assets adapter for sphinx.environment.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """

View File

@ -5,7 +5,7 @@
Index entries adapters for sphinx.environment. Index entries adapters for sphinx.environment.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
import re import re

View File

@ -5,7 +5,7 @@
Toctree adapter for sphinx.environment. Toctree adapter for sphinx.environment.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """

View File

@ -5,7 +5,7 @@
The data collector components for sphinx.environment. The data collector components for sphinx.environment.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """

View File

@ -5,7 +5,7 @@
The image collector for sphinx.environment. The image collector for sphinx.environment.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """

View File

@ -5,7 +5,7 @@
The dependencies collector components for sphinx.environment. The dependencies collector components for sphinx.environment.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """

View File

@ -5,7 +5,7 @@
Index entries collector for sphinx.environment. Index entries collector for sphinx.environment.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """

View File

@ -5,7 +5,7 @@
The metadata collector components for sphinx.environment. The metadata collector components for sphinx.environment.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """

View File

@ -5,7 +5,7 @@
The title collector components for sphinx.environment. The title collector components for sphinx.environment.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """

View File

@ -5,7 +5,7 @@
Toctree collector for sphinx.environment. Toctree collector for sphinx.environment.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
@ -262,7 +262,7 @@ class TocTreeCollector(EnvironmentCollector):
continue continue
figtype = env.get_domain('std').get_figtype(subnode) # type: ignore figtype = env.get_domain('std').get_figtype(subnode)
if figtype and subnode['ids']: if figtype and subnode['ids']:
register_fignumber(docname, secnum, figtype, subnode) register_fignumber(docname, secnum, figtype, subnode)

View File

@ -6,7 +6,7 @@
Contains SphinxError and a few subclasses (in an extra module to avoid Contains SphinxError and a few subclasses (in an extra module to avoid
circular import problems). circular import problems).
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """

View File

@ -7,7 +7,7 @@
Gracefully adapted from the TextPress system by Armin. Gracefully adapted from the TextPress system by Armin.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
from __future__ import print_function from __future__ import print_function

View File

@ -5,6 +5,6 @@
Contains Sphinx features not activated by default. Contains Sphinx features not activated by default.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """

View File

@ -11,7 +11,7 @@
Copyright 2008 Société des arts technologiques (SAT), Copyright 2008 Société des arts technologiques (SAT),
http://www.sat.qc.ca/ http://www.sat.qc.ca/
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
@ -27,7 +27,7 @@ from fnmatch import fnmatch
from sphinx import __display_version__ from sphinx import __display_version__
from sphinx.cmd.quickstart import EXTENSIONS from sphinx.cmd.quickstart import EXTENSIONS
from sphinx.util import rst from sphinx.util import rst
from sphinx.util.osutil import FileAvoidWrite, walk from sphinx.util.osutil import FileAvoidWrite, ensuredir, walk
if False: if False:
# For type annotation # For type annotation
@ -117,7 +117,11 @@ def create_package_file(root, master_package, subroot, py_files, opts, subs, is_
text += '\n' text += '\n'
# build a list of directories that are szvpackages (contain an INITPY file) # build a list of directories that are szvpackages (contain an INITPY file)
subs = [sub for sub in subs if path.isfile(path.join(root, sub, INITPY))] # and also checks the INITPY file is not empty, or there are other python
# source files in that folder.
# (depending on settings - but shall_skip() takes care of that)
subs = [sub for sub in subs if not
shall_skip(path.join(root, sub, INITPY), opts)]
# if there are some package directories, add a TOC for theses subpackages # if there are some package directories, add a TOC for theses subpackages
if subs: if subs:
text += format_heading(2, 'Subpackages') text += format_heading(2, 'Subpackages')
@ -358,8 +362,8 @@ Note: By default this script will not overwrite already created files.""")
group = parser.add_argument_group('extension options') group = parser.add_argument_group('extension options')
for ext in EXTENSIONS: for ext in EXTENSIONS:
group.add_argument('--ext-' + ext, action='store_true', group.add_argument('--ext-%s' % ext, action='append_const',
dest='ext_' + ext, default=False, const='sphinx.ext.%s' % ext, dest='extensions',
help='enable %s extension' % ext) help='enable %s extension' % ext)
return parser return parser
@ -382,8 +386,8 @@ def main(argv=sys.argv[1:]):
if not path.isdir(rootpath): if not path.isdir(rootpath):
print('%s is not a directory.' % rootpath, file=sys.stderr) print('%s is not a directory.' % rootpath, file=sys.stderr)
sys.exit(1) sys.exit(1)
if not path.isdir(args.destdir) and not args.dryrun: if not args.dryrun:
os.makedirs(args.destdir) ensuredir(args.destdir)
excludes = [path.abspath(exclude) for exclude in args.exclude_pattern] excludes = [path.abspath(exclude) for exclude in args.exclude_pattern]
modules = recurse_tree(rootpath, excludes, args) modules = recurse_tree(rootpath, excludes, args)
@ -408,20 +412,19 @@ def main(argv=sys.argv[1:]):
suffix = '.' + args.suffix, suffix = '.' + args.suffix,
master = 'index', master = 'index',
epub = True, epub = True,
ext_autodoc = True, extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode',
ext_viewcode = True, 'sphinx.ext.todo'],
ext_todo = True,
makefile = True, makefile = True,
batchfile = True, batchfile = True,
make_mode = True,
mastertocmaxdepth = args.maxdepth, mastertocmaxdepth = args.maxdepth,
mastertoctree = text, mastertoctree = text,
language = 'en', language = 'en',
module_path = rootpath, module_path = rootpath,
append_syspath = args.append_syspath, append_syspath = args.append_syspath,
) )
enabled_exts = {'ext_' + ext: getattr(args, 'ext_' + ext) if args.extensions:
for ext in EXTENSIONS if getattr(args, 'ext_' + ext)} d['extensions'].extend(args.extensions)
d.update(enabled_exts)
if isinstance(args.header, binary_type): if isinstance(args.header, binary_type):
d['project'] = d['project'].decode('utf-8') d['project'] = d['project'].decode('utf-8')

View File

@ -7,46 +7,46 @@
the doctree, thus avoiding duplication between docstrings and documentation the doctree, thus avoiding duplication between docstrings and documentation
for those who like elaborate docstrings. for those who like elaborate docstrings.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
import re import re
import sys import sys
import inspect import inspect
import traceback
import warnings import warnings
from six import PY2, iterkeys, iteritems, itervalues, text_type, class_types, string_types from six import iteritems, itervalues, text_type, class_types, string_types
from docutils import nodes
from docutils.utils import assemble_option_dict
from docutils.parsers.rst import Directive
from docutils.statemachine import ViewList from docutils.statemachine import ViewList
import sphinx import sphinx
from sphinx.ext.autodoc.importer import _MockImporter from sphinx.deprecation import RemovedInSphinx20Warning
from sphinx.ext.autodoc.importer import mock, import_object, get_object_members
from sphinx.ext.autodoc.importer import _MockImporter # to keep compatibility # NOQA
from sphinx.ext.autodoc.inspector import format_annotation, formatargspec # to keep compatibility # NOQA from sphinx.ext.autodoc.inspector import format_annotation, formatargspec # to keep compatibility # NOQA
from sphinx.util import rpartition, force_decode from sphinx.util import rpartition, force_decode
from sphinx.locale import _ from sphinx.locale import _
from sphinx.pycode import ModuleAnalyzer, PycodeError from sphinx.pycode import ModuleAnalyzer, PycodeError
from sphinx.application import ExtensionError from sphinx.application import ExtensionError
from sphinx.util import logging from sphinx.util import logging
from sphinx.util.nodes import nested_parse_with_titles
from sphinx.util.inspect import Signature, isdescriptor, safe_getmembers, \ from sphinx.util.inspect import Signature, isdescriptor, safe_getmembers, \
safe_getattr, object_description, is_builtin_class_method, \ safe_getattr, object_description, is_builtin_class_method, \
isenumclass, isenumattribute, getdoc isenumattribute, getdoc
from sphinx.util.docstrings import prepare_docstring from sphinx.util.docstrings import prepare_docstring
if False: if False:
# For type annotation # For type annotation
from types import ModuleType # NOQA from types import ModuleType # NOQA
from typing import Any, Callable, Dict, Iterator, List, Sequence, Set, Tuple, Type, Union # NOQA from typing import Any, Callable, Dict, Iterator, List, Sequence, Set, Tuple, Type, Union # NOQA
from docutils import nodes # NOQA
from docutils.utils import Reporter # NOQA from docutils.utils import Reporter # NOQA
from sphinx.application import Sphinx # NOQA from sphinx.application import Sphinx # NOQA
from sphinx.ext.autodoc.directive import DocumenterBridge # NOQA
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# This type isn't exposed directly in any modules, but can be found # This type isn't exposed directly in any modules, but can be found
# here in most Python versions # here in most Python versions
MethodDescriptorType = type(type.__subclasses__) MethodDescriptorType = type(type.__subclasses__)
@ -63,42 +63,11 @@ py_ext_sig_re = re.compile(
''', re.VERBOSE) ''', re.VERBOSE)
class DefDict(dict):
"""A dict that returns a default on nonexisting keys."""
def __init__(self, default):
# type: (Any) -> None
dict.__init__(self)
self.default = default
def __getitem__(self, key):
# type: (Any) -> Any
try:
return dict.__getitem__(self, key)
except KeyError:
return self.default
def __bool__(self):
# type: () -> bool
# docutils check "if option_spec"
return True
__nonzero__ = __bool__ # for python2 compatibility
def identity(x): def identity(x):
# type: (Any) -> Any # type: (Any) -> Any
return x return x
class Options(dict):
"""A dict/attribute hybrid that returns None on nonexisting keys."""
def __getattr__(self, name):
# type: (unicode) -> Any
try:
return self[name.replace('_', '-')]
except KeyError:
return None
ALL = object() ALL = object()
INSTANCEATTR = object() INSTANCEATTR = object()
@ -146,6 +115,9 @@ class AutodocReporter(object):
""" """
def __init__(self, viewlist, reporter): def __init__(self, viewlist, reporter):
# type: (ViewList, Reporter) -> None # type: (ViewList, Reporter) -> None
warnings.warn('AutodocReporter is now deprecated. '
'Use sphinx.util.docutils.switch_source_input() instead.',
RemovedInSphinx20Warning)
self.viewlist = viewlist self.viewlist = viewlist
self.reporter = reporter self.reporter = reporter
@ -284,14 +256,10 @@ class Documenter(object):
option_spec = {'noindex': bool_option} # type: Dict[unicode, Callable] option_spec = {'noindex': bool_option} # type: Dict[unicode, Callable]
@staticmethod def get_attr(self, obj, name, *defargs):
def get_attr(obj, name, *defargs):
# type: (Any, unicode, Any) -> Any # type: (Any, unicode, Any) -> Any
"""getattr() override for types such as Zope interfaces.""" """getattr() override for types such as Zope interfaces."""
for typ, func in iteritems(AutoDirective._special_attrgetters): return autodoc_attrgetter(self.env.app, obj, name, *defargs)
if isinstance(obj, typ):
return func(obj, name, *defargs)
return safe_getattr(obj, name, *defargs)
@classmethod @classmethod
def can_document_member(cls, member, membername, isattr, parent): def can_document_member(cls, member, membername, isattr, parent):
@ -300,7 +268,7 @@ class Documenter(object):
raise NotImplementedError('must be implemented in subclasses') raise NotImplementedError('must be implemented in subclasses')
def __init__(self, directive, name, indent=u''): def __init__(self, directive, name, indent=u''):
# type: (Directive, unicode, unicode) -> None # type: (DocumenterBridge, unicode, unicode) -> None
self.directive = directive self.directive = directive
self.env = directive.env self.env = directive.env
self.options = directive.genopt self.options = directive.genopt
@ -324,6 +292,12 @@ class Documenter(object):
# the module analyzer to get at attribute docs, or None # the module analyzer to get at attribute docs, or None
self.analyzer = None # type: Any self.analyzer = None # type: Any
@property
def documenters(self):
# type: () -> Dict[unicode, Type[Documenter]]
"""Returns registered Documenter classes"""
return get_documenters(self.env.app)
def add_line(self, line, source, *lineno): def add_line(self, line, source, *lineno):
# type: (unicode, unicode, int) -> None # type: (unicode, unicode, int) -> None
"""Append one line of generated reST to the output.""" """Append one line of generated reST to the output."""
@ -354,8 +328,7 @@ class Documenter(object):
explicit_modname, path, base, args, retann = \ explicit_modname, path, base, args, retann = \
py_ext_sig_re.match(self.name).groups() # type: ignore py_ext_sig_re.match(self.name).groups() # type: ignore
except AttributeError: except AttributeError:
self.directive.warn('invalid signature for auto%s (%r)' % logger.warning('invalid signature for auto%s (%r)' % (self.objtype, self.name))
(self.objtype, self.name))
return False return False
# support explicit module and class name separation via :: # support explicit module and class name separation via ::
@ -384,53 +357,17 @@ class Documenter(object):
Returns True if successful, False if an error occurred. Returns True if successful, False if an error occurred.
""" """
if self.objpath: with mock(self.env.config.autodoc_mock_imports):
logger.debug('[autodoc] from %s import %s', try:
self.modname, '.'.join(self.objpath)) ret = import_object(self.modname, self.objpath, self.objtype,
# always enable mock import hook attrgetter=self.get_attr,
# it will do nothing if autodoc_mock_imports is empty warningiserror=self.env.config.autodoc_warningiserror)
import_hook = _MockImporter(self.env.config.autodoc_mock_imports) self.module, self.parent, self.object_name, self.object = ret
try: return True
logger.debug('[autodoc] import %s', self.modname) except ImportError as exc:
with warnings.catch_warnings(): logger.warning(exc.args[0])
warnings.filterwarnings("ignore", category=ImportWarning) self.env.note_reread()
with logging.skip_warningiserror(not self.env.config.autodoc_warningiserror): return False
__import__(self.modname)
parent = None
obj = self.module = sys.modules[self.modname]
logger.debug('[autodoc] => %r', obj)
for part in self.objpath:
parent = obj
logger.debug('[autodoc] getattr(_, %r)', part)
obj = self.get_attr(obj, part)
logger.debug('[autodoc] => %r', obj)
self.object_name = part
self.parent = parent
self.object = obj
return True
# this used to only catch SyntaxError, ImportError and AttributeError,
# but importing modules with side effects can raise all kinds of errors
except (Exception, SystemExit) as e:
if self.objpath:
errmsg = 'autodoc: failed to import %s %r from module %r' % \
(self.objtype, '.'.join(self.objpath), self.modname)
else:
errmsg = 'autodoc: failed to import %s %r' % \
(self.objtype, self.fullname)
if isinstance(e, SystemExit):
errmsg += ('; the module executes module level statement ' +
'and it might call sys.exit().')
else:
errmsg += '; the following exception was raised:\n%s' % \
traceback.format_exc()
if PY2:
errmsg = errmsg.decode('utf-8') # type: ignore
logger.debug(errmsg)
self.directive.warn(errmsg)
self.env.note_reread()
return False
finally:
import_hook.disable()
def get_real_modname(self): def get_real_modname(self):
# type: () -> str # type: () -> str
@ -488,8 +425,8 @@ class Documenter(object):
try: try:
args = self.format_args() args = self.format_args()
except Exception as err: except Exception as err:
self.directive.warn('error while formatting arguments for ' logger.warning('error while formatting arguments for %s: %s' %
'%s: %s' % (self.fullname, err)) (self.fullname, err))
args = None args = None
retann = self.retann retann = self.retann
@ -601,57 +538,24 @@ class Documenter(object):
If *want_all* is True, return all members. Else, only return those If *want_all* is True, return all members. Else, only return those
members given by *self.options.members* (which may also be none). members given by *self.options.members* (which may also be none).
""" """
analyzed_member_names = set() members = get_object_members(self.object, self.objpath, self.get_attr, self.analyzer)
if self.analyzer:
attr_docs = self.analyzer.find_attr_docs()
namespace = '.'.join(self.objpath)
for item in iteritems(attr_docs):
if item[0][0] == namespace:
analyzed_member_names.add(item[0][1])
if not want_all: if not want_all:
if not self.options.members: if not self.options.members:
return False, [] return False, []
# specific members given # specific members given
members = [] selected = []
for mname in self.options.members: for name in self.options.members:
try: if name in members:
members.append((mname, self.get_attr(self.object, mname))) selected.append((name, members[name].value))
except AttributeError: else:
if mname not in analyzed_member_names: logger.warning('missing attribute %s in object %s' %
self.directive.warn('missing attribute %s in object %s' (name, self.fullname))
% (mname, self.fullname)) return False, sorted(selected)
elif self.options.inherited_members: elif self.options.inherited_members:
# safe_getmembers() uses dir() which pulls in members from all return False, sorted((m.name, m.value) for m in itervalues(members))
# base classes
members = safe_getmembers(self.object, attr_getter=self.get_attr)
else: else:
# __dict__ contains only the members directly defined in return False, sorted((m.name, m.value) for m in itervalues(members)
# the class (but get them via getattr anyway, to e.g. get if m.directly_defined)
# unbound method objects instead of function objects);
# using list(iterkeys()) because apparently there are objects for which
# __dict__ changes while getting attributes
try:
obj_dict = self.get_attr(self.object, '__dict__')
except AttributeError:
members = []
else:
members = [(mname, self.get_attr(self.object, mname, None))
for mname in list(iterkeys(obj_dict))]
# Py34 doesn't have enum members in __dict__.
if isenumclass(self.object):
members.extend(
item for item in self.object.__members__.items()
if item not in members
)
membernames = set(m[0] for m in members)
# add instance attributes from the analyzer
for aname in analyzed_member_names:
if aname not in membernames and \
(want_all or aname in self.options.members):
members.append((aname, INSTANCEATTR))
return False, sorted(members)
def filter_members(self, members, want_all): def filter_members(self, members, want_all):
# type: (List[Tuple[unicode, Any]], bool) -> List[Tuple[unicode, Any, bool]] # type: (List[Tuple[unicode, Any]], bool) -> List[Tuple[unicode, Any, bool]]
@ -710,8 +614,7 @@ class Documenter(object):
elif (namespace, membername) in attr_docs: elif (namespace, membername) in attr_docs:
if want_all and membername.startswith('_'): if want_all and membername.startswith('_'):
# ignore members whose name starts with _ by default # ignore members whose name starts with _ by default
keep = self.options.private_members and \ keep = self.options.private_members
(has_doc or self.options.undoc_members)
else: else:
# keep documented attributes # keep documented attributes
keep = True keep = True
@ -764,7 +667,7 @@ class Documenter(object):
# document non-skipped members # document non-skipped members
memberdocumenters = [] # type: List[Tuple[Documenter, bool]] memberdocumenters = [] # type: List[Tuple[Documenter, bool]]
for (mname, member, isattr) in self.filter_members(members, want_all): for (mname, member, isattr) in self.filter_members(members, want_all):
classes = [cls for cls in itervalues(AutoDirective._registry) classes = [cls for cls in itervalues(self.documenters)
if cls.can_document_member(member, mname, isattr, self)] if cls.can_document_member(member, mname, isattr, self)]
if not classes: if not classes:
# don't know how to document this member # don't know how to document this member
@ -815,11 +718,11 @@ class Documenter(object):
""" """
if not self.parse_name(): if not self.parse_name():
# need a module to import # need a module to import
self.directive.warn( logger.warning(
'don\'t know which module to import for autodocumenting ' 'don\'t know which module to import for autodocumenting '
'%r (try placing a "module" or "currentmodule" directive ' '%r (try placing a "module" or "currentmodule" directive '
'in the document, or giving an explicit module name)' 'in the document, or giving an explicit module name)' %
% self.name) self.name)
return return
# now, import the module and get object to document # now, import the module and get object to document
@ -893,7 +796,7 @@ class ModuleDocumenter(Documenter):
'platform': identity, 'deprecated': bool_option, 'platform': identity, 'deprecated': bool_option,
'member-order': identity, 'exclude-members': members_set_option, 'member-order': identity, 'exclude-members': members_set_option,
'private-members': bool_option, 'special-members': members_option, 'private-members': bool_option, 'special-members': members_option,
'imported-members': bool_option, 'imported-members': bool_option, 'ignore-module-all': bool_option
} # type: Dict[unicode, Callable] } # type: Dict[unicode, Callable]
@classmethod @classmethod
@ -905,15 +808,15 @@ class ModuleDocumenter(Documenter):
def resolve_name(self, modname, parents, path, base): def resolve_name(self, modname, parents, path, base):
# type: (str, Any, str, Any) -> Tuple[str, List[unicode]] # type: (str, Any, str, Any) -> Tuple[str, List[unicode]]
if modname is not None: if modname is not None:
self.directive.warn('"::" in automodule name doesn\'t make sense') logger.warning('"::" in automodule name doesn\'t make sense')
return (path or '') + base, [] return (path or '') + base, []
def parse_name(self): def parse_name(self):
# type: () -> bool # type: () -> bool
ret = Documenter.parse_name(self) ret = Documenter.parse_name(self)
if self.args or self.retann: if self.args or self.retann:
self.directive.warn('signature arguments or return annotation ' logger.warning('signature arguments or return annotation '
'given for automodule %s' % self.fullname) 'given for automodule %s' % self.fullname)
return ret return ret
def add_directive_header(self, sig): def add_directive_header(self, sig):
@ -935,7 +838,8 @@ class ModuleDocumenter(Documenter):
def get_object_members(self, want_all): def get_object_members(self, want_all):
# type: (bool) -> Tuple[bool, List[Tuple[unicode, object]]] # type: (bool) -> Tuple[bool, List[Tuple[unicode, object]]]
if want_all: if want_all:
if not hasattr(self.object, '__all__'): if (self.options.ignore_module_all or not
hasattr(self.object, '__all__')):
# for implicit module members, check __module__ to avoid # for implicit module members, check __module__ to avoid
# documenting imported objects # documenting imported objects
return True, safe_getmembers(self.object) return True, safe_getmembers(self.object)
@ -944,7 +848,7 @@ class ModuleDocumenter(Documenter):
# Sometimes __all__ is broken... # Sometimes __all__ is broken...
if not isinstance(memberlist, (list, tuple)) or not \ if not isinstance(memberlist, (list, tuple)) or not \
all(isinstance(entry, string_types) for entry in memberlist): all(isinstance(entry, string_types) for entry in memberlist):
self.directive.warn( logger.warning(
'__all__ should be a list of strings, not %r ' '__all__ should be a list of strings, not %r '
'(in module %s) -- ignoring __all__' % '(in module %s) -- ignoring __all__' %
(memberlist, self.fullname)) (memberlist, self.fullname))
@ -957,10 +861,10 @@ class ModuleDocumenter(Documenter):
try: try:
ret.append((mname, safe_getattr(self.object, mname))) ret.append((mname, safe_getattr(self.object, mname)))
except AttributeError: except AttributeError:
self.directive.warn( logger.warning(
'missing attribute mentioned in :members: or __all__: ' 'missing attribute mentioned in :members: or __all__: '
'module %s, attribute %s' % ( 'module %s, attribute %s' %
safe_getattr(self.object, '__name__', '???'), mname)) (safe_getattr(self.object, '__name__', '???'), mname))
return False, ret return False, ret
@ -1269,6 +1173,17 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
return return
ModuleLevelDocumenter.document_members(self, all_members) ModuleLevelDocumenter.document_members(self, all_members)
def generate(self, more_content=None, real_modname=None,
check_module=False, all_members=False):
# Do not pass real_modname and use the name from the __module__
# attribute of the class.
# If a class gets imported into the module real_modname
# the analyzer won't find the source of the class, if
# it looks in real_modname.
return super(ClassDocumenter, self).generate(more_content=more_content,
check_module=check_module,
all_members=all_members)
class ExceptionDocumenter(ClassDocumenter): class ExceptionDocumenter(ClassDocumenter):
""" """
@ -1488,118 +1403,56 @@ class InstanceAttributeDocumenter(AttributeDocumenter):
AttributeDocumenter.add_content(self, more_content, no_docstring=True) AttributeDocumenter.add_content(self, more_content, no_docstring=True)
class AutoDirective(Directive): class DeprecatedDict(dict):
""" def __init__(self, message):
The AutoDirective class is used for all autodoc directives. It dispatches self.message = message
most of the work to one of the Documenters, which it selects through its super(DeprecatedDict, self).__init__()
*_registry* dictionary.
The *_special_attrgetters* attribute is used to customize ``getattr()`` def __setitem__(self, key, value):
calls that the Documenters make; its entries are of the form ``type: warnings.warn(self.message, RemovedInSphinx20Warning)
getattr_function``. super(DeprecatedDict, self).__setitem__(key, value)
def setdefault(self, key, default=None):
warnings.warn(self.message, RemovedInSphinx20Warning)
super(DeprecatedDict, self).setdefault(key, default)
def update(self, other=None):
warnings.warn(self.message, RemovedInSphinx20Warning)
super(DeprecatedDict, self).update(other)
class AutodocRegistry(object):
"""
A registry of Documenters and attrgetters.
Note: When importing an object, all items along the import chain are Note: When importing an object, all items along the import chain are
accessed using the descendant's *_special_attrgetters*, thus this accessed using the descendant's *_special_attrgetters*, thus this
dictionary should include all necessary functions for accessing dictionary should include all necessary functions for accessing
attributes of the parents. attributes of the parents.
""" """
# a registry of objtype -> documenter class # a registry of objtype -> documenter class (Deprecated)
_registry = {} # type: Dict[unicode, Type[Documenter]] _registry = DeprecatedDict(
'AutoDirective._registry has been deprecated. '
'Please use app.add_autodocumenter() instead.'
) # type: Dict[unicode, Type[Documenter]]
# a registry of type -> getattr function # a registry of type -> getattr function
_special_attrgetters = {} # type: Dict[Type, Callable] _special_attrgetters = DeprecatedDict(
'AutoDirective._special_attrgetters has been deprecated. '
'Please use app.add_autodoc_attrgetter() instead.'
) # type: Dict[Type, Callable]
# flags that can be given in autodoc_default_flags
_default_flags = set([
'members', 'undoc-members', 'inherited-members', 'show-inheritance',
'private-members', 'special-members',
])
# standard docutils directive settings AutoDirective = AutodocRegistry # for backward compatibility
has_content = True
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = True
# allow any options to be passed; the options are parsed further
# by the selected Documenter
option_spec = DefDict(identity)
def warn(self, msg):
# type: (unicode) -> None
self.warnings.append(self.reporter.warning(msg, line=self.lineno))
def run(self):
# type: () -> List[nodes.Node]
self.filename_set = set() # type: Set[unicode]
# a set of dependent filenames
self.reporter = self.state.document.reporter
self.env = self.state.document.settings.env
self.warnings = [] # type: List[unicode]
self.result = ViewList()
try:
source, lineno = self.reporter.get_source_and_line(self.lineno)
except AttributeError:
source = lineno = None
logger.debug('[autodoc] %s:%s: input:\n%s',
source, lineno, self.block_text)
# find out what documenter to call
objtype = self.name[4:]
doc_class = self._registry[objtype]
# add default flags
for flag in self._default_flags:
if flag not in doc_class.option_spec:
continue
negated = self.options.pop('no-' + flag, 'not given') is None
if flag in self.env.config.autodoc_default_flags and \
not negated:
self.options[flag] = None
# process the options with the selected documenter's option_spec
try:
self.genopt = Options(assemble_option_dict(
self.options.items(), doc_class.option_spec))
except (KeyError, ValueError, TypeError) as err:
# an option is either unknown or has a wrong type
msg = self.reporter.error('An option to %s is either unknown or '
'has an invalid value: %s' % (self.name, err),
line=self.lineno)
return [msg]
# generate the output
documenter = doc_class(self, self.arguments[0])
documenter.generate(more_content=self.content)
if not self.result:
return self.warnings
logger.debug('[autodoc] output:\n%s', '\n'.join(self.result))
# record all filenames as dependencies -- this will at least
# partially make automatic invalidation possible
for fn in self.filename_set:
self.state.document.settings.record_dependencies.add(fn)
# use a custom reporter that correctly assigns lines to source
# filename/description and lineno
old_reporter = self.state.memo.reporter
self.state.memo.reporter = AutodocReporter(self.result,
self.state.memo.reporter)
if documenter.titles_allowed:
node = nodes.section()
# necessary so that the child nodes get the right source/line set
node.document = self.state.document
nested_parse_with_titles(self.state, self.result, node)
else:
node = nodes.paragraph()
node.document = self.state.document
self.state.nested_parse(self.result, 0, node)
self.state.memo.reporter = old_reporter
return self.warnings + node.children
def add_documenter(cls): def add_documenter(cls):
# type: (Type[Documenter]) -> None # type: (Type[Documenter]) -> None
"""Register a new Documenter.""" """Register a new Documenter."""
warnings.warn('sphinx.ext.autodoc.add_documenter() has been deprecated. '
'Please use app.add_autodocumenter() instead.',
RemovedInSphinx20Warning)
if not issubclass(cls, Documenter): if not issubclass(cls, Documenter):
raise ExtensionError('autodoc documenter %r must be a subclass ' raise ExtensionError('autodoc documenter %r must be a subclass '
'of Documenter' % cls) 'of Documenter' % cls)
@ -1610,6 +1463,29 @@ def add_documenter(cls):
AutoDirective._registry[cls.objtype] = cls AutoDirective._registry[cls.objtype] = cls
def get_documenters(app):
# type: (Sphinx) -> Dict[unicode, Type[Documenter]]
"""Returns registered Documenter classes"""
classes = dict(AutoDirective._registry) # registered directly
if app:
classes.update(app.registry.documenters) # registered by API
return classes
def autodoc_attrgetter(app, obj, name, *defargs):
# type: (Sphinx, Any, unicode, Any) -> Any
"""Alternative getattr() for types"""
candidates = dict(AutoDirective._special_attrgetters)
if app:
candidates.update(app.registry.autodoc_attrgettrs)
for typ, func in iteritems(candidates):
if isinstance(obj, typ):
return func(obj, name, *defargs)
return safe_getattr(obj, name, *defargs)
def setup(app): def setup(app):
# type: (Sphinx) -> Dict[unicode, Any] # type: (Sphinx) -> Dict[unicode, Any]
app.add_autodocumenter(ModuleDocumenter) app.add_autodocumenter(ModuleDocumenter)

View File

@ -0,0 +1,155 @@
# -*- coding: utf-8 -*-
"""
sphinx.ext.autodoc.directive
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from docutils import nodes
from docutils.parsers.rst import Directive
from docutils.statemachine import ViewList
from docutils.utils import assemble_option_dict
from sphinx.ext.autodoc import get_documenters
from sphinx.util import logging
from sphinx.util.docutils import switch_source_input
from sphinx.util.nodes import nested_parse_with_titles
if False:
# For type annotation
from typing import Any, Dict, List, Set, Type # NOQA
from docutils.statemachine import State, StateMachine, StringList # NOQA
from docutils.utils import Reporter # NOQA
from sphinx.config import Config # NOQA
from sphinx.environment import BuildEnvironment # NOQA
from sphinx.ext.autodoc import Documenter # NOQA
logger = logging.getLogger(__name__)
# common option names for autodoc directives
AUTODOC_DEFAULT_OPTIONS = ['members', 'undoc-members', 'inherited-members',
'show-inheritance', 'private-members', 'special-members',
'ignore-module-all']
class DummyOptionSpec(object):
"""An option_spec allows any options."""
def __getitem__(self, key):
# type: (Any) -> Any
return lambda x: x
class Options(dict):
"""A dict/attribute hybrid that returns None on nonexisting keys."""
def __getattr__(self, name):
# type: (unicode) -> Any
try:
return self[name.replace('_', '-')]
except KeyError:
return None
class DocumenterBridge(object):
"""A parameters container for Documenters."""
def __init__(self, env, reporter, options, lineno):
# type: (BuildEnvironment, Reporter, Options, int) -> None
self.env = env
self.reporter = reporter
self.genopt = options
self.lineno = lineno
self.filename_set = set() # type: Set[unicode]
self.result = ViewList()
def warn(self, msg):
# type: (unicode) -> None
logger.warning(msg, line=self.lineno)
def process_documenter_options(documenter, config, options):
# type: (Type[Documenter], Config, Dict) -> Options
"""Recognize options of Documenter from user input."""
for name in AUTODOC_DEFAULT_OPTIONS:
if name not in documenter.option_spec:
continue
else:
negated = options.pop('no-' + name, True) is None
if name in config.autodoc_default_flags and not negated:
options[name] = None
return Options(assemble_option_dict(options.items(), documenter.option_spec))
def parse_generated_content(state, content, documenter):
# type: (State, StringList, Documenter) -> List[nodes.Node]
"""Parse a generated content by Documenter."""
with switch_source_input(state, content):
if documenter.titles_allowed:
node = nodes.section()
# necessary so that the child nodes get the right source/line set
node.document = state.document
nested_parse_with_titles(state, content, node)
else:
node = nodes.paragraph()
node.document = state.document
state.nested_parse(content, 0, node)
return node.children
class AutodocDirective(Directive):
"""A directive class for all autodoc directives. It works as a dispatcher of Documenters.
It invokes a Documenter on running. After the processing, it parses and returns
the generated content by Documenter.
"""
option_spec = DummyOptionSpec()
has_content = True
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = True
def run(self):
# type: () -> List[nodes.Node]
env = self.state.document.settings.env
reporter = self.state.document.reporter
try:
source, lineno = reporter.get_source_and_line(self.lineno)
except AttributeError:
source, lineno = (None, None)
logger.debug('[autodoc] %s:%s: input:\n%s', source, lineno, self.block_text)
# look up target Documenter
objtype = self.name[4:] # strip prefix (auto-).
doccls = get_documenters(env.app)[objtype]
# process the options with the selected documenter's option_spec
try:
documenter_options = process_documenter_options(doccls, env.config, self.options)
except (KeyError, ValueError, TypeError) as exc:
# an option is either unknown or has a wrong type
logger.error('An option to %s is either unknown or has an invalid value: %s' %
(self.name, exc), line=lineno)
return []
# generate the output
params = DocumenterBridge(env, reporter, documenter_options, lineno)
documenter = doccls(params, self.arguments[0])
documenter.generate(more_content=self.content)
if not params.result:
return []
logger.debug('[autodoc] output:\n%s', '\n'.join(params.result))
# record all filenames as dependencies -- this will at least
# partially make automatic invalidation possible
for fn in params.filename_set:
self.state.document.settings.record_dependencies.add(fn)
result = parse_generated_content(self.state, params.result, documenter)
return result

View File

@ -5,18 +5,25 @@
Importer utilities for autodoc Importer utilities for autodoc
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
import sys import sys
import warnings
import traceback
import contextlib
from collections import namedtuple
from types import FunctionType, MethodType, ModuleType from types import FunctionType, MethodType, ModuleType
from six import PY2
from sphinx.util import logging from sphinx.util import logging
from sphinx.util.inspect import isenumclass, safe_getattr
if False: if False:
# For type annotation # For type annotation
from typing import Any, List, Set # NOQA from typing import Any, Callable, Dict, Generator, List, Optional # NOQA
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -75,16 +82,9 @@ class _MockModule(ModuleType):
class _MockImporter(object): class _MockImporter(object):
def __init__(self, names): def __init__(self, names):
# type: (List[str]) -> None # type: (List[str]) -> None
self.base_packages = set() # type: Set[str] self.names = names
for n in names:
# Convert module names:
# ['a.b.c', 'd.e']
# to a set of base packages:
# set(['a', 'd'])
self.base_packages.add(n.split('.')[0])
self.mocked_modules = [] # type: List[str] self.mocked_modules = [] # type: List[str]
# enable hook by adding itself to meta_path # enable hook by adding itself to meta_path
sys.meta_path = sys.meta_path + [self] sys.meta_path = sys.meta_path + [self]
@ -100,9 +100,10 @@ class _MockImporter(object):
def find_module(self, name, path=None): def find_module(self, name, path=None):
# type: (str, str) -> Any # type: (str, str) -> Any
base_package = name.split('.')[0] # check if name is (or is a descendant of) one of our base_packages
if base_package in self.base_packages: for n in self.names:
return self if n == name or name.startswith(n + '.'):
return self
return None return None
def load_module(self, name): def load_module(self, name):
@ -116,3 +117,112 @@ class _MockImporter(object):
sys.modules[name] = module sys.modules[name] = module
self.mocked_modules.append(name) self.mocked_modules.append(name)
return module return module
@contextlib.contextmanager
def mock(names):
# type: (List[str]) -> Generator
try:
importer = _MockImporter(names)
yield
finally:
importer.disable()
def import_module(modname, warningiserror=False):
"""
Call __import__(modname), convert exceptions to ImportError
"""
try:
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=ImportWarning)
with logging.skip_warningiserror(not warningiserror):
__import__(modname)
return sys.modules[modname]
except BaseException as exc:
# Importing modules may cause any side effects, including
# SystemExit, so we need to catch all errors.
raise ImportError(exc, traceback.format_exc())
def import_object(modname, objpath, objtype='', attrgetter=safe_getattr, warningiserror=False):
# type: (str, List[unicode], str, Callable[[Any, unicode], Any], bool) -> Any
if objpath:
logger.debug('[autodoc] from %s import %s', modname, '.'.join(objpath))
else:
logger.debug('[autodoc] import %s', modname)
try:
module = import_module(modname, warningiserror=warningiserror)
logger.debug('[autodoc] => %r', module)
obj = module
parent = None
object_name = None
for attrname in objpath:
parent = obj
logger.debug('[autodoc] getattr(_, %r)', attrname)
obj = attrgetter(obj, attrname)
logger.debug('[autodoc] => %r', obj)
object_name = attrname
return [module, parent, object_name, obj]
except (AttributeError, ImportError) as exc:
if objpath:
errmsg = ('autodoc: failed to import %s %r from module %r' %
(objtype, '.'.join(objpath), modname))
else:
errmsg = 'autodoc: failed to import %s %r' % (objtype, modname)
if isinstance(exc, ImportError):
# import_module() raises ImportError having real exception obj and
# traceback
real_exc, traceback_msg = exc.args
if isinstance(real_exc, SystemExit):
errmsg += ('; the module executes module level statement '
'and it might call sys.exit().')
elif isinstance(real_exc, ImportError):
errmsg += '; the following exception was raised:\n%s' % real_exc.args[0]
else:
errmsg += '; the following exception was raised:\n%s' % traceback_msg
else:
errmsg += '; the following exception was raised:\n%s' % traceback.format_exc()
if PY2:
errmsg = errmsg.decode('utf-8') # type: ignore
logger.debug(errmsg)
raise ImportError(errmsg)
Attribute = namedtuple('Attribute', ['name', 'directly_defined', 'value'])
def get_object_members(subject, objpath, attrgetter, analyzer=None):
# type: (Any, List[unicode], Callable, Any) -> Dict[str, Attribute] # NOQA
"""Get members and attributes of target object."""
# the members directly defined in the class
obj_dict = attrgetter(subject, '__dict__', {})
# Py34 doesn't have enum members in __dict__.
if sys.version_info[:2] == (3, 4) and isenumclass(subject):
obj_dict = dict(obj_dict)
for name, value in subject.__members__.items():
obj_dict[name] = value
members = {}
for name in dir(subject):
try:
value = attrgetter(subject, name)
directly_defined = name in obj_dict
members[name] = Attribute(name, directly_defined, value)
except AttributeError:
continue
if analyzer:
# append instance attributes (cf. self.attr1) if analyzer knows
from sphinx.ext.autodoc import INSTANCEATTR
namespace = '.'.join(objpath)
for (ns, name) in analyzer.find_attr_docs():
if namespace == ns and name not in members:
members[name] = Attribute(name, True, INSTANCEATTR)
return members

View File

@ -5,14 +5,16 @@
Inspect utilities for autodoc Inspect utilities for autodoc
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
import typing import typing
import warnings
from six import StringIO, string_types from six import StringIO, string_types
from sphinx.deprecation import RemovedInSphinx20Warning
from sphinx.util.inspect import object_description from sphinx.util.inspect import object_description
if False: if False:
@ -29,6 +31,9 @@ def format_annotation(annotation):
Displaying complex types from ``typing`` relies on its private API. Displaying complex types from ``typing`` relies on its private API.
""" """
warnings.warn('format_annotation() is now deprecated. '
'Please use sphinx.util.inspect.Signature instead.',
RemovedInSphinx20Warning)
if isinstance(annotation, typing.TypeVar): # type: ignore if isinstance(annotation, typing.TypeVar): # type: ignore
return annotation.__name__ return annotation.__name__
if annotation == Ellipsis: if annotation == Ellipsis:
@ -65,7 +70,7 @@ def format_annotation(annotation):
elif (hasattr(typing, 'UnionMeta') and elif (hasattr(typing, 'UnionMeta') and
isinstance(annotation, typing.UnionMeta) and # type: ignore isinstance(annotation, typing.UnionMeta) and # type: ignore
hasattr(annotation, '__union_params__')): hasattr(annotation, '__union_params__')):
params = annotation.__union_params__ # type: ignore params = annotation.__union_params__
if params is not None: if params is not None:
param_str = ', '.join(format_annotation(p) for p in params) param_str = ', '.join(format_annotation(p) for p in params)
return '%s[%s]' % (qualified_name, param_str) return '%s[%s]' % (qualified_name, param_str)
@ -74,7 +79,7 @@ def format_annotation(annotation):
getattr(annotation, '__args__', None) is not None and getattr(annotation, '__args__', None) is not None and
hasattr(annotation, '__result__')): hasattr(annotation, '__result__')):
# Skipped in the case of plain typing.Callable # Skipped in the case of plain typing.Callable
args = annotation.__args__ # type: ignore args = annotation.__args__
if args is None: if args is None:
return qualified_name return qualified_name
elif args is Ellipsis: elif args is Ellipsis:
@ -84,15 +89,15 @@ def format_annotation(annotation):
args_str = '[%s]' % ', '.join(formatted_args) args_str = '[%s]' % ', '.join(formatted_args)
return '%s[%s, %s]' % (qualified_name, return '%s[%s, %s]' % (qualified_name,
args_str, args_str,
format_annotation(annotation.__result__)) # type: ignore format_annotation(annotation.__result__))
elif (hasattr(typing, 'TupleMeta') and elif (hasattr(typing, 'TupleMeta') and
isinstance(annotation, typing.TupleMeta) and # type: ignore isinstance(annotation, typing.TupleMeta) and # type: ignore
hasattr(annotation, '__tuple_params__') and hasattr(annotation, '__tuple_params__') and
hasattr(annotation, '__tuple_use_ellipsis__')): hasattr(annotation, '__tuple_use_ellipsis__')):
params = annotation.__tuple_params__ # type: ignore params = annotation.__tuple_params__
if params is not None: if params is not None:
param_strings = [format_annotation(p) for p in params] param_strings = [format_annotation(p) for p in params]
if annotation.__tuple_use_ellipsis__: # type: ignore if annotation.__tuple_use_ellipsis__:
param_strings.append('...') param_strings.append('...')
return '%s[%s]' % (qualified_name, return '%s[%s]' % (qualified_name,
', '.join(param_strings)) ', '.join(param_strings))
@ -107,6 +112,9 @@ def formatargspec(function, args, varargs=None, varkw=None, defaults=None,
An enhanced version of ``inspect.formatargspec()`` that handles typing An enhanced version of ``inspect.formatargspec()`` that handles typing
annotations better. annotations better.
""" """
warnings.warn('formatargspec() is now deprecated. '
'Please use sphinx.util.inspect.Signature instead.',
RemovedInSphinx20Warning)
def format_arg_with_annotation(name): def format_arg_with_annotation(name):
# type: (str) -> str # type: (str) -> str

View File

@ -5,7 +5,7 @@
Allow reference sections by :ref: role using its title. Allow reference sections by :ref: role using its title.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """

View File

@ -49,7 +49,7 @@
resolved to a Python object, and otherwise it becomes simple emphasis. resolved to a Python object, and otherwise it becomes simple emphasis.
This can be used as the default role to make links 'smart'. This can be used as the default role to make links 'smart'.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
@ -72,7 +72,9 @@ from sphinx import addnodes
from sphinx.environment.adapters.toctree import TocTree from sphinx.environment.adapters.toctree import TocTree
from sphinx.util import import_object, rst, logging from sphinx.util import import_object, rst, logging
from sphinx.pycode import ModuleAnalyzer, PycodeError from sphinx.pycode import ModuleAnalyzer, PycodeError
from sphinx.ext.autodoc import Options from sphinx.ext.autodoc import get_documenters
from sphinx.ext.autodoc.directive import DocumenterBridge, Options
from sphinx.ext.autodoc.importer import import_module
if False: if False:
# For type annotation # For type annotation
@ -152,13 +154,13 @@ def autosummary_table_visit_html(self, node):
# -- autodoc integration ------------------------------------------------------- # -- autodoc integration -------------------------------------------------------
class FakeDirective(object): class FakeDirective(DocumenterBridge):
env = {} # type: Dict def __init__(self):
genopt = Options() super(FakeDirective, self).__init__({}, None, Options(), 0) # type: ignore
def get_documenter(obj, parent): def get_documenter(app, obj, parent):
# type: (Any, Any) -> Type[Documenter] # type: (Sphinx, Any, Any) -> Type[Documenter]
"""Get an autodoc.Documenter class suitable for documenting the given """Get an autodoc.Documenter class suitable for documenting the given
object. object.
@ -166,8 +168,7 @@ def get_documenter(obj, parent):
another Python object (e.g. a module or a class) to which *obj* another Python object (e.g. a module or a class) to which *obj*
belongs to. belongs to.
""" """
from sphinx.ext.autodoc import AutoDirective, DataDocumenter, \ from sphinx.ext.autodoc import DataDocumenter, ModuleDocumenter
ModuleDocumenter
if inspect.ismodule(obj): if inspect.ismodule(obj):
# ModuleDocumenter.can_document_member always returns False # ModuleDocumenter.can_document_member always returns False
@ -175,7 +176,7 @@ def get_documenter(obj, parent):
# Construct a fake documenter for *parent* # Construct a fake documenter for *parent*
if parent is not None: if parent is not None:
parent_doc_cls = get_documenter(parent, None) parent_doc_cls = get_documenter(app, parent, None)
else: else:
parent_doc_cls = ModuleDocumenter parent_doc_cls = ModuleDocumenter
@ -185,7 +186,7 @@ def get_documenter(obj, parent):
parent_doc = parent_doc_cls(FakeDirective(), "") parent_doc = parent_doc_cls(FakeDirective(), "")
# Get the corrent documenter class for *obj* # Get the corrent documenter class for *obj*
classes = [cls for cls in AutoDirective._registry.values() classes = [cls for cls in get_documenters(app).values()
if cls.can_document_member(obj, '', False, parent_doc)] if cls.can_document_member(obj, '', False, parent_doc)]
if classes: if classes:
classes.sort(key=lambda cls: cls.priority) classes.sort(key=lambda cls: cls.priority)
@ -288,7 +289,7 @@ class Autosummary(Directive):
full_name = modname + '::' + full_name[len(modname) + 1:] full_name = modname + '::' + full_name[len(modname) + 1:]
# NB. using full_name here is important, since Documenters # NB. using full_name here is important, since Documenters
# handle module prefixes slightly differently # handle module prefixes slightly differently
documenter = get_documenter(obj, parent)(self, full_name) documenter = get_documenter(self.env.app, obj, parent)(self, full_name)
if not documenter.parse_name(): if not documenter.parse_name():
self.warn('failed to parse name %s' % real_name) self.warn('failed to parse name %s' % real_name)
items.append((display_name, '', '', real_name)) items.append((display_name, '', '', real_name))
@ -324,7 +325,7 @@ class Autosummary(Directive):
# -- Grab the summary # -- Grab the summary
documenter.add_content(None) documenter.add_content(None)
doc = list(documenter.process_doc([self.result.data])) doc = self.result.data
while doc and not doc[0].strip(): while doc and not doc[0].strip():
doc.pop(0) doc.pop(0)
@ -512,8 +513,7 @@ def _import_by_name(name):
modname = '.'.join(name_parts[:-1]) modname = '.'.join(name_parts[:-1])
if modname: if modname:
try: try:
__import__(modname) mod = import_module(modname)
mod = sys.modules[modname]
return getattr(mod, name_parts[-1]), mod, modname return getattr(mod, name_parts[-1]), mod, modname
except (ImportError, IndexError, AttributeError): except (ImportError, IndexError, AttributeError):
pass pass
@ -525,9 +525,10 @@ def _import_by_name(name):
last_j = j last_j = j
modname = '.'.join(name_parts[:j]) modname = '.'.join(name_parts[:j])
try: try:
__import__(modname) import_module(modname)
except ImportError: except ImportError:
continue continue
if modname in sys.modules: if modname in sys.modules:
break break
@ -614,7 +615,8 @@ def process_generate_options(app):
generate_autosummary_docs(genfiles, builder=app.builder, generate_autosummary_docs(genfiles, builder=app.builder,
warn=logger.warning, info=logger.info, warn=logger.warning, info=logger.info,
suffix=suffix, base_path=app.srcdir) suffix=suffix, base_path=app.srcdir,
app=app)
def setup(app): def setup(app):

View File

@ -14,7 +14,7 @@
generate: generate:
sphinx-autogen -o source/generated source/*.rst sphinx-autogen -o source/generated source/*.rst
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
from __future__ import print_function from __future__ import print_function
@ -33,24 +33,11 @@ from sphinx import __display_version__
from sphinx import package_dir from sphinx import package_dir
from sphinx.ext.autosummary import import_by_name, get_documenter from sphinx.ext.autosummary import import_by_name, get_documenter
from sphinx.jinja2glue import BuiltinTemplateLoader from sphinx.jinja2glue import BuiltinTemplateLoader
from sphinx.registry import SphinxComponentRegistry
from sphinx.util.osutil import ensuredir from sphinx.util.osutil import ensuredir
from sphinx.util.inspect import safe_getattr from sphinx.util.inspect import safe_getattr
from sphinx.util.rst import escape as rst_escape from sphinx.util.rst import escape as rst_escape
# Add documenters to AutoDirective registry
from sphinx.ext.autodoc import add_documenter, \
ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter, \
FunctionDocumenter, MethodDocumenter, AttributeDocumenter, \
InstanceAttributeDocumenter
add_documenter(ModuleDocumenter)
add_documenter(ClassDocumenter)
add_documenter(ExceptionDocumenter)
add_documenter(DataDocumenter)
add_documenter(FunctionDocumenter)
add_documenter(MethodDocumenter)
add_documenter(AttributeDocumenter)
add_documenter(InstanceAttributeDocumenter)
if False: if False:
# For type annotation # For type annotation
from typing import Any, Callable, Dict, Tuple, List # NOQA from typing import Any, Callable, Dict, Tuple, List # NOQA
@ -60,6 +47,30 @@ if False:
from sphinx.environment import BuildEnvironment # NOQA from sphinx.environment import BuildEnvironment # NOQA
class DummyApplication(object):
"""Dummy Application class for sphinx-autogen command."""
def __init__(self):
# type: () -> None
self.registry = SphinxComponentRegistry()
def setup_documenters(app):
# type: (Any) -> None
from sphinx.ext.autodoc import (
ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter,
FunctionDocumenter, MethodDocumenter, AttributeDocumenter,
InstanceAttributeDocumenter
)
documenters = [
ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter,
FunctionDocumenter, MethodDocumenter, AttributeDocumenter,
InstanceAttributeDocumenter
]
for documenter in documenters:
app.registry.add_documenter(documenter.objtype, documenter)
def _simple_info(msg): def _simple_info(msg):
# type: (unicode) -> None # type: (unicode) -> None
print(msg) print(msg)
@ -81,8 +92,8 @@ def _underline(title, line='='):
def generate_autosummary_docs(sources, output_dir=None, suffix='.rst', def generate_autosummary_docs(sources, output_dir=None, suffix='.rst',
warn=_simple_warn, info=_simple_info, warn=_simple_warn, info=_simple_info,
base_path=None, builder=None, template_dir=None, base_path=None, builder=None, template_dir=None,
imported_members=False): imported_members=False, app=None):
# type: (List[unicode], unicode, unicode, Callable, Callable, unicode, Builder, unicode, bool) -> None # NOQA # type: (List[unicode], unicode, unicode, Callable, Callable, unicode, Builder, unicode, bool, Any) -> None # NOQA
showed_sources = list(sorted(sources)) showed_sources = list(sorted(sources))
if len(showed_sources) > 20: if len(showed_sources) > 20:
@ -148,7 +159,7 @@ def generate_autosummary_docs(sources, output_dir=None, suffix='.rst',
new_files.append(fn) new_files.append(fn)
with open(fn, 'w') as f: with open(fn, 'w') as f:
doc = get_documenter(obj, parent) doc = get_documenter(app, obj, parent)
if template_name is not None: if template_name is not None:
template = template_env.get_template(template_name) template = template_env.get_template(template_name)
@ -167,7 +178,7 @@ def generate_autosummary_docs(sources, output_dir=None, suffix='.rst',
value = safe_getattr(obj, name) value = safe_getattr(obj, name)
except AttributeError: except AttributeError:
continue continue
documenter = get_documenter(value, obj) documenter = get_documenter(app, value, obj)
if documenter.objtype == typ: if documenter.objtype == typ:
if typ == 'method': if typ == 'method':
items.append(name) items.append(name)
@ -392,11 +403,14 @@ The format of the autosummary directive is documented in the
def main(argv=sys.argv[1:]): def main(argv=sys.argv[1:]):
# type: (List[str]) -> None # type: (List[str]) -> None
app = DummyApplication()
setup_documenters(app)
args = get_parser().parse_args(argv) args = get_parser().parse_args(argv)
generate_autosummary_docs(args.source_file, args.output_dir, generate_autosummary_docs(args.source_file, args.output_dir,
'.' + args.suffix, '.' + args.suffix,
template_dir=args.templates, template_dir=args.templates,
imported_members=args.imported_members) imported_members=args.imported_members,
app=app)
if __name__ == '__main__': if __name__ == '__main__':

Some files were not shown because too many files have changed in this diff Show More