Merge branch 'master' into feature-autosummary-packages

This commit is contained in:
woutdenolf
2019-06-01 12:42:28 +02:00
committed by GitHub
76 changed files with 1314 additions and 487 deletions
+2
View File
@@ -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'
+1
View File
@@ -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!
+335 -216
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -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>`_.
+4
View File
@@ -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
View File
@@ -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`_
+2 -2
View File
@@ -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
+2 -2
View File
@@ -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:
+3 -3
View File
@@ -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
+8 -1
View File
@@ -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
View File
@@ -19,7 +19,7 @@ i18n API
.. _ext-i18n:
Extension internationalization (`i18n`) and localization (`l10n`) using i18n API
---------------------------------------------------------------------------------
--------------------------------------------------------------------------------
.. versionadded:: 1.8
+3 -3
View File
@@ -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**
+2 -2
View File
@@ -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()
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
-----------------------------
+2 -1
View File
@@ -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
-----------
+2 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -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" %}
+1 -1
View File
@@ -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
View File
@@ -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
+12 -4
View File
@@ -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
+12
View File
@@ -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
+2 -2
View File
@@ -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.
+3 -3
View File
@@ -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).
+2 -2
View File
@@ -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.
+7 -6
View File
@@ -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.
+3 -3
View File
@@ -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.
+18 -17
View File
@@ -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::
+79 -36
View File
@@ -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
View File
@@ -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))
+6 -6
View File
@@ -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'
+5 -3
View File
@@ -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
+2 -1
View File
@@ -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:
+3 -1
View File
@@ -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, '', '', '', '', ''))
+5 -3
View File
@@ -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:
+1 -1
View File
@@ -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'])
+3
View File
@@ -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
View File
@@ -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
View File
@@ -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')
+8 -1
View File
@@ -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:
+1 -1
View File
@@ -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
+26 -8
View File
@@ -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:
+48 -33
View File
@@ -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
View File
@@ -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)
+2 -2
View File
@@ -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
+1 -1
View File
@@ -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%
+4 -3
View File
@@ -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) {
+1 -1
View File
@@ -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>
+2
View File
@@ -42,7 +42,9 @@
{{ toc }}
</div>
{%- endif %}
<div role="main">
{% block body %}{% endblock %}
</div>
</div>
</div>
{% endblock %}
+1 -1
View File
@@ -244,7 +244,7 @@ date_format_mappings = {
'%x': 'medium', # Locales appropriate date representation.
'%X': 'medium', # Locales 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
View File
@@ -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__,
+1 -1
View File
@@ -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
+3 -3
View File
@@ -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
View File
@@ -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
+12
View File
@@ -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
+6
View File
@@ -0,0 +1,6 @@
.. automodule:: coverage_ignored
:members:
.. automodule:: coverage_not_ignored
:members:
+141
View File
@@ -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
+10
View File
@@ -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
View File
@@ -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
)
+15
View File
@@ -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()
+27 -2
View File
@@ -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'
+19
View File
@@ -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
+4 -1
View File
@@ -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):
+16 -1
View File
@@ -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)
+5 -1
View File
@@ -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
View File
@@ -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