mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge branch 'master' into 4183_follow_pep440
This commit is contained in:
commit
f5b1aff2d1
@ -6,7 +6,6 @@ cache: pip
|
|||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
- PYTHONFAULTHANDLER=x
|
- PYTHONFAULTHANDLER=x
|
||||||
- PYTHONWARNINGS=all
|
|
||||||
- SKIP_LATEX_BUILD=1
|
- SKIP_LATEX_BUILD=1
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
|
1
AUTHORS
1
AUTHORS
@ -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
|
||||||
|
36
CHANGES
36
CHANGES
@ -19,6 +19,8 @@ Incompatible changes
|
|||||||
* #4226: apidoc: Generate new style makefile (make-mode)
|
* #4226: apidoc: Generate new style makefile (make-mode)
|
||||||
* #4274: sphinx-build returns 2 as an exit code on argument error
|
* #4274: sphinx-build returns 2 as an exit code on argument error
|
||||||
* #4389: output directory will be created after loading extensions
|
* #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.
|
||||||
|
|
||||||
Deprecated
|
Deprecated
|
||||||
----------
|
----------
|
||||||
@ -27,6 +29,12 @@ Deprecated
|
|||||||
values will be accepted at 2.0.
|
values will be accepted at 2.0.
|
||||||
* ``format_annotation()`` and ``formatargspec()`` is deprecated. Please use
|
* ``format_annotation()`` and ``formatargspec()`` is deprecated. Please use
|
||||||
``sphinx.util.inspect.Signature`` instead.
|
``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
|
||||||
--------------
|
--------------
|
||||||
@ -62,6 +70,8 @@ Features added
|
|||||||
* #947: autodoc now supports ignore-module-all to ignore a module's ``__all__``
|
* #947: autodoc now supports ignore-module-all to ignore a module's ``__all__``
|
||||||
* #4332: Let LaTeX obey :confval:`math_numfig` for equation numbering
|
* #4332: Let LaTeX obey :confval:`math_numfig` for equation numbering
|
||||||
* #4093: sphinx-build creates empty directories for unknown targets/builders
|
* #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
|
* #4183: doctest: ``:pyversion:`` option also follows PEP-440 specification
|
||||||
|
|
||||||
Features removed
|
Features removed
|
||||||
@ -108,13 +118,18 @@ Bugs fixed
|
|||||||
one of figures and tables
|
one of figures and tables
|
||||||
* #4330: PDF 'howto' documents have an incoherent default LaTeX tocdepth counter
|
* #4330: PDF 'howto' documents have an incoherent default LaTeX tocdepth counter
|
||||||
setting
|
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
|
||||||
@ -129,11 +144,27 @@ 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
|
* ``VerbatimHighlightColor`` is a new
|
||||||
:ref:`LaTeX 'sphinxsetup' <latexsphinxsetup>` key (refs: #4285)
|
:ref:`LaTeX 'sphinxsetup' <latexsphinxsetup>` key (refs: #4285)
|
||||||
* Easier customizability of LaTeX macros involved in rendering of code-blocks
|
* Easier customizability of LaTeX macros involved in rendering of code-blocks
|
||||||
* Show traceback if conf.py raises an exception (refs: #4369)
|
* 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
|
||||||
----------
|
----------
|
||||||
@ -158,9 +189,6 @@ Bugs fixed
|
|||||||
* Fix links to external option docs with intersphinx (refs: #3769)
|
* Fix links to external option docs with intersphinx (refs: #3769)
|
||||||
* #4091: Private members not documented without :undoc-members:
|
* #4091: Private members not documented without :undoc-members:
|
||||||
|
|
||||||
Testing
|
|
||||||
--------
|
|
||||||
|
|
||||||
Release 1.6.5 (released Oct 23, 2017)
|
Release 1.6.5 (released Oct 23, 2017)
|
||||||
=====================================
|
=====================================
|
||||||
|
|
||||||
|
4
EXAMPLES
4
EXAMPLES
@ -93,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/
|
||||||
@ -259,7 +259,7 @@ 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/
|
||||||
|
6
doc/_templates/index.html
vendored
6
doc/_templates/index.html
vendored
@ -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>
|
||||||
|
@ -354,6 +354,63 @@ General configuration
|
|||||||
The LaTeX builder obeys this setting (if :confval:`numfig` is set to
|
The LaTeX builder obeys this setting (if :confval:`numfig` is set to
|
||||||
``True``).
|
``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``.
|
||||||
@ -785,15 +842,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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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:
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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.
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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/>`_.
|
||||||
|
@ -642,15 +642,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
|
||||||
|
@ -137,6 +137,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):
|
||||||
|
@ -19,8 +19,9 @@ 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.utils import Reporter, get_source_line, normalize_language_tag
|
from docutils.utils import Reporter, get_source_line, normalize_language_tag
|
||||||
@ -40,14 +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.transforms import SphinxTransformer
|
from sphinx.transforms import SphinxTransformer, SphinxSmartQuotes
|
||||||
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
|
||||||
@ -66,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,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
|
||||||
@ -584,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
|
||||||
@ -596,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)
|
||||||
|
|
||||||
@ -645,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):
|
||||||
|
@ -14,17 +14,15 @@
|
|||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import inspect
|
import inspect
|
||||||
import traceback
|
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 mock, import_module
|
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.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
|
||||||
@ -32,21 +30,23 @@ 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,56 +357,15 @@ class Documenter(object):
|
|||||||
|
|
||||||
Returns True if successful, False if an error occurred.
|
Returns True if successful, False if an error occurred.
|
||||||
"""
|
"""
|
||||||
if self.objpath:
|
|
||||||
logger.debug('[autodoc] from %s import %s',
|
|
||||||
self.modname, '.'.join(self.objpath))
|
|
||||||
# always enable mock import hook
|
|
||||||
# it will do nothing if autodoc_mock_imports is empty
|
|
||||||
with mock(self.env.config.autodoc_mock_imports):
|
with mock(self.env.config.autodoc_mock_imports):
|
||||||
try:
|
try:
|
||||||
logger.debug('[autodoc] import %s', self.modname)
|
ret = import_object(self.modname, self.objpath, self.objtype,
|
||||||
obj = import_module(self.modname, self.env.config.autodoc_warningiserror)
|
attrgetter=self.get_attr,
|
||||||
parent = None
|
warningiserror=self.env.config.autodoc_warningiserror)
|
||||||
self.module = obj
|
self.module, self.parent, self.object_name, self.object = ret
|
||||||
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
|
return True
|
||||||
except (AttributeError, ImportError) as exc:
|
except ImportError as exc:
|
||||||
if self.objpath:
|
logger.warning(exc.args[0])
|
||||||
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(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)
|
|
||||||
self.directive.warn(errmsg)
|
|
||||||
self.env.note_reread()
|
self.env.note_reread()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -493,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
|
||||||
@ -606,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]]
|
||||||
@ -768,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
|
||||||
@ -819,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
|
||||||
@ -909,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):
|
||||||
@ -949,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))
|
||||||
@ -962,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
|
||||||
|
|
||||||
|
|
||||||
@ -1504,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', 'ignore-module-all'
|
|
||||||
])
|
|
||||||
|
|
||||||
# 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)
|
||||||
@ -1626,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)
|
||||||
|
155
sphinx/ext/autodoc/directive.py
Normal file
155
sphinx/ext/autodoc/directive.py
Normal 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
|
@ -13,13 +13,17 @@ import sys
|
|||||||
import warnings
|
import warnings
|
||||||
import traceback
|
import traceback
|
||||||
import contextlib
|
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, Generator, List, Set # NOQA
|
from typing import Any, Callable, Dict, Generator, List, Optional, Set # NOQA
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -144,3 +148,86 @@ def import_module(modname, warningiserror=False):
|
|||||||
# Importing modules may cause any side effects, including
|
# Importing modules may cause any side effects, including
|
||||||
# SystemExit, so we need to catch all errors.
|
# SystemExit, so we need to catch all errors.
|
||||||
raise ImportError(exc, traceback.format_exc())
|
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
|
||||||
|
@ -72,7 +72,8 @@ 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
|
from sphinx.ext.autodoc.importer import import_module
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
@ -153,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.
|
||||||
|
|
||||||
@ -167,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
|
||||||
@ -176,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
|
||||||
|
|
||||||
@ -186,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)
|
||||||
@ -289,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))
|
||||||
@ -325,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)
|
||||||
@ -615,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):
|
||||||
|
@ -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__':
|
||||||
|
@ -130,8 +130,8 @@ class InheritanceGraph(object):
|
|||||||
graphviz dot graph from them.
|
graphviz dot graph from them.
|
||||||
"""
|
"""
|
||||||
def __init__(self, class_names, currmodule, show_builtins=False,
|
def __init__(self, class_names, currmodule, show_builtins=False,
|
||||||
private_bases=False, parts=0, aliases=None):
|
private_bases=False, parts=0, aliases=None, top_classes=[]):
|
||||||
# type: (unicode, str, bool, bool, int, Optional[Dict[unicode, unicode]]) -> None
|
# type: (unicode, str, bool, bool, int, Optional[Dict[unicode, unicode]], List[Any]) -> None # NOQA
|
||||||
"""*class_names* is a list of child classes to show bases from.
|
"""*class_names* is a list of child classes to show bases from.
|
||||||
|
|
||||||
If *show_builtins* is True, then Python builtins will be shown
|
If *show_builtins* is True, then Python builtins will be shown
|
||||||
@ -140,7 +140,7 @@ class InheritanceGraph(object):
|
|||||||
self.class_names = class_names
|
self.class_names = class_names
|
||||||
classes = self._import_classes(class_names, currmodule)
|
classes = self._import_classes(class_names, currmodule)
|
||||||
self.class_info = self._class_info(classes, show_builtins,
|
self.class_info = self._class_info(classes, show_builtins,
|
||||||
private_bases, parts, aliases)
|
private_bases, parts, aliases, top_classes)
|
||||||
if not self.class_info:
|
if not self.class_info:
|
||||||
raise InheritanceException('No classes found for '
|
raise InheritanceException('No classes found for '
|
||||||
'inheritance diagram')
|
'inheritance diagram')
|
||||||
@ -153,13 +153,16 @@ class InheritanceGraph(object):
|
|||||||
classes.extend(import_classes(name, currmodule))
|
classes.extend(import_classes(name, currmodule))
|
||||||
return classes
|
return classes
|
||||||
|
|
||||||
def _class_info(self, classes, show_builtins, private_bases, parts, aliases):
|
def _class_info(self, classes, show_builtins, private_bases, parts, aliases, top_classes):
|
||||||
# type: (List[Any], bool, bool, int, Optional[Dict[unicode, unicode]]) -> List[Tuple[unicode, unicode, List[unicode], unicode]] # NOQA
|
# type: (List[Any], bool, bool, int, Optional[Dict[unicode, unicode]], List[Any]) -> List[Tuple[unicode, unicode, List[unicode], unicode]] # NOQA
|
||||||
"""Return name and bases for all classes that are ancestors of
|
"""Return name and bases for all classes that are ancestors of
|
||||||
*classes*.
|
*classes*.
|
||||||
|
|
||||||
*parts* gives the number of dotted name parts that is removed from the
|
*parts* gives the number of dotted name parts that is removed from the
|
||||||
displayed node names.
|
displayed node names.
|
||||||
|
|
||||||
|
*top_classes* gives the name(s) of the top most ancestor class to traverse
|
||||||
|
to. Multiple names can be specified separated by comma.
|
||||||
"""
|
"""
|
||||||
all_classes = {}
|
all_classes = {}
|
||||||
py_builtins = vars(builtins).values()
|
py_builtins = vars(builtins).values()
|
||||||
@ -189,6 +192,10 @@ class InheritanceGraph(object):
|
|||||||
|
|
||||||
baselist = [] # type: List[unicode]
|
baselist = [] # type: List[unicode]
|
||||||
all_classes[cls] = (nodename, fullname, baselist, tooltip)
|
all_classes[cls] = (nodename, fullname, baselist, tooltip)
|
||||||
|
|
||||||
|
if fullname in top_classes:
|
||||||
|
return
|
||||||
|
|
||||||
for base in cls.__bases__:
|
for base in cls.__bases__:
|
||||||
if not show_builtins and base in py_builtins:
|
if not show_builtins and base in py_builtins:
|
||||||
continue
|
continue
|
||||||
@ -322,6 +329,7 @@ class InheritanceDiagram(Directive):
|
|||||||
'parts': directives.nonnegative_int,
|
'parts': directives.nonnegative_int,
|
||||||
'private-bases': directives.flag,
|
'private-bases': directives.flag,
|
||||||
'caption': directives.unchanged,
|
'caption': directives.unchanged,
|
||||||
|
'top-classes': directives.unchanged_required,
|
||||||
}
|
}
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
@ -334,6 +342,11 @@ class InheritanceDiagram(Directive):
|
|||||||
# Store the original content for use as a hash
|
# Store the original content for use as a hash
|
||||||
node['parts'] = self.options.get('parts', 0)
|
node['parts'] = self.options.get('parts', 0)
|
||||||
node['content'] = ', '.join(class_names)
|
node['content'] = ', '.join(class_names)
|
||||||
|
node['top-classes'] = []
|
||||||
|
for cls in self.options.get('top-classes', '').split(','):
|
||||||
|
cls = cls.strip()
|
||||||
|
if cls:
|
||||||
|
node['top-classes'].append(cls)
|
||||||
|
|
||||||
# Create a graph starting with the list of classes
|
# Create a graph starting with the list of classes
|
||||||
try:
|
try:
|
||||||
@ -341,7 +354,8 @@ class InheritanceDiagram(Directive):
|
|||||||
class_names, env.ref_context.get('py:module'),
|
class_names, env.ref_context.get('py:module'),
|
||||||
parts=node['parts'],
|
parts=node['parts'],
|
||||||
private_bases='private-bases' in self.options,
|
private_bases='private-bases' in self.options,
|
||||||
aliases=env.config.inheritance_alias)
|
aliases=env.config.inheritance_alias,
|
||||||
|
top_classes=node['top-classes'])
|
||||||
except InheritanceException as err:
|
except InheritanceException as err:
|
||||||
return [node.document.reporter.warning(err.args[0],
|
return [node.document.reporter.warning(err.args[0],
|
||||||
line=self.lineno)]
|
line=self.lineno)]
|
||||||
|
@ -71,12 +71,12 @@ def doctree_read(app, doctree):
|
|||||||
code = analyzer.code.decode(analyzer.encoding)
|
code = analyzer.code.decode(analyzer.encoding)
|
||||||
else:
|
else:
|
||||||
code = analyzer.code
|
code = analyzer.code
|
||||||
if entry is None or entry[0] != code:
|
if entry is False:
|
||||||
|
return
|
||||||
|
elif entry is None or entry[0] != code:
|
||||||
analyzer.find_tags()
|
analyzer.find_tags()
|
||||||
entry = code, analyzer.tags, {}, refname
|
entry = code, analyzer.tags, {}, refname
|
||||||
env._viewcode_modules[modname] = entry # type: ignore
|
env._viewcode_modules[modname] = entry # type: ignore
|
||||||
elif entry is False:
|
|
||||||
return
|
|
||||||
_, tags, used, _ = entry
|
_, tags, used, _ = entry
|
||||||
if fullname in tags:
|
if fullname in tags:
|
||||||
used[fullname] = docname
|
used[fullname] = docname
|
||||||
|
@ -23,7 +23,7 @@ from sphinx.transforms import (
|
|||||||
ApplySourceWorkaround, ExtraTranslatableNodes, CitationReferences,
|
ApplySourceWorkaround, ExtraTranslatableNodes, CitationReferences,
|
||||||
DefaultSubstitutions, MoveModuleTargets, HandleCodeBlocks, SortIds,
|
DefaultSubstitutions, MoveModuleTargets, HandleCodeBlocks, SortIds,
|
||||||
AutoNumbering, AutoIndexUpgrader, FilterSystemMessages,
|
AutoNumbering, AutoIndexUpgrader, FilterSystemMessages,
|
||||||
UnreferencedFootnotesDetector
|
UnreferencedFootnotesDetector, SphinxSmartQuotes
|
||||||
)
|
)
|
||||||
from sphinx.transforms.compact_bullet_list import RefOnlyBulletListTransform
|
from sphinx.transforms.compact_bullet_list import RefOnlyBulletListTransform
|
||||||
from sphinx.transforms.i18n import (
|
from sphinx.transforms.i18n import (
|
||||||
@ -86,8 +86,15 @@ class SphinxStandaloneReader(SphinxBaseReader):
|
|||||||
def __init__(self, app, *args, **kwargs):
|
def __init__(self, app, *args, **kwargs):
|
||||||
# type: (Sphinx, Any, Any) -> None
|
# type: (Sphinx, Any, Any) -> None
|
||||||
self.transforms = self.transforms + app.registry.get_transforms()
|
self.transforms = self.transforms + app.registry.get_transforms()
|
||||||
|
self.smart_quotes = app.env.settings['smart_quotes']
|
||||||
SphinxBaseReader.__init__(self, *args, **kwargs) # type: ignore
|
SphinxBaseReader.__init__(self, *args, **kwargs) # type: ignore
|
||||||
|
|
||||||
|
def get_transforms(self):
|
||||||
|
transforms = SphinxBaseReader.get_transforms(self)
|
||||||
|
if self.smart_quotes:
|
||||||
|
transforms.append(SphinxSmartQuotes)
|
||||||
|
return transforms
|
||||||
|
|
||||||
|
|
||||||
class SphinxI18nReader(SphinxBaseReader):
|
class SphinxI18nReader(SphinxBaseReader):
|
||||||
"""
|
"""
|
||||||
|
@ -15,8 +15,6 @@ from docutils.parsers.rst import states
|
|||||||
from docutils.statemachine import StringList
|
from docutils.statemachine import StringList
|
||||||
from docutils.transforms.universal import SmartQuotes
|
from docutils.transforms.universal import SmartQuotes
|
||||||
|
|
||||||
from sphinx.transforms import SphinxSmartQuotes
|
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
# For type annotation
|
# For type annotation
|
||||||
from typing import Any, Dict, List, Type # NOQA
|
from typing import Any, Dict, List, Type # NOQA
|
||||||
@ -63,10 +61,11 @@ class RSTParser(docutils.parsers.rst.Parser):
|
|||||||
|
|
||||||
def get_transforms(self):
|
def get_transforms(self):
|
||||||
# type: () -> List[Type[Transform]]
|
# type: () -> List[Type[Transform]]
|
||||||
"""Sphinx's reST parser replaces a transform class for smart-quotes by own's"""
|
"""Sphinx's reST parser replaces a transform class for smart-quotes by own's
|
||||||
|
|
||||||
|
refs: sphinx.io.SphinxStandaloneReader"""
|
||||||
transforms = docutils.parsers.rst.Parser.get_transforms(self)
|
transforms = docutils.parsers.rst.Parser.get_transforms(self)
|
||||||
transforms.remove(SmartQuotes)
|
transforms.remove(SmartQuotes)
|
||||||
transforms.append(SphinxSmartQuotes)
|
|
||||||
return transforms
|
return transforms
|
||||||
|
|
||||||
def parse(self, inputstring, document):
|
def parse(self, inputstring, document):
|
||||||
|
@ -38,6 +38,7 @@ if False:
|
|||||||
from sphinx.builders import Builder # NOQA
|
from sphinx.builders import Builder # NOQA
|
||||||
from sphinx.domains import Domain, Index # NOQA
|
from sphinx.domains import Domain, Index # NOQA
|
||||||
from sphinx.environment import BuildEnvironment # NOQA
|
from sphinx.environment import BuildEnvironment # NOQA
|
||||||
|
from sphinx.ext.autodoc import Documenter # NOQA
|
||||||
from sphinx.util.typing import RoleFunction # NOQA
|
from sphinx.util.typing import RoleFunction # NOQA
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -51,7 +52,9 @@ EXTENSION_BLACKLIST = {
|
|||||||
|
|
||||||
class SphinxComponentRegistry(object):
|
class SphinxComponentRegistry(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
self.autodoc_attrgettrs = {} # type: Dict[Type, Callable[[Any, unicode, Any], Any]]
|
||||||
self.builders = {} # type: Dict[unicode, Type[Builder]]
|
self.builders = {} # type: Dict[unicode, Type[Builder]]
|
||||||
|
self.documenters = {} # type: Dict[unicode, Type[Documenter]]
|
||||||
self.domains = {} # type: Dict[unicode, Type[Domain]]
|
self.domains = {} # type: Dict[unicode, Type[Domain]]
|
||||||
self.domain_directives = {} # type: Dict[unicode, Dict[unicode, Any]]
|
self.domain_directives = {} # type: Dict[unicode, Dict[unicode, Any]]
|
||||||
self.domain_indices = {} # type: Dict[unicode, List[Type[Index]]]
|
self.domain_indices = {} # type: Dict[unicode, List[Type[Index]]]
|
||||||
@ -284,6 +287,14 @@ class SphinxComponentRegistry(object):
|
|||||||
# type: () -> List[Type[Transform]]
|
# type: () -> List[Type[Transform]]
|
||||||
return self.post_transforms
|
return self.post_transforms
|
||||||
|
|
||||||
|
def add_documenter(self, objtype, documenter):
|
||||||
|
# type: (unicode, Type[Documenter]) -> None
|
||||||
|
self.documenters[objtype] = documenter
|
||||||
|
|
||||||
|
def add_autodoc_attrgetter(self, typ, attrgetter):
|
||||||
|
# type: (Type, Callable[[Any, unicode, Any], Any]) -> None
|
||||||
|
self.autodoc_attrgettrs[typ] = attrgetter
|
||||||
|
|
||||||
def load_extension(self, app, extname):
|
def load_extension(self, app, extname):
|
||||||
# type: (Sphinx, unicode) -> None
|
# type: (Sphinx, unicode) -> None
|
||||||
"""Load a Sphinx extension."""
|
"""Load a Sphinx extension."""
|
||||||
|
@ -18,7 +18,7 @@ from contextlib import contextmanager
|
|||||||
|
|
||||||
import docutils
|
import docutils
|
||||||
from docutils.languages import get_language
|
from docutils.languages import get_language
|
||||||
from docutils.statemachine import ViewList
|
from docutils.statemachine import StateMachine, ViewList
|
||||||
from docutils.parsers.rst import directives, roles, convert_directive_function
|
from docutils.parsers.rst import directives, roles, convert_directive_function
|
||||||
from docutils.utils import Reporter
|
from docutils.utils import Reporter
|
||||||
|
|
||||||
@ -31,8 +31,9 @@ report_re = re.compile('^(.+?:(?:\\d+)?): \\((DEBUG|INFO|WARNING|ERROR|SEVERE)/(
|
|||||||
|
|
||||||
if False:
|
if False:
|
||||||
# For type annotation
|
# For type annotation
|
||||||
from typing import Any, Callable, Iterator, List, Tuple # NOQA
|
from typing import Any, Callable, Generator, Iterator, List, Tuple # NOQA
|
||||||
from docutils import nodes # NOQA
|
from docutils import nodes # NOQA
|
||||||
|
from docutils.statemachine import State # NOQA
|
||||||
from sphinx.environment import BuildEnvironment # NOQA
|
from sphinx.environment import BuildEnvironment # NOQA
|
||||||
from sphinx.io import SphinxFileInput # NOQA
|
from sphinx.io import SphinxFileInput # NOQA
|
||||||
|
|
||||||
@ -216,3 +217,22 @@ def directive_helper(obj, has_content=None, argument_spec=None, **option_spec):
|
|||||||
raise ExtensionError(__('when adding directive classes, no '
|
raise ExtensionError(__('when adding directive classes, no '
|
||||||
'additional arguments may be given'))
|
'additional arguments may be given'))
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def switch_source_input(state, content):
|
||||||
|
# type: (State, ViewList) -> Generator
|
||||||
|
"""Switch current source input of state temporarily."""
|
||||||
|
try:
|
||||||
|
# remember the original ``get_source_and_line()`` method
|
||||||
|
get_source_and_line = state.memo.reporter.get_source_and_line
|
||||||
|
|
||||||
|
# replace it by new one
|
||||||
|
state_machine = StateMachine([], None)
|
||||||
|
state_machine.input_lines = content
|
||||||
|
state.memo.reporter.get_source_and_line = state_machine.get_source_and_line
|
||||||
|
|
||||||
|
yield
|
||||||
|
finally:
|
||||||
|
# restore the method
|
||||||
|
state.memo.reporter.get_source_and_line = get_source_and_line
|
||||||
|
@ -53,7 +53,7 @@ VERBOSITY_MAP.update({
|
|||||||
COLOR_MAP = defaultdict(lambda: 'blue') # type: Dict[int, unicode]
|
COLOR_MAP = defaultdict(lambda: 'blue') # type: Dict[int, unicode]
|
||||||
COLOR_MAP.update({
|
COLOR_MAP.update({
|
||||||
logging.ERROR: 'darkred',
|
logging.ERROR: 'darkred',
|
||||||
logging.WARNING: 'darkred',
|
logging.WARNING: 'red',
|
||||||
logging.DEBUG: 'darkgray',
|
logging.DEBUG: 'darkgray',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -549,7 +549,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
|||||||
'author': document.settings.author, # treat as a raw LaTeX code
|
'author': document.settings.author, # treat as a raw LaTeX code
|
||||||
'indexname': _('Index'),
|
'indexname': _('Index'),
|
||||||
})
|
})
|
||||||
if not self.elements['releasename']:
|
if not self.elements['releasename'] and self.elements['release']:
|
||||||
self.elements.update({
|
self.elements.update({
|
||||||
'releasename': _('Release'),
|
'releasename': _('Release'),
|
||||||
})
|
})
|
||||||
|
@ -35,14 +35,6 @@ def pytest_report_header(config):
|
|||||||
sys.version.split()[0])
|
sys.version.split()[0])
|
||||||
|
|
||||||
|
|
||||||
def _filter_warnings():
|
|
||||||
def ignore(**kwargs): warnings.filterwarnings('ignore', **kwargs)
|
|
||||||
|
|
||||||
ignore(category=DeprecationWarning, module='site') # virtualenv
|
|
||||||
ignore(category=PendingDeprecationWarning, module=r'_pytest\..*')
|
|
||||||
ignore(category=ImportWarning, module='pkgutil')
|
|
||||||
|
|
||||||
|
|
||||||
def _initialize_test_directory(session):
|
def _initialize_test_directory(session):
|
||||||
testroot = os.path.join(str(session.config.rootdir), 'tests')
|
testroot = os.path.join(str(session.config.rootdir), 'tests')
|
||||||
tempdir = os.path.abspath(os.getenv('SPHINX_TEST_TEMPDIR',
|
tempdir = os.path.abspath(os.getenv('SPHINX_TEST_TEMPDIR',
|
||||||
@ -58,5 +50,4 @@ def _initialize_test_directory(session):
|
|||||||
|
|
||||||
|
|
||||||
def pytest_sessionstart(session):
|
def pytest_sessionstart(session):
|
||||||
_filter_warnings()
|
|
||||||
_initialize_test_directory(session)
|
_initialize_test_directory(session)
|
||||||
|
@ -21,6 +21,7 @@ from docutils.statemachine import ViewList
|
|||||||
|
|
||||||
from sphinx.ext.autodoc import AutoDirective, add_documenter, \
|
from sphinx.ext.autodoc import AutoDirective, add_documenter, \
|
||||||
ModuleLevelDocumenter, FunctionDocumenter, cut_lines, between, ALL
|
ModuleLevelDocumenter, FunctionDocumenter, cut_lines, between, ALL
|
||||||
|
from sphinx.util import logging
|
||||||
|
|
||||||
app = None
|
app = None
|
||||||
|
|
||||||
@ -30,7 +31,7 @@ def setup_module(rootdir, sphinx_test_tempdir):
|
|||||||
global app
|
global app
|
||||||
srcdir = sphinx_test_tempdir / 'autodoc-root'
|
srcdir = sphinx_test_tempdir / 'autodoc-root'
|
||||||
if not srcdir.exists():
|
if not srcdir.exists():
|
||||||
(rootdir/'test-root').copytree(srcdir)
|
(rootdir / 'test-root').copytree(srcdir)
|
||||||
app = SphinxTestApp(srcdir=srcdir)
|
app = SphinxTestApp(srcdir=srcdir)
|
||||||
app.builder.env.app = app
|
app.builder.env.app = app
|
||||||
app.builder.env.temp_data['docname'] = 'dummy'
|
app.builder.env.temp_data['docname'] = 'dummy'
|
||||||
@ -47,7 +48,7 @@ directive = options = None
|
|||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def setup_test():
|
def setup_test():
|
||||||
global options, directive
|
global options, directive
|
||||||
global processed_docstrings, processed_signatures, _warnings
|
global processed_docstrings, processed_signatures
|
||||||
|
|
||||||
options = Struct(
|
options = Struct(
|
||||||
inherited_members = False,
|
inherited_members = False,
|
||||||
@ -70,24 +71,17 @@ def setup_test():
|
|||||||
env = app.builder.env,
|
env = app.builder.env,
|
||||||
genopt = options,
|
genopt = options,
|
||||||
result = ViewList(),
|
result = ViewList(),
|
||||||
warn = warnfunc,
|
|
||||||
filename_set = set(),
|
filename_set = set(),
|
||||||
)
|
)
|
||||||
|
|
||||||
processed_docstrings = []
|
processed_docstrings = []
|
||||||
processed_signatures = []
|
processed_signatures = []
|
||||||
_warnings = []
|
|
||||||
|
|
||||||
|
|
||||||
_warnings = []
|
|
||||||
processed_docstrings = []
|
processed_docstrings = []
|
||||||
processed_signatures = []
|
processed_signatures = []
|
||||||
|
|
||||||
|
|
||||||
def warnfunc(msg):
|
|
||||||
_warnings.append(msg)
|
|
||||||
|
|
||||||
|
|
||||||
def process_docstring(app, what, name, obj, options, lines):
|
def process_docstring(app, what, name, obj, options, lines):
|
||||||
processed_docstrings.append((what, name))
|
processed_docstrings.append((what, name))
|
||||||
if name == 'bar':
|
if name == 'bar':
|
||||||
@ -111,20 +105,21 @@ def skip_member(app, what, name, obj, skip, options):
|
|||||||
|
|
||||||
@pytest.mark.usefixtures('setup_test')
|
@pytest.mark.usefixtures('setup_test')
|
||||||
def test_generate():
|
def test_generate():
|
||||||
|
logging.setup(app, app._status, app._warning)
|
||||||
|
|
||||||
def assert_warns(warn_str, objtype, name, **kw):
|
def assert_warns(warn_str, objtype, name, **kw):
|
||||||
inst = AutoDirective._registry[objtype](directive, name)
|
inst = app.registry.documenters[objtype](directive, name)
|
||||||
inst.generate(**kw)
|
inst.generate(**kw)
|
||||||
assert len(directive.result) == 0, directive.result
|
assert len(directive.result) == 0, directive.result
|
||||||
assert len(_warnings) == 1, _warnings
|
assert warn_str in app._warning.getvalue()
|
||||||
assert warn_str in _warnings[0], _warnings
|
app._warning.truncate(0)
|
||||||
del _warnings[:]
|
|
||||||
|
|
||||||
def assert_works(objtype, name, **kw):
|
def assert_works(objtype, name, **kw):
|
||||||
inst = AutoDirective._registry[objtype](directive, name)
|
inst = app.registry.documenters[objtype](directive, name)
|
||||||
inst.generate(**kw)
|
inst.generate(**kw)
|
||||||
assert directive.result
|
assert directive.result
|
||||||
# print '\n'.join(directive.result)
|
# print '\n'.join(directive.result)
|
||||||
assert len(_warnings) == 0, _warnings
|
assert app._warning.getvalue() == ''
|
||||||
del directive.result[:]
|
del directive.result[:]
|
||||||
|
|
||||||
def assert_processes(items, objtype, name, **kw):
|
def assert_processes(items, objtype, name, **kw):
|
||||||
@ -134,18 +129,18 @@ def test_generate():
|
|||||||
assert set(processed_docstrings) | set(processed_signatures) == set(items)
|
assert set(processed_docstrings) | set(processed_signatures) == set(items)
|
||||||
|
|
||||||
def assert_result_contains(item, objtype, name, **kw):
|
def assert_result_contains(item, objtype, name, **kw):
|
||||||
inst = AutoDirective._registry[objtype](directive, name)
|
inst = app.registry.documenters[objtype](directive, name)
|
||||||
inst.generate(**kw)
|
inst.generate(**kw)
|
||||||
# print '\n'.join(directive.result)
|
# print '\n'.join(directive.result)
|
||||||
assert len(_warnings) == 0, _warnings
|
assert app._warning.getvalue() == ''
|
||||||
assert item in directive.result
|
assert item in directive.result
|
||||||
del directive.result[:]
|
del directive.result[:]
|
||||||
|
|
||||||
def assert_order(items, objtype, name, member_order, **kw):
|
def assert_order(items, objtype, name, member_order, **kw):
|
||||||
inst = AutoDirective._registry[objtype](directive, name)
|
inst = app.registry.documenters[objtype](directive, name)
|
||||||
inst.options.member_order = member_order
|
inst.options.member_order = member_order
|
||||||
inst.generate(**kw)
|
inst.generate(**kw)
|
||||||
assert len(_warnings) == 0, _warnings
|
assert app._warning.getvalue() == ''
|
||||||
items = list(reversed(items))
|
items = list(reversed(items))
|
||||||
lineiter = iter(directive.result)
|
lineiter = iter(directive.result)
|
||||||
# for line in directive.result:
|
# for line in directive.result:
|
||||||
|
5
tests/roots/test-inheritance/basic_diagram.rst
Normal file
5
tests/roots/test-inheritance/basic_diagram.rst
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
Basic Diagram
|
||||||
|
==============
|
||||||
|
|
||||||
|
.. inheritance-diagram::
|
||||||
|
dummy.test
|
6
tests/roots/test-inheritance/conf.py
Normal file
6
tests/roots/test-inheritance/conf.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import sys, os
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.abspath('.'))
|
||||||
|
|
||||||
|
extensions = ['sphinx.ext.inheritance_diagram']
|
||||||
|
source_suffix = '.rst'
|
4
tests/roots/test-inheritance/contents.rst
Normal file
4
tests/roots/test-inheritance/contents.rst
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
.. toctree::
|
||||||
|
:glob:
|
||||||
|
|
||||||
|
*
|
@ -0,0 +1,6 @@
|
|||||||
|
Diagram using module with 2 top classes
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
.. inheritance-diagram::
|
||||||
|
dummy.test
|
||||||
|
:top-classes: dummy.test.B, dummy.test.C
|
7
tests/roots/test-inheritance/diagram_w_1_top_class.rst
Normal file
7
tests/roots/test-inheritance/diagram_w_1_top_class.rst
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
Diagram using 1 top class
|
||||||
|
=========================
|
||||||
|
|
||||||
|
.. inheritance-diagram::
|
||||||
|
dummy.test
|
||||||
|
:top-classes: dummy.test.B
|
||||||
|
|
9
tests/roots/test-inheritance/diagram_w_2_top_classes.rst
Normal file
9
tests/roots/test-inheritance/diagram_w_2_top_classes.rst
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
Diagram using 2 top classes
|
||||||
|
===========================
|
||||||
|
|
||||||
|
.. inheritance-diagram::
|
||||||
|
dummy.test.F
|
||||||
|
dummy.test.D
|
||||||
|
dummy.test.E
|
||||||
|
:top-classes: dummy.test.B, dummy.test.C
|
||||||
|
|
7
tests/roots/test-inheritance/diagram_w_parts.rst
Normal file
7
tests/roots/test-inheritance/diagram_w_parts.rst
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
Diagram using the parts option
|
||||||
|
==============================
|
||||||
|
|
||||||
|
.. inheritance-diagram::
|
||||||
|
dummy.test
|
||||||
|
:parts: 1
|
||||||
|
|
0
tests/roots/test-inheritance/dummy/__init__.py
Normal file
0
tests/roots/test-inheritance/dummy/__init__.py
Normal file
30
tests/roots/test-inheritance/dummy/test.py
Normal file
30
tests/roots/test-inheritance/dummy/test.py
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
"""
|
||||||
|
|
||||||
|
Test with a class diagram like this::
|
||||||
|
|
||||||
|
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
|
||||||
|
|
7
tests/roots/test-smartquotes/conf.py
Normal file
7
tests/roots/test-smartquotes/conf.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
master_doc = 'index'
|
||||||
|
|
||||||
|
latex_documents = [
|
||||||
|
(master_doc, 'test.tex', 'The basic Sphinx documentation for testing', 'Sphinx', 'report')
|
||||||
|
]
|
4
tests/roots/test-smartquotes/index.rst
Normal file
4
tests/roots/test-smartquotes/index.rst
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
test-smartquotes
|
||||||
|
================
|
||||||
|
|
||||||
|
-- "Sphinx" is a tool that makes it easy ...
|
@ -20,6 +20,7 @@ from docutils.statemachine import ViewList
|
|||||||
|
|
||||||
from sphinx.ext.autodoc import AutoDirective, add_documenter, \
|
from sphinx.ext.autodoc import AutoDirective, add_documenter, \
|
||||||
ModuleLevelDocumenter, FunctionDocumenter, cut_lines, between, ALL
|
ModuleLevelDocumenter, FunctionDocumenter, cut_lines, between, ALL
|
||||||
|
from sphinx.util import logging
|
||||||
|
|
||||||
app = None
|
app = None
|
||||||
|
|
||||||
@ -51,7 +52,7 @@ directive = options = None
|
|||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def setup_test():
|
def setup_test():
|
||||||
global options, directive
|
global options, directive
|
||||||
global processed_docstrings, processed_signatures, _warnings
|
global processed_docstrings, processed_signatures
|
||||||
|
|
||||||
options = Struct(
|
options = Struct(
|
||||||
inherited_members = False,
|
inherited_members = False,
|
||||||
@ -75,28 +76,24 @@ def setup_test():
|
|||||||
env = app.builder.env,
|
env = app.builder.env,
|
||||||
genopt = options,
|
genopt = options,
|
||||||
result = ViewList(),
|
result = ViewList(),
|
||||||
warn = warnfunc,
|
|
||||||
filename_set = set(),
|
filename_set = set(),
|
||||||
)
|
)
|
||||||
|
|
||||||
processed_docstrings = []
|
processed_docstrings = []
|
||||||
processed_signatures = []
|
processed_signatures = []
|
||||||
_warnings = []
|
|
||||||
|
app._status.truncate(0)
|
||||||
|
app._warning.truncate(0)
|
||||||
|
|
||||||
yield
|
yield
|
||||||
|
|
||||||
AutoDirective._special_attrgetters.clear()
|
AutoDirective._special_attrgetters.clear()
|
||||||
|
|
||||||
|
|
||||||
_warnings = []
|
|
||||||
processed_docstrings = []
|
processed_docstrings = []
|
||||||
processed_signatures = []
|
processed_signatures = []
|
||||||
|
|
||||||
|
|
||||||
def warnfunc(msg):
|
|
||||||
_warnings.append(msg)
|
|
||||||
|
|
||||||
|
|
||||||
def process_docstring(app, what, name, obj, options, lines):
|
def process_docstring(app, what, name, obj, options, lines):
|
||||||
processed_docstrings.append((what, name))
|
processed_docstrings.append((what, name))
|
||||||
if name == 'bar':
|
if name == 'bar':
|
||||||
@ -120,8 +117,10 @@ def skip_member(app, what, name, obj, skip, options):
|
|||||||
|
|
||||||
@pytest.mark.usefixtures('setup_test')
|
@pytest.mark.usefixtures('setup_test')
|
||||||
def test_parse_name():
|
def test_parse_name():
|
||||||
|
logging.setup(app, app._status, app._warning)
|
||||||
|
|
||||||
def verify(objtype, name, result):
|
def verify(objtype, name, result):
|
||||||
inst = AutoDirective._registry[objtype](directive, name)
|
inst = app.registry.documenters[objtype](directive, name)
|
||||||
assert inst.parse_name()
|
assert inst.parse_name()
|
||||||
assert (inst.modname, inst.objpath, inst.args, inst.retann) == result
|
assert (inst.modname, inst.objpath, inst.args, inst.retann) == result
|
||||||
|
|
||||||
@ -129,8 +128,7 @@ def test_parse_name():
|
|||||||
verify('module', 'test_autodoc', ('test_autodoc', [], None, None))
|
verify('module', 'test_autodoc', ('test_autodoc', [], None, None))
|
||||||
verify('module', 'test.test_autodoc', ('test.test_autodoc', [], None, None))
|
verify('module', 'test.test_autodoc', ('test.test_autodoc', [], None, None))
|
||||||
verify('module', 'test(arg)', ('test', [], 'arg', None))
|
verify('module', 'test(arg)', ('test', [], 'arg', None))
|
||||||
assert 'signature arguments' in _warnings[0]
|
assert 'signature arguments' in app._warning.getvalue()
|
||||||
del _warnings[:]
|
|
||||||
|
|
||||||
# for functions/classes
|
# for functions/classes
|
||||||
verify('function', 'test_autodoc.raises',
|
verify('function', 'test_autodoc.raises',
|
||||||
@ -164,7 +162,7 @@ def test_parse_name():
|
|||||||
@pytest.mark.usefixtures('setup_test')
|
@pytest.mark.usefixtures('setup_test')
|
||||||
def test_format_signature():
|
def test_format_signature():
|
||||||
def formatsig(objtype, name, obj, args, retann):
|
def formatsig(objtype, name, obj, args, retann):
|
||||||
inst = AutoDirective._registry[objtype](directive, name)
|
inst = app.registry.documenters[objtype](directive, name)
|
||||||
inst.fullname = name
|
inst.fullname = name
|
||||||
inst.doc_as_attr = False # for class objtype
|
inst.doc_as_attr = False # for class objtype
|
||||||
inst.object = obj
|
inst.object = obj
|
||||||
@ -246,7 +244,6 @@ def test_format_signature():
|
|||||||
# test exception handling (exception is caught and args is '')
|
# test exception handling (exception is caught and args is '')
|
||||||
directive.env.config.autodoc_docstring_signature = False
|
directive.env.config.autodoc_docstring_signature = False
|
||||||
assert formatsig('function', 'int', int, None, None) == ''
|
assert formatsig('function', 'int', int, None, None) == ''
|
||||||
del _warnings[:]
|
|
||||||
|
|
||||||
# test processing by event handler
|
# test processing by event handler
|
||||||
assert formatsig('method', 'bar', H.foo1, None, None) == '42'
|
assert formatsig('method', 'bar', H.foo1, None, None) == '42'
|
||||||
@ -270,7 +267,7 @@ def test_format_signature():
|
|||||||
@pytest.mark.usefixtures('setup_test')
|
@pytest.mark.usefixtures('setup_test')
|
||||||
def test_get_doc():
|
def test_get_doc():
|
||||||
def getdocl(objtype, obj, encoding=None):
|
def getdocl(objtype, obj, encoding=None):
|
||||||
inst = AutoDirective._registry[objtype](directive, 'tmp')
|
inst = app.registry.documenters[objtype](directive, 'tmp')
|
||||||
inst.object = obj
|
inst.object = obj
|
||||||
inst.objpath = [obj.__name__]
|
inst.objpath = [obj.__name__]
|
||||||
inst.doc_as_attr = False
|
inst.doc_as_attr = False
|
||||||
@ -449,7 +446,7 @@ def test_get_doc():
|
|||||||
@pytest.mark.usefixtures('setup_test')
|
@pytest.mark.usefixtures('setup_test')
|
||||||
def test_docstring_processing():
|
def test_docstring_processing():
|
||||||
def process(objtype, name, obj):
|
def process(objtype, name, obj):
|
||||||
inst = AutoDirective._registry[objtype](directive, name)
|
inst = app.registry.documenters[objtype](directive, name)
|
||||||
inst.object = obj
|
inst.object = obj
|
||||||
inst.fullname = name
|
inst.fullname = name
|
||||||
return list(inst.process_doc(inst.get_doc()))
|
return list(inst.process_doc(inst.get_doc()))
|
||||||
@ -506,7 +503,7 @@ def test_docstring_property_processing():
|
|||||||
def genarate_docstring(objtype, name, **kw):
|
def genarate_docstring(objtype, name, **kw):
|
||||||
del processed_docstrings[:]
|
del processed_docstrings[:]
|
||||||
del processed_signatures[:]
|
del processed_signatures[:]
|
||||||
inst = AutoDirective._registry[objtype](directive, name)
|
inst = app.registry.documenters[objtype](directive, name)
|
||||||
inst.generate(**kw)
|
inst.generate(**kw)
|
||||||
results = list(directive.result)
|
results = list(directive.result)
|
||||||
docstrings = inst.get_doc()[0]
|
docstrings = inst.get_doc()[0]
|
||||||
@ -540,6 +537,8 @@ def test_docstring_property_processing():
|
|||||||
|
|
||||||
@pytest.mark.usefixtures('setup_test')
|
@pytest.mark.usefixtures('setup_test')
|
||||||
def test_new_documenter():
|
def test_new_documenter():
|
||||||
|
logging.setup(app, app._status, app._warning)
|
||||||
|
|
||||||
class MyDocumenter(ModuleLevelDocumenter):
|
class MyDocumenter(ModuleLevelDocumenter):
|
||||||
objtype = 'integer'
|
objtype = 'integer'
|
||||||
directivetype = 'data'
|
directivetype = 'data'
|
||||||
@ -555,10 +554,11 @@ def test_new_documenter():
|
|||||||
add_documenter(MyDocumenter)
|
add_documenter(MyDocumenter)
|
||||||
|
|
||||||
def assert_result_contains(item, objtype, name, **kw):
|
def assert_result_contains(item, objtype, name, **kw):
|
||||||
inst = AutoDirective._registry[objtype](directive, name)
|
app._warning.truncate(0)
|
||||||
|
inst = app.registry.documenters[objtype](directive, name)
|
||||||
inst.generate(**kw)
|
inst.generate(**kw)
|
||||||
# print '\n'.join(directive.result)
|
# print '\n'.join(directive.result)
|
||||||
assert len(_warnings) == 0, _warnings
|
assert app._warning.getvalue() == ''
|
||||||
assert item in directive.result
|
assert item in directive.result
|
||||||
del directive.result[:]
|
del directive.result[:]
|
||||||
|
|
||||||
@ -581,7 +581,7 @@ def test_attrgetter_using():
|
|||||||
AutoDirective._special_attrgetters[type] = special_getattr
|
AutoDirective._special_attrgetters[type] = special_getattr
|
||||||
|
|
||||||
del getattr_spy[:]
|
del getattr_spy[:]
|
||||||
inst = AutoDirective._registry[objtype](directive, name)
|
inst = app.registry.documenters[objtype](directive, name)
|
||||||
inst.generate(**kw)
|
inst.generate(**kw)
|
||||||
|
|
||||||
hooked_members = [s[1] for s in getattr_spy]
|
hooked_members = [s[1] for s in getattr_spy]
|
||||||
@ -602,20 +602,22 @@ def test_attrgetter_using():
|
|||||||
|
|
||||||
@pytest.mark.usefixtures('setup_test')
|
@pytest.mark.usefixtures('setup_test')
|
||||||
def test_generate():
|
def test_generate():
|
||||||
|
logging.setup(app, app._status, app._warning)
|
||||||
|
|
||||||
def assert_warns(warn_str, objtype, name, **kw):
|
def assert_warns(warn_str, objtype, name, **kw):
|
||||||
inst = AutoDirective._registry[objtype](directive, name)
|
inst = app.registry.documenters[objtype](directive, name)
|
||||||
inst.generate(**kw)
|
inst.generate(**kw)
|
||||||
assert len(directive.result) == 0, directive.result
|
assert len(directive.result) == 0, directive.result
|
||||||
assert len(_warnings) == 1, _warnings
|
|
||||||
assert warn_str in _warnings[0], _warnings
|
assert warn_str in app._warning.getvalue()
|
||||||
del _warnings[:]
|
app._warning.truncate(0)
|
||||||
|
|
||||||
def assert_works(objtype, name, **kw):
|
def assert_works(objtype, name, **kw):
|
||||||
inst = AutoDirective._registry[objtype](directive, name)
|
inst = app.registry.documenters[objtype](directive, name)
|
||||||
inst.generate(**kw)
|
inst.generate(**kw)
|
||||||
assert directive.result
|
assert directive.result
|
||||||
# print '\n'.join(directive.result)
|
# print '\n'.join(directive.result)
|
||||||
assert len(_warnings) == 0, _warnings
|
assert app._warning.getvalue() == ''
|
||||||
del directive.result[:]
|
del directive.result[:]
|
||||||
|
|
||||||
def assert_processes(items, objtype, name, **kw):
|
def assert_processes(items, objtype, name, **kw):
|
||||||
@ -625,18 +627,18 @@ def test_generate():
|
|||||||
assert set(processed_docstrings) | set(processed_signatures) == set(items)
|
assert set(processed_docstrings) | set(processed_signatures) == set(items)
|
||||||
|
|
||||||
def assert_result_contains(item, objtype, name, **kw):
|
def assert_result_contains(item, objtype, name, **kw):
|
||||||
inst = AutoDirective._registry[objtype](directive, name)
|
inst = app.registry.documenters[objtype](directive, name)
|
||||||
inst.generate(**kw)
|
inst.generate(**kw)
|
||||||
# print '\n'.join(directive.result)
|
# print '\n'.join(directive.result)
|
||||||
assert len(_warnings) == 0, _warnings
|
assert app._warning.getvalue() == ''
|
||||||
assert item in directive.result
|
assert item in directive.result
|
||||||
del directive.result[:]
|
del directive.result[:]
|
||||||
|
|
||||||
def assert_order(items, objtype, name, member_order, **kw):
|
def assert_order(items, objtype, name, member_order, **kw):
|
||||||
inst = AutoDirective._registry[objtype](directive, name)
|
inst = app.registry.documenters[objtype](directive, name)
|
||||||
inst.options.member_order = member_order
|
inst.options.member_order = member_order
|
||||||
inst.generate(**kw)
|
inst.generate(**kw)
|
||||||
assert len(_warnings) == 0, _warnings
|
assert app._warning.getvalue() == ''
|
||||||
items = list(reversed(items))
|
items = list(reversed(items))
|
||||||
lineiter = iter(directive.result)
|
lineiter = iter(directive.result)
|
||||||
# for line in directive.result:
|
# for line in directive.result:
|
||||||
|
@ -165,13 +165,15 @@ def test_latex_warnings(app, status, warning):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.sphinx('latex', testroot='basic')
|
@pytest.mark.sphinx('latex', testroot='basic')
|
||||||
def test_latex_title(app, status, warning):
|
def test_latex_basic(app, status, warning):
|
||||||
app.builder.build_all()
|
app.builder.build_all()
|
||||||
result = (app.outdir / 'test.tex').text(encoding='utf8')
|
result = (app.outdir / 'test.tex').text(encoding='utf8')
|
||||||
print(result)
|
print(result)
|
||||||
print(status.getvalue())
|
print(status.getvalue())
|
||||||
print(warning.getvalue())
|
print(warning.getvalue())
|
||||||
assert '\\title{The basic Sphinx documentation for testing}' in result
|
assert r'\title{The basic Sphinx documentation for testing}' in result
|
||||||
|
assert r'\release{}' in result
|
||||||
|
assert r'\renewcommand{\releasename}{}' in result
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.sphinx('latex', testroot='latex-title')
|
@pytest.mark.sphinx('latex', testroot='latex-title')
|
||||||
@ -184,6 +186,18 @@ def test_latex_title_after_admonitions(app, status, warning):
|
|||||||
assert '\\title{test-latex-title}' in result
|
assert '\\title{test-latex-title}' in result
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.sphinx('latex', testroot='basic',
|
||||||
|
confoverrides={'release': '1.0'})
|
||||||
|
def test_latex_release(app, status, warning):
|
||||||
|
app.builder.build_all()
|
||||||
|
result = (app.outdir / 'test.tex').text(encoding='utf8')
|
||||||
|
print(result)
|
||||||
|
print(status.getvalue())
|
||||||
|
print(warning.getvalue())
|
||||||
|
assert r'\release{1.0}' in result
|
||||||
|
assert r'\renewcommand{\releasename}{Release}' in result
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.sphinx('latex', testroot='numfig',
|
@pytest.mark.sphinx('latex', testroot='numfig',
|
||||||
confoverrides={'numfig': True})
|
confoverrides={'numfig': True})
|
||||||
def test_numref(app, status, warning):
|
def test_numref(app, status, warning):
|
||||||
|
@ -57,10 +57,14 @@ def test_mangle_signature():
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.sphinx('dummy', **default_kw)
|
@pytest.mark.sphinx('dummy', **default_kw)
|
||||||
def test_get_items_summary(app, status, warning):
|
def test_get_items_summary(make_app, app_params):
|
||||||
|
import sphinx.ext.autosummary
|
||||||
|
import sphinx.ext.autosummary.generate
|
||||||
|
args, kwargs = app_params
|
||||||
|
app = make_app(*args, **kwargs)
|
||||||
|
sphinx.ext.autosummary.generate.setup_documenters(app)
|
||||||
# monkey-patch Autosummary.get_items so we can easily get access to it's
|
# monkey-patch Autosummary.get_items so we can easily get access to it's
|
||||||
# results..
|
# results..
|
||||||
import sphinx.ext.autosummary
|
|
||||||
orig_get_items = sphinx.ext.autosummary.Autosummary.get_items
|
orig_get_items = sphinx.ext.autosummary.Autosummary.get_items
|
||||||
|
|
||||||
autosummary_items = {}
|
autosummary_items = {}
|
||||||
@ -73,6 +77,10 @@ def test_get_items_summary(app, status, warning):
|
|||||||
|
|
||||||
def handler(app, what, name, obj, options, lines):
|
def handler(app, what, name, obj, options, lines):
|
||||||
assert isinstance(lines, list)
|
assert isinstance(lines, list)
|
||||||
|
|
||||||
|
# ensure no docstring is processed twice:
|
||||||
|
assert 'THIS HAS BEEN HANDLED' not in lines
|
||||||
|
lines.append('THIS HAS BEEN HANDLED')
|
||||||
app.connect('autodoc-process-docstring', handler)
|
app.connect('autodoc-process-docstring', handler)
|
||||||
|
|
||||||
sphinx.ext.autosummary.Autosummary.get_items = new_get_items
|
sphinx.ext.autosummary.Autosummary.get_items = new_get_items
|
||||||
@ -81,7 +89,7 @@ def test_get_items_summary(app, status, warning):
|
|||||||
finally:
|
finally:
|
||||||
sphinx.ext.autosummary.Autosummary.get_items = orig_get_items
|
sphinx.ext.autosummary.Autosummary.get_items = orig_get_items
|
||||||
|
|
||||||
html_warnings = warning.getvalue()
|
html_warnings = app._warning.getvalue()
|
||||||
assert html_warnings == ''
|
assert html_warnings == ''
|
||||||
|
|
||||||
expected_values = {
|
expected_values = {
|
||||||
|
133
tests/test_ext_inheritance.py
Normal file
133
tests/test_ext_inheritance.py
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
test_inheritance
|
||||||
|
~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Tests for :mod:`sphinx.ext.inheritance_diagram` module.
|
||||||
|
|
||||||
|
:copyright: Copyright 2015 by the Sphinx team, see AUTHORS.
|
||||||
|
:license: BSD, see LICENSE for details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import pytest
|
||||||
|
from sphinx.ext.inheritance_diagram import InheritanceDiagram
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.sphinx(buildername="html", testroot="inheritance")
|
||||||
|
@pytest.mark.usefixtures('if_graphviz_found')
|
||||||
|
def test_inheritance_diagram(app, status, warning):
|
||||||
|
# monkey-patch InheritaceDiagram.run() so we can get access to its
|
||||||
|
# results.
|
||||||
|
orig_run = InheritanceDiagram.run
|
||||||
|
graphs = {}
|
||||||
|
|
||||||
|
def new_run(self):
|
||||||
|
result = orig_run(self)
|
||||||
|
node = result[0]
|
||||||
|
source = os.path.basename(node.document.current_source).replace(".rst", "")
|
||||||
|
graphs[source] = node['graph']
|
||||||
|
return result
|
||||||
|
|
||||||
|
InheritanceDiagram.run = new_run
|
||||||
|
|
||||||
|
try:
|
||||||
|
app.builder.build_all()
|
||||||
|
finally:
|
||||||
|
InheritanceDiagram.run = orig_run
|
||||||
|
|
||||||
|
assert app.statuscode == 0
|
||||||
|
|
||||||
|
html_warnings = warning.getvalue()
|
||||||
|
assert html_warnings == ""
|
||||||
|
|
||||||
|
# note: it is better to split these asserts into separate test functions
|
||||||
|
# but I can't figure out how to build only a specific .rst file
|
||||||
|
|
||||||
|
# basic inheritance diagram showing all classes
|
||||||
|
for cls in graphs['basic_diagram'].class_info:
|
||||||
|
# use in b/c traversing order is different sometimes
|
||||||
|
assert cls in [
|
||||||
|
('dummy.test.A', 'dummy.test.A', [], None),
|
||||||
|
('dummy.test.F', 'dummy.test.F', ['dummy.test.C'], None),
|
||||||
|
('dummy.test.C', 'dummy.test.C', ['dummy.test.A'], None),
|
||||||
|
('dummy.test.E', 'dummy.test.E', ['dummy.test.B'], None),
|
||||||
|
('dummy.test.D', 'dummy.test.D',
|
||||||
|
['dummy.test.B', 'dummy.test.C'], None),
|
||||||
|
('dummy.test.B', 'dummy.test.B', ['dummy.test.A'], None)
|
||||||
|
]
|
||||||
|
|
||||||
|
# inheritance diagram using :parts: 1 option
|
||||||
|
for cls in graphs['diagram_w_parts'].class_info:
|
||||||
|
assert cls in [
|
||||||
|
('A', 'dummy.test.A', [], None),
|
||||||
|
('F', 'dummy.test.F', ['C'], None),
|
||||||
|
('C', 'dummy.test.C', ['A'], None),
|
||||||
|
('E', 'dummy.test.E', ['B'], None),
|
||||||
|
('D', 'dummy.test.D', ['B', 'C'], None),
|
||||||
|
('B', 'dummy.test.B', ['A'], None)
|
||||||
|
]
|
||||||
|
|
||||||
|
# inheritance diagram with 1 top class
|
||||||
|
# :top-classes: dummy.test.B
|
||||||
|
# rendering should be
|
||||||
|
# A
|
||||||
|
# \
|
||||||
|
# B C
|
||||||
|
# / \ / \
|
||||||
|
# E D F
|
||||||
|
#
|
||||||
|
for cls in graphs['diagram_w_1_top_class'].class_info:
|
||||||
|
assert cls in [
|
||||||
|
('dummy.test.A', 'dummy.test.A', [], None),
|
||||||
|
('dummy.test.F', 'dummy.test.F', ['dummy.test.C'], None),
|
||||||
|
('dummy.test.C', 'dummy.test.C', ['dummy.test.A'], None),
|
||||||
|
('dummy.test.E', 'dummy.test.E', ['dummy.test.B'], None),
|
||||||
|
('dummy.test.D', 'dummy.test.D',
|
||||||
|
['dummy.test.B', 'dummy.test.C'], None),
|
||||||
|
('dummy.test.B', 'dummy.test.B', [], None)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# inheritance diagram with 2 top classes
|
||||||
|
# :top-classes: dummy.test.B, dummy.test.C
|
||||||
|
# Note: we're specifying separate classes, not the entire module here
|
||||||
|
# rendering should be
|
||||||
|
#
|
||||||
|
# B C
|
||||||
|
# / \ / \
|
||||||
|
# E D F
|
||||||
|
#
|
||||||
|
for cls in graphs['diagram_w_2_top_classes'].class_info:
|
||||||
|
assert cls in [
|
||||||
|
('dummy.test.F', 'dummy.test.F', ['dummy.test.C'], None),
|
||||||
|
('dummy.test.C', 'dummy.test.C', [], None),
|
||||||
|
('dummy.test.E', 'dummy.test.E', ['dummy.test.B'], None),
|
||||||
|
('dummy.test.D', 'dummy.test.D',
|
||||||
|
['dummy.test.B', 'dummy.test.C'], None),
|
||||||
|
('dummy.test.B', 'dummy.test.B', [], None)
|
||||||
|
]
|
||||||
|
|
||||||
|
# inheritance diagram with 2 top classes and specifiying the entire module
|
||||||
|
# rendering should be
|
||||||
|
#
|
||||||
|
# A
|
||||||
|
# B C
|
||||||
|
# / \ / \
|
||||||
|
# E D F
|
||||||
|
#
|
||||||
|
# Note: dummy.test.A is included in the graph before its descendants are even processed
|
||||||
|
# b/c we've specified to load the entire module. The way InheritanceGraph works it is very
|
||||||
|
# hard to exclude parent classes once after they have been included in the graph.
|
||||||
|
# If you'd like to not show class A in the graph don't specify the entire module.
|
||||||
|
# this is a known issue.
|
||||||
|
for cls in graphs['diagram_module_w_2_top_classes'].class_info:
|
||||||
|
assert cls in [
|
||||||
|
('dummy.test.F', 'dummy.test.F', ['dummy.test.C'], None),
|
||||||
|
('dummy.test.C', 'dummy.test.C', [], None),
|
||||||
|
('dummy.test.E', 'dummy.test.E', ['dummy.test.B'], None),
|
||||||
|
('dummy.test.D', 'dummy.test.D',
|
||||||
|
['dummy.test.B', 'dummy.test.C'], None),
|
||||||
|
('dummy.test.B', 'dummy.test.B', [], None),
|
||||||
|
('dummy.test.A', 'dummy.test.A', [], None),
|
||||||
|
]
|
92
tests/test_smartquotes.py
Normal file
92
tests/test_smartquotes.py
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
test_smartquotes
|
||||||
|
~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Test smart quotes.
|
||||||
|
|
||||||
|
:copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
|
||||||
|
:license: BSD, see LICENSE for details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from sphinx.util import docutils
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.sphinx(buildername='html', testroot='smartquotes', freshenv=True)
|
||||||
|
def test_basic(app, status, warning):
|
||||||
|
app.build()
|
||||||
|
|
||||||
|
content = (app.outdir / 'index.html').text()
|
||||||
|
assert u'<p>– “Sphinx” is a tool that makes it easy …</p>' in content
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.sphinx(buildername='text', testroot='smartquotes', freshenv=True)
|
||||||
|
def test_text_builder(app, status, warning):
|
||||||
|
app.build()
|
||||||
|
|
||||||
|
content = (app.outdir / 'index.txt').text()
|
||||||
|
assert u'-- "Sphinx" is a tool that makes it easy ...' in content
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.sphinx(buildername='man', testroot='smartquotes', freshenv=True)
|
||||||
|
def test_man_builder(app, status, warning):
|
||||||
|
app.build()
|
||||||
|
|
||||||
|
content = (app.outdir / 'python.1').text()
|
||||||
|
assert u'\\-\\- "Sphinx" is a tool that makes it easy ...' in content
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.sphinx(buildername='latex', testroot='smartquotes', freshenv=True)
|
||||||
|
def test_latex_builder(app, status, warning):
|
||||||
|
app.build()
|
||||||
|
|
||||||
|
content = (app.outdir / 'test.tex').text()
|
||||||
|
assert u'\\textendash{} “Sphinx” is a tool that makes it easy …' in content
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.sphinx(buildername='html', testroot='smartquotes', freshenv=True,
|
||||||
|
confoverrides={'language': 'ja'})
|
||||||
|
def test_ja_html_builder(app, status, warning):
|
||||||
|
app.build()
|
||||||
|
|
||||||
|
content = (app.outdir / 'index.html').text()
|
||||||
|
assert u'<p>-- "Sphinx" is a tool that makes it easy ...</p>' in content
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.sphinx(buildername='html', testroot='smartquotes', freshenv=True,
|
||||||
|
confoverrides={'smartquotes': False})
|
||||||
|
def test_smartquotes_disabled(app, status, warning):
|
||||||
|
app.build()
|
||||||
|
|
||||||
|
content = (app.outdir / 'index.html').text()
|
||||||
|
assert u'<p>-- "Sphinx" is a tool that makes it easy ...</p>' in content
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(docutils.__version_info__ < (0, 14),
|
||||||
|
reason='docutils-0.14 or above is required')
|
||||||
|
@pytest.mark.sphinx(buildername='html', testroot='smartquotes', freshenv=True,
|
||||||
|
confoverrides={'smartquotes_action': 'q'})
|
||||||
|
def test_smartquotes_action(app, status, warning):
|
||||||
|
app.build()
|
||||||
|
|
||||||
|
content = (app.outdir / 'index.html').text()
|
||||||
|
assert u'<p>-- “Sphinx” is a tool that makes it easy ...</p>' in content
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.sphinx(buildername='html', testroot='smartquotes', freshenv=True,
|
||||||
|
confoverrides={'language': 'ja', 'smartquotes_excludes': {}})
|
||||||
|
def test_smartquotes_excludes_language(app, status, warning):
|
||||||
|
app.build()
|
||||||
|
|
||||||
|
content = (app.outdir / 'index.html').text()
|
||||||
|
assert u'<p>– 「Sphinx」 is a tool that makes it easy …</p>' in content
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.sphinx(buildername='man', testroot='smartquotes', freshenv=True,
|
||||||
|
confoverrides={'smartquotes_excludes': {}})
|
||||||
|
def test_smartquotes_excludes_builders(app, status, warning):
|
||||||
|
app.build()
|
||||||
|
|
||||||
|
content = (app.outdir / 'python.1').text()
|
||||||
|
assert u'– “Sphinx” is a tool that makes it easy …' in content
|
@ -10,10 +10,14 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
from sphinx.ext.autosummary.generate import setup_documenters
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.sphinx('html', testroot='templating')
|
@pytest.mark.sphinx('html', testroot='templating')
|
||||||
def test_layout_overloading(app, status, warning):
|
def test_layout_overloading(make_app, app_params):
|
||||||
|
args, kwargs = app_params
|
||||||
|
app = make_app(*args, **kwargs)
|
||||||
|
setup_documenters(app)
|
||||||
app.builder.build_update()
|
app.builder.build_update()
|
||||||
|
|
||||||
result = (app.outdir / 'contents.html').text(encoding='utf-8')
|
result = (app.outdir / 'contents.html').text(encoding='utf-8')
|
||||||
@ -22,7 +26,10 @@ def test_layout_overloading(app, status, warning):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.sphinx('html', testroot='templating')
|
@pytest.mark.sphinx('html', testroot='templating')
|
||||||
def test_autosummary_class_template_overloading(app, status, warning):
|
def test_autosummary_class_template_overloading(make_app, app_params):
|
||||||
|
args, kwargs = app_params
|
||||||
|
app = make_app(*args, **kwargs)
|
||||||
|
setup_documenters(app)
|
||||||
app.builder.build_update()
|
app.builder.build_update()
|
||||||
|
|
||||||
result = (app.outdir / 'generated' / 'sphinx.application.TemplateBridge.html').text(
|
result = (app.outdir / 'generated' / 'sphinx.application.TemplateBridge.html').text(
|
||||||
|
@ -183,7 +183,7 @@ def test_warning_location(app, status, warning):
|
|||||||
assert 'index.txt:10: WARNING: message2' in warning.getvalue()
|
assert 'index.txt:10: WARNING: message2' in warning.getvalue()
|
||||||
|
|
||||||
logger.warning('message3', location=None)
|
logger.warning('message3', location=None)
|
||||||
assert colorize('darkred', 'WARNING: message3') in warning.getvalue()
|
assert colorize('red', 'WARNING: message3') in warning.getvalue()
|
||||||
|
|
||||||
node = nodes.Node()
|
node = nodes.Node()
|
||||||
node.source, node.line = ('index.txt', 10)
|
node.source, node.line = ('index.txt', 10)
|
||||||
@ -200,7 +200,7 @@ def test_warning_location(app, status, warning):
|
|||||||
|
|
||||||
node.source, node.line = (None, None)
|
node.source, node.line = (None, None)
|
||||||
logger.warning('message7', location=node)
|
logger.warning('message7', location=node)
|
||||||
assert colorize('darkred', 'WARNING: message7') in warning.getvalue()
|
assert colorize('red', 'WARNING: message7') in warning.getvalue()
|
||||||
|
|
||||||
|
|
||||||
def test_pending_warnings(app, status, warning):
|
def test_pending_warnings(app, status, warning):
|
||||||
@ -236,7 +236,7 @@ def test_colored_logs(app, status, warning):
|
|||||||
assert colorize('darkgray', 'message1') in status.getvalue()
|
assert colorize('darkgray', 'message1') in status.getvalue()
|
||||||
assert 'message2\n' in status.getvalue() # not colored
|
assert 'message2\n' in status.getvalue() # not colored
|
||||||
assert 'message3\n' in status.getvalue() # not colored
|
assert 'message3\n' in status.getvalue() # not colored
|
||||||
assert colorize('darkred', 'WARNING: message4') in warning.getvalue()
|
assert colorize('red', 'WARNING: message4') in warning.getvalue()
|
||||||
assert 'WARNING: message5\n' in warning.getvalue() # not colored
|
assert 'WARNING: message5\n' in warning.getvalue() # not colored
|
||||||
assert colorize('darkred', 'WARNING: message6') in warning.getvalue()
|
assert colorize('darkred', 'WARNING: message6') in warning.getvalue()
|
||||||
|
|
||||||
|
2
tox.ini
2
tox.ini
@ -21,7 +21,7 @@ deps =
|
|||||||
du13: docutils==0.13.1
|
du13: docutils==0.13.1
|
||||||
du14: docutils==0.14
|
du14: docutils==0.14
|
||||||
setenv =
|
setenv =
|
||||||
PYTHONWARNINGS = all,ignore::ImportWarning:pkgutil
|
PYTHONWARNINGS = all,ignore::ImportWarning:pkgutil,ignore::ImportWarning:importlib._bootstrap,ignore::ImportWarning:importlib._bootstrap_external,ignore::ImportWarning:pytest_cov.plugin,ignore::DeprecationWarning:site,ignore::DeprecationWarning:_pytest.assertion.rewrite,ignore::DeprecationWarning:_pytest.fixtures
|
||||||
SPHINX_TEST_TEMPDIR = {envdir}/testbuild
|
SPHINX_TEST_TEMPDIR = {envdir}/testbuild
|
||||||
commands=
|
commands=
|
||||||
pytest -Wall --durations 25 {posargs}
|
pytest -Wall --durations 25 {posargs}
|
||||||
|
Loading…
Reference in New Issue
Block a user