mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2026-09-03 20:52:55 -05:00
Merge branch 'master' into feature-autosummary-packages
This commit is contained in:
@@ -25,6 +25,8 @@ matrix:
|
||||
env: TOXENV=py38
|
||||
- python: '3.6'
|
||||
env: TOXENV=docs
|
||||
- python: '3.6'
|
||||
env: TOXENV=docslint
|
||||
- python: '3.6'
|
||||
env: TOXENV=mypy
|
||||
- python: '3.6'
|
||||
|
||||
@@ -82,6 +82,7 @@ Other contributors, listed alphabetically, are:
|
||||
* Stephen Finucane -- setup command improvements and documentation
|
||||
* Daniel Pizetta -- inheritance diagram improvements
|
||||
* KINEBUCHI Tomohiko -- typing Sphinx as well as docutils
|
||||
* Adrián Chaves (Gallaecio) -- coverage builder improvements
|
||||
|
||||
Many thanks for all contributions!
|
||||
|
||||
|
||||
+2
-2
@@ -303,8 +303,8 @@ Debugging Tips
|
||||
* Set the debugging options in the `Docutils configuration file
|
||||
<http://docutils.sourceforge.net/docs/user/config.html>`_.
|
||||
|
||||
* JavaScript stemming algorithms in ``sphinx/search/*.py`` (except ``en.py``) are
|
||||
generated by this
|
||||
* JavaScript stemming algorithms in ``sphinx/search/*.py`` (except ``en.py``)
|
||||
are generated by this
|
||||
`modified snowballcode generator <https://github.com/shibukawa/snowball>`_.
|
||||
Generated `JSX <https://jsx.github.io/>`_ files are
|
||||
in `this repository <https://github.com/shibukawa/snowball-stemmer.jsx>`_.
|
||||
|
||||
@@ -59,6 +59,10 @@ style-check:
|
||||
type-check:
|
||||
mypy sphinx
|
||||
|
||||
.PHONY: doclinter
|
||||
doclinter:
|
||||
python utils/doclinter.py CHANGES *.rst doc/
|
||||
|
||||
.PHONY: pylint
|
||||
pylint:
|
||||
@pylint --rcfile utils/pylintrc sphinx
|
||||
|
||||
+6
-3
@@ -46,7 +46,8 @@ This is the current list of contributed extensions in that repository:
|
||||
- coffeedomain: a domain for (auto)documenting CoffeeScript source code
|
||||
- context: a builder for ConTeXt
|
||||
- disqus: embed Disqus comments in documents
|
||||
- documentedlist: converts a Python list to a table in the generated documentation
|
||||
- documentedlist: converts a Python list to a table in the generated
|
||||
documentation
|
||||
- doxylink: Link to external Doxygen-generated HTML documentation
|
||||
- domaintools_: A tool for easy domain creation
|
||||
- email: obfuscate email addresses
|
||||
@@ -76,7 +77,8 @@ This is the current list of contributed extensions in that repository:
|
||||
- nicovideo: embed videos from nicovideo
|
||||
- nwdiag: embed network diagrams by using nwdiag_
|
||||
- omegat: support tools to collaborate with OmegaT_ (Sphinx 1.1 needed)
|
||||
- osaka: convert standard Japanese doc to Osaka dialect (this is a joke extension)
|
||||
- osaka: convert standard Japanese doc to Osaka dialect (this is a joke
|
||||
extension)
|
||||
- paverutils: an alternate integration of Sphinx with Paver_
|
||||
- phpdomain: an extension for PHP support
|
||||
- plantuml: embed UML diagram by using PlantUML_
|
||||
@@ -96,7 +98,8 @@ This is the current list of contributed extensions in that repository:
|
||||
- sword: an extension inserting Bible verses from Sword_
|
||||
- tikz: draw pictures with the `TikZ/PGF LaTeX package`_
|
||||
- traclinks: create TracLinks_ to a Trac_ instance from within Sphinx
|
||||
- versioning: Sphinx extension that allows building versioned docs for self-hosting
|
||||
- versioning: Sphinx extension that allows building versioned docs for
|
||||
self-hosting
|
||||
- whooshindex: whoosh indexer extension
|
||||
- youtube: embed videos from YouTube_
|
||||
- zopeext: provide an ``autointerface`` directive for using `Zope interfaces`_
|
||||
|
||||
@@ -77,7 +77,7 @@ create directives should extend this class.
|
||||
|
||||
.. seealso::
|
||||
|
||||
`The docutils documentation on creating directives <docutils directives>`_
|
||||
`The docutils documentation on creating directives <docutils directives_>`_
|
||||
|
||||
This class contains a ``run`` method. This method is a requirement and it is
|
||||
part of every directive. It contains the main logic of the directive and it
|
||||
@@ -87,7 +87,7 @@ nodes available: text, paragraph, reference, table, etc.
|
||||
|
||||
.. seealso::
|
||||
|
||||
`The docutils documentation on nodes <docutils nodes>`_
|
||||
`The docutils documentation on nodes <docutils nodes_>`_
|
||||
|
||||
The ``nodes.paragraph`` class creates a new paragraph node. A paragraph
|
||||
node typically contains some text that we can set during instantiation using
|
||||
|
||||
@@ -23,8 +23,8 @@ We want the extension to add the following to Sphinx:
|
||||
only shown in the output if a new config value is set. Todo entries should not
|
||||
be in the output by default.
|
||||
|
||||
* A ``todolist`` directive that creates a list of all todo entries throughout the
|
||||
documentation.
|
||||
* A ``todolist`` directive that creates a list of all todo entries throughout
|
||||
the documentation.
|
||||
|
||||
For that, we will need to add the following elements to Sphinx:
|
||||
|
||||
|
||||
@@ -145,9 +145,9 @@ Sphinx core events
|
||||
------------------
|
||||
|
||||
These events are known to the core. The arguments shown are given to the
|
||||
registered event handlers. Use :meth:`.Sphinx.connect` in an extension's ``setup``
|
||||
function (note that ``conf.py`` can also have a ``setup`` function) to connect
|
||||
handlers to the events. Example:
|
||||
registered event handlers. Use :meth:`.Sphinx.connect` in an extension's
|
||||
``setup`` function (note that ``conf.py`` can also have a ``setup`` function) to
|
||||
connect handlers to the events. Example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
|
||||
@@ -171,6 +171,11 @@ The following is a list of deprecated interfaces.
|
||||
- 4.0
|
||||
- N/A
|
||||
|
||||
* - ``sphinx.ext.apidoc.makename()``
|
||||
- 2.1
|
||||
- 4.0
|
||||
- ``sphinx.ext.apidoc.module_join()``
|
||||
|
||||
* - ``sphinx.ext.autodoc.importer.MockFinder``
|
||||
- 2.1
|
||||
- 4.0
|
||||
@@ -1066,7 +1071,9 @@ The following is a list of deprecated interfaces.
|
||||
* - ``sphinx.websupport``
|
||||
- 1.6
|
||||
- 2.0
|
||||
- `sphinxcontrib-websupport <https://pypi.org/project/sphinxcontrib-websupport/>`_
|
||||
- `sphinxcontrib-websupport`_
|
||||
|
||||
.. _sphinxcontrib-websupport: https://pypi.org/project/sphinxcontrib-websupport/
|
||||
|
||||
* - ``StandaloneHTMLBuilder.css_files``
|
||||
- 1.6
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ i18n API
|
||||
.. _ext-i18n:
|
||||
|
||||
Extension internationalization (`i18n`) and localization (`l10n`) using i18n API
|
||||
---------------------------------------------------------------------------------
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
.. versionadded:: 1.8
|
||||
|
||||
|
||||
@@ -144,9 +144,9 @@ in which a Sphinx project is built: this works in several phases.
|
||||
|
||||
Now that the metadata and cross-reference data of all existing documents is
|
||||
known, all temporary nodes are replaced by nodes that can be converted into
|
||||
output using components called transforms. For example, links are created for
|
||||
object references that exist, and simple literal nodes are created for those
|
||||
that don't.
|
||||
output using components called transforms. For example, links are created
|
||||
for object references that exist, and simple literal nodes are created for
|
||||
those that don't.
|
||||
|
||||
**Phase 4: Writing**
|
||||
|
||||
|
||||
@@ -56,8 +56,8 @@ Logging API
|
||||
:meth:`SphinxLoggerAdapter.warning`.
|
||||
|
||||
**color**
|
||||
The color of logs. By default, info and verbose level logs are not colored,
|
||||
and debug level ones are colored as ``"darkgray"``.
|
||||
The color of logs. By default, info and verbose level logs are not
|
||||
colored, and debug level ones are colored as ``"darkgray"``.
|
||||
|
||||
.. autofunction:: pending_logging()
|
||||
|
||||
|
||||
@@ -138,8 +138,8 @@ Both APIs parse the content into a given node. They are used like this::
|
||||
|
||||
.. 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()``.
|
||||
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
|
||||
return ``node.children`` from the Directive.
|
||||
|
||||
+7
-10
@@ -37,9 +37,6 @@ How do I...
|
||||
You'll still need to mark up classes and such, but the headings and code
|
||||
examples come through cleanly.
|
||||
|
||||
... create HTML slides from Sphinx documents?
|
||||
See the "Hieroglyph" package at https://github.com/nyergler/hieroglyph.
|
||||
|
||||
For many more extensions and other contributed stuff, see the sphinx-contrib_
|
||||
repository.
|
||||
|
||||
@@ -51,12 +48,10 @@ Using Sphinx with...
|
||||
--------------------
|
||||
|
||||
Read the Docs
|
||||
`Read the Docs <https://readthedocs.org>`_ is a documentation hosting service based around
|
||||
Sphinx. They will host sphinx documentation, along with supporting a number
|
||||
of other features including version support, PDF generation, and more. The
|
||||
`Getting Started
|
||||
<https://docs.readthedocs.io/en/stable/intro/getting-started-with-sphinx.html>`_
|
||||
guide is a good place to start.
|
||||
`Read the Docs <https://readthedocs.org>`_ is a documentation hosting
|
||||
service based around Sphinx. They will host sphinx documentation, along
|
||||
with supporting a number of other features including version support, PDF
|
||||
generation, and more. The `Getting Started`_ guide is a good place to start.
|
||||
|
||||
Epydoc
|
||||
There's a third-party extension providing an `api role`_ which refers to
|
||||
@@ -83,7 +78,8 @@ GitHub Pages
|
||||
Sphinx HTML output properly.
|
||||
|
||||
MediaWiki
|
||||
See https://bitbucket.org/kevindunn/sphinx-wiki/wiki/Home, a project by Kevin Dunn.
|
||||
See https://bitbucket.org/kevindunn/sphinx-wiki/wiki/Home, a project by
|
||||
Kevin Dunn.
|
||||
|
||||
Google Analytics
|
||||
You can use a custom ``layout.html`` template, like this:
|
||||
@@ -149,6 +145,7 @@ Google Search
|
||||
|
||||
3. Add ``searchbox.html`` to the :confval:`html_sidebars` configuration value.
|
||||
|
||||
.. _Getting Started: https://docs.readthedocs.io/en/stable/intro/getting-started-with-sphinx.html
|
||||
.. _api role: https://git.savannah.gnu.org/cgit/kenozooid.git/tree/doc/extapi.py
|
||||
.. _xhtml to reST: http://docutils.sourceforge.net/sandbox/xhtml2rest/xhtml2rest.py
|
||||
|
||||
|
||||
+2
-2
@@ -17,10 +17,10 @@ docs have a look at `Epydoc <http://epydoc.sourceforge.net/>`_, which also
|
||||
understands reST.
|
||||
|
||||
For a great "introduction" to writing docs in general -- the whys and hows, see
|
||||
also `Write the docs <https://write-the-docs.readthedocs.io/>`_, written by Eric
|
||||
Holscher.
|
||||
also `Write the docs`_, written by Eric Holscher.
|
||||
|
||||
.. _rinohtype: https://github.com/brechtm/rinohtype
|
||||
.. _Write the docs: http://www.writethedocs.org/guide/writing/beginners-guide-to-docs/
|
||||
|
||||
Conversion from other systems
|
||||
-----------------------------
|
||||
|
||||
@@ -4,7 +4,8 @@ sphinx-apidoc
|
||||
Synopsis
|
||||
--------
|
||||
|
||||
**sphinx-apidoc** [*OPTIONS*] -o <*OUTPUT_PATH*> <*MODULE_PATH*> [*EXCLUDE_PATTERN*, ...]
|
||||
**sphinx-apidoc** [*OPTIONS*] -o <*OUTPUT_PATH*> <*MODULE_PATH*>
|
||||
[*EXCLUDE_PATTERN*, ...]
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
@@ -302,7 +302,8 @@ variables to customize behavior:
|
||||
|
||||
.. describe:: SPHINXOPTS
|
||||
|
||||
Additional options for :program:`sphinx-build`.
|
||||
Additional options for :program:`sphinx-build`. These options can
|
||||
also be set via the shortcut variable **O** (capital 'o').
|
||||
|
||||
.. _when-deprecation-warnings-are-displayed:
|
||||
|
||||
|
||||
+2
-1
@@ -297,7 +297,8 @@ in the future.
|
||||
|
||||
The "page name" of the current file, i.e. either the document name if the
|
||||
file is generated from a reST source, or the equivalent hierarchical name
|
||||
relative to the output directory (``[directory/]filename_without_extension``).
|
||||
relative to the output directory
|
||||
(``[directory/]filename_without_extension``).
|
||||
|
||||
.. data:: project
|
||||
|
||||
|
||||
+3
-2
@@ -49,8 +49,9 @@ Python :mod:`ConfigParser` module) and has the following structure:
|
||||
* The **inherit** setting gives the name of a "base theme", or ``none``. The
|
||||
base theme will be used to locate missing templates (most themes will not have
|
||||
to supply most templates if they use ``basic`` as the base theme), its options
|
||||
will be inherited, and all of its static files will be used as well. If you want
|
||||
to also inherit the stylesheet, include it via CSS' ``@import`` in your own.
|
||||
will be inherited, and all of its static files will be used as well. If you
|
||||
want to also inherit the stylesheet, include it via CSS' ``@import`` in your
|
||||
own.
|
||||
|
||||
* The **stylesheet** setting gives the name of a CSS file which will be
|
||||
referenced in the HTML header. If you need more than one CSS file, either
|
||||
|
||||
@@ -63,7 +63,7 @@ This dict can then be used as context for templates. The goal is to be easy to
|
||||
integrate with your existing templating system. An example using `Jinja2
|
||||
<http://jinja.pocoo.org/>`_ is:
|
||||
|
||||
.. sourcecode:: html+jinja
|
||||
.. code-block:: html+jinja
|
||||
|
||||
{%- extends "layout.html" %}
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ The builder's "name" must be given to the **-b** command-line option of
|
||||
|
||||
.. versionadded:: 1.0
|
||||
|
||||
.. module:: sphinx.builders.htmlhelp
|
||||
.. module:: sphinxcontrib.htmlhelp
|
||||
.. class:: HTMLHelpBuilder
|
||||
|
||||
This builder produces the same output as the standalone HTML builder, but
|
||||
|
||||
+24
-19
@@ -296,25 +296,26 @@ General configuration
|
||||
|
||||
Sphinx supports following warning types:
|
||||
|
||||
* app.add_node
|
||||
* app.add_directive
|
||||
* app.add_role
|
||||
* app.add_generic_role
|
||||
* app.add_source_parser
|
||||
* download.not_readable
|
||||
* image.not_readable
|
||||
* ref.term
|
||||
* ref.ref
|
||||
* ref.numref
|
||||
* ref.keyword
|
||||
* ref.option
|
||||
* ref.citation
|
||||
* ref.footnote
|
||||
* ref.doc
|
||||
* ref.python
|
||||
* misc.highlighting_failure
|
||||
* toc.secnum
|
||||
* epub.unknown_project_files
|
||||
* ``app.add_node``
|
||||
* ``app.add_directive``
|
||||
* ``app.add_role``
|
||||
* ``app.add_generic_role``
|
||||
* ``app.add_source_parser``
|
||||
* ``download.not_readable``
|
||||
* ``image.not_readable``
|
||||
* ``ref.term``
|
||||
* ``ref.ref``
|
||||
* ``ref.numref``
|
||||
* ``ref.keyword``
|
||||
* ``ref.option``
|
||||
* ``ref.citation``
|
||||
* ``ref.footnote``
|
||||
* ``ref.doc``
|
||||
* ``ref.python``
|
||||
* ``misc.highlighting_failure``
|
||||
* ``toc.secnum``
|
||||
* ``epub.unknown_project_files``
|
||||
* ``autosectionlabel.*``
|
||||
|
||||
You can choose from these types.
|
||||
|
||||
@@ -334,6 +335,10 @@ General configuration
|
||||
|
||||
Added ``ref.footnote``
|
||||
|
||||
.. versionchanged:: 2.1
|
||||
|
||||
Added ``autosectionlabel.*``
|
||||
|
||||
.. confval:: needs_sphinx
|
||||
|
||||
If set to a ``major.minor`` version string like ``'1.1'``, Sphinx will
|
||||
|
||||
@@ -40,10 +40,8 @@ you can also enable the :mod:`napoleon <sphinx.ext.napoleon>` extension.
|
||||
:mod:`napoleon <sphinx.ext.napoleon>` is a preprocessor that converts your
|
||||
docstrings to correct reStructuredText before :mod:`autodoc` processes them.
|
||||
|
||||
.. _Google:
|
||||
https://github.com/google/styleguide/blob/gh-pages/pyguide.md#38-comments-and-docstrings
|
||||
.. _NumPy:
|
||||
https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt
|
||||
.. _Google: https://github.com/google/styleguide/blob/gh-pages/pyguide.md#38-comments-and-docstrings
|
||||
.. _NumPy: https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt
|
||||
|
||||
|
||||
Directives
|
||||
@@ -431,6 +429,16 @@ There are also config values that you can set:
|
||||
This config value only requires to declare the top-level modules that
|
||||
should be mocked.
|
||||
|
||||
.. confval:: autodoc_typehints
|
||||
|
||||
This value controls how to represents typehints. The setting takes the
|
||||
following values:
|
||||
|
||||
* ``'signature'`` -- Show typehints as its signature (default)
|
||||
* ``'none'`` -- Do not show typehints
|
||||
|
||||
.. versionadded: 2.1
|
||||
|
||||
.. confval:: autodoc_warningiserror
|
||||
|
||||
This value controls the behavior of :option:`sphinx-build -W` during
|
||||
|
||||
@@ -22,6 +22,16 @@ should check:
|
||||
|
||||
.. confval:: coverage_ignore_classes
|
||||
|
||||
.. confval:: coverage_ignore_pyobjects
|
||||
|
||||
List of `Python regular expressions`_.
|
||||
|
||||
If any of these regular expressions matches any part of the full import path
|
||||
of a Python object, that Python object is excluded from the documentation
|
||||
coverage report.
|
||||
|
||||
.. versionadded:: 2.1
|
||||
|
||||
.. confval:: coverage_c_path
|
||||
|
||||
.. confval:: coverage_c_regexes
|
||||
@@ -40,3 +50,5 @@ should check:
|
||||
``False`` by default.
|
||||
|
||||
.. versionadded:: 1.1
|
||||
|
||||
.. _Python regular expressions: https://docs.python.org/library/re
|
||||
@@ -84,8 +84,8 @@ It adds these directives:
|
||||
``dot`` command within the directive.
|
||||
|
||||
.. versionadded:: 1.5
|
||||
All three directives support a ``align`` option to align the graph horizontal.
|
||||
The values "left", "center", "right" are allowed.
|
||||
All three directives support a ``align`` option to align the graph
|
||||
horizontal. The values "left", "center", "right" are allowed.
|
||||
|
||||
.. versionadded:: 1.6
|
||||
All three directives support a ``name`` option to set the label to graph.
|
||||
|
||||
@@ -33,6 +33,6 @@ This extension is quite simple, and features only one directive:
|
||||
def setup(app):
|
||||
app.add_config_value('releaselevel', '', 'env')
|
||||
|
||||
The second argument is the default value, the third should always be ``'env'``
|
||||
for such values (it selects if Sphinx re-reads the documents if the value
|
||||
changes).
|
||||
The second argument is the default value, the third should always be
|
||||
``'env'`` for such values (it selects if Sphinx re-reads the documents if the
|
||||
value changes).
|
||||
|
||||
@@ -36,8 +36,8 @@ It adds this directive:
|
||||
with ``lib.``, you can give ``:parts: -1`` to remove that prefix from the
|
||||
displayed node names.
|
||||
|
||||
The directive also supports a ``private-bases`` flag option; if given, private base
|
||||
classes (those whose name starts with ``_``) will be included.
|
||||
The directive also supports a ``private-bases`` flag option; if given,
|
||||
private base classes (those whose name starts with ``_``) will be included.
|
||||
|
||||
You can use ``caption`` option to give a caption to the diagram.
|
||||
|
||||
|
||||
@@ -17,8 +17,9 @@ There are two additional directives when using this extension:
|
||||
``True``.
|
||||
|
||||
.. versionadded:: 1.3.2
|
||||
This directive supports an ``class`` option that determines the class attribute
|
||||
for HTML output. If not given, the class defaults to ``admonition-todo``.
|
||||
This directive supports an ``class`` option that determines the class
|
||||
attribute for HTML output. If not given, the class defaults to
|
||||
``admonition-todo``.
|
||||
|
||||
|
||||
.. rst:directive:: todolist
|
||||
@@ -46,8 +47,8 @@ Configuration
|
||||
|
||||
.. confval:: todo_link_only
|
||||
|
||||
If this is ``True``, :rst:dir:`todolist` produce output without file path and line,
|
||||
The default is ``False``.
|
||||
If this is ``True``, :rst:dir:`todolist` produce output without file path and
|
||||
line, The default is ``False``.
|
||||
|
||||
.. versionadded:: 1.4
|
||||
|
||||
@@ -57,5 +58,5 @@ autodoc provides the following an additional event:
|
||||
|
||||
.. versionadded:: 1.5
|
||||
|
||||
Emitted when a todo is defined. *node* is the defined ``sphinx.ext.todo.todo_node``
|
||||
node.
|
||||
Emitted when a todo is defined. *node* is the defined
|
||||
``sphinx.ext.todo.todo_node`` node.
|
||||
|
||||
@@ -6,7 +6,7 @@ Once Sphinx is :doc:`installed </usage/installation>`, you can proceed with
|
||||
setting up your first Sphinx project. To ease the process of getting started,
|
||||
Sphinx provides a tool, :program:`sphinx-quickstart`, which will generate a
|
||||
documentation source directory and populate it with some defaults. We're going
|
||||
to use the :program:`sphinx-quickstart` tool here, though it's use by no means
|
||||
to use the :program:`sphinx-quickstart` tool here, though its use is by no means
|
||||
necessary.
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ configuration values from a few questions it asks you. To use this, run:
|
||||
|
||||
$ sphinx-quickstart
|
||||
|
||||
Answer each question asked. Be sure to say yes to the ``autodoc`` extension, as
|
||||
Answer each question asked. Be sure to say "yes" to the ``autodoc`` extension, as
|
||||
we will use this later.
|
||||
|
||||
There is also an automatic "API documentation" generator called
|
||||
@@ -103,7 +103,7 @@ In Sphinx source files, you can use most features of standard
|
||||
For example, you can add cross-file references in a portable way (which works
|
||||
for all output types) using the :rst:role:`ref` role.
|
||||
|
||||
For an example, if you are viewing the HTML version you can look at the source
|
||||
For an example, if you are viewing the HTML version, you can look at the source
|
||||
for this document -- use the "Show Source" link in the sidebar.
|
||||
|
||||
.. todo:: Update the below link when we add new guides on these.
|
||||
|
||||
@@ -686,8 +686,8 @@ Glossary
|
||||
|
||||
(When the glossary is sorted, the first term determines the sort order.)
|
||||
|
||||
If you want to specify "grouping key" for general index entries, you can put a "key"
|
||||
as "term : key". For example::
|
||||
If you want to specify "grouping key" for general index entries, you can put
|
||||
a "key" as "term : key". For example::
|
||||
|
||||
.. glossary::
|
||||
|
||||
@@ -697,12 +697,12 @@ Glossary
|
||||
|
||||
Note that "key" is used for grouping key as is.
|
||||
The "key" isn't normalized; key "A" and "a" become different groups.
|
||||
The whole characters in "key" is used instead of a first character; it is used for
|
||||
"Combining Character Sequence" and "Surrogate Pairs" grouping key.
|
||||
The whole characters in "key" is used instead of a first character; it is
|
||||
used for "Combining Character Sequence" and "Surrogate Pairs" grouping key.
|
||||
|
||||
In i18n situation, you can specify "localized term : key" even if original text only
|
||||
have "term" part. In this case, translated "localized term" will be categorized in
|
||||
"key" group.
|
||||
In i18n situation, you can specify "localized term : key" even if original
|
||||
text only have "term" part. In this case, translated "localized term" will be
|
||||
categorized in "key" group.
|
||||
|
||||
.. versionadded:: 0.6
|
||||
You can now give the glossary directive a ``:sorted:`` flag that will
|
||||
@@ -958,16 +958,16 @@ this reason, the following directive exists:
|
||||
.. warning::
|
||||
|
||||
Tables with more than 30 rows are rendered using ``longtable``, not
|
||||
``tabulary``, in order to allow pagebreaks. The ``L``, ``R``, ... specifiers
|
||||
do not work for these tables.
|
||||
``tabulary``, in order to allow pagebreaks. The ``L``, ``R``, ...
|
||||
specifiers do not work for these tables.
|
||||
|
||||
Tables that contain list-like elements such as object descriptions,
|
||||
blockquotes or any kind of lists cannot be set out of the box with
|
||||
``tabulary``. They are therefore set with the standard LaTeX ``tabular`` (or
|
||||
``longtable``) environment if you don't give a ``tabularcolumns`` directive.
|
||||
If you do, the table will be set with ``tabulary`` but you must use the
|
||||
``p{width}`` construct (or Sphinx's ``\X`` and ``\Y`` specifiers described
|
||||
below) for the columns containing these elements.
|
||||
``tabulary``. They are therefore set with the standard LaTeX ``tabular``
|
||||
(or ``longtable``) environment if you don't give a ``tabularcolumns``
|
||||
directive. If you do, the table will be set with ``tabulary`` but you
|
||||
must use the ``p{width}`` construct (or Sphinx's ``\X`` and ``\Y``
|
||||
specifiers described below) for the columns containing these elements.
|
||||
|
||||
Literal blocks do not work with ``tabulary`` at all, so tables containing
|
||||
a literal block are always set with ``tabular``. The verbatim environment
|
||||
@@ -996,10 +996,11 @@ this reason, the following directive exists:
|
||||
.. versionchanged:: 1.6
|
||||
|
||||
Merged cells from complex grid tables (either multi-row, multi-column, or
|
||||
both) now allow blockquotes, lists, literal blocks, ... as do regular cells.
|
||||
both) now allow blockquotes, lists, literal blocks, ... as do regular
|
||||
cells.
|
||||
|
||||
Sphinx's merged cells interact well with ``p{width}``, ``\X{a}{b}``, ``Y{f}``
|
||||
and tabulary's columns.
|
||||
Sphinx's merged cells interact well with ``p{width}``, ``\X{a}{b}``,
|
||||
``Y{f}`` and tabulary's columns.
|
||||
|
||||
.. note::
|
||||
|
||||
|
||||
@@ -128,17 +128,28 @@ declarations:
|
||||
|
||||
This directive will also cause an entry in the global module index.
|
||||
|
||||
The ``platform`` option, if present, is a comma-separated list of the
|
||||
platforms on which the module is available (if it is available on all
|
||||
platforms, the option should be omitted). The keys are short identifiers;
|
||||
examples that are in use include "IRIX", "Mac", "Windows", and "Unix". It is
|
||||
important to use a key which has already been used when applicable.
|
||||
.. rubric:: options
|
||||
|
||||
The ``synopsis`` option should consist of one sentence describing the
|
||||
module's purpose -- it is currently only used in the Global Module Index.
|
||||
.. rst:directive:option:: platform: platforms
|
||||
:type: comma separated list
|
||||
|
||||
The ``deprecated`` option can be given (with no value) to mark a module as
|
||||
deprecated; it will be designated as such in various locations then.
|
||||
Indicate platforms which the module is available (if it is available on
|
||||
all platforms, the option should be omitted). The keys are short
|
||||
identifiers; examples that are in use include "IRIX", "Mac", "Windows"
|
||||
and "Unix". It is important to use a key which has already been used when
|
||||
applicable.
|
||||
|
||||
.. rst:directive:option:: synopsis: purpose
|
||||
:type: text
|
||||
|
||||
Consist of one sentence describing the module's purpose -- it is currently
|
||||
only used in the Global Module Index.
|
||||
|
||||
.. rst:directive:option:: deprecated
|
||||
:type: no argument
|
||||
|
||||
Mark a module as deprecated; it will be designated as such in various
|
||||
locations then.
|
||||
|
||||
.. rst:directive:: .. py:currentmodule:: name
|
||||
|
||||
@@ -169,12 +180,14 @@ The following directives are provided for module and class contents:
|
||||
This information can (in any ``py`` directive) optionally be given in a
|
||||
structured form, see :ref:`info-field-lists`.
|
||||
|
||||
The ``async`` option can be given (with no value) to indicate the function is
|
||||
an async method.
|
||||
.. rubric:: options
|
||||
|
||||
.. versionchanged:: 2.1
|
||||
.. rst:directive:option:: async
|
||||
:type: no value
|
||||
|
||||
``:async:`` option added.
|
||||
Indicate the function is an async function.
|
||||
|
||||
.. versionadded:: 2.1
|
||||
|
||||
.. rst:directive:: .. py:data:: name
|
||||
|
||||
@@ -223,19 +236,43 @@ The following directives are provided for module and class contents:
|
||||
described for ``function``. See also :ref:`signatures` and
|
||||
:ref:`info-field-lists`.
|
||||
|
||||
The ``async`` option can be given (with no value) to indicate the method is
|
||||
an async method.
|
||||
.. rubric:: options
|
||||
|
||||
The ``classmethod`` option and ``staticmethod`` option can be given (with
|
||||
no value) to indicate the method is a class method (or a static method).
|
||||
.. rst:directive:option:: abstractmethod
|
||||
:type: no value
|
||||
|
||||
The ``property`` option can be given (with no value) to indicate the method
|
||||
is a property.
|
||||
Indicate the method is an abstract method.
|
||||
|
||||
.. versionchanged:: 2.1
|
||||
.. versionadded:: 2.1
|
||||
|
||||
.. rst:directive:option:: async
|
||||
:type: no value
|
||||
|
||||
Indicate the method is an async method.
|
||||
|
||||
.. versionadded:: 2.1
|
||||
|
||||
.. rst:directive:option:: classmethod
|
||||
:type: no value
|
||||
|
||||
Indicate the method is a class method.
|
||||
|
||||
.. versionadded:: 2.1
|
||||
|
||||
.. rst:directive:option:: property
|
||||
:type: no value
|
||||
|
||||
Indicate the method is a property.
|
||||
|
||||
.. versionadded:: 2.1
|
||||
|
||||
.. rst:directive:option:: staticmethod
|
||||
:type: no value
|
||||
|
||||
Indicate the method is a static method.
|
||||
|
||||
.. versionadded:: 2.1
|
||||
|
||||
``:async:``, ``:classmethod:``, ``:property:`` and ``:staticmethod:``
|
||||
options added.
|
||||
|
||||
.. rst:directive:: .. py:staticmethod:: name(parameters)
|
||||
|
||||
@@ -720,7 +757,8 @@ visibility statement (``public``, ``private`` or ``protected``).
|
||||
|
||||
.. cpp:enum-struct:: protected MyScopedVisibilityEnum : std::underlying_type<MySpecificEnum>::type
|
||||
|
||||
A scoped enum with non-default visibility, and with a specified underlying type.
|
||||
A scoped enum with non-default visibility, and with a specified
|
||||
underlying type.
|
||||
|
||||
.. rst:directive:: .. cpp:enumerator:: name
|
||||
.. cpp:enumerator:: name = constant
|
||||
@@ -760,7 +798,8 @@ visibility statement (``public``, ``private`` or ``protected``).
|
||||
**Valid Expressions**
|
||||
|
||||
- :cpp:expr:`*r`, when :cpp:expr:`r` is dereferenceable.
|
||||
- :cpp:expr:`++r`, with return type :cpp:expr:`It&`, when :cpp:expr:`r` is incrementable.
|
||||
- :cpp:expr:`++r`, with return type :cpp:expr:`It&`, when
|
||||
:cpp:expr:`r` is incrementable.
|
||||
|
||||
This will render as follows:
|
||||
|
||||
@@ -799,11 +838,12 @@ Anonymous Entities
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
C++ supports anonymous namespaces, classes, enums, and unions.
|
||||
For the sake of documentation they must be given some name that starts with ``@``,
|
||||
e.g., ``@42`` or ``@data``.
|
||||
For the sake of documentation they must be given some name that starts with
|
||||
``@``, e.g., ``@42`` or ``@data``.
|
||||
These names can also be used in cross-references and (type) expressions,
|
||||
though nested symbols will be found even when omitted.
|
||||
The ``@...`` name will always be rendered as **[anonymous]** (possibly as a link).
|
||||
The ``@...`` name will always be rendered as **[anonymous]** (possibly as a
|
||||
link).
|
||||
|
||||
Example::
|
||||
|
||||
@@ -835,8 +875,8 @@ Explicit ref: :cpp:var:`Data::@data::a`. Short-hand ref: :cpp:var:`Data::a`.
|
||||
Aliasing Declarations
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Sometimes it may be helpful list declarations elsewhere than their main documentation,
|
||||
e.g., when creating a synopsis of a class interface.
|
||||
Sometimes it may be helpful list declarations elsewhere than their main
|
||||
documentation, e.g., when creating a synopsis of a class interface.
|
||||
The following directive can be used for this purpose.
|
||||
|
||||
.. rst:directive:: .. cpp:alias:: name or function signature
|
||||
@@ -1079,7 +1119,8 @@ These roles link to the given declaration types:
|
||||
be properly qualified relative to the position of the link.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
The :rst:role:`cpp:struct` role as alias for the :rst:role:`cpp:class` role.
|
||||
The :rst:role:`cpp:struct` role as alias for the :rst:role:`cpp:class`
|
||||
role.
|
||||
|
||||
.. admonition:: Note on References with Templates Parameters/Arguments
|
||||
|
||||
@@ -1126,7 +1167,8 @@ References using the :rst:role:`cpp:func` role:
|
||||
- Specific overload: ``void C::f()``, :cpp:func:`void C::f()`
|
||||
- Specific overload: ``void C::f(int)``, :cpp:func:`void C::f(int)`
|
||||
- Specific overload: ``void C::f(double)``, :cpp:func:`void C::f(double)`
|
||||
- Specific overload: ``void C::f(double) const``, :cpp:func:`void C::f(double) const`
|
||||
- Specific overload: ``void C::f(double) const``,
|
||||
:cpp:func:`void C::f(double) const`
|
||||
|
||||
Note that the :confval:`add_function_parentheses` configuration variable
|
||||
does not influence specific overload references.
|
||||
@@ -1155,8 +1197,9 @@ and template arguments for the prefix of qualified names. For example:
|
||||
- ``template\<typename TOuter> template\<typename TInner> Wrapper::Outer<TOuter>::Inner``
|
||||
(:cpp:class:`template\<typename TOuter> template\<typename TInner> Wrapper::Outer<TOuter>::Inner`)
|
||||
|
||||
Currently the lookup only succeed if the template parameter identifiers are equal strings.
|
||||
That is, ``template\<typename UOuter> Wrapper::Outer`` will not work.
|
||||
Currently the lookup only succeed if the template parameter identifiers are
|
||||
equal strings. That is, ``template\<typename UOuter> Wrapper::Outer`` will not
|
||||
work.
|
||||
|
||||
As a shorthand notation, if a template parameter list is omitted,
|
||||
then the lookup will assume either a primary template or a non-template,
|
||||
@@ -1447,8 +1490,8 @@ The reStructuredText domain (name **rst**) provides the following directives:
|
||||
|
||||
.. rubric:: options
|
||||
|
||||
.. rst:directive:option:: type
|
||||
:type: description for the option of directive
|
||||
.. rst:directive:option:: type: description of argument
|
||||
:type: text
|
||||
|
||||
Describe the type of option value.
|
||||
|
||||
@@ -1459,7 +1502,7 @@ The reStructuredText domain (name **rst**) provides the following directives:
|
||||
.. rst:directive:option:: maxdepth
|
||||
:type: integer or no value
|
||||
|
||||
.. versionadded:: 2.1
|
||||
.. versionadded:: 2.1
|
||||
|
||||
.. rst:directive:: .. rst:role:: name
|
||||
|
||||
|
||||
+10
-3
@@ -343,9 +343,16 @@ class Sphinx:
|
||||
status = (self.statuscode == 0 and
|
||||
__('succeeded') or __('finished with problems'))
|
||||
if self._warncount:
|
||||
logger.info(bold(__('build %s, %s warning.',
|
||||
'build %s, %s warnings.', self._warncount) %
|
||||
(status, self._warncount)))
|
||||
if self.warningiserror:
|
||||
msg = __('build %s, %s warning (with warnings treated as errors).',
|
||||
'build %s, %s warnings (with warnings treated as errors).',
|
||||
self._warncount)
|
||||
else:
|
||||
msg = __('build %s, %s warning.',
|
||||
'build %s, %s warnings.',
|
||||
self._warncount)
|
||||
|
||||
logger.info(bold(msg % (status, self._warncount)))
|
||||
else:
|
||||
logger.info(bold(__('build %s.') % status))
|
||||
|
||||
|
||||
@@ -180,12 +180,10 @@ class LaTeXBuilder(Builder):
|
||||
# Add special settings for latex_engine
|
||||
self.context.update(ADDITIONAL_SETTINGS.get(self.config.latex_engine, {}))
|
||||
|
||||
# for xelatex+French, don't use polyglossia by default
|
||||
if self.config.latex_engine == 'xelatex':
|
||||
if self.config.language:
|
||||
if self.config.language[:2] == 'fr':
|
||||
self.context['polyglossia'] = ''
|
||||
self.context['babel'] = r'\usepackage{babel}'
|
||||
# Add special settings for (latex_engine, language_code)
|
||||
if self.config.language:
|
||||
key = (self.config.latex_engine, self.config.language[:2])
|
||||
self.context.update(ADDITIONAL_SETTINGS.get(key, {}))
|
||||
|
||||
# Apply extension settings to context
|
||||
self.context['packages'] = self.usepackages
|
||||
@@ -441,6 +439,8 @@ def default_latex_engine(config):
|
||||
""" Better default latex_engine settings for specific languages. """
|
||||
if config.language == 'ja':
|
||||
return 'platex'
|
||||
elif (config.language or '').startswith('zh'):
|
||||
return 'xelatex'
|
||||
else:
|
||||
return 'pdflatex'
|
||||
|
||||
|
||||
@@ -36,11 +36,13 @@ class ExtBabel(Babel):
|
||||
# polyglossia calls new orthography (Neue Rechtschreibung) as
|
||||
# german (with new spelling option).
|
||||
return 'german'
|
||||
elif not language:
|
||||
elif language:
|
||||
return language
|
||||
elif language_code.startswith('zh'):
|
||||
return 'english' # fallback to english (behaves like supported)
|
||||
else:
|
||||
self.supported = False
|
||||
return 'english' # fallback to english
|
||||
else:
|
||||
return language
|
||||
|
||||
def get_mainlanguage_options(self):
|
||||
# type: () -> str
|
||||
|
||||
@@ -108,7 +108,8 @@ class CheckExternalLinksBuilder(Builder):
|
||||
kwargs = {
|
||||
'allow_redirects': True,
|
||||
'headers': {
|
||||
'Accept': 'text/html,application/xhtml+xml;q=0.9,*/*;q=0.8'
|
||||
'Accept': 'text/html,application/xhtml+xml;q=0.9,*/*;q=0.8',
|
||||
'User-Agent': requests.useragent_header[0][1],
|
||||
},
|
||||
}
|
||||
if self.app.config.linkcheck_timeout:
|
||||
|
||||
@@ -576,6 +576,7 @@ class PyMethod(PyObject):
|
||||
|
||||
option_spec = PyObject.option_spec.copy()
|
||||
option_spec.update({
|
||||
'abstractmethod': directives.flag,
|
||||
'async': directives.flag,
|
||||
'classmethod': directives.flag,
|
||||
'property': directives.flag,
|
||||
@@ -592,6 +593,8 @@ class PyMethod(PyObject):
|
||||
def get_signature_prefix(self, sig):
|
||||
# type: (str) -> str
|
||||
prefix = []
|
||||
if 'abstractmethod' in self.options:
|
||||
prefix.append('abstract')
|
||||
if 'async' in self.options:
|
||||
prefix.append('async')
|
||||
if 'classmethod' in self.options:
|
||||
@@ -849,7 +852,6 @@ class PythonModuleIndex(Index):
|
||||
last = entries[-1]
|
||||
entries[-1] = IndexEntry(last[0], 1, last[2], last[3],
|
||||
last[4], last[5], last[6])
|
||||
entries.append(IndexEntry(stripped + package, 1, '', '', '', '', ''))
|
||||
elif not prev_modname.startswith(package):
|
||||
# submodule without parent in list, add dummy entry
|
||||
entries.append(IndexEntry(stripped + package, 1, '', '', '', '', ''))
|
||||
|
||||
@@ -139,6 +139,7 @@ class ReSTDirectiveOption(ReSTMarkup):
|
||||
|
||||
def add_target_and_index(self, name, sig, signode):
|
||||
# type: (str, str, addnodes.desc_signature) -> None
|
||||
directive_name = self.current_directive
|
||||
targetname = '-'.join([self.objtype, self.current_directive, name])
|
||||
if targetname not in self.state.document.ids:
|
||||
signode['names'].append(targetname)
|
||||
@@ -146,12 +147,13 @@ class ReSTDirectiveOption(ReSTMarkup):
|
||||
signode['first'] = (not self.names)
|
||||
self.state.document.note_explicit_target(signode)
|
||||
|
||||
objname = ':'.join(filter(None, [directive_name, name]))
|
||||
domain = cast(ReSTDomain, self.env.get_domain('rst'))
|
||||
domain.note_object(self.objtype, name, location=(self.env.docname, self.lineno))
|
||||
domain.note_object(self.objtype, objname, location=(self.env.docname, self.lineno))
|
||||
|
||||
if self.current_directive:
|
||||
if directive_name:
|
||||
key = name[0].upper()
|
||||
pair = [_('%s (directive)') % self.current_directive,
|
||||
pair = [_('%s (directive)') % directive_name,
|
||||
_(':%s: (directive option)') % name]
|
||||
self.indexnode['entries'].append(('pair', '; '.join(pair), targetname, '', key))
|
||||
else:
|
||||
|
||||
@@ -442,7 +442,7 @@ class ProductionList(SphinxDirective):
|
||||
name, tokens = rule.split(':', 1)
|
||||
except ValueError:
|
||||
break
|
||||
subnode = addnodes.production()
|
||||
subnode = addnodes.production(rule)
|
||||
subnode['tokenname'] = name.strip()
|
||||
if subnode['tokenname']:
|
||||
idname = nodes.make_id('grammar-token-%s' % subnode['tokenname'])
|
||||
|
||||
@@ -100,6 +100,9 @@ class ImageCollector(EnvironmentCollector):
|
||||
filename)
|
||||
try:
|
||||
mimetype = guess_mimetype(filename)
|
||||
if mimetype is None:
|
||||
basename, suffix = path.splitext(filename)
|
||||
mimetype = 'image/x-' + suffix[1:]
|
||||
if mimetype not in candidates:
|
||||
globbed.setdefault(mimetype, []).append(new_imgpath)
|
||||
except OSError as err:
|
||||
|
||||
+31
-9
@@ -56,6 +56,8 @@ template_dir = path.join(package_dir, 'templates', 'apidoc')
|
||||
def makename(package, module):
|
||||
# type: (str, str) -> str
|
||||
"""Join package and module with a dot."""
|
||||
warnings.warn('makename() is deprecated.',
|
||||
RemovedInSphinx40Warning)
|
||||
# Both package and module can be None/empty.
|
||||
if package:
|
||||
name = package
|
||||
@@ -66,6 +68,12 @@ def makename(package, module):
|
||||
return name
|
||||
|
||||
|
||||
def module_join(*modnames):
|
||||
# type: (*str) -> str
|
||||
"""Join module names with dots."""
|
||||
return '.'.join(filter(None, modnames))
|
||||
|
||||
|
||||
def write_file(name, text, opts):
|
||||
# type: (str, str, Any) -> None
|
||||
"""Write the output file for module/package <name>."""
|
||||
@@ -97,7 +105,7 @@ def format_directive(module, package=None):
|
||||
"""Create the automodule directive and add the options."""
|
||||
warnings.warn('format_directive() is deprecated.',
|
||||
RemovedInSphinx40Warning)
|
||||
directive = '.. automodule:: %s\n' % makename(package, module)
|
||||
directive = '.. automodule:: %s\n' % module_join(package, module)
|
||||
for option in OPTIONS:
|
||||
directive += ' :%s:\n' % option
|
||||
return directive
|
||||
@@ -106,7 +114,7 @@ def format_directive(module, package=None):
|
||||
def create_module_file(package, basename, opts):
|
||||
# type: (str, str, Any) -> None
|
||||
"""Build the text of the file and write the file."""
|
||||
qualname = makename(package, basename)
|
||||
qualname = module_join(package, basename)
|
||||
context = {
|
||||
'show_headings': not opts.noheadings,
|
||||
'basename': basename,
|
||||
@@ -123,17 +131,18 @@ def create_package_file(root, master_package, subroot, py_files, opts, subs, is_
|
||||
# build a list of sub packages (directories containing an INITPY file)
|
||||
subpackages = [sub for sub in subs if not
|
||||
shall_skip(path.join(root, sub, INITPY), opts, excludes)]
|
||||
subpackages = [makename(makename(master_package, subroot), pkgname)
|
||||
subpackages = [module_join(master_package, subroot, pkgname)
|
||||
for pkgname in subpackages]
|
||||
# build a list of sub modules
|
||||
submodules = [path.splitext(sub)[0] for sub in py_files
|
||||
if not shall_skip(path.join(root, sub), opts, excludes) and
|
||||
if not is_skipped_module(path.join(root, sub), opts, excludes) and
|
||||
sub != INITPY]
|
||||
submodules = [makename(master_package, makename(subroot, modname))
|
||||
submodules = [module_join(master_package, subroot, modname)
|
||||
for modname in submodules]
|
||||
|
||||
pkgname = module_join(master_package, subroot)
|
||||
context = {
|
||||
'pkgname': makename(master_package, subroot),
|
||||
'pkgname': pkgname,
|
||||
'subpackages': subpackages,
|
||||
'submodules': submodules,
|
||||
'is_namespace': is_namespace,
|
||||
@@ -143,7 +152,7 @@ def create_package_file(root, master_package, subroot, py_files, opts, subs, is_
|
||||
'show_headings': not opts.noheadings,
|
||||
}
|
||||
text = ReSTRenderer(template_dir).render('package.rst', context)
|
||||
write_file(makename(master_package, subroot), text, opts)
|
||||
write_file(pkgname, text, opts)
|
||||
|
||||
if submodules and opts.separatemodules:
|
||||
for submodule in submodules:
|
||||
@@ -198,6 +207,19 @@ def shall_skip(module, opts, excludes=[]):
|
||||
return False
|
||||
|
||||
|
||||
def is_skipped_module(filename, opts, excludes):
|
||||
# type: (str, Any, List[str]) -> bool
|
||||
"""Check if we want to skip this module."""
|
||||
if not path.exists(filename):
|
||||
# skip if the file doesn't exist
|
||||
return True
|
||||
elif path.basename(filename).startswith('_') and not opts.includeprivate:
|
||||
# skip if the module has a "private" name
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def recurse_tree(rootpath, excludes, opts):
|
||||
# type: (str, List[str], Any) -> List[str]
|
||||
"""
|
||||
@@ -250,12 +272,12 @@ def recurse_tree(rootpath, excludes, opts):
|
||||
if not is_namespace or len(py_files) > 0:
|
||||
create_package_file(root, root_package, subpackage,
|
||||
py_files, opts, subs, is_namespace, excludes)
|
||||
toplevels.append(makename(root_package, subpackage))
|
||||
toplevels.append(module_join(root_package, subpackage))
|
||||
else:
|
||||
# if we are at the root level, we don't require it to be a package
|
||||
assert root == rootpath and root_package is None
|
||||
for py_file in py_files:
|
||||
if not shall_skip(path.join(rootpath, py_file), opts):
|
||||
if not is_skipped_module(path.join(rootpath, py_file), opts, excludes):
|
||||
module = path.splitext(py_file)[0]
|
||||
create_module_file(root_package, module, opts)
|
||||
toplevels.append(module)
|
||||
|
||||
+102
-30
@@ -17,6 +17,7 @@ from typing import Any
|
||||
from docutils.statemachine import StringList
|
||||
|
||||
import sphinx
|
||||
from sphinx.config import ENUM
|
||||
from sphinx.deprecation import RemovedInSphinx40Warning
|
||||
from sphinx.ext.autodoc.importer import import_object, get_object_members
|
||||
from sphinx.ext.autodoc.mock import mock
|
||||
@@ -65,6 +66,7 @@ def identity(x):
|
||||
|
||||
ALL = object()
|
||||
INSTANCEATTR = object()
|
||||
SLOTSATTR = object()
|
||||
|
||||
|
||||
def members_option(arg):
|
||||
@@ -363,8 +365,8 @@ class Documenter:
|
||||
return False
|
||||
return True
|
||||
|
||||
def format_args(self):
|
||||
# type: () -> str
|
||||
def format_args(self, **kwargs):
|
||||
# type: (Any) -> str
|
||||
"""Format the argument signature of *self.object*.
|
||||
|
||||
Should return None if the object does not have a signature.
|
||||
@@ -383,8 +385,8 @@ class Documenter:
|
||||
# directives of course)
|
||||
return '.'.join(self.objpath) or self.modname
|
||||
|
||||
def format_signature(self):
|
||||
# type: () -> str
|
||||
def format_signature(self, **kwargs):
|
||||
# type: (Any) -> str
|
||||
"""Format the signature (arguments and return annotation) of the object.
|
||||
|
||||
Let the user process it via the ``autodoc-process-signature`` event.
|
||||
@@ -395,7 +397,11 @@ class Documenter:
|
||||
else:
|
||||
# try to introspect the signature
|
||||
try:
|
||||
args = self.format_args()
|
||||
try:
|
||||
args = self.format_args(**kwargs)
|
||||
except TypeError:
|
||||
# retry without arguments for old documenters
|
||||
args = self.format_args()
|
||||
except Exception as err:
|
||||
logger.warning(__('error while formatting arguments for %s: %s') %
|
||||
(self.fullname, err), type='autodoc')
|
||||
@@ -954,15 +960,15 @@ class DocstringSignatureMixin:
|
||||
return lines
|
||||
return super().get_doc(None, ignore) # type: ignore
|
||||
|
||||
def format_signature(self):
|
||||
# type: () -> str
|
||||
def format_signature(self, **kwargs):
|
||||
# type: (Any) -> str
|
||||
if self.args is None and self.env.config.autodoc_docstring_signature: # type: ignore
|
||||
# only act if a signature is not explicitly given already, and if
|
||||
# the feature is enabled
|
||||
result = self._find_signature()
|
||||
if result is not None:
|
||||
self.args, self.retann = result
|
||||
return super().format_signature() # type: ignore
|
||||
return super().format_signature(**kwargs) # type: ignore
|
||||
|
||||
|
||||
class DocstringStripSignatureMixin(DocstringSignatureMixin):
|
||||
@@ -970,8 +976,8 @@ class DocstringStripSignatureMixin(DocstringSignatureMixin):
|
||||
Mixin for AttributeDocumenter to provide the
|
||||
feature of stripping any function signature from the docstring.
|
||||
"""
|
||||
def format_signature(self):
|
||||
# type: () -> str
|
||||
def format_signature(self, **kwargs):
|
||||
# type: (Any) -> str
|
||||
if self.args is None and self.env.config.autodoc_docstring_signature: # type: ignore
|
||||
# only act if a signature is not explicitly given already, and if
|
||||
# the feature is enabled
|
||||
@@ -981,7 +987,7 @@ class DocstringStripSignatureMixin(DocstringSignatureMixin):
|
||||
# DocstringSignatureMixin.format_signature.
|
||||
# Documenter.format_signature use self.args value to format.
|
||||
_args, self.retann = result
|
||||
return super().format_signature()
|
||||
return super().format_signature(**kwargs)
|
||||
|
||||
|
||||
class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type: ignore
|
||||
@@ -998,8 +1004,11 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
|
||||
return (inspect.isfunction(member) or inspect.isbuiltin(member) or
|
||||
(inspect.isroutine(member) and isinstance(parent, ModuleDocumenter)))
|
||||
|
||||
def format_args(self):
|
||||
# type: () -> str
|
||||
def format_args(self, **kwargs):
|
||||
# type: (Any) -> str
|
||||
if self.env.config.autodoc_typehints == 'none':
|
||||
kwargs.setdefault('show_annotation', False)
|
||||
|
||||
if inspect.isbuiltin(self.object) or inspect.ismethoddescriptor(self.object):
|
||||
# cannot introspect arguments of a C function or method
|
||||
return None
|
||||
@@ -1009,9 +1018,9 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
|
||||
not inspect.isbuiltin(self.object) and
|
||||
not inspect.isclass(self.object) and
|
||||
hasattr(self.object, '__call__')):
|
||||
args = Signature(self.object.__call__).format_args()
|
||||
args = Signature(self.object.__call__).format_args(**kwargs)
|
||||
else:
|
||||
args = Signature(self.object).format_args()
|
||||
args = Signature(self.object).format_args(**kwargs)
|
||||
except TypeError:
|
||||
if (inspect.is_builtin_class_method(self.object, '__new__') and
|
||||
inspect.is_builtin_class_method(self.object, '__init__')):
|
||||
@@ -1022,10 +1031,10 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
|
||||
# signature without the first argument.
|
||||
try:
|
||||
sig = Signature(self.object.__new__, bound_method=True, has_retval=False)
|
||||
args = sig.format_args()
|
||||
args = sig.format_args(**kwargs)
|
||||
except TypeError:
|
||||
sig = Signature(self.object.__init__, bound_method=True, has_retval=False)
|
||||
args = sig.format_args()
|
||||
args = sig.format_args(**kwargs)
|
||||
|
||||
# escape backslashes for reST
|
||||
args = args.replace('\\', '\\\\')
|
||||
@@ -1053,8 +1062,8 @@ class DecoratorDocumenter(FunctionDocumenter):
|
||||
# must be lower than FunctionDocumenter
|
||||
priority = -1
|
||||
|
||||
def format_args(self):
|
||||
args = super().format_args()
|
||||
def format_args(self, **kwargs):
|
||||
args = super().format_args(**kwargs)
|
||||
if ',' in args:
|
||||
return args
|
||||
else:
|
||||
@@ -1097,8 +1106,11 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
|
||||
self.doc_as_attr = True
|
||||
return ret
|
||||
|
||||
def format_args(self):
|
||||
# type: () -> str
|
||||
def format_args(self, **kwargs):
|
||||
# type: (Any) -> str
|
||||
if self.env.config.autodoc_typehints == 'none':
|
||||
kwargs.setdefault('show_annotation', False)
|
||||
|
||||
# for classes, the relevant signature is the __init__ method's
|
||||
initmeth = self.get_attr(self.object, '__init__', None)
|
||||
# classes without __init__ method, default __init__ or
|
||||
@@ -1108,18 +1120,19 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
|
||||
not(inspect.ismethod(initmeth) or inspect.isfunction(initmeth)):
|
||||
return None
|
||||
try:
|
||||
return Signature(initmeth, bound_method=True, has_retval=False).format_args()
|
||||
sig = Signature(initmeth, bound_method=True, has_retval=False)
|
||||
return sig.format_args(**kwargs)
|
||||
except TypeError:
|
||||
# still not possible: happens e.g. for old-style classes
|
||||
# with __init__ in C
|
||||
return None
|
||||
|
||||
def format_signature(self):
|
||||
# type: () -> str
|
||||
def format_signature(self, **kwargs):
|
||||
# type: (Any) -> str
|
||||
if self.doc_as_attr:
|
||||
return ''
|
||||
|
||||
return super().format_signature()
|
||||
return super().format_signature(**kwargs)
|
||||
|
||||
def add_directive_header(self, sig):
|
||||
# type: (str) -> None
|
||||
@@ -1310,15 +1323,18 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
|
||||
|
||||
return ret
|
||||
|
||||
def format_args(self):
|
||||
# type: () -> str
|
||||
def format_args(self, **kwargs):
|
||||
# type: (Any) -> str
|
||||
if self.env.config.autodoc_typehints == 'none':
|
||||
kwargs.setdefault('show_annotation', False)
|
||||
|
||||
if inspect.isbuiltin(self.object) or inspect.ismethoddescriptor(self.object):
|
||||
# can never get arguments of a C function or method
|
||||
return None
|
||||
if inspect.isstaticmethod(self.object, cls=self.parent, name=self.object_name):
|
||||
args = Signature(self.object, bound_method=False).format_args()
|
||||
args = Signature(self.object, bound_method=False).format_args(**kwargs)
|
||||
else:
|
||||
args = Signature(self.object, bound_method=True).format_args()
|
||||
args = Signature(self.object, bound_method=True).format_args(**kwargs)
|
||||
# escape backslashes for reST
|
||||
args = args.replace('\\', '\\\\')
|
||||
return args
|
||||
@@ -1329,6 +1345,8 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
|
||||
|
||||
sourcename = self.get_sourcename()
|
||||
obj = self.parent.__dict__.get(self.object_name, self.object)
|
||||
if inspect.isabstractmethod(obj):
|
||||
self.add_line(' :abstractmethod:', sourcename)
|
||||
if inspect.iscoroutinefunction(obj):
|
||||
self.add_line(' :async:', sourcename)
|
||||
if inspect.isclassmethod(obj):
|
||||
@@ -1446,7 +1464,10 @@ class PropertyDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter): #
|
||||
def add_directive_header(self, sig):
|
||||
# type: (str) -> None
|
||||
super().add_directive_header(sig)
|
||||
self.add_line(' :property:', self.get_sourcename())
|
||||
sourcename = self.get_sourcename()
|
||||
if inspect.isabstractmethod(self.object):
|
||||
self.add_line(' :abstractmethod:', sourcename)
|
||||
self.add_line(' :property:', sourcename)
|
||||
|
||||
|
||||
class InstanceAttributeDocumenter(AttributeDocumenter):
|
||||
@@ -1481,6 +1502,55 @@ class InstanceAttributeDocumenter(AttributeDocumenter):
|
||||
super().add_content(more_content, no_docstring=True)
|
||||
|
||||
|
||||
class SlotsAttributeDocumenter(AttributeDocumenter):
|
||||
"""
|
||||
Specialized Documenter subclass for attributes that cannot be imported
|
||||
because they are attributes in __slots__.
|
||||
"""
|
||||
objtype = 'slotsattribute'
|
||||
directivetype = 'attribute'
|
||||
member_order = 60
|
||||
|
||||
# must be higher than AttributeDocumenter
|
||||
priority = 11
|
||||
|
||||
@classmethod
|
||||
def can_document_member(cls, member, membername, isattr, parent):
|
||||
# type: (Any, str, bool, Any) -> bool
|
||||
"""This documents only SLOTSATTR members."""
|
||||
return member is SLOTSATTR
|
||||
|
||||
def import_object(self):
|
||||
# type: () -> bool
|
||||
"""Never import anything."""
|
||||
# disguise as an attribute
|
||||
self.objtype = 'attribute'
|
||||
self._datadescriptor = True
|
||||
|
||||
with mock(self.env.config.autodoc_mock_imports):
|
||||
try:
|
||||
ret = import_object(self.modname, self.objpath[:-1], 'class',
|
||||
attrgetter=self.get_attr,
|
||||
warningiserror=self.env.config.autodoc_warningiserror)
|
||||
self.module, _, _, self.parent = ret
|
||||
return True
|
||||
except ImportError as exc:
|
||||
logger.warning(exc.args[0], type='autodoc', subtype='import_object')
|
||||
self.env.note_reread()
|
||||
return False
|
||||
|
||||
def get_doc(self, encoding=None, ignore=1):
|
||||
# type: (str, int) -> List[List[str]]
|
||||
"""Decode and return lines of the docstring(s) for the object."""
|
||||
name = self.objpath[-1]
|
||||
__slots__ = safe_getattr(self.parent, '__slots__', [])
|
||||
if isinstance(__slots__, dict) and isinstance(__slots__.get(name), str):
|
||||
docstring = prepare_docstring(__slots__[name])
|
||||
return [docstring]
|
||||
else:
|
||||
return []
|
||||
|
||||
|
||||
def get_documenters(app):
|
||||
# type: (Sphinx) -> Dict[str, Type[Documenter]]
|
||||
"""Returns registered Documenter classes"""
|
||||
@@ -1509,6 +1579,7 @@ def setup(app):
|
||||
app.add_autodocumenter(AttributeDocumenter)
|
||||
app.add_autodocumenter(PropertyDocumenter)
|
||||
app.add_autodocumenter(InstanceAttributeDocumenter)
|
||||
app.add_autodocumenter(SlotsAttributeDocumenter)
|
||||
|
||||
app.add_config_value('autoclass_content', 'class', True)
|
||||
app.add_config_value('autodoc_member_order', 'alphabetic', True)
|
||||
@@ -1516,6 +1587,7 @@ def setup(app):
|
||||
app.add_config_value('autodoc_default_options', {}, True)
|
||||
app.add_config_value('autodoc_docstring_signature', True, True)
|
||||
app.add_config_value('autodoc_mock_imports', [], True)
|
||||
app.add_config_value('autodoc_typehints', "signature", True, ENUM("signature", "none"))
|
||||
app.add_config_value('autodoc_warningiserror', True, True)
|
||||
app.add_config_value('autodoc_inherit_docstrings', True, True)
|
||||
app.add_event('autodoc-process-docstring')
|
||||
|
||||
@@ -15,7 +15,7 @@ from collections import namedtuple
|
||||
|
||||
from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
|
||||
from sphinx.util import logging
|
||||
from sphinx.util.inspect import isenumclass, safe_getattr
|
||||
from sphinx.util.inspect import isclass, isenumclass, safe_getattr
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
@@ -127,6 +127,13 @@ def get_object_members(subject, objpath, attrgetter, analyzer=None):
|
||||
if name not in superclass.__dict__:
|
||||
members[name] = Attribute(name, True, value)
|
||||
|
||||
# members in __slots__
|
||||
if isclass(subject) and hasattr(subject, '__slots__'):
|
||||
from sphinx.ext.autodoc import SLOTSATTR
|
||||
|
||||
for name in subject.__slots__:
|
||||
members[name] = Attribute(name, True, SLOTSATTR)
|
||||
|
||||
# other members
|
||||
for name in dir(subject):
|
||||
try:
|
||||
|
||||
@@ -55,7 +55,7 @@ def register_sections_as_label(app, document):
|
||||
if name in labels:
|
||||
logger.warning(__('duplicate label %s, other instance in %s'),
|
||||
name, app.env.doc2path(labels[name][0]),
|
||||
location=node)
|
||||
location=node, type='autosectionlabel', subtype=docname)
|
||||
|
||||
anonlabels[name] = docname, labelid
|
||||
labels[name] = docname, labelid, sectname
|
||||
|
||||
@@ -63,7 +63,7 @@ from typing import List, cast
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.parsers.rst import directives
|
||||
from docutils.parsers.rst.states import RSTStateMachine, state_classes
|
||||
from docutils.parsers.rst.states import RSTStateMachine, Struct, state_classes
|
||||
from docutils.statemachine import StringList
|
||||
|
||||
import sphinx
|
||||
@@ -175,7 +175,10 @@ _app = None # type: Sphinx
|
||||
class FakeDirective(DocumenterBridge):
|
||||
def __init__(self):
|
||||
# type: () -> None
|
||||
super().__init__({}, None, Options(), 0, None) # type: ignore
|
||||
settings = Struct(tab_width=8)
|
||||
document = Struct(settings=settings)
|
||||
state = Struct(document=document)
|
||||
super().__init__({}, None, Options(), 0, state) # type: ignore
|
||||
|
||||
|
||||
def get_documenter(app, obj, parent):
|
||||
@@ -329,7 +332,12 @@ class Autosummary(SphinxDirective):
|
||||
|
||||
# -- Grab the signature
|
||||
|
||||
sig = documenter.format_signature()
|
||||
try:
|
||||
sig = documenter.format_signature(show_annotation=False)
|
||||
except TypeError:
|
||||
# the documenter does not support ``show_annotation`` option
|
||||
sig = documenter.format_signature()
|
||||
|
||||
if not sig:
|
||||
sig = ''
|
||||
else:
|
||||
@@ -436,16 +444,26 @@ def mangle_signature(sig, max_chars=30):
|
||||
# Remove parenthesis
|
||||
s = re.sub(r"^\((.*)\)$", r"\1", s).strip()
|
||||
|
||||
# Strip strings (which can contain things that confuse the code below)
|
||||
s = re.sub(r"\\\\", "", s)
|
||||
s = re.sub(r"\\'", "", s)
|
||||
s = re.sub(r"'[^']*'", "", s)
|
||||
# Strip literals (which can contain things that confuse the code below)
|
||||
s = re.sub(r"\\\\", "", s) # escaped backslash (maybe inside string)
|
||||
s = re.sub(r"\\'", "", s) # escaped single quote
|
||||
s = re.sub(r'\\"', "", s) # escaped double quote
|
||||
s = re.sub(r"'[^']*'", "", s) # string literal (w/ single quote)
|
||||
s = re.sub(r'"[^"]*"', "", s) # string literal (w/ double quote)
|
||||
|
||||
# Strip complex objects (maybe default value of arguments)
|
||||
while re.search(r'\([^)]*\)', s): # contents of parenthesis (ex. NamedTuple(attr=...))
|
||||
s = re.sub(r'\([^)]*\)', '', s)
|
||||
while re.search(r'<[^>]*>', s): # contents of angle brackets (ex. <object>)
|
||||
s = re.sub(r'<[^>]*>', '', s)
|
||||
while re.search(r'{[^}]*}', s): # contents of curly brackets (ex. dict)
|
||||
s = re.sub(r'{[^}]*}', '', s)
|
||||
|
||||
# Parse the signature to arguments + options
|
||||
args = [] # type: List[str]
|
||||
opts = [] # type: List[str]
|
||||
|
||||
opt_re = re.compile(r"^(.*, |)([a-zA-Z0-9_*]+)=")
|
||||
opt_re = re.compile(r"^(.*, |)([a-zA-Z0-9_*]+)\s*=\s*")
|
||||
while s:
|
||||
m = opt_re.search(s)
|
||||
if not m:
|
||||
|
||||
@@ -25,7 +25,7 @@ import pydoc
|
||||
import re
|
||||
import sys
|
||||
|
||||
from jinja2 import FileSystemLoader, TemplateNotFound
|
||||
from jinja2 import BaseLoader, FileSystemLoader, TemplateNotFound
|
||||
from jinja2.sandbox import SandboxedEnvironment
|
||||
|
||||
import sphinx.locale
|
||||
@@ -35,9 +35,9 @@ from sphinx.ext.autosummary import import_by_name, get_documenter
|
||||
from sphinx.jinja2glue import BuiltinTemplateLoader
|
||||
from sphinx.locale import __
|
||||
from sphinx.registry import SphinxComponentRegistry
|
||||
from sphinx.util import rst
|
||||
from sphinx.util.inspect import safe_getattr
|
||||
from sphinx.util.osutil import ensuredir
|
||||
from sphinx.util.rst import escape as rst_escape
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
@@ -59,12 +59,14 @@ def setup_documenters(app):
|
||||
from sphinx.ext.autodoc import (
|
||||
ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter,
|
||||
FunctionDocumenter, MethodDocumenter, AttributeDocumenter,
|
||||
InstanceAttributeDocumenter
|
||||
InstanceAttributeDocumenter, DecoratorDocumenter, PropertyDocumenter,
|
||||
SlotsAttributeDocumenter,
|
||||
)
|
||||
documenters = [
|
||||
ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter,
|
||||
FunctionDocumenter, MethodDocumenter, AttributeDocumenter,
|
||||
InstanceAttributeDocumenter
|
||||
InstanceAttributeDocumenter, DecoratorDocumenter, PropertyDocumenter,
|
||||
SlotsAttributeDocumenter,
|
||||
] # type: List[Type[Documenter]]
|
||||
for documenter in documenters:
|
||||
app.registry.add_documenter(documenter.objtype, documenter)
|
||||
@@ -87,6 +89,42 @@ def _underline(title, line='='):
|
||||
return title + '\n' + line * len(title)
|
||||
|
||||
|
||||
class AutosummaryRenderer:
|
||||
"""A helper class for rendering."""
|
||||
|
||||
def __init__(self, builder, template_dir):
|
||||
# type: (Builder, str) -> None
|
||||
loader = None # type: BaseLoader
|
||||
template_dirs = [os.path.join(package_dir, 'ext', 'autosummary', 'templates')]
|
||||
if builder is None:
|
||||
if template_dir:
|
||||
template_dirs.insert(0, template_dir)
|
||||
loader = FileSystemLoader(template_dirs)
|
||||
else:
|
||||
# allow the user to override the templates
|
||||
loader = BuiltinTemplateLoader()
|
||||
loader.init(builder, dirs=template_dirs)
|
||||
|
||||
self.env = SandboxedEnvironment(loader=loader)
|
||||
self.env.filters['escape'] = rst.escape
|
||||
self.env.filters['e'] = rst.escape
|
||||
self.env.filters['underline'] = _underline
|
||||
|
||||
def exists(self, template_name):
|
||||
# type: (str) -> bool
|
||||
"""Check if template file exists."""
|
||||
try:
|
||||
self.env.get_template(template_name)
|
||||
return True
|
||||
except TemplateNotFound:
|
||||
return False
|
||||
|
||||
def render(self, template_name, context):
|
||||
# type: (str, Dict) -> str
|
||||
"""Render a template file."""
|
||||
return self.env.get_template(template_name).render(context)
|
||||
|
||||
|
||||
# -- Generating output ---------------------------------------------------------
|
||||
|
||||
def generate_autosummary_docs(sources, # type: List[str]
|
||||
@@ -114,26 +152,7 @@ def generate_autosummary_docs(sources, # type: List[str]
|
||||
if base_path is not None:
|
||||
sources = [os.path.join(base_path, filename) for filename in sources]
|
||||
|
||||
# create our own templating environment
|
||||
template_dirs = None # type: List[str]
|
||||
template_dirs = [os.path.join(package_dir, 'ext',
|
||||
'autosummary', 'templates')]
|
||||
|
||||
template_loader = None # type: Union[BuiltinTemplateLoader, FileSystemLoader]
|
||||
if builder is not None:
|
||||
# allow the user to override the templates
|
||||
template_loader = BuiltinTemplateLoader()
|
||||
template_loader.init(builder, dirs=template_dirs)
|
||||
else:
|
||||
if template_dir:
|
||||
template_dirs.insert(0, template_dir)
|
||||
template_loader = FileSystemLoader(template_dirs)
|
||||
template_env = SandboxedEnvironment(loader=template_loader)
|
||||
template_env.filters['underline'] = _underline
|
||||
|
||||
# replace the builtin html filters
|
||||
template_env.filters['escape'] = rst_escape
|
||||
template_env.filters['e'] = rst_escape
|
||||
template = AutosummaryRenderer(builder, template_dir)
|
||||
|
||||
# read
|
||||
items = find_autosummary_in_files(sources)
|
||||
@@ -189,14 +208,10 @@ def generate_autosummary_docs(sources, # type: List[str]
|
||||
with open(fn, 'w') as f:
|
||||
doc = get_documenter(app, obj, parent)
|
||||
|
||||
if template_name is not None:
|
||||
template = template_env.get_template(template_name)
|
||||
else:
|
||||
try:
|
||||
template = template_env.get_template('autosummary/%s.rst'
|
||||
% doc.objtype)
|
||||
except TemplateNotFound:
|
||||
template = template_env.get_template('autosummary/base.rst')
|
||||
if template_name is None:
|
||||
template_name = 'autosummary/%s.rst' % doc.objtype
|
||||
if not template.exists(template_name):
|
||||
template_name = 'autosummary/base.rst'
|
||||
|
||||
def get_members(obj, types, include_public=[], imported=True):
|
||||
# type: (Any, Set[str], List[str], bool) -> Tuple[List[str], List[str]] # NOQA
|
||||
@@ -273,7 +288,7 @@ def generate_autosummary_docs(sources, # type: List[str]
|
||||
ns['objtype'] = doc.objtype
|
||||
ns['underline'] = len(name) * '='
|
||||
|
||||
rendered = template.render(**ns)
|
||||
rendered = template.render(template_name, ns)
|
||||
f.write(rendered)
|
||||
|
||||
# descend recursively to new files
|
||||
|
||||
+14
-3
@@ -79,6 +79,8 @@ class CoverageBuilder(Builder):
|
||||
self.config.coverage_ignore_classes)
|
||||
self.fun_ignorexps = compile_regex_list('coverage_ignore_functions',
|
||||
self.config.coverage_ignore_functions)
|
||||
self.py_ignorexps = compile_regex_list('coverage_ignore_pyobjects',
|
||||
self.config.coverage_ignore_pyobjects)
|
||||
|
||||
def get_outdated_docs(self):
|
||||
# type: () -> str
|
||||
@@ -130,6 +132,12 @@ class CoverageBuilder(Builder):
|
||||
op.write(' * %-50s [%9s]\n' % (name, typ))
|
||||
op.write('\n')
|
||||
|
||||
def ignore_pyobj(self, full_name):
|
||||
for exp in self.py_ignorexps:
|
||||
if exp.search(full_name):
|
||||
return True
|
||||
return False
|
||||
|
||||
def build_py_coverage(self):
|
||||
# type: () -> None
|
||||
objects = self.env.domaindata['py']['objects']
|
||||
@@ -143,7 +151,7 @@ class CoverageBuilder(Builder):
|
||||
if exp.match(mod_name):
|
||||
ignore = True
|
||||
break
|
||||
if ignore:
|
||||
if ignore or self.ignore_pyobj(mod_name):
|
||||
continue
|
||||
|
||||
try:
|
||||
@@ -169,6 +177,8 @@ class CoverageBuilder(Builder):
|
||||
continue
|
||||
|
||||
full_name = '%s.%s' % (mod_name, name)
|
||||
if self.ignore_pyobj(full_name):
|
||||
continue
|
||||
|
||||
if inspect.isfunction(obj):
|
||||
if full_name not in objects:
|
||||
@@ -209,11 +219,11 @@ class CoverageBuilder(Builder):
|
||||
if skip_undoc and not attr.__doc__:
|
||||
# skip methods without docstring if wished
|
||||
continue
|
||||
|
||||
full_attr_name = '%s.%s' % (full_name, attr_name)
|
||||
if self.ignore_pyobj(full_attr_name):
|
||||
continue
|
||||
if full_attr_name not in objects:
|
||||
attrs.append(attr_name)
|
||||
|
||||
if attrs:
|
||||
# some attributes are undocumented
|
||||
classes[name] = attrs
|
||||
@@ -270,6 +280,7 @@ def setup(app):
|
||||
app.add_config_value('coverage_ignore_modules', [], False)
|
||||
app.add_config_value('coverage_ignore_functions', [], False)
|
||||
app.add_config_value('coverage_ignore_classes', [], False)
|
||||
app.add_config_value('coverage_ignore_pyobjects', [], False)
|
||||
app.add_config_value('coverage_c_path', [], False)
|
||||
app.add_config_value('coverage_c_regexes', {}, False)
|
||||
app.add_config_value('coverage_ignore_c_items', {}, False)
|
||||
|
||||
@@ -25,11 +25,11 @@ if errorlevel 9009 (
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
|
||||
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
goto end
|
||||
|
||||
:help
|
||||
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
|
||||
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
|
||||
:end
|
||||
popd
|
||||
|
||||
@@ -9,7 +9,7 @@ if "%SPHINXBUILD%" == "" (
|
||||
)
|
||||
set BUILDDIR={{ rbuilddir }}
|
||||
set SOURCEDIR={{ rsrcdir }}
|
||||
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% %SOURCEDIR%
|
||||
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% %O% %SOURCEDIR%
|
||||
set I18NSPHINXOPTS=%SPHINXOPTS% %SOURCEDIR%
|
||||
if NOT "%PAPER%" == "" (
|
||||
set ALLSPHINXOPTS=-D latex_elements.papersize=%PAPER%paper %ALLSPHINXOPTS%
|
||||
|
||||
@@ -319,12 +319,13 @@ var Search = {
|
||||
for (var prefix in objects) {
|
||||
for (var name in objects[prefix]) {
|
||||
var fullname = (prefix ? prefix + '.' : '') + name;
|
||||
if (fullname.toLowerCase().indexOf(object) > -1) {
|
||||
var fullnameLower = fullname.toLowerCase()
|
||||
if (fullnameLower.indexOf(object) > -1) {
|
||||
var score = 0;
|
||||
var parts = fullname.split('.');
|
||||
var parts = fullnameLower.split('.');
|
||||
// check for different match types: exact matches of full name or
|
||||
// "last name" (i.e. last dotted part)
|
||||
if (fullname == object || parts[parts.length - 1] == object) {
|
||||
if (fullnameLower == object || parts[parts.length - 1] == object) {
|
||||
score += Scorer.objNameMatch;
|
||||
// matches in last name
|
||||
} else if (parts[parts.length - 1].indexOf(object) > -1) {
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
<div class="topnav" role="navigation" aria-label="top navigation">
|
||||
{{ nav() }}
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="content" role="main">
|
||||
{#{%- if display_toc %}
|
||||
<div id="toc">
|
||||
<h3>{{ _('Table of Contents') }}</h3>
|
||||
|
||||
@@ -42,7 +42,9 @@
|
||||
{{ toc }}
|
||||
</div>
|
||||
{%- endif %}
|
||||
<div role="main">
|
||||
{% block body %}{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
+1
-1
@@ -244,7 +244,7 @@ date_format_mappings = {
|
||||
'%x': 'medium', # Locale’s appropriate date representation.
|
||||
'%X': 'medium', # Locale’s appropriate time representation.
|
||||
'%y': 'YY', # Year without century as a zero-padded decimal number.
|
||||
'%Y': 'YYYY', # Year with century as a decimal number.
|
||||
'%Y': 'yyyy', # Year with century as a decimal number.
|
||||
'%Z': 'zzzz', # Time zone name (no characters if no time zone exists).
|
||||
'%%': '%',
|
||||
}
|
||||
|
||||
+19
-8
@@ -170,6 +170,12 @@ def isdescriptor(x):
|
||||
return False
|
||||
|
||||
|
||||
def isabstractmethod(obj):
|
||||
# type: (Any) -> bool
|
||||
"""Check if the object is an abstractmethod."""
|
||||
return safe_getattr(obj, '__isabstractmethod__', False) is True
|
||||
|
||||
|
||||
def isattributedescriptor(obj):
|
||||
# type: (Any) -> bool
|
||||
"""Check if the object is an attribute like descriptor."""
|
||||
@@ -229,7 +235,7 @@ def isproperty(obj):
|
||||
|
||||
|
||||
def safe_getattr(obj, name, *defargs):
|
||||
# type: (Any, str, str) -> object
|
||||
# type: (Any, str, Any) -> Any
|
||||
"""A getattr() that turns all exceptions into AttributeErrors."""
|
||||
try:
|
||||
return getattr(obj, name, *defargs)
|
||||
@@ -317,9 +323,9 @@ def is_builtin_class_method(obj, attr_name):
|
||||
classes = [c for c in inspect.getmro(obj) if attr_name in c.__dict__]
|
||||
cls = classes[0] if classes else object
|
||||
|
||||
if not hasattr(builtins, safe_getattr(cls, '__name__', '')): # type: ignore
|
||||
if not hasattr(builtins, safe_getattr(cls, '__name__', '')):
|
||||
return False
|
||||
return getattr(builtins, safe_getattr(cls, '__name__', '')) is cls # type: ignore
|
||||
return getattr(builtins, safe_getattr(cls, '__name__', '')) is cls
|
||||
|
||||
|
||||
class Signature:
|
||||
@@ -391,8 +397,8 @@ class Signature:
|
||||
else:
|
||||
return None
|
||||
|
||||
def format_args(self):
|
||||
# type: () -> str
|
||||
def format_args(self, show_annotation=True):
|
||||
# type: (bool) -> str
|
||||
args = []
|
||||
last_kind = None
|
||||
for i, param in enumerate(self.parameters.values()):
|
||||
@@ -413,7 +419,7 @@ class Signature:
|
||||
param.POSITIONAL_OR_KEYWORD,
|
||||
param.KEYWORD_ONLY):
|
||||
arg.write(param.name)
|
||||
if param.annotation is not param.empty:
|
||||
if show_annotation and param.annotation is not param.empty:
|
||||
if isinstance(param.annotation, str) and param.name in self.annotations:
|
||||
arg.write(': ')
|
||||
arg.write(self.format_annotation(self.annotations[param.name]))
|
||||
@@ -503,6 +509,8 @@ class Signature:
|
||||
args = ', '.join(self.format_annotation(a) for a in annotation.__args__[:-1])
|
||||
returns = self.format_annotation(annotation.__args__[-1])
|
||||
return '%s[[%s], %s]' % (qualname, args, returns)
|
||||
elif annotation._special:
|
||||
return qualname
|
||||
else:
|
||||
args = ', '.join(self.format_annotation(a) for a in annotation.__args__)
|
||||
return '%s[%s]' % (qualname, args)
|
||||
@@ -534,8 +542,11 @@ class Signature:
|
||||
not hasattr(annotation, '__tuple_params__')):
|
||||
# This is for Python 3.6+, 3.5 case is handled below
|
||||
params = annotation.__args__
|
||||
param_str = ', '.join(self.format_annotation(p) for p in params)
|
||||
return '%s[%s]' % (qualname, param_str)
|
||||
if params:
|
||||
param_str = ', '.join(self.format_annotation(p) for p in params)
|
||||
return '%s[%s]' % (qualname, param_str)
|
||||
else:
|
||||
return qualname
|
||||
elif (hasattr(typing, 'GenericMeta') and # for py36 or below
|
||||
isinstance(annotation, typing.GenericMeta)):
|
||||
# In Python 3.5.2+, all arguments are stored in __args__,
|
||||
|
||||
@@ -31,7 +31,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# \x00 means the "<" was backslash-escaped
|
||||
explicit_title_re = re.compile(r'^(.+?)\s*(?<!\x00)<(.*?)>$', re.DOTALL)
|
||||
explicit_title_re = re.compile(r'^(.+?)\s*(?<!\x00)<([^<]*?)>$', re.DOTALL)
|
||||
caption_ref_re = explicit_title_re # b/w compat alias
|
||||
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
|
||||
|
||||
def visit_desc_addname(self, node):
|
||||
# type: (nodes.Element) -> None
|
||||
self.body.append(self.starttag(node, 'code', '', CLASS='descclassname'))
|
||||
self.body.append(self.starttag(node, 'code', '', CLASS='sig-prename descclassname'))
|
||||
|
||||
def depart_desc_addname(self, node):
|
||||
# type: (nodes.Element) -> None
|
||||
@@ -140,7 +140,7 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
|
||||
|
||||
def visit_desc_name(self, node):
|
||||
# type: (nodes.Element) -> None
|
||||
self.body.append(self.starttag(node, 'code', '', CLASS='descname'))
|
||||
self.body.append(self.starttag(node, 'code', '', CLASS='sig-name descname'))
|
||||
|
||||
def depart_desc_name(self, node):
|
||||
# type: (nodes.Element) -> None
|
||||
@@ -175,7 +175,7 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
|
||||
if self.optional_param_level == 0:
|
||||
self.required_params_left -= 1
|
||||
if not node.hasattr('noemph'):
|
||||
self.body.append('<em>')
|
||||
self.body.append('<em class="sig-param">')
|
||||
|
||||
def depart_desc_parameter(self, node):
|
||||
# type: (nodes.Element) -> None
|
||||
|
||||
+11
-1
@@ -213,7 +213,17 @@ ADDITIONAL_SETTINGS = {
|
||||
'fncychap': '',
|
||||
'geometry': '\\usepackage[dvipdfm]{geometry}',
|
||||
},
|
||||
} # type: Dict[str, Dict[str, Any]]
|
||||
|
||||
# special settings for latex_engine + language_code
|
||||
('xelatex', 'fr'): {
|
||||
# use babel instead of polyglossia by default
|
||||
'polyglossia': '',
|
||||
'babel': '\\usepackage{babel}',
|
||||
},
|
||||
('xelatex', 'zh'): {
|
||||
'fontenc': '\\usepackage{xeCJK}',
|
||||
},
|
||||
} # type: Dict[Any, Dict[str, Any]]
|
||||
|
||||
EXTRA_RE = re.compile(r'^(.*\S)\s+\(([^()]*)\)\s*$')
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
from abc import abstractmethod
|
||||
|
||||
|
||||
class Base():
|
||||
def meth(self):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def abstractmeth(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
@abstractmethod
|
||||
def staticmeth():
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def classmeth(cls):
|
||||
pass
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def prop(self):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def coroutinemeth(self):
|
||||
pass
|
||||
@@ -0,0 +1,11 @@
|
||||
class Foo:
|
||||
__slots__ = ['attr']
|
||||
|
||||
|
||||
class Bar:
|
||||
__slots__ = {'attr1': 'docstring of attr1',
|
||||
'attr2': 'docstring of attr2',
|
||||
'attr3': None}
|
||||
|
||||
def __init__(self):
|
||||
self.attr2 = None #: docstring of instance attr2
|
||||
@@ -0,0 +1,10 @@
|
||||
def incr(a: int, b: int = 1) -> int:
|
||||
return a + b
|
||||
|
||||
|
||||
class Math:
|
||||
def __init__(self, s: str, o: object = None) -> None:
|
||||
pass
|
||||
|
||||
def incr(self, a: int, b: int = 1) -> int:
|
||||
return a + b
|
||||
@@ -1,4 +1,5 @@
|
||||
from os import * # NOQA
|
||||
from typing import Union
|
||||
|
||||
|
||||
class Foo:
|
||||
@@ -11,3 +12,7 @@ class Foo:
|
||||
@property
|
||||
def baz(self):
|
||||
pass
|
||||
|
||||
|
||||
def bar(x: Union[int, str], y: int = 1):
|
||||
pass
|
||||
|
||||
@@ -8,4 +8,5 @@
|
||||
|
||||
autosummary_dummy_module
|
||||
autosummary_dummy_module.Foo
|
||||
autosummary_dummy_module.bar
|
||||
autosummary_importfail
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.coverage']
|
||||
|
||||
coverage_ignore_pyobjects = [
|
||||
r'^coverage_ignored(\..*)?$',
|
||||
r'\.Ignored$',
|
||||
r'\.Documented\.ignored\d$',
|
||||
]
|
||||
@@ -0,0 +1,22 @@
|
||||
class Documented:
|
||||
"""Documented"""
|
||||
|
||||
def ignored1(self):
|
||||
pass
|
||||
|
||||
def ignored2(self):
|
||||
pass
|
||||
|
||||
def not_ignored1(self):
|
||||
pass
|
||||
|
||||
def not_ignored2(self):
|
||||
pass
|
||||
|
||||
|
||||
class Ignored:
|
||||
pass
|
||||
|
||||
|
||||
class NotIgnored:
|
||||
pass
|
||||
@@ -0,0 +1,22 @@
|
||||
class Documented:
|
||||
"""Documented"""
|
||||
|
||||
def ignored1(self):
|
||||
pass
|
||||
|
||||
def ignored2(self):
|
||||
pass
|
||||
|
||||
def not_ignored1(self):
|
||||
pass
|
||||
|
||||
def not_ignored2(self):
|
||||
pass
|
||||
|
||||
|
||||
class Ignored:
|
||||
pass
|
||||
|
||||
|
||||
class NotIgnored:
|
||||
pass
|
||||
@@ -0,0 +1,6 @@
|
||||
.. automodule:: coverage_ignored
|
||||
:members:
|
||||
|
||||
|
||||
.. automodule:: coverage_not_ignored
|
||||
:members:
|
||||
@@ -1317,6 +1317,46 @@ def test_instance_attributes(app):
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
def test_slots(app):
|
||||
options = {"members": None,
|
||||
"undoc-members": True}
|
||||
actual = do_autodoc(app, 'module', 'target.slots', options)
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:module:: target.slots',
|
||||
'',
|
||||
'',
|
||||
'.. py:class:: Bar()',
|
||||
' :module: target.slots',
|
||||
'',
|
||||
' ',
|
||||
' .. py:attribute:: Bar.attr1',
|
||||
' :module: target.slots',
|
||||
' ',
|
||||
' docstring of attr1',
|
||||
' ',
|
||||
' ',
|
||||
' .. py:attribute:: Bar.attr2',
|
||||
' :module: target.slots',
|
||||
' ',
|
||||
' docstring of instance attr2',
|
||||
' ',
|
||||
' ',
|
||||
' .. py:attribute:: Bar.attr3',
|
||||
' :module: target.slots',
|
||||
' ',
|
||||
'',
|
||||
'.. py:class:: Foo',
|
||||
' :module: target.slots',
|
||||
'',
|
||||
' ',
|
||||
' .. py:attribute:: Foo.attr',
|
||||
' :module: target.slots',
|
||||
' ',
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
def test_enum_class(app):
|
||||
options = {"members": None,
|
||||
@@ -1482,6 +1522,55 @@ def test_mocked_module_imports(app, warning):
|
||||
assert warning.getvalue() == ''
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('setup_test')
|
||||
def test_abstractmethods():
|
||||
options = {"members": None,
|
||||
"undoc-members": None}
|
||||
actual = do_autodoc(app, 'module', 'target.abstractmethods', options)
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:module:: target.abstractmethods',
|
||||
'',
|
||||
'',
|
||||
'.. py:class:: Base',
|
||||
' :module: target.abstractmethods',
|
||||
'',
|
||||
' ',
|
||||
' .. py:method:: Base.abstractmeth()',
|
||||
' :module: target.abstractmethods',
|
||||
' :abstractmethod:',
|
||||
' ',
|
||||
' ',
|
||||
' .. py:method:: Base.classmeth()',
|
||||
' :module: target.abstractmethods',
|
||||
' :abstractmethod:',
|
||||
' :classmethod:',
|
||||
' ',
|
||||
' ',
|
||||
' .. py:method:: Base.coroutinemeth()',
|
||||
' :module: target.abstractmethods',
|
||||
' :abstractmethod:',
|
||||
' :async:',
|
||||
' ',
|
||||
' ',
|
||||
' .. py:method:: Base.meth()',
|
||||
' :module: target.abstractmethods',
|
||||
' ',
|
||||
' ',
|
||||
' .. py:method:: Base.prop',
|
||||
' :module: target.abstractmethods',
|
||||
' :abstractmethod:',
|
||||
' :property:',
|
||||
' ',
|
||||
' ',
|
||||
' .. py:method:: Base.staticmeth()',
|
||||
' :module: target.abstractmethods',
|
||||
' :abstractmethod:',
|
||||
' :staticmethod:',
|
||||
' '
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('setup_test')
|
||||
def test_partialfunction():
|
||||
options = {"members": None}
|
||||
@@ -1597,6 +1686,58 @@ def test_partialmethod(app):
|
||||
assert list(actual) == expected
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
def test_autodoc_typehints_signature(app):
|
||||
app.config.autodoc_typehints = "signature"
|
||||
|
||||
options = {"members": None,
|
||||
"undoc-members": True}
|
||||
actual = do_autodoc(app, 'module', 'target.typehints', options)
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:module:: target.typehints',
|
||||
'',
|
||||
'',
|
||||
'.. py:class:: Math(s: str, o: object = None)',
|
||||
' :module: target.typehints',
|
||||
'',
|
||||
' ',
|
||||
' .. py:method:: Math.incr(a: int, b: int = 1) -> int',
|
||||
' :module: target.typehints',
|
||||
' ',
|
||||
'',
|
||||
'.. py:function:: incr(a: int, b: int = 1) -> int',
|
||||
' :module: target.typehints',
|
||||
''
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
def test_autodoc_typehints_none(app):
|
||||
app.config.autodoc_typehints = "none"
|
||||
|
||||
options = {"members": None,
|
||||
"undoc-members": True}
|
||||
actual = do_autodoc(app, 'module', 'target.typehints', options)
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:module:: target.typehints',
|
||||
'',
|
||||
'',
|
||||
'.. py:class:: Math(s, o = None)',
|
||||
' :module: target.typehints',
|
||||
'',
|
||||
' ',
|
||||
' .. py:method:: Math.incr(a, b = 1) -> int',
|
||||
' :module: target.typehints',
|
||||
' ',
|
||||
'',
|
||||
'.. py:function:: incr(a, b = 1) -> int',
|
||||
' :module: target.typehints',
|
||||
''
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
def test_autodoc_default_options(app):
|
||||
# no settings
|
||||
|
||||
@@ -165,6 +165,16 @@ def test_latex_basic(app, status, warning):
|
||||
assert r'\renewcommand{\releasename}{}' in result
|
||||
|
||||
|
||||
@pytest.mark.sphinx('latex', testroot='basic', confoverrides={'language': 'zh'})
|
||||
def test_latex_additional_settings_for_language_code(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'\usepackage{xeCJK}' in result
|
||||
|
||||
|
||||
@pytest.mark.sphinx('latex', testroot='latex-title')
|
||||
def test_latex_title_after_admonitions(app, status, warning):
|
||||
app.builder.build_all()
|
||||
|
||||
+65
-2
@@ -18,7 +18,10 @@ from sphinx.addnodes import (
|
||||
desc, desc_addname, desc_annotation, desc_content, desc_name, desc_optional,
|
||||
desc_parameter, desc_parameterlist, desc_returns, desc_signature
|
||||
)
|
||||
from sphinx.domains.python import py_sig_re, _pseudo_parse_arglist, PythonDomain
|
||||
from sphinx.domains import IndexEntry
|
||||
from sphinx.domains.python import (
|
||||
py_sig_re, _pseudo_parse_arglist, PythonDomain, PythonModuleIndex
|
||||
)
|
||||
from sphinx.testing import restructuredtext
|
||||
from sphinx.testing.util import assert_node
|
||||
|
||||
@@ -335,7 +338,9 @@ def test_pymethod_options(app):
|
||||
" .. py:method:: meth4\n"
|
||||
" :async:\n"
|
||||
" .. py:method:: meth5\n"
|
||||
" :property:\n")
|
||||
" :property:\n"
|
||||
" .. py:method:: meth6\n"
|
||||
" :abstractmethod:\n")
|
||||
domain = app.env.get_domain('py')
|
||||
doctree = restructuredtext.parse(app, text)
|
||||
assert_node(doctree, (addnodes.index,
|
||||
@@ -350,6 +355,8 @@ def test_pymethod_options(app):
|
||||
addnodes.index,
|
||||
desc,
|
||||
addnodes.index,
|
||||
desc,
|
||||
addnodes.index,
|
||||
desc)])]))
|
||||
|
||||
# method
|
||||
@@ -400,6 +407,16 @@ def test_pymethod_options(app):
|
||||
assert 'Class.meth5' in domain.objects
|
||||
assert domain.objects['Class.meth5'] == ('index', 'method')
|
||||
|
||||
# :abstractmethod:
|
||||
assert_node(doctree[1][1][10], addnodes.index,
|
||||
entries=[('single', 'meth6() (Class method)', 'Class.meth6', '', None)])
|
||||
assert_node(doctree[1][1][11], ([desc_signature, ([desc_annotation, "abstract "],
|
||||
[desc_name, "meth6"],
|
||||
[desc_parameterlist, ()])],
|
||||
[desc_content, ()]))
|
||||
assert 'Class.meth6' in domain.objects
|
||||
assert domain.objects['Class.meth6'] == ('index', 'method')
|
||||
|
||||
|
||||
def test_pyclassmethod(app):
|
||||
text = (".. py:class:: Class\n"
|
||||
@@ -460,3 +477,49 @@ def test_pyattribute(app):
|
||||
[desc_content, ()]))
|
||||
assert 'Class.attr' in domain.objects
|
||||
assert domain.objects['Class.attr'] == ('index', 'attribute')
|
||||
|
||||
|
||||
@pytest.mark.sphinx(freshenv=True)
|
||||
def test_module_index(app):
|
||||
text = (".. py:module:: docutils\n"
|
||||
".. py:module:: sphinx\n"
|
||||
".. py:module:: sphinx.config\n"
|
||||
".. py:module:: sphinx.builders\n"
|
||||
".. py:module:: sphinx.builders.html\n"
|
||||
".. py:module:: sphinx_intl\n")
|
||||
restructuredtext.parse(app, text)
|
||||
index = PythonModuleIndex(app.env.get_domain('py'))
|
||||
assert index.generate() == (
|
||||
[('d', [IndexEntry('docutils', 0, 'index', 'module-docutils', '', '', '')]),
|
||||
('s', [IndexEntry('sphinx', 1, 'index', 'module-sphinx', '', '', ''),
|
||||
IndexEntry('sphinx.builders', 2, 'index', 'module-sphinx.builders', '', '', ''), # NOQA
|
||||
IndexEntry('sphinx.builders.html', 2, 'index', 'module-sphinx.builders.html', '', '', ''), # NOQA
|
||||
IndexEntry('sphinx.config', 2, 'index', 'module-sphinx.config', '', '', ''),
|
||||
IndexEntry('sphinx_intl', 0, 'index', 'module-sphinx_intl', '', '', '')])],
|
||||
False
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.sphinx(freshenv=True)
|
||||
def test_module_index_submodule(app):
|
||||
text = ".. py:module:: sphinx.config\n"
|
||||
restructuredtext.parse(app, text)
|
||||
index = PythonModuleIndex(app.env.get_domain('py'))
|
||||
assert index.generate() == (
|
||||
[('s', [IndexEntry('sphinx', 1, '', '', '', '', ''),
|
||||
IndexEntry('sphinx.config', 2, 'index', 'module-sphinx.config', '', '', '')])],
|
||||
False
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.sphinx(freshenv=True)
|
||||
def test_module_index_not_collapsed(app):
|
||||
text = (".. py:module:: docutils\n"
|
||||
".. py:module:: sphinx\n")
|
||||
restructuredtext.parse(app, text)
|
||||
index = PythonModuleIndex(app.env.get_domain('py'))
|
||||
assert index.generate() == (
|
||||
[('d', [IndexEntry('docutils', 0, 'index', 'module-docutils', '', '', '')]),
|
||||
('s', [IndexEntry('sphinx', 0, 'index', 'module-sphinx', '', '', '')])],
|
||||
True
|
||||
)
|
||||
|
||||
@@ -401,6 +401,21 @@ def test_subpackage_in_toc(make_app, apidoc):
|
||||
assert (outdir / 'parent.child.foo.rst').isfile()
|
||||
|
||||
|
||||
def test_private(tempdir):
|
||||
(tempdir / 'hello.py').write_text('')
|
||||
(tempdir / '_world.py').write_text('')
|
||||
|
||||
# without --private option
|
||||
apidoc_main(['-o', tempdir, tempdir])
|
||||
assert (tempdir / 'hello.rst').exists()
|
||||
assert not (tempdir / '_world.rst').exists()
|
||||
|
||||
# with --private option
|
||||
apidoc_main(['--private', '-o', tempdir, tempdir])
|
||||
assert (tempdir / 'hello.rst').exists()
|
||||
assert (tempdir / '_world.rst').exists()
|
||||
|
||||
|
||||
def test_toc_file(tempdir):
|
||||
outdir = path(tempdir)
|
||||
(outdir / 'module').makedirs()
|
||||
|
||||
@@ -14,9 +14,13 @@ import os
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
from docutils import nodes
|
||||
|
||||
from sphinx.ext.autosummary import mangle_signature, import_by_name, extract_summary
|
||||
from sphinx.testing.util import etree_parse
|
||||
from sphinx import addnodes
|
||||
from sphinx.ext.autosummary import (
|
||||
autosummary_table, autosummary_toc, mangle_signature, import_by_name, extract_summary
|
||||
)
|
||||
from sphinx.testing.util import assert_node, etree_parse
|
||||
from sphinx.util.docutils import new_document
|
||||
|
||||
html_warnfile = StringIO()
|
||||
@@ -44,11 +48,13 @@ def test_mangle_signature():
|
||||
(a, b[, c]) :: (a, b[, c])
|
||||
(a, b[, cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx]) :: (a, b[, ...)
|
||||
(a, b='c=d, e=f, g=h', c=3) :: (a[, b, c])
|
||||
(a, b="c=d, e=f, g=h", c=3) :: (a[, b, c])
|
||||
(a, b='c=d, \\'e=f,\\' g=h', c=3) :: (a[, b, c])
|
||||
(a, b='c=d, ', e='\\\\' g=h, c=3) :: (a[, b, e, c])
|
||||
(a, b={'c=d, ': 3, '\\\\': 3}) :: (a[, b])
|
||||
(a=1, b=2, c=3) :: ([a, b, c])
|
||||
(a=1, b=<SomeClass: a, b, c>, c=3) :: ([a, b, c])
|
||||
(a=1, b=T(a=1, b=2), c=3) :: ([a, b, c])
|
||||
(a: int, b: int) -> str :: (a, b)
|
||||
"""
|
||||
|
||||
@@ -180,6 +186,25 @@ def test_escaping(app, status, warning):
|
||||
@pytest.mark.sphinx('dummy', testroot='ext-autosummary')
|
||||
def test_autosummary_generate(app, status, warning):
|
||||
app.builder.build_all()
|
||||
|
||||
doctree = app.env.get_doctree('index')
|
||||
assert_node(doctree, (nodes.paragraph,
|
||||
nodes.paragraph,
|
||||
addnodes.tabular_col_spec,
|
||||
autosummary_table,
|
||||
autosummary_toc))
|
||||
assert_node(doctree[3],
|
||||
[autosummary_table, nodes.table, nodes.tgroup, (nodes.colspec,
|
||||
nodes.colspec,
|
||||
[nodes.tbody, (nodes.row,
|
||||
nodes.row,
|
||||
nodes.row,
|
||||
nodes.row)])])
|
||||
assert doctree[3][0][0][2][0].astext() == 'autosummary_dummy_module\n\n'
|
||||
assert doctree[3][0][0][2][1].astext() == 'autosummary_dummy_module.Foo()\n\n'
|
||||
assert doctree[3][0][0][2][2].astext() == 'autosummary_dummy_module.bar(x[, y])\n\n'
|
||||
assert doctree[3][0][0][2][3].astext() == 'autosummary_importfail\n\n'
|
||||
|
||||
module = (app.srcdir / 'generated' / 'autosummary_dummy_module.rst').text()
|
||||
assert (' .. autosummary::\n'
|
||||
' \n'
|
||||
|
||||
@@ -45,3 +45,22 @@ def test_build(app, status, warning):
|
||||
assert 'classes' in undoc_py['autodoc_target']
|
||||
assert 'Class' in undoc_py['autodoc_target']['classes']
|
||||
assert 'undocmeth' in undoc_py['autodoc_target']['classes']['Class']
|
||||
|
||||
|
||||
@pytest.mark.sphinx('coverage', testroot='ext-coverage')
|
||||
def test_coverage_ignore_pyobjects(app, status, warning):
|
||||
app.builder.build_all()
|
||||
actual = (app.outdir / 'python.txt').text()
|
||||
expected = '''Undocumented Python objects
|
||||
===========================
|
||||
coverage_not_ignored
|
||||
--------------------
|
||||
Classes:
|
||||
* Documented -- missing methods:
|
||||
|
||||
- not_ignored1
|
||||
- not_ignored2
|
||||
* NotIgnored
|
||||
|
||||
'''
|
||||
assert actual == expected
|
||||
|
||||
@@ -196,7 +196,7 @@ def test_Signature_partialmethod():
|
||||
|
||||
def test_Signature_annotations():
|
||||
from typing_test_data import (f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10,
|
||||
f11, f12, f13, f14, f15, f16, f17, Node)
|
||||
f11, f12, f13, f14, f15, f16, f17, f18, Node)
|
||||
|
||||
# Class annotations
|
||||
sig = inspect.Signature(f0).format_args()
|
||||
@@ -273,6 +273,9 @@ def test_Signature_annotations():
|
||||
sig = inspect.Signature(f17).format_args()
|
||||
assert sig == '(*, arg3, arg4)'
|
||||
|
||||
sig = inspect.Signature(f18).format_args()
|
||||
assert sig == '(self, arg1: Union[int, Tuple] = 10) -> List[Dict]'
|
||||
|
||||
# type hints by string
|
||||
sig = inspect.Signature(Node.children).format_args()
|
||||
if (3, 5, 0) <= sys.version_info < (3, 5, 3):
|
||||
|
||||
@@ -17,7 +17,7 @@ from docutils.parsers import rst
|
||||
from docutils.utils import new_document
|
||||
|
||||
from sphinx.transforms import ApplySourceWorkaround
|
||||
from sphinx.util.nodes import NodeMatcher, extract_messages, clean_astext
|
||||
from sphinx.util.nodes import NodeMatcher, extract_messages, clean_astext, split_explicit_title
|
||||
|
||||
|
||||
def _transform(doctree):
|
||||
@@ -178,3 +178,18 @@ def test_clean_astext():
|
||||
node = nodes.paragraph(text='hello world')
|
||||
node += nodes.raw('', 'raw text', format='html')
|
||||
assert 'hello world' == clean_astext(node)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'title, expected',
|
||||
[
|
||||
# implicit
|
||||
('hello', (False, 'hello', 'hello')),
|
||||
# explicit
|
||||
('hello <world>', (True, 'hello', 'world')),
|
||||
# explicit (title having angle brackets)
|
||||
('hello <world> <sphinx>', (True, 'hello <world>', 'sphinx')),
|
||||
]
|
||||
)
|
||||
def test_split_explicit_target(title, expected):
|
||||
assert expected == split_explicit_title(title)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from numbers import Integral
|
||||
from typing import Any, List, TypeVar, Union, Callable, Tuple, Optional
|
||||
from typing import Any, Dict, List, TypeVar, Union, Callable, Tuple, Optional
|
||||
|
||||
|
||||
def f0(x: int, y: Integral) -> None:
|
||||
@@ -88,6 +88,10 @@ def f17(*, arg3, arg4):
|
||||
pass
|
||||
|
||||
|
||||
def f18(self, arg1: Union[int, Tuple] = 10) -> List[Dict]:
|
||||
pass
|
||||
|
||||
|
||||
class Node:
|
||||
def __init__(self, parent: Optional['Node']) -> None:
|
||||
pass
|
||||
|
||||
+29
-8
@@ -14,7 +14,10 @@ import sys
|
||||
from typing import List
|
||||
|
||||
|
||||
MAX_LINE_LENGTH = 100
|
||||
MAX_LINE_LENGTH = 85
|
||||
LONG_INTERPRETED_TEXT = re.compile(r'^\s*\W*(:(\w+:)+)?`.*`\W*$')
|
||||
CODE_BLOCK_DIRECTIVE = re.compile(r'^(\s*)\.\. code-block::')
|
||||
LEADING_SPACES = re.compile(r'^(\s*)')
|
||||
|
||||
|
||||
def lint(path: str) -> int:
|
||||
@@ -22,13 +25,28 @@ def lint(path: str) -> int:
|
||||
document = f.readlines()
|
||||
|
||||
errors = 0
|
||||
in_code_block = False
|
||||
code_block_depth = 0
|
||||
for i, line in enumerate(document):
|
||||
if line.endswith(' '):
|
||||
print('%s:%d: the line ends with whitespace.' %
|
||||
(path, i + 1))
|
||||
errors += 1
|
||||
|
||||
if len(line) > MAX_LINE_LENGTH:
|
||||
matched = CODE_BLOCK_DIRECTIVE.match(line)
|
||||
if matched:
|
||||
in_code_block = True
|
||||
code_block_depth = len(matched.group(1))
|
||||
elif in_code_block:
|
||||
if line.strip() == '':
|
||||
pass
|
||||
else:
|
||||
spaces = LEADING_SPACES.match(line).group(1)
|
||||
if len(spaces) < code_block_depth:
|
||||
in_code_block = False
|
||||
elif LONG_INTERPRETED_TEXT.match(line):
|
||||
pass
|
||||
elif len(line) > MAX_LINE_LENGTH:
|
||||
if re.match(r'^\s*\.\. ', line):
|
||||
# ignore directives and hyperlink targets
|
||||
pass
|
||||
@@ -42,12 +60,15 @@ def lint(path: str) -> int:
|
||||
|
||||
def main(args: List[str]) -> int:
|
||||
errors = 0
|
||||
for directory in args:
|
||||
for root, dirs, files in os.walk(directory):
|
||||
for filename in files:
|
||||
if filename.endswith('.rst'):
|
||||
path = os.path.join(root, filename)
|
||||
errors += lint(path)
|
||||
for path in args:
|
||||
if os.path.isfile(path):
|
||||
errors += lint(path)
|
||||
elif os.path.isdir(path):
|
||||
for root, dirs, files in os.walk(path):
|
||||
for filename in files:
|
||||
if filename.endswith('.rst'):
|
||||
path = os.path.join(root, filename)
|
||||
errors += lint(path)
|
||||
|
||||
if errors:
|
||||
return 1
|
||||
|
||||
Reference in New Issue
Block a user