mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge remote-tracking branch 'sphinx/3.x' into 3.x
This commit is contained in:
commit
f9e9bdc4a0
@ -8,4 +8,7 @@ jobs:
|
|||||||
- checkout
|
- checkout
|
||||||
- run: /python3.6/bin/pip install -U pip setuptools
|
- run: /python3.6/bin/pip install -U pip setuptools
|
||||||
- run: /python3.6/bin/pip install -U .[test]
|
- run: /python3.6/bin/pip install -U .[test]
|
||||||
- run: make test PYTHON=/python3.6/bin/python
|
- run: mkdir -p test-reports/pytest
|
||||||
|
- run: make test PYTHON=/python3.6/bin/python TEST=--junitxml=test-reports/pytest/results.xml
|
||||||
|
- store_test_results:
|
||||||
|
path: test-reports
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -9,6 +9,7 @@
|
|||||||
.mypy_cache/
|
.mypy_cache/
|
||||||
.pytest_cache/
|
.pytest_cache/
|
||||||
.ropeproject/
|
.ropeproject/
|
||||||
|
.vscode/
|
||||||
TAGS
|
TAGS
|
||||||
.tags
|
.tags
|
||||||
.tox/
|
.tox/
|
||||||
|
1
AUTHORS
1
AUTHORS
@ -85,6 +85,7 @@ Other contributors, listed alphabetically, are:
|
|||||||
* Daniel Pizetta -- inheritance diagram improvements
|
* Daniel Pizetta -- inheritance diagram improvements
|
||||||
* KINEBUCHI Tomohiko -- typing Sphinx as well as docutils
|
* KINEBUCHI Tomohiko -- typing Sphinx as well as docutils
|
||||||
* Adrián Chaves (Gallaecio) -- coverage builder improvements
|
* Adrián Chaves (Gallaecio) -- coverage builder improvements
|
||||||
|
* Lars Hupfeldt Nielsen - OpenSSL FIPS mode md5 bug fix
|
||||||
|
|
||||||
Many thanks for all contributions!
|
Many thanks for all contributions!
|
||||||
|
|
||||||
|
133
CHANGES
133
CHANGES
@ -7,26 +7,116 @@ Dependencies
|
|||||||
Incompatible changes
|
Incompatible changes
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
|
* #7477: imgconverter: Invoke "magick convert" command by default on Windows
|
||||||
|
|
||||||
Deprecated
|
Deprecated
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
* The first argument for sphinx.ext.autosummary.generate.AutosummaryRenderer has
|
||||||
|
been changed to Sphinx object
|
||||||
|
* ``sphinx.ext.autosummary.generate.AutosummaryRenderer`` takes an object type
|
||||||
|
as an argument
|
||||||
|
* The ``ignore`` argument of ``sphinx.ext.autodoc.Documenter.get_doc()``
|
||||||
|
* The ``template_dir`` argument of ``sphinx.ext.autosummary.generate.
|
||||||
|
AutosummaryRenderer``
|
||||||
* The ``module`` argument of ``sphinx.ext.autosummary.generate.
|
* The ``module`` argument of ``sphinx.ext.autosummary.generate.
|
||||||
find_autosummary_in_docstring()``
|
find_autosummary_in_docstring()``
|
||||||
|
* The ``builder`` argument of ``sphinx.ext.autosummary.generate.
|
||||||
|
generate_autosummary_docs()``
|
||||||
|
* The ``template_dir`` argument of ``sphinx.ext.autosummary.generate.
|
||||||
|
generate_autosummary_docs()``
|
||||||
|
* The ``ignore`` argument of ``sphinx.util.docstring.prepare_docstring()``
|
||||||
|
* ``sphinx.ext.autosummary.generate.AutosummaryRenderer.exists()``
|
||||||
|
* ``sphinx.util.rpartition()``
|
||||||
|
|
||||||
Features added
|
Features added
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
* LaTeX: Make the ``toplevel_sectioning`` setting optional in LaTeX theme
|
* LaTeX: Make the ``toplevel_sectioning`` setting optional in LaTeX theme
|
||||||
|
* LaTeX: Allow to override papersize and pointsize from LaTeX themes
|
||||||
|
* LaTeX: Add :confval:`latex_theme_options` to override theme options
|
||||||
* #7410: Allow to suppress "circular toctree references detected" warnings using
|
* #7410: Allow to suppress "circular toctree references detected" warnings using
|
||||||
:confval:`suppress_warnings`
|
:confval:`suppress_warnings`
|
||||||
|
* C, added scope control directives, :rst:dir:`c:namespace`,
|
||||||
|
:rst:dir:`c:namespace-push`, and :rst:dir:`c:namespace-pop`.
|
||||||
|
* #2044: autodoc: Suppress default value for instance attributes
|
||||||
|
* #7473: autodoc: consider a member public if docstring contains
|
||||||
|
``:meta public:`` in info-field-list
|
||||||
|
* #7487: autodoc: Allow to generate docs for singledispatch functions by
|
||||||
|
py:autofunction
|
||||||
|
* #7143: autodoc: Support final classes and methods
|
||||||
|
* #7466: autosummary: headings in generated documents are not translated
|
||||||
|
* #7490: autosummary: Add ``:caption:`` option to autosummary directive to set a
|
||||||
|
caption to the toctree
|
||||||
|
* #248, #6040: autosummary: Add ``:recursive:`` option to autosummary directive
|
||||||
|
to generate stub files recursively
|
||||||
|
* #4030: autosummary: Add :confval:`autosummary_context` to add template
|
||||||
|
variables for custom templates
|
||||||
|
* #7530: html: Support nested <kbd> elements
|
||||||
|
* #7481: html theme: Add right margin to footnote/citation labels
|
||||||
|
* #7482: html theme: CSS spacing for code blocks with captions and line numbers
|
||||||
|
* #7443: html theme: Add new options :confval:`globaltoc_collapse` and
|
||||||
|
:confval:`globaltoc_includehidden` to control the behavior of globaltoc in
|
||||||
|
sidebar
|
||||||
|
* #7484: html theme: Avoid clashes between sidebar and other blocks
|
||||||
|
* #7476: html theme: Relbar breadcrumb should contain current page
|
||||||
|
* #7506: html theme: A canonical URL is not escaped
|
||||||
|
* #7533: html theme: Avoid whitespace at the beginning of genindex.html
|
||||||
|
* #7541: html theme: Add a "clearer" at the end of the "body"
|
||||||
|
* #7542: html theme: Make admonition/topic/sidebar scrollable
|
||||||
|
* #7543: html theme: Add top and bottom margins to tables
|
||||||
|
* C and C++: allow semicolon in the end of declarations.
|
||||||
|
* C++, parse parameterized noexcept specifiers.
|
||||||
|
* #7294: C++, parse expressions with user-defined literals.
|
||||||
|
* C++, parse trailing return types.
|
||||||
|
* #7143: py domain: Add ``:final:`` option to :rst:dir:`py:class:`,
|
||||||
|
:rst:dir:`py:exception:` and :rst:dir:`py:method:` directives
|
||||||
|
* #7596: py domain: Change a type annotation for variables to a hyperlink
|
||||||
|
* #7582: napoleon: a type for attribute are represented like type annotation
|
||||||
|
* #7683: Add ``allowed_exceptions`` parameter to ``Sphinx.emit()`` to allow
|
||||||
|
handlers to raise specified exceptions
|
||||||
|
|
||||||
Bugs fixed
|
Bugs fixed
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
* #6703: autodoc: incremental build does not work for imported objects
|
||||||
|
* #7564: autodoc: annotations not to be shown for descriptors
|
||||||
|
* #6588: autodoc: Decorated inherited method has no documentation
|
||||||
|
* #7469: autodoc: The change of autodoc-process-docstring for variables is
|
||||||
|
cached unexpectedly
|
||||||
|
* #7559: autodoc: misdetects a sync function is async
|
||||||
|
* #6857: autodoc: failed to detect a classmethod on Enum class
|
||||||
|
* #7562: autodoc: a typehint contains spaces is wrongly rendered under
|
||||||
|
autodoc_typehints='description' mode
|
||||||
|
* #7551: autodoc: failed to import nested class
|
||||||
|
* #7362: autodoc: does not render correct signatures for built-in functions
|
||||||
|
* #7654: autodoc: ``Optional[Union[foo, bar]]`` is presented as
|
||||||
|
``Union[foo, bar, None]``
|
||||||
|
* #7629: autodoc: autofunction emits an unfriendly warning if an invalid object
|
||||||
|
specified
|
||||||
|
* #7650: autodoc: undecorated signature is shown for decorated functions
|
||||||
|
* #7676: autodoc: typo in the default value of autodoc_member_order
|
||||||
|
* #7551: autosummary: a nested class is indexed as non-nested class
|
||||||
|
* #7661: autosummary: autosummary directive emits warnings twices if failed to
|
||||||
|
import the target module
|
||||||
|
* #7535: sphinx-autogen: crashes when custom template uses inheritance
|
||||||
|
* #7536: sphinx-autogen: crashes when template uses i18n feature
|
||||||
|
* #7653: sphinx-quickstart: Fix multiple directory creation for nested relpath
|
||||||
|
* #2785: html: Bad alignment of equation links
|
||||||
|
* #7581: napoleon: bad parsing of inline code in attribute docstrings
|
||||||
|
* #7628: imgconverter: runs imagemagick once unnecessary for builders not
|
||||||
|
supporting images
|
||||||
|
* #7610: incorrectly renders consecutive backslashes for docutils-0.16
|
||||||
|
* #7646: handle errors on event handlers
|
||||||
|
* C++, fix rendering and xrefs in nested names explicitly starting
|
||||||
|
in global scope, e.g., ``::A::B``.
|
||||||
|
* C, fix rendering and xrefs in nested names explicitly starting
|
||||||
|
in global scope, e.g., ``.A.B``.
|
||||||
|
|
||||||
Testing
|
Testing
|
||||||
--------
|
--------
|
||||||
|
|
||||||
Release 3.0.2 (in development)
|
Release 3.0.4 (in development)
|
||||||
==============================
|
==============================
|
||||||
|
|
||||||
Dependencies
|
Dependencies
|
||||||
@ -44,9 +134,50 @@ Features added
|
|||||||
Bugs fixed
|
Bugs fixed
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
* #7567: autodoc: parametrized types are shown twice for generic types
|
||||||
|
* #7637: autodoc: system defined TypeVars are shown in Python 3.9
|
||||||
|
* #7611: md5 fails when OpenSSL FIPS is enabled
|
||||||
|
* #7626: release package does not contain ``CODE_OF_CONDUCT``
|
||||||
|
|
||||||
Testing
|
Testing
|
||||||
--------
|
--------
|
||||||
|
|
||||||
|
Release 3.0.3 (released Apr 26, 2020)
|
||||||
|
=====================================
|
||||||
|
|
||||||
|
Features added
|
||||||
|
--------------
|
||||||
|
|
||||||
|
* C, parse array declarators with static, qualifiers, and VLA specification.
|
||||||
|
|
||||||
|
Bugs fixed
|
||||||
|
----------
|
||||||
|
|
||||||
|
* #7516: autodoc: crashes if target object raises an error on accessing
|
||||||
|
its attributes
|
||||||
|
|
||||||
|
Release 3.0.2 (released Apr 19, 2020)
|
||||||
|
=====================================
|
||||||
|
|
||||||
|
Features added
|
||||||
|
--------------
|
||||||
|
|
||||||
|
* C, parse attributes and add :confval:`c_id_attributes`
|
||||||
|
and :confval:`c_paren_attributes` to support user-defined attributes.
|
||||||
|
|
||||||
|
Bugs fixed
|
||||||
|
----------
|
||||||
|
|
||||||
|
* #7461: py domain: fails with IndexError for empty tuple in type annotation
|
||||||
|
* #7510: py domain: keyword-only arguments are documented as having a default of
|
||||||
|
None
|
||||||
|
* #7418: std domain: :rst:role:`term` role could not match case-insensitively
|
||||||
|
* #7461: autodoc: empty tuple in type annotation is not shown correctly
|
||||||
|
* #7479: autodoc: Sphinx builds has been slower since 3.0.0 on mocking
|
||||||
|
* C++, fix spacing issue in east-const declarations.
|
||||||
|
* #7414: LaTeX: Xindy language options were incorrect
|
||||||
|
* sphinx crashes with ImportError on python3.5.1
|
||||||
|
|
||||||
Release 3.0.1 (released Apr 11, 2020)
|
Release 3.0.1 (released Apr 11, 2020)
|
||||||
=====================================
|
=====================================
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ include LICENSE
|
|||||||
include AUTHORS
|
include AUTHORS
|
||||||
include CHANGES
|
include CHANGES
|
||||||
include CHANGES.old
|
include CHANGES.old
|
||||||
|
include CODE_OF_CONDUCT
|
||||||
include CONTRIBUTING.rst
|
include CONTRIBUTING.rst
|
||||||
include EXAMPLES
|
include EXAMPLES
|
||||||
|
|
||||||
|
2
Makefile
2
Makefile
@ -50,7 +50,7 @@ clean-buildfiles:
|
|||||||
|
|
||||||
.PHONY: clean-mypyfiles
|
.PHONY: clean-mypyfiles
|
||||||
clean-mypyfiles:
|
clean-mypyfiles:
|
||||||
rm -rf **/.mypy_cache/
|
find . -name '.mypy_cache' -exec rm -rf {} +
|
||||||
|
|
||||||
.PHONY: style-check
|
.PHONY: style-check
|
||||||
style-check:
|
style-check:
|
||||||
|
5
doc/_themes/sphinx13/layout.html
vendored
5
doc/_themes/sphinx13/layout.html
vendored
@ -13,11 +13,6 @@
|
|||||||
{% block sidebar1 %}{{ sidebar() }}{% endblock %}
|
{% block sidebar1 %}{{ sidebar() }}{% endblock %}
|
||||||
{% block sidebar2 %}{% endblock %}
|
{% block sidebar2 %}{% endblock %}
|
||||||
|
|
||||||
{% block linktags %}
|
|
||||||
{{ super() }}
|
|
||||||
<link rel="canonical" href="http://www.sphinx-doc.org/en/master/{{ pagename }}{{ file_suffix }}" />
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block extrahead %}
|
{% block extrahead %}
|
||||||
<link href='https://fonts.googleapis.com/css?family=Open+Sans:300,400,700'
|
<link href='https://fonts.googleapis.com/css?family=Open+Sans:300,400,700'
|
||||||
rel='stylesheet' type='text/css' />
|
rel='stylesheet' type='text/css' />
|
||||||
|
1
doc/_themes/sphinx13/static/sphinx13.css
vendored
1
doc/_themes/sphinx13/static/sphinx13.css
vendored
@ -127,6 +127,7 @@ div.sphinxsidebar {
|
|||||||
float: right;
|
float: right;
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
max-height: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.sphinxsidebar .logo {
|
div.sphinxsidebar .logo {
|
||||||
|
@ -26,7 +26,8 @@ modindex_common_prefix = ['sphinx.']
|
|||||||
html_static_path = ['_static']
|
html_static_path = ['_static']
|
||||||
html_sidebars = {'index': ['indexsidebar.html', 'searchbox.html']}
|
html_sidebars = {'index': ['indexsidebar.html', 'searchbox.html']}
|
||||||
html_additional_pages = {'index': 'index.html'}
|
html_additional_pages = {'index': 'index.html'}
|
||||||
html_use_opensearch = 'http://sphinx-doc.org'
|
html_use_opensearch = 'https://www.sphinx-doc.org/en/master'
|
||||||
|
html_baseurl = 'https://www.sphinx-doc.org/en/master/'
|
||||||
|
|
||||||
htmlhelp_basename = 'Sphinxdoc'
|
htmlhelp_basename = 'Sphinxdoc'
|
||||||
|
|
||||||
|
@ -230,11 +230,15 @@ connect handlers to the events. Example:
|
|||||||
|
|
||||||
.. event:: missing-reference (app, env, node, contnode)
|
.. event:: missing-reference (app, env, node, contnode)
|
||||||
|
|
||||||
Emitted when a cross-reference to a Python module or object cannot be
|
Emitted when a cross-reference to an object cannot be resolved.
|
||||||
resolved. If the event handler can resolve the reference, it should return a
|
If the event handler can resolve the reference, it should return a
|
||||||
new docutils node to be inserted in the document tree in place of the node
|
new docutils node to be inserted in the document tree in place of the node
|
||||||
*node*. Usually this node is a :class:`reference` node containing *contnode*
|
*node*. Usually this node is a :class:`reference` node containing *contnode*
|
||||||
as a child.
|
as a child.
|
||||||
|
If the handler can not resolve the cross-reference,
|
||||||
|
it can either return ``None`` to let other handlers try,
|
||||||
|
or raise :class:`NoUri` to prevent other handlers in trying and suppress
|
||||||
|
a warning about this cross-reference being unresolved.
|
||||||
|
|
||||||
:param env: The build environment (``app.builder.env``).
|
:param env: The build environment (``app.builder.env``).
|
||||||
:param node: The :class:`pending_xref` node to be resolved. Its attributes
|
:param node: The :class:`pending_xref` node to be resolved. Its attributes
|
||||||
|
@ -26,12 +26,63 @@ The following is a list of deprecated interfaces.
|
|||||||
- (will be) Removed
|
- (will be) Removed
|
||||||
- Alternatives
|
- Alternatives
|
||||||
|
|
||||||
|
* - The first argument for
|
||||||
|
``sphinx.ext.autosummary.generate.AutosummaryRenderer`` has been changed
|
||||||
|
to Sphinx object
|
||||||
|
- 3.1
|
||||||
|
- 5.0
|
||||||
|
- N/A
|
||||||
|
|
||||||
|
* - ``sphinx.ext.autosummary.generate.AutosummaryRenderer`` takes an object
|
||||||
|
type as an argument
|
||||||
|
- 3.1
|
||||||
|
- 5.0
|
||||||
|
- N/A
|
||||||
|
|
||||||
|
* - The ``ignore`` argument of ``sphinx.ext.autodoc.Documenter.get_doc()``
|
||||||
|
- 3.1
|
||||||
|
- 5.0
|
||||||
|
- N/A
|
||||||
|
|
||||||
|
* - The ``template_dir`` argument of
|
||||||
|
``sphinx.ext.autosummary.generate.AutosummaryRenderer``
|
||||||
|
- 3.1
|
||||||
|
- 5.0
|
||||||
|
- N/A
|
||||||
|
|
||||||
* - The ``module`` argument of
|
* - The ``module`` argument of
|
||||||
``sphinx.ext.autosummary.generate.find_autosummary_in_docstring()``
|
``sphinx.ext.autosummary.generate.find_autosummary_in_docstring()``
|
||||||
- 3.0
|
- 3.0
|
||||||
- 5.0
|
- 5.0
|
||||||
- N/A
|
- N/A
|
||||||
|
|
||||||
|
* - The ``builder`` argument of
|
||||||
|
``sphinx.ext.autosummary.generate.generate_autosummary_docs()``
|
||||||
|
- 3.1
|
||||||
|
- 5.0
|
||||||
|
- N/A
|
||||||
|
|
||||||
|
* - The ``template_dir`` argument of
|
||||||
|
``sphinx.ext.autosummary.generate.generate_autosummary_docs()``
|
||||||
|
- 3.1
|
||||||
|
- 5.0
|
||||||
|
- N/A
|
||||||
|
|
||||||
|
* - ``sphinx.ext.autosummary.generate.AutosummaryRenderer.exists()``
|
||||||
|
- 3.1
|
||||||
|
- 5.0
|
||||||
|
- N/A
|
||||||
|
|
||||||
|
* - The ``ignore`` argument of ``sphinx.util.docstring.prepare_docstring()``
|
||||||
|
- 3.1
|
||||||
|
- 5.0
|
||||||
|
- N/A
|
||||||
|
|
||||||
|
* - ``sphinx.util.rpartition()``
|
||||||
|
- 3.1
|
||||||
|
- 5.0
|
||||||
|
- ``str.rpartition()``
|
||||||
|
|
||||||
* - ``desc_signature['first']``
|
* - ``desc_signature['first']``
|
||||||
-
|
-
|
||||||
- 3.0
|
- 3.0
|
||||||
|
@ -90,9 +90,9 @@ section describe an easy way to translate with *sphinx-intl*.
|
|||||||
locale_dirs = ['locale/'] # path is example but recommended.
|
locale_dirs = ['locale/'] # path is example but recommended.
|
||||||
gettext_compact = False # optional.
|
gettext_compact = False # optional.
|
||||||
|
|
||||||
This case-study assumes that :confval:`locale_dirs` is set to ``locale/`` and
|
This case-study assumes that BUILDDIR is set to ``_build``,
|
||||||
:confval:`gettext_compact` is set to ``False`` (the Sphinx document is
|
:confval:`locale_dirs` is set to ``locale/`` and :confval:`gettext_compact`
|
||||||
already configured as such).
|
is set to ``False`` (the Sphinx document is already configured as such).
|
||||||
|
|
||||||
#. Extract translatable messages into pot files.
|
#. Extract translatable messages into pot files.
|
||||||
|
|
||||||
|
@ -1385,7 +1385,7 @@ that use Sphinx's HTMLWriter class.
|
|||||||
|
|
||||||
.. versionadded:: 1.3
|
.. versionadded:: 1.3
|
||||||
|
|
||||||
.. versionchanged:: 2.4
|
.. versionchanged:: 3.0
|
||||||
|
|
||||||
It is disabled for images having ``no-scaled-link`` class
|
It is disabled for images having ``no-scaled-link`` class
|
||||||
|
|
||||||
@ -2119,6 +2119,13 @@ These options influence LaTeX output.
|
|||||||
|
|
||||||
.. versionadded:: 3.0
|
.. versionadded:: 3.0
|
||||||
|
|
||||||
|
.. confval:: latex_theme_options
|
||||||
|
|
||||||
|
A dictionary of options that influence the look and feel of the selected
|
||||||
|
theme.
|
||||||
|
|
||||||
|
.. versionadded:: 3.1
|
||||||
|
|
||||||
.. confval:: latex_theme_path
|
.. confval:: latex_theme_path
|
||||||
|
|
||||||
A list of paths that contain custom LaTeX themes as subdirectories. Relative
|
A list of paths that contain custom LaTeX themes as subdirectories. Relative
|
||||||
@ -2479,6 +2486,30 @@ Options for the XML builder
|
|||||||
match any sequence of characters *including* slashes.
|
match any sequence of characters *including* slashes.
|
||||||
|
|
||||||
|
|
||||||
|
.. _c-config:
|
||||||
|
|
||||||
|
Options for the C domain
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
.. confval:: c_id_attributes
|
||||||
|
|
||||||
|
A list of strings that the parser additionally should accept as attributes.
|
||||||
|
This can for example be used when attributes have been ``#define`` d for
|
||||||
|
portability.
|
||||||
|
|
||||||
|
.. versionadded:: 3.0
|
||||||
|
|
||||||
|
.. confval:: c_paren_attributes
|
||||||
|
|
||||||
|
A list of strings that the parser additionally should accept as attributes
|
||||||
|
with one argument. That is, if ``my_align_as`` is in the list, then
|
||||||
|
``my_align_as(X)`` is parsed as an attribute for all strings ``X`` that have
|
||||||
|
balanced braces (``()``, ``[]``, and ``{}``). This can for example be used
|
||||||
|
when attributes have been ``#define`` d for portability.
|
||||||
|
|
||||||
|
.. versionadded:: 3.0
|
||||||
|
|
||||||
|
|
||||||
.. _cpp-config:
|
.. _cpp-config:
|
||||||
|
|
||||||
Options for the C++ domain
|
Options for the C++ domain
|
||||||
|
@ -154,6 +154,21 @@ inserting them into the page source under a suitable :rst:dir:`py:module`,
|
|||||||
|
|
||||||
.. versionadded:: 3.0
|
.. versionadded:: 3.0
|
||||||
|
|
||||||
|
* autodoc considers a member public if its docstring contains
|
||||||
|
``:meta public:`` in its :ref:`info-field-lists`, even if it starts with
|
||||||
|
an underscore.
|
||||||
|
For example:
|
||||||
|
|
||||||
|
.. code-block:: rst
|
||||||
|
|
||||||
|
def _my_function(my_arg, my_other_arg):
|
||||||
|
"""blah blah blah
|
||||||
|
|
||||||
|
:meta public:
|
||||||
|
"""
|
||||||
|
|
||||||
|
.. versionadded:: 3.1
|
||||||
|
|
||||||
* Python "special" members (that is, those named like ``__special__``) will
|
* Python "special" members (that is, those named like ``__special__``) will
|
||||||
be included if the ``special-members`` flag option is given::
|
be included if the ``special-members`` flag option is given::
|
||||||
|
|
||||||
|
@ -32,7 +32,8 @@ The :mod:`sphinx.ext.autosummary` extension does this in two parts:
|
|||||||
|
|
||||||
The :rst:dir:`autosummary` directive can also optionally serve as a
|
The :rst:dir:`autosummary` directive can also optionally serve as a
|
||||||
:rst:dir:`toctree` entry for the included items. Optionally, stub
|
:rst:dir:`toctree` entry for the included items. Optionally, stub
|
||||||
``.rst`` files for these items can also be automatically generated.
|
``.rst`` files for these items can also be automatically generated
|
||||||
|
when :confval:`autosummary_generate` is `True`.
|
||||||
|
|
||||||
For example, ::
|
For example, ::
|
||||||
|
|
||||||
@ -76,6 +77,12 @@ The :mod:`sphinx.ext.autosummary` extension does this in two parts:
|
|||||||
directory. If no argument is given, output is placed in the same directory
|
directory. If no argument is given, output is placed in the same directory
|
||||||
as the file that contains the directive.
|
as the file that contains the directive.
|
||||||
|
|
||||||
|
You can also use ``caption`` option to give a caption to the toctree.
|
||||||
|
|
||||||
|
.. versionadded:: 3.1
|
||||||
|
|
||||||
|
caption option added.
|
||||||
|
|
||||||
* If you don't want the :rst:dir:`autosummary` to show function signatures in
|
* If you don't want the :rst:dir:`autosummary` to show function signatures in
|
||||||
the listing, include the ``nosignatures`` option::
|
the listing, include the ``nosignatures`` option::
|
||||||
|
|
||||||
@ -99,6 +106,17 @@ The :mod:`sphinx.ext.autosummary` extension does this in two parts:
|
|||||||
|
|
||||||
.. versionadded:: 1.0
|
.. versionadded:: 1.0
|
||||||
|
|
||||||
|
* You can specify the ``recursive`` option to generate documents for
|
||||||
|
modules and sub-packages recursively. It defaults to disabled.
|
||||||
|
For example, ::
|
||||||
|
|
||||||
|
.. autosummary::
|
||||||
|
:recursive:
|
||||||
|
|
||||||
|
sphinx.environment.BuildEnvironment
|
||||||
|
|
||||||
|
.. versionadded:: 3.1
|
||||||
|
|
||||||
|
|
||||||
:program:`sphinx-autogen` -- generate autodoc stub pages
|
:program:`sphinx-autogen` -- generate autodoc stub pages
|
||||||
--------------------------------------------------------
|
--------------------------------------------------------
|
||||||
@ -133,10 +151,17 @@ Generating stub pages automatically
|
|||||||
If you do not want to create stub pages with :program:`sphinx-autogen`, you can
|
If you do not want to create stub pages with :program:`sphinx-autogen`, you can
|
||||||
also use these config values:
|
also use these config values:
|
||||||
|
|
||||||
|
.. confval:: autosummary_context
|
||||||
|
|
||||||
|
A dictionary of values to pass into the template engine's context for
|
||||||
|
autosummary stubs files.
|
||||||
|
|
||||||
|
.. versionadded:: 3.1
|
||||||
|
|
||||||
.. confval:: autosummary_generate
|
.. confval:: autosummary_generate
|
||||||
|
|
||||||
Boolean indicating whether to scan all found documents for autosummary
|
Boolean indicating whether to scan all found documents for autosummary
|
||||||
directives, and to generate stub pages for each.
|
directives, and to generate stub pages for each. It is disabled by default.
|
||||||
|
|
||||||
Can also be a list of documents for which stub pages should be generated.
|
Can also be a list of documents for which stub pages should be generated.
|
||||||
|
|
||||||
@ -263,6 +288,12 @@ The following variables available in the templates:
|
|||||||
List containing names of "public" attributes in the class/module. Only
|
List containing names of "public" attributes in the class/module. Only
|
||||||
available for classes and modules.
|
available for classes and modules.
|
||||||
|
|
||||||
|
.. data:: modules
|
||||||
|
|
||||||
|
List containing names of "public" modules in the package. Only available for
|
||||||
|
modules that are packages.
|
||||||
|
|
||||||
|
.. versionadded:: 3.1
|
||||||
|
|
||||||
Additionally, the following filters are available
|
Additionally, the following filters are available
|
||||||
|
|
||||||
|
@ -133,7 +133,7 @@ a comma-separated list of group names.
|
|||||||
.. testcode::
|
.. testcode::
|
||||||
|
|
||||||
1+1 # this will give no output!
|
1+1 # this will give no output!
|
||||||
print 2+2 # this will give output
|
print(2+2) # this will give output
|
||||||
|
|
||||||
.. testoutput::
|
.. testoutput::
|
||||||
|
|
||||||
@ -161,7 +161,7 @@ a comma-separated list of group names.
|
|||||||
|
|
||||||
.. testcode::
|
.. testcode::
|
||||||
|
|
||||||
print 'Output text.'
|
print('Output text.')
|
||||||
|
|
||||||
.. testoutput::
|
.. testoutput::
|
||||||
:hide:
|
:hide:
|
||||||
@ -328,7 +328,7 @@ The doctest extension uses the following configuration values:
|
|||||||
|
|
||||||
Some documentation text.
|
Some documentation text.
|
||||||
|
|
||||||
>>> print 1
|
>>> print(1)
|
||||||
1
|
1
|
||||||
|
|
||||||
Some more documentation text.
|
Some more documentation text.
|
||||||
@ -344,7 +344,7 @@ The doctest extension uses the following configuration values:
|
|||||||
|
|
||||||
.. doctest::
|
.. doctest::
|
||||||
|
|
||||||
>>> print 1
|
>>> print(1)
|
||||||
1
|
1
|
||||||
|
|
||||||
Some more documentation text.
|
Some more documentation text.
|
||||||
|
@ -34,7 +34,19 @@ Configuration
|
|||||||
A path to :command:`convert` command. By default, the imgconverter uses
|
A path to :command:`convert` command. By default, the imgconverter uses
|
||||||
the command from search paths.
|
the command from search paths.
|
||||||
|
|
||||||
|
On windows platform, :command:`magick` command is used by default.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.1
|
||||||
|
|
||||||
|
Use :command:`magick` command by default on windows
|
||||||
|
|
||||||
.. confval:: image_converter_args
|
.. confval:: image_converter_args
|
||||||
|
|
||||||
Additional command-line arguments to give to :command:`convert`, as a list.
|
Additional command-line arguments to give to :command:`convert`, as a list.
|
||||||
The default is an empty list ``[]``.
|
The default is an empty list ``[]``.
|
||||||
|
|
||||||
|
On windows platform, it defaults to ``["convert"]``.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.1
|
||||||
|
|
||||||
|
Use ``["convert"]`` by default on windows
|
||||||
|
@ -212,6 +212,15 @@ The following directives are provided for module and class contents:
|
|||||||
Describes an exception class. The signature can, but need not include
|
Describes an exception class. The signature can, but need not include
|
||||||
parentheses with constructor arguments.
|
parentheses with constructor arguments.
|
||||||
|
|
||||||
|
.. rubric:: options
|
||||||
|
|
||||||
|
.. rst:directive:option:: final
|
||||||
|
:type: no value
|
||||||
|
|
||||||
|
Indicate the class is a final class.
|
||||||
|
|
||||||
|
.. versionadded:: 3.1
|
||||||
|
|
||||||
.. rst:directive:: .. py:class:: name
|
.. rst:directive:: .. py:class:: name
|
||||||
.. py:class:: name(parameters)
|
.. py:class:: name(parameters)
|
||||||
|
|
||||||
@ -235,6 +244,15 @@ The following directives are provided for module and class contents:
|
|||||||
|
|
||||||
The first way is the preferred one.
|
The first way is the preferred one.
|
||||||
|
|
||||||
|
.. rubric:: options
|
||||||
|
|
||||||
|
.. rst:directive:option:: final
|
||||||
|
:type: no value
|
||||||
|
|
||||||
|
Indicate the class is a final class.
|
||||||
|
|
||||||
|
.. versionadded:: 3.1
|
||||||
|
|
||||||
.. rst:directive:: .. py:attribute:: name
|
.. rst:directive:: .. py:attribute:: name
|
||||||
|
|
||||||
Describes an object data attribute. The description should include
|
Describes an object data attribute. The description should include
|
||||||
@ -283,6 +301,13 @@ The following directives are provided for module and class contents:
|
|||||||
|
|
||||||
.. versionadded:: 2.1
|
.. versionadded:: 2.1
|
||||||
|
|
||||||
|
.. rst:directive:option:: final
|
||||||
|
:type: no value
|
||||||
|
|
||||||
|
Indicate the class is a final method.
|
||||||
|
|
||||||
|
.. versionadded:: 3.1
|
||||||
|
|
||||||
.. rst:directive:option:: property
|
.. rst:directive:option:: property
|
||||||
:type: no value
|
:type: no value
|
||||||
|
|
||||||
@ -706,6 +731,77 @@ Inline Expressions and Types
|
|||||||
.. versionadded:: 3.0
|
.. versionadded:: 3.0
|
||||||
|
|
||||||
|
|
||||||
|
Namespacing
|
||||||
|
~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. versionadded:: 3.1
|
||||||
|
|
||||||
|
The C language it self does not support namespacing, but it can sometimes be
|
||||||
|
useful to emulate it in documentation, e.g., to show alternate declarations.
|
||||||
|
The feature may also be used to document members of structs/unions/enums
|
||||||
|
separate from their parent declaration.
|
||||||
|
|
||||||
|
The current scope can be changed using three namespace directives. They manage
|
||||||
|
a stack declarations where ``c:namespace`` resets the stack and changes a given
|
||||||
|
scope.
|
||||||
|
|
||||||
|
The ``c:namespace-push`` directive changes the scope to a given inner scope
|
||||||
|
of the current one.
|
||||||
|
|
||||||
|
The ``c:namespace-pop`` directive undoes the most recent
|
||||||
|
``c:namespace-push`` directive.
|
||||||
|
|
||||||
|
.. rst:directive:: .. c:namespace:: scope specification
|
||||||
|
|
||||||
|
Changes the current scope for the subsequent objects to the given scope, and
|
||||||
|
resets the namespace directive stack. Note that nested scopes can be
|
||||||
|
specified by separating with a dot, e.g.::
|
||||||
|
|
||||||
|
.. c:namespace:: Namespace1.Namespace2.SomeStruct.AnInnerStruct
|
||||||
|
|
||||||
|
All subsequent objects will be defined as if their name were declared with
|
||||||
|
the scope prepended. The subsequent cross-references will be searched for
|
||||||
|
starting in the current scope.
|
||||||
|
|
||||||
|
Using ``NULL`` or ``0`` as the scope will change to global scope.
|
||||||
|
|
||||||
|
.. rst:directive:: .. c:namespace-push:: scope specification
|
||||||
|
|
||||||
|
Change the scope relatively to the current scope. For example, after::
|
||||||
|
|
||||||
|
.. c:namespace:: A.B
|
||||||
|
|
||||||
|
.. c:namespace-push:: C.D
|
||||||
|
|
||||||
|
the current scope will be ``A.B.C.D``.
|
||||||
|
|
||||||
|
.. rst:directive:: .. c:namespace-pop::
|
||||||
|
|
||||||
|
Undo the previous ``c:namespace-push`` directive (*not* just pop a scope).
|
||||||
|
For example, after::
|
||||||
|
|
||||||
|
.. c:namespace:: A.B
|
||||||
|
|
||||||
|
.. c:namespace-push:: C.D
|
||||||
|
|
||||||
|
.. c:namespace-pop::
|
||||||
|
|
||||||
|
the current scope will be ``A.B`` (*not* ``A.B.C``).
|
||||||
|
|
||||||
|
If no previous ``c:namespace-push`` directive has been used, but only a
|
||||||
|
``c:namespace`` directive, then the current scope will be reset to global
|
||||||
|
scope. That is, ``.. c:namespace:: A.B`` is equivalent to::
|
||||||
|
|
||||||
|
.. c:namespace:: NULL
|
||||||
|
|
||||||
|
.. c:namespace-push:: A.B
|
||||||
|
|
||||||
|
Configuration Variables
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
See :ref:`c-config`.
|
||||||
|
|
||||||
|
|
||||||
.. _cpp-domain:
|
.. _cpp-domain:
|
||||||
|
|
||||||
The C++ Domain
|
The C++ Domain
|
||||||
|
@ -155,6 +155,21 @@ These themes are:
|
|||||||
previous/next page using the keyboard's left and right arrows. Defaults to
|
previous/next page using the keyboard's left and right arrows. Defaults to
|
||||||
``False``.
|
``False``.
|
||||||
|
|
||||||
|
- **globaltoc_collapse** (true or false): Only expand subsections
|
||||||
|
of the current document in ``globaltoc.html``
|
||||||
|
(see :confval:`html_sidebars`).
|
||||||
|
Defaults to ``True``.
|
||||||
|
|
||||||
|
.. versionadded:: 3.1
|
||||||
|
|
||||||
|
- **globaltoc_includehidden** (true or false): Show even those
|
||||||
|
subsections in ``globaltoc.html`` (see :confval:`html_sidebars`)
|
||||||
|
which have been included with the ``:hidden:`` flag of the
|
||||||
|
:rst:dir:`toctree` directive.
|
||||||
|
Defaults to ``False``.
|
||||||
|
|
||||||
|
.. versionadded:: 3.1
|
||||||
|
|
||||||
**alabaster**
|
**alabaster**
|
||||||
`Alabaster theme`_ is a modified "Kr" Sphinx theme from @kennethreitz
|
`Alabaster theme`_ is a modified "Kr" Sphinx theme from @kennethreitz
|
||||||
(especially as used in his Requests project), which was itself originally
|
(especially as used in his Requests project), which was itself originally
|
||||||
|
@ -54,6 +54,8 @@ strict_optional = False
|
|||||||
filterwarnings =
|
filterwarnings =
|
||||||
all
|
all
|
||||||
ignore::DeprecationWarning:docutils.io
|
ignore::DeprecationWarning:docutils.io
|
||||||
|
ignore::DeprecationWarning:pyximport.pyximport
|
||||||
|
ignore::PendingDeprecationWarning:sphinx.util.pycompat
|
||||||
markers =
|
markers =
|
||||||
sphinx
|
sphinx
|
||||||
apidoc
|
apidoc
|
||||||
|
@ -56,6 +56,7 @@ if __version__.endswith('+'):
|
|||||||
__version__ = __version__[:-1] # remove '+' for PEP-440 version spec.
|
__version__ = __version__[:-1] # remove '+' for PEP-440 version spec.
|
||||||
try:
|
try:
|
||||||
ret = subprocess.run(['git', 'show', '-s', '--pretty=format:%h'],
|
ret = subprocess.run(['git', 'show', '-s', '--pretty=format:%h'],
|
||||||
|
cwd=package_dir,
|
||||||
stdout=PIPE, stderr=PIPE)
|
stdout=PIPE, stderr=PIPE)
|
||||||
if ret.stdout:
|
if ret.stdout:
|
||||||
__display_version__ += '/' + ret.stdout.decode('ascii').strip()
|
__display_version__ += '/' + ret.stdout.decode('ascii').strip()
|
||||||
|
@ -112,6 +112,13 @@ class desc_signature(nodes.Part, nodes.Inline, nodes.TextElement):
|
|||||||
In that case all child nodes must be ``desc_signature_line`` nodes.
|
In that case all child nodes must be ``desc_signature_line`` nodes.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def child_text_separator(self):
|
||||||
|
if self.get('is_multiline'):
|
||||||
|
return ' '
|
||||||
|
else:
|
||||||
|
return super().child_text_separator
|
||||||
|
|
||||||
|
|
||||||
class desc_signature_line(nodes.Part, nodes.Inline, nodes.FixedTextElement):
|
class desc_signature_line(nodes.Part, nodes.Inline, nodes.FixedTextElement):
|
||||||
"""Node for a line in a multi-line object signatures.
|
"""Node for a line in a multi-line object signatures.
|
||||||
@ -150,6 +157,9 @@ class desc_parameterlist(nodes.Part, nodes.Inline, nodes.FixedTextElement):
|
|||||||
"""Node for a general parameter list."""
|
"""Node for a general parameter list."""
|
||||||
child_text_separator = ', '
|
child_text_separator = ', '
|
||||||
|
|
||||||
|
def astext(self):
|
||||||
|
return '({})'.format(super().astext())
|
||||||
|
|
||||||
|
|
||||||
class desc_parameter(nodes.Part, nodes.Inline, nodes.FixedTextElement):
|
class desc_parameter(nodes.Part, nodes.Inline, nodes.FixedTextElement):
|
||||||
"""Node for a single parameter."""
|
"""Node for a single parameter."""
|
||||||
|
@ -436,22 +436,32 @@ class Sphinx:
|
|||||||
logger.debug('[app] disconnecting event: [id=%s]', listener_id)
|
logger.debug('[app] disconnecting event: [id=%s]', listener_id)
|
||||||
self.events.disconnect(listener_id)
|
self.events.disconnect(listener_id)
|
||||||
|
|
||||||
def emit(self, event: str, *args: Any) -> List:
|
def emit(self, event: str, *args: Any,
|
||||||
|
allowed_exceptions: Tuple["Type[Exception]", ...] = ()) -> List:
|
||||||
"""Emit *event* and pass *arguments* to the callback functions.
|
"""Emit *event* and pass *arguments* to the callback functions.
|
||||||
|
|
||||||
Return the return values of all callbacks as a list. Do not emit core
|
Return the return values of all callbacks as a list. Do not emit core
|
||||||
Sphinx events in extensions!
|
Sphinx events in extensions!
|
||||||
"""
|
|
||||||
return self.events.emit(event, *args)
|
|
||||||
|
|
||||||
def emit_firstresult(self, event: str, *args: Any) -> Any:
|
.. versionchanged:: 3.1
|
||||||
|
|
||||||
|
Added *allowed_exceptions* to specify path-through exceptions
|
||||||
|
"""
|
||||||
|
return self.events.emit(event, *args, allowed_exceptions=allowed_exceptions)
|
||||||
|
|
||||||
|
def emit_firstresult(self, event: str, *args: Any,
|
||||||
|
allowed_exceptions: Tuple["Type[Exception]", ...] = ()) -> Any:
|
||||||
"""Emit *event* and pass *arguments* to the callback functions.
|
"""Emit *event* and pass *arguments* to the callback functions.
|
||||||
|
|
||||||
Return the result of the first callback that doesn't return ``None``.
|
Return the result of the first callback that doesn't return ``None``.
|
||||||
|
|
||||||
.. versionadded:: 0.5
|
.. versionadded:: 0.5
|
||||||
|
.. versionchanged:: 3.1
|
||||||
|
|
||||||
|
Added *allowed_exceptions* to specify path-through exceptions
|
||||||
"""
|
"""
|
||||||
return self.events.emit_firstresult(event, *args)
|
return self.events.emit_firstresult(event, *args,
|
||||||
|
allowed_exceptions=allowed_exceptions)
|
||||||
|
|
||||||
# registering addon parts
|
# registering addon parts
|
||||||
|
|
||||||
@ -990,7 +1000,7 @@ class Sphinx:
|
|||||||
if isinstance(lexer, Lexer):
|
if isinstance(lexer, Lexer):
|
||||||
warnings.warn('app.add_lexer() API changed; '
|
warnings.warn('app.add_lexer() API changed; '
|
||||||
'Please give lexer class instead instance',
|
'Please give lexer class instead instance',
|
||||||
RemovedInSphinx40Warning)
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
lexers[alias] = lexer
|
lexers[alias] = lexer
|
||||||
else:
|
else:
|
||||||
lexer_classes[alias] = lexer
|
lexer_classes[alias] = lexer
|
||||||
|
@ -173,7 +173,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
|
|||||||
"""Replace all characters not allowed in text an attribute values."""
|
"""Replace all characters not allowed in text an attribute values."""
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
'%s.esc() is deprecated. Use html.escape() instead.' % self.__class__.__name__,
|
'%s.esc() is deprecated. Use html.escape() instead.' % self.__class__.__name__,
|
||||||
RemovedInSphinx40Warning)
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
name = name.replace('&', '&')
|
name = name.replace('&', '&')
|
||||||
name = name.replace('<', '<')
|
name = name.replace('<', '<')
|
||||||
name = name.replace('>', '>')
|
name = name.replace('>', '>')
|
||||||
|
@ -32,7 +32,7 @@ deprecated_alias('sphinx.builders.applehelp',
|
|||||||
|
|
||||||
def setup(app: Sphinx) -> Dict[str, Any]:
|
def setup(app: Sphinx) -> Dict[str, Any]:
|
||||||
warnings.warn('sphinx.builders.applehelp has been moved to sphinxcontrib-applehelp.',
|
warnings.warn('sphinx.builders.applehelp has been moved to sphinxcontrib-applehelp.',
|
||||||
RemovedInSphinx40Warning)
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
app.setup_extension('sphinxcontrib.applehelp')
|
app.setup_extension('sphinxcontrib.applehelp')
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -13,7 +13,6 @@ import posixpath
|
|||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import warnings
|
import warnings
|
||||||
from hashlib import md5
|
|
||||||
from os import path
|
from os import path
|
||||||
from typing import Any, Dict, IO, Iterable, Iterator, List, Set, Tuple
|
from typing import Any, Dict, IO, Iterable, Iterator, List, Set, Tuple
|
||||||
|
|
||||||
@ -38,7 +37,7 @@ from sphinx.highlighting import PygmentsBridge
|
|||||||
from sphinx.locale import _, __
|
from sphinx.locale import _, __
|
||||||
from sphinx.search import js_index
|
from sphinx.search import js_index
|
||||||
from sphinx.theming import HTMLThemeFactory
|
from sphinx.theming import HTMLThemeFactory
|
||||||
from sphinx.util import logging, progress_message, status_iterator
|
from sphinx.util import logging, progress_message, status_iterator, md5
|
||||||
from sphinx.util.docutils import is_html5_writer_available, new_document
|
from sphinx.util.docutils import is_html5_writer_available, new_document
|
||||||
from sphinx.util.fileutil import copy_asset
|
from sphinx.util.fileutil import copy_asset
|
||||||
from sphinx.util.i18n import format_date
|
from sphinx.util.i18n import format_date
|
||||||
@ -882,7 +881,7 @@ class StandaloneHTMLBuilder(Builder):
|
|||||||
'The %s.feed() method signature is deprecated. Update to '
|
'The %s.feed() method signature is deprecated. Update to '
|
||||||
'%s.feed(docname, filename, title, doctree).' % (
|
'%s.feed(docname, filename, title, doctree).' % (
|
||||||
indexer_name, indexer_name),
|
indexer_name, indexer_name),
|
||||||
RemovedInSphinx40Warning)
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
|
|
||||||
def _get_local_toctree(self, docname: str, collapse: bool = True, **kwargs: Any) -> str:
|
def _get_local_toctree(self, docname: str, collapse: bool = True, **kwargs: Any) -> str:
|
||||||
if 'includehidden' not in kwargs:
|
if 'includehidden' not in kwargs:
|
||||||
@ -1243,6 +1242,9 @@ def setup(app: Sphinx) -> Dict[str, Any]:
|
|||||||
# load default math renderer
|
# load default math renderer
|
||||||
app.setup_extension('sphinx.ext.mathjax')
|
app.setup_extension('sphinx.ext.mathjax')
|
||||||
|
|
||||||
|
# load transforms for HTML builder
|
||||||
|
app.setup_extension('sphinx.builders.html.transforms')
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'version': 'builtin',
|
'version': 'builtin',
|
||||||
'parallel_read_safe': True,
|
'parallel_read_safe': True,
|
||||||
|
69
sphinx/builders/html/transforms.py
Normal file
69
sphinx/builders/html/transforms.py
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
"""
|
||||||
|
sphinx.builders.html.transforms
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Transforms for HTML builder.
|
||||||
|
|
||||||
|
:copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
|
||||||
|
:license: BSD, see LICENSE for details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import re
|
||||||
|
from typing import Any, Dict
|
||||||
|
|
||||||
|
from docutils import nodes
|
||||||
|
|
||||||
|
from sphinx.application import Sphinx
|
||||||
|
from sphinx.transforms.post_transforms import SphinxPostTransform
|
||||||
|
from sphinx.util.nodes import NodeMatcher
|
||||||
|
|
||||||
|
|
||||||
|
class KeyboardTransform(SphinxPostTransform):
|
||||||
|
"""Transform :kbd: role to more detailed form.
|
||||||
|
|
||||||
|
Before::
|
||||||
|
|
||||||
|
<literal class="kbd">
|
||||||
|
Control-x
|
||||||
|
|
||||||
|
After::
|
||||||
|
|
||||||
|
<literal class="kbd">
|
||||||
|
<literal class="kbd">
|
||||||
|
Control
|
||||||
|
-
|
||||||
|
<literal class="kbd">
|
||||||
|
x
|
||||||
|
"""
|
||||||
|
default_priority = 400
|
||||||
|
builders = ('html',)
|
||||||
|
pattern = re.compile(r'(-|\+|\^|\s+)')
|
||||||
|
|
||||||
|
def run(self, **kwargs: Any) -> None:
|
||||||
|
matcher = NodeMatcher(nodes.literal, classes=["kbd"])
|
||||||
|
for node in self.document.traverse(matcher): # type: nodes.literal
|
||||||
|
parts = self.pattern.split(node[-1].astext())
|
||||||
|
if len(parts) == 1:
|
||||||
|
continue
|
||||||
|
|
||||||
|
node.pop()
|
||||||
|
while parts:
|
||||||
|
key = parts.pop(0)
|
||||||
|
node += nodes.literal('', key, classes=["kbd"])
|
||||||
|
|
||||||
|
try:
|
||||||
|
# key separator (ex. -, +, ^)
|
||||||
|
sep = parts.pop(0)
|
||||||
|
node += nodes.Text(sep)
|
||||||
|
except IndexError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def setup(app: Sphinx) -> Dict[str, Any]:
|
||||||
|
app.add_post_transform(KeyboardTransform)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'version': 'builtin',
|
||||||
|
'parallel_read_safe': True,
|
||||||
|
'parallel_write_safe': True,
|
||||||
|
}
|
@ -32,7 +32,7 @@ deprecated_alias('sphinx.builders.htmlhelp',
|
|||||||
|
|
||||||
def setup(app: Sphinx) -> Dict[str, Any]:
|
def setup(app: Sphinx) -> Dict[str, Any]:
|
||||||
warnings.warn('sphinx.builders.htmlhelp has been moved to sphinxcontrib-htmlhelp.',
|
warnings.warn('sphinx.builders.htmlhelp has been moved to sphinxcontrib-htmlhelp.',
|
||||||
RemovedInSphinx40Warning)
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
app.setup_extension('sphinxcontrib.htmlhelp')
|
app.setup_extension('sphinxcontrib.htmlhelp')
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -54,13 +54,13 @@ XINDY_LANG_OPTIONS = {
|
|||||||
'hr': '-L croatian -C utf8 ',
|
'hr': '-L croatian -C utf8 ',
|
||||||
'cs': '-L czech -C utf8 ',
|
'cs': '-L czech -C utf8 ',
|
||||||
'da': '-L danish -C utf8 ',
|
'da': '-L danish -C utf8 ',
|
||||||
'nl': '-L dutch -C ij-as-ij-utf8 ',
|
'nl': '-L dutch-ij-as-ij -C utf8 ',
|
||||||
'en': '-L english -C utf8 ',
|
'en': '-L english -C utf8 ',
|
||||||
'eo': '-L esperanto -C utf8 ',
|
'eo': '-L esperanto -C utf8 ',
|
||||||
'et': '-L estonian -C utf8 ',
|
'et': '-L estonian -C utf8 ',
|
||||||
'fi': '-L finnish -C utf8 ',
|
'fi': '-L finnish -C utf8 ',
|
||||||
'fr': '-L french -C utf8 ',
|
'fr': '-L french -C utf8 ',
|
||||||
'de': '-L german -C din5007-utf8 ',
|
'de': '-L german-din5007 -C utf8 ',
|
||||||
'is': '-L icelandic -C utf8 ',
|
'is': '-L icelandic -C utf8 ',
|
||||||
'it': '-L italian -C utf8 ',
|
'it': '-L italian -C utf8 ',
|
||||||
'la': '-L latin -C utf8 ',
|
'la': '-L latin -C utf8 ',
|
||||||
@ -73,9 +73,9 @@ XINDY_LANG_OPTIONS = {
|
|||||||
'pl': '-L polish -C utf8 ',
|
'pl': '-L polish -C utf8 ',
|
||||||
'pt': '-L portuguese -C utf8 ',
|
'pt': '-L portuguese -C utf8 ',
|
||||||
'ro': '-L romanian -C utf8 ',
|
'ro': '-L romanian -C utf8 ',
|
||||||
'sk': '-L slovak -C small-utf8 ', # there is also slovak-large
|
'sk': '-L slovak-small -C utf8 ', # there is also slovak-large
|
||||||
'sl': '-L slovenian -C utf8 ',
|
'sl': '-L slovenian -C utf8 ',
|
||||||
'es': '-L spanish -C modern-utf8 ', # there is also spanish-traditional
|
'es': '-L spanish-modern -C utf8 ', # there is also spanish-traditional
|
||||||
'sv': '-L swedish -C utf8 ',
|
'sv': '-L swedish -C utf8 ',
|
||||||
'tr': '-L turkish -C utf8 ',
|
'tr': '-L turkish -C utf8 ',
|
||||||
'hsb': '-L upper-sorbian -C utf8 ',
|
'hsb': '-L upper-sorbian -C utf8 ',
|
||||||
@ -86,7 +86,7 @@ XINDY_LANG_OPTIONS = {
|
|||||||
'be': '-L belarusian -C utf8 ',
|
'be': '-L belarusian -C utf8 ',
|
||||||
'bg': '-L bulgarian -C utf8 ',
|
'bg': '-L bulgarian -C utf8 ',
|
||||||
'mk': '-L macedonian -C utf8 ',
|
'mk': '-L macedonian -C utf8 ',
|
||||||
'mn': '-L mongolian -C cyrillic-utf8 ',
|
'mn': '-L mongolian-cyrillic -C utf8 ',
|
||||||
'ru': '-L russian -C utf8 ',
|
'ru': '-L russian -C utf8 ',
|
||||||
'sr': '-L serbian -C utf8 ',
|
'sr': '-L serbian -C utf8 ',
|
||||||
'sh-cyrl': '-L serbian -C utf8 ',
|
'sh-cyrl': '-L serbian -C utf8 ',
|
||||||
@ -96,7 +96,7 @@ XINDY_LANG_OPTIONS = {
|
|||||||
# can work only with xelatex/lualatex, not supported by texindy+pdflatex
|
# can work only with xelatex/lualatex, not supported by texindy+pdflatex
|
||||||
'el': '-L greek -C utf8 ',
|
'el': '-L greek -C utf8 ',
|
||||||
# FIXME, not compatible with [:2] slice but does Sphinx support Greek ?
|
# FIXME, not compatible with [:2] slice but does Sphinx support Greek ?
|
||||||
'el-polyton': '-L greek -C polytonic-utf8 ',
|
'el-polyton': '-L greek-polytonic -C utf8 ',
|
||||||
}
|
}
|
||||||
|
|
||||||
XINDY_CYRILLIC_SCRIPTS = [
|
XINDY_CYRILLIC_SCRIPTS = [
|
||||||
@ -314,6 +314,8 @@ class LaTeXBuilder(Builder):
|
|||||||
self.context['title'] = title
|
self.context['title'] = title
|
||||||
self.context['author'] = author
|
self.context['author'] = author
|
||||||
self.context['docclass'] = theme.docclass
|
self.context['docclass'] = theme.docclass
|
||||||
|
self.context['papersize'] = theme.papersize
|
||||||
|
self.context['pointsize'] = theme.pointsize
|
||||||
self.context['wrapperclass'] = theme.wrapperclass
|
self.context['wrapperclass'] = theme.wrapperclass
|
||||||
|
|
||||||
def assemble_doctree(self, indexfile: str, toctree_only: bool, appendices: List[str]) -> nodes.document: # NOQA
|
def assemble_doctree(self, indexfile: str, toctree_only: bool, appendices: List[str]) -> nodes.document: # NOQA
|
||||||
@ -361,7 +363,7 @@ class LaTeXBuilder(Builder):
|
|||||||
|
|
||||||
def apply_transforms(self, doctree: nodes.document) -> None:
|
def apply_transforms(self, doctree: nodes.document) -> None:
|
||||||
warnings.warn('LaTeXBuilder.apply_transforms() is deprecated.',
|
warnings.warn('LaTeXBuilder.apply_transforms() is deprecated.',
|
||||||
RemovedInSphinx40Warning)
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
|
|
||||||
def finish(self) -> None:
|
def finish(self) -> None:
|
||||||
self.copy_image_files()
|
self.copy_image_files()
|
||||||
@ -491,6 +493,14 @@ def validate_config_values(app: Sphinx, config: Config) -> None:
|
|||||||
config.latex_elements.pop(key)
|
config.latex_elements.pop(key)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_latex_theme_options(app: Sphinx, config: Config) -> None:
|
||||||
|
for key in list(config.latex_theme_options):
|
||||||
|
if key not in Theme.UPDATABLE_KEYS:
|
||||||
|
msg = __("Unknown theme option: latex_theme_options[%r], ignored.")
|
||||||
|
logger.warning(msg % (key,))
|
||||||
|
config.latex_theme_options.pop(key)
|
||||||
|
|
||||||
|
|
||||||
def default_latex_engine(config: Config) -> str:
|
def default_latex_engine(config: Config) -> str:
|
||||||
""" Better default latex_engine settings for specific languages. """
|
""" Better default latex_engine settings for specific languages. """
|
||||||
if config.language == 'ja':
|
if config.language == 'ja':
|
||||||
@ -537,6 +547,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
|
|||||||
|
|
||||||
app.add_builder(LaTeXBuilder)
|
app.add_builder(LaTeXBuilder)
|
||||||
app.connect('config-inited', validate_config_values, priority=800)
|
app.connect('config-inited', validate_config_values, priority=800)
|
||||||
|
app.connect('config-inited', validate_latex_theme_options, priority=800)
|
||||||
|
|
||||||
app.add_config_value('latex_engine', default_latex_engine, None,
|
app.add_config_value('latex_engine', default_latex_engine, None,
|
||||||
ENUM('pdflatex', 'xelatex', 'lualatex', 'platex', 'uplatex'))
|
ENUM('pdflatex', 'xelatex', 'lualatex', 'platex', 'uplatex'))
|
||||||
@ -553,6 +564,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
|
|||||||
app.add_config_value('latex_elements', {}, None)
|
app.add_config_value('latex_elements', {}, None)
|
||||||
app.add_config_value('latex_additional_files', [], None)
|
app.add_config_value('latex_additional_files', [], None)
|
||||||
app.add_config_value('latex_theme', 'manual', None, [str])
|
app.add_config_value('latex_theme', 'manual', None, [str])
|
||||||
|
app.add_config_value('latex_theme_options', {}, None)
|
||||||
app.add_config_value('latex_theme_path', [], None, [list])
|
app.add_config_value('latex_theme_path', [], None, [list])
|
||||||
|
|
||||||
app.add_config_value('latex_docclass', default_latex_docclass, None)
|
app.add_config_value('latex_docclass', default_latex_docclass, None)
|
||||||
|
@ -69,8 +69,8 @@ LUALATEX_DEFAULT_FONTPKG = XELATEX_DEFAULT_FONTPKG
|
|||||||
|
|
||||||
DEFAULT_SETTINGS = {
|
DEFAULT_SETTINGS = {
|
||||||
'latex_engine': 'pdflatex',
|
'latex_engine': 'pdflatex',
|
||||||
'papersize': 'letterpaper',
|
'papersize': '',
|
||||||
'pointsize': '10pt',
|
'pointsize': '',
|
||||||
'pxunit': '.75bp',
|
'pxunit': '.75bp',
|
||||||
'classoptions': '',
|
'classoptions': '',
|
||||||
'extraclassoptions': '',
|
'extraclassoptions': '',
|
||||||
|
@ -24,50 +24,59 @@ logger = logging.getLogger(__name__)
|
|||||||
class Theme:
|
class Theme:
|
||||||
"""A set of LaTeX configurations."""
|
"""A set of LaTeX configurations."""
|
||||||
|
|
||||||
|
LATEX_ELEMENTS_KEYS = ['papersize', 'pointsize']
|
||||||
|
UPDATABLE_KEYS = ['papersize', 'pointsize']
|
||||||
|
|
||||||
def __init__(self, name: str) -> None:
|
def __init__(self, name: str) -> None:
|
||||||
self.name = name
|
self.name = name
|
||||||
self.docclass = name
|
self.docclass = name
|
||||||
self.wrapperclass = name
|
self.wrapperclass = name
|
||||||
|
self.papersize = 'letterpaper'
|
||||||
|
self.pointsize = '10pt'
|
||||||
self.toplevel_sectioning = 'chapter'
|
self.toplevel_sectioning = 'chapter'
|
||||||
|
|
||||||
|
def update(self, config: Config) -> None:
|
||||||
|
"""Override theme settings by user's configuration."""
|
||||||
|
for key in self.LATEX_ELEMENTS_KEYS:
|
||||||
|
if config.latex_elements.get(key):
|
||||||
|
value = config.latex_elements[key]
|
||||||
|
setattr(self, key, value)
|
||||||
|
|
||||||
|
for key in self.UPDATABLE_KEYS:
|
||||||
|
if key in config.latex_theme_options:
|
||||||
|
value = config.latex_theme_options[key]
|
||||||
|
setattr(self, key, value)
|
||||||
|
|
||||||
|
|
||||||
class BuiltInTheme(Theme):
|
class BuiltInTheme(Theme):
|
||||||
"""A built-in LaTeX theme."""
|
"""A built-in LaTeX theme."""
|
||||||
|
|
||||||
def __init__(self, name: str, config: Config) -> None:
|
def __init__(self, name: str, config: Config) -> None:
|
||||||
# Note: Don't call supermethod here.
|
super().__init__(name)
|
||||||
self.name = name
|
|
||||||
self.latex_docclass = config.latex_docclass # type: Dict[str, str]
|
|
||||||
|
|
||||||
@property
|
if name == 'howto':
|
||||||
def docclass(self) -> str: # type: ignore
|
self.docclass = config.latex_docclass.get('howto', 'article')
|
||||||
if self.name == 'howto':
|
|
||||||
return self.latex_docclass.get('howto', 'article')
|
|
||||||
else:
|
else:
|
||||||
return self.latex_docclass.get('manual', 'report')
|
self.docclass = config.latex_docclass.get('manual', 'report')
|
||||||
|
|
||||||
@property
|
if name in ('manual', 'howto'):
|
||||||
def wrapperclass(self) -> str: # type: ignore
|
self.wrapperclass = 'sphinx' + name
|
||||||
if self.name in ('manual', 'howto'):
|
|
||||||
return 'sphinx' + self.name
|
|
||||||
else:
|
else:
|
||||||
return self.name
|
self.wrapperclass = name
|
||||||
|
|
||||||
@property
|
|
||||||
def toplevel_sectioning(self) -> str: # type: ignore
|
|
||||||
# we assume LaTeX class provides \chapter command except in case
|
# we assume LaTeX class provides \chapter command except in case
|
||||||
# of non-Japanese 'howto' case
|
# of non-Japanese 'howto' case
|
||||||
if self.name == 'howto' and not self.docclass.startswith('j'):
|
if name == 'howto' and not self.docclass.startswith('j'):
|
||||||
return 'section'
|
self.toplevel_sectioning = 'section'
|
||||||
else:
|
else:
|
||||||
return 'chapter'
|
self.toplevel_sectioning = 'chapter'
|
||||||
|
|
||||||
|
|
||||||
class UserTheme(Theme):
|
class UserTheme(Theme):
|
||||||
"""A user defined LaTeX theme."""
|
"""A user defined LaTeX theme."""
|
||||||
|
|
||||||
REQUIRED_CONFIG_KEYS = ['docclass', 'wrapperclass']
|
REQUIRED_CONFIG_KEYS = ['docclass', 'wrapperclass']
|
||||||
OPTIONAL_CONFIG_KEYS = ['toplevel_sectioning']
|
OPTIONAL_CONFIG_KEYS = ['papersize', 'pointsize', 'toplevel_sectioning']
|
||||||
|
|
||||||
def __init__(self, name: str, filename: str) -> None:
|
def __init__(self, name: str, filename: str) -> None:
|
||||||
super().__init__(name)
|
super().__init__(name)
|
||||||
@ -97,6 +106,7 @@ class ThemeFactory:
|
|||||||
def __init__(self, app: Sphinx) -> None:
|
def __init__(self, app: Sphinx) -> None:
|
||||||
self.themes = {} # type: Dict[str, Theme]
|
self.themes = {} # type: Dict[str, Theme]
|
||||||
self.theme_paths = [path.join(app.srcdir, p) for p in app.config.latex_theme_path]
|
self.theme_paths = [path.join(app.srcdir, p) for p in app.config.latex_theme_path]
|
||||||
|
self.config = app.config
|
||||||
self.load_builtin_themes(app.config)
|
self.load_builtin_themes(app.config)
|
||||||
|
|
||||||
def load_builtin_themes(self, config: Config) -> None:
|
def load_builtin_themes(self, config: Config) -> None:
|
||||||
@ -107,13 +117,14 @@ class ThemeFactory:
|
|||||||
def get(self, name: str) -> Theme:
|
def get(self, name: str) -> Theme:
|
||||||
"""Get a theme for given *name*."""
|
"""Get a theme for given *name*."""
|
||||||
if name in self.themes:
|
if name in self.themes:
|
||||||
return self.themes[name]
|
theme = self.themes[name]
|
||||||
else:
|
else:
|
||||||
theme = self.find_user_theme(name)
|
theme = self.find_user_theme(name)
|
||||||
if theme:
|
if not theme:
|
||||||
|
theme = Theme(name)
|
||||||
|
|
||||||
|
theme.update(self.config)
|
||||||
return theme
|
return theme
|
||||||
else:
|
|
||||||
return Theme(name)
|
|
||||||
|
|
||||||
def find_user_theme(self, name: str) -> Theme:
|
def find_user_theme(self, name: str) -> Theme:
|
||||||
"""Find a theme named as *name* from latex_theme_path."""
|
"""Find a theme named as *name* from latex_theme_path."""
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
from docutils.writers.latex2e import Babel
|
from docutils.writers.latex2e import Babel
|
||||||
|
|
||||||
|
|
||||||
@ -40,7 +42,7 @@ class ExtBabel(Babel):
|
|||||||
self.supported = False
|
self.supported = False
|
||||||
return 'english' # fallback to english
|
return 'english' # fallback to english
|
||||||
|
|
||||||
def get_mainlanguage_options(self) -> str:
|
def get_mainlanguage_options(self) -> Optional[str]:
|
||||||
"""Return options for polyglossia's ``\\setmainlanguage``."""
|
"""Return options for polyglossia's ``\\setmainlanguage``."""
|
||||||
if self.use_polyglossia is False:
|
if self.use_polyglossia is False:
|
||||||
return None
|
return None
|
||||||
|
@ -187,7 +187,7 @@ def do_prompt(text: str, default: str = None, validator: Callable[[str], Any] =
|
|||||||
def convert_python_source(source: str, rex: Pattern = re.compile(r"[uU]('.*?')")) -> str:
|
def convert_python_source(source: str, rex: Pattern = re.compile(r"[uU]('.*?')")) -> str:
|
||||||
# remove Unicode literal prefixes
|
# remove Unicode literal prefixes
|
||||||
warnings.warn('convert_python_source() is deprecated.',
|
warnings.warn('convert_python_source() is deprecated.',
|
||||||
RemovedInSphinx40Warning)
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
return rex.sub('\\1', source)
|
return rex.sub('\\1', source)
|
||||||
|
|
||||||
|
|
||||||
@ -357,6 +357,7 @@ def generate(d: Dict, overwrite: bool = True, silent: bool = False, templatedir:
|
|||||||
d.setdefault('extensions', [])
|
d.setdefault('extensions', [])
|
||||||
d['copyright'] = time.strftime('%Y') + ', ' + d['author']
|
d['copyright'] = time.strftime('%Y') + ', ' + d['author']
|
||||||
|
|
||||||
|
d["path"] = os.path.abspath(d['path'])
|
||||||
ensuredir(d['path'])
|
ensuredir(d['path'])
|
||||||
|
|
||||||
srcdir = path.join(d['path'], 'source') if d['sep'] else d['path']
|
srcdir = path.join(d['path'], 'source') if d['sep'] else d['path']
|
||||||
|
@ -444,7 +444,7 @@ def check_unicode(config: Config) -> None:
|
|||||||
since that can result in UnicodeErrors all over the place
|
since that can result in UnicodeErrors all over the place
|
||||||
"""
|
"""
|
||||||
warnings.warn('sphinx.config.check_unicode() is deprecated.',
|
warnings.warn('sphinx.config.check_unicode() is deprecated.',
|
||||||
RemovedInSphinx40Warning)
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
|
|
||||||
nonascii_re = re.compile(br'[\x80-\xff]')
|
nonascii_re = re.compile(br'[\x80-\xff]')
|
||||||
|
|
||||||
|
@ -12,7 +12,10 @@ import sys
|
|||||||
import warnings
|
import warnings
|
||||||
from importlib import import_module
|
from importlib import import_module
|
||||||
from typing import Any, Dict
|
from typing import Any, Dict
|
||||||
from typing import Type # for python3.5.1
|
|
||||||
|
if False:
|
||||||
|
# For type annotation
|
||||||
|
from typing import Type # for python3.5.1
|
||||||
|
|
||||||
|
|
||||||
class RemovedInSphinx40Warning(DeprecationWarning):
|
class RemovedInSphinx40Warning(DeprecationWarning):
|
||||||
@ -26,13 +29,13 @@ class RemovedInSphinx50Warning(PendingDeprecationWarning):
|
|||||||
RemovedInNextVersionWarning = RemovedInSphinx40Warning
|
RemovedInNextVersionWarning = RemovedInSphinx40Warning
|
||||||
|
|
||||||
|
|
||||||
def deprecated_alias(modname: str, objects: Dict, warning: Type[Warning]) -> None:
|
def deprecated_alias(modname: str, objects: Dict, warning: "Type[Warning]") -> None:
|
||||||
module = import_module(modname)
|
module = import_module(modname)
|
||||||
sys.modules[modname] = _ModuleWrapper(module, modname, objects, warning) # type: ignore
|
sys.modules[modname] = _ModuleWrapper(module, modname, objects, warning) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
class _ModuleWrapper:
|
class _ModuleWrapper:
|
||||||
def __init__(self, module: Any, modname: str, objects: Dict, warning: Type[Warning]
|
def __init__(self, module: Any, modname: str, objects: Dict, warning: "Type[Warning]"
|
||||||
) -> None:
|
) -> None:
|
||||||
self._module = module
|
self._module = module
|
||||||
self._modname = modname
|
self._modname = modname
|
||||||
@ -52,7 +55,7 @@ class _ModuleWrapper:
|
|||||||
class DeprecatedDict(dict):
|
class DeprecatedDict(dict):
|
||||||
"""A deprecated dict which warns on each access."""
|
"""A deprecated dict which warns on each access."""
|
||||||
|
|
||||||
def __init__(self, data: Dict, message: str, warning: Type[Warning]) -> None:
|
def __init__(self, data: Dict, message: str, warning: "Type[Warning]") -> None:
|
||||||
self.message = message
|
self.message = message
|
||||||
self.warning = warning
|
self.warning = warning
|
||||||
super().__init__(data)
|
super().__init__(data)
|
||||||
@ -73,6 +76,6 @@ class DeprecatedDict(dict):
|
|||||||
warnings.warn(self.message, self.warning, stacklevel=2)
|
warnings.warn(self.message, self.warning, stacklevel=2)
|
||||||
return super().get(key, default)
|
return super().get(key, default)
|
||||||
|
|
||||||
def update(self, other: Dict = None) -> None: # type: ignore
|
def update(self, other: Dict) -> None: # type: ignore
|
||||||
warnings.warn(self.message, self.warning, stacklevel=2)
|
warnings.warn(self.message, self.warning, stacklevel=2)
|
||||||
super().update(other)
|
super().update(other)
|
||||||
|
@ -31,10 +31,12 @@ from sphinx.util.cfamily import (
|
|||||||
NoOldIdError, ASTBaseBase, verify_description_mode, StringifyTransform,
|
NoOldIdError, ASTBaseBase, verify_description_mode, StringifyTransform,
|
||||||
BaseParser, DefinitionError, UnsupportedMultiCharacterCharLiteral,
|
BaseParser, DefinitionError, UnsupportedMultiCharacterCharLiteral,
|
||||||
identifier_re, anon_identifier_re, integer_literal_re, octal_literal_re,
|
identifier_re, anon_identifier_re, integer_literal_re, octal_literal_re,
|
||||||
hex_literal_re, binary_literal_re, float_literal_re,
|
hex_literal_re, binary_literal_re, integers_literal_suffix_re,
|
||||||
|
float_literal_re, float_literal_suffix_re,
|
||||||
char_literal_re
|
char_literal_re
|
||||||
)
|
)
|
||||||
from sphinx.util.docfields import Field, TypedField
|
from sphinx.util.docfields import Field, TypedField
|
||||||
|
from sphinx.util.docutils import SphinxDirective
|
||||||
from sphinx.util.nodes import make_refnode
|
from sphinx.util.nodes import make_refnode
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -184,11 +186,17 @@ class ASTNestedName(ASTBase):
|
|||||||
# If lastIsName, then wrap all of the prefix in a desc_addname,
|
# If lastIsName, then wrap all of the prefix in a desc_addname,
|
||||||
# else append directly to signode.
|
# else append directly to signode.
|
||||||
# TODO: also for C?
|
# TODO: also for C?
|
||||||
# NOTE: Breathe relies on the prefix being in the desc_addname node,
|
# NOTE: Breathe previously relied on the prefix being in the desc_addname node,
|
||||||
# so it can remove it in inner declarations.
|
# so it can remove it in inner declarations.
|
||||||
dest = signode
|
dest = signode
|
||||||
if mode == 'lastIsName':
|
if mode == 'lastIsName':
|
||||||
dest = addnodes.desc_addname()
|
dest = addnodes.desc_addname()
|
||||||
|
if self.rooted:
|
||||||
|
prefix += '.'
|
||||||
|
if mode == 'lastIsName' and len(names) == 0:
|
||||||
|
signode += nodes.Text('.')
|
||||||
|
else:
|
||||||
|
dest += nodes.Text('.')
|
||||||
for i in range(len(names)):
|
for i in range(len(names)):
|
||||||
ident = names[i]
|
ident = names[i]
|
||||||
if not first:
|
if not first:
|
||||||
@ -791,20 +799,60 @@ class ASTDeclSpecs(ASTBase):
|
|||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
class ASTArray(ASTBase):
|
class ASTArray(ASTBase):
|
||||||
def __init__(self, size: ASTExpression):
|
def __init__(self, static: bool, const: bool, volatile: bool, restrict: bool,
|
||||||
|
vla: bool, size: ASTExpression):
|
||||||
|
self.static = static
|
||||||
|
self.const = const
|
||||||
|
self.volatile = volatile
|
||||||
|
self.restrict = restrict
|
||||||
|
self.vla = vla
|
||||||
self.size = size
|
self.size = size
|
||||||
|
if vla:
|
||||||
|
assert size is None
|
||||||
|
if size is not None:
|
||||||
|
assert not vla
|
||||||
|
|
||||||
def _stringify(self, transform: StringifyTransform) -> str:
|
def _stringify(self, transform: StringifyTransform) -> str:
|
||||||
if self.size:
|
el = []
|
||||||
return '[' + transform(self.size) + ']'
|
if self.static:
|
||||||
else:
|
el.append('static')
|
||||||
return '[]'
|
if self.restrict:
|
||||||
|
el.append('restrict')
|
||||||
|
if self.volatile:
|
||||||
|
el.append('volatile')
|
||||||
|
if self.const:
|
||||||
|
el.append('const')
|
||||||
|
if self.vla:
|
||||||
|
return '[' + ' '.join(el) + '*]'
|
||||||
|
elif self.size:
|
||||||
|
el.append(transform(self.size))
|
||||||
|
return '[' + ' '.join(el) + ']'
|
||||||
|
|
||||||
def describe_signature(self, signode: TextElement, mode: str,
|
def describe_signature(self, signode: TextElement, mode: str,
|
||||||
env: "BuildEnvironment", symbol: "Symbol") -> None:
|
env: "BuildEnvironment", symbol: "Symbol") -> None:
|
||||||
verify_description_mode(mode)
|
verify_description_mode(mode)
|
||||||
signode.append(nodes.Text("["))
|
signode.append(nodes.Text("["))
|
||||||
if self.size:
|
addSpace = False
|
||||||
|
|
||||||
|
def _add(signode: TextElement, text: str) -> bool:
|
||||||
|
if addSpace:
|
||||||
|
signode += nodes.Text(' ')
|
||||||
|
signode += addnodes.desc_annotation(text, text)
|
||||||
|
return True
|
||||||
|
|
||||||
|
if self.static:
|
||||||
|
addSpace = _add(signode, 'static')
|
||||||
|
if self.restrict:
|
||||||
|
addSpace = _add(signode, 'restrict')
|
||||||
|
if self.volatile:
|
||||||
|
addSpace = _add(signode, 'volatile')
|
||||||
|
if self.const:
|
||||||
|
addSpace = _add(signode, 'const')
|
||||||
|
if self.vla:
|
||||||
|
signode.append(nodes.Text('*'))
|
||||||
|
elif self.size:
|
||||||
|
if addSpace:
|
||||||
|
signode += nodes.Text(' ')
|
||||||
self.size.describe_signature(signode, mode, env, symbol)
|
self.size.describe_signature(signode, mode, env, symbol)
|
||||||
signode.append(nodes.Text("]"))
|
signode.append(nodes.Text("]"))
|
||||||
|
|
||||||
@ -1271,10 +1319,12 @@ class ASTEnumerator(ASTBase):
|
|||||||
|
|
||||||
|
|
||||||
class ASTDeclaration(ASTBaseBase):
|
class ASTDeclaration(ASTBaseBase):
|
||||||
def __init__(self, objectType: str, directiveType: str, declaration: Any) -> None:
|
def __init__(self, objectType: str, directiveType: str, declaration: Any,
|
||||||
|
semicolon: bool = False) -> None:
|
||||||
self.objectType = objectType
|
self.objectType = objectType
|
||||||
self.directiveType = directiveType
|
self.directiveType = directiveType
|
||||||
self.declaration = declaration
|
self.declaration = declaration
|
||||||
|
self.semicolon = semicolon
|
||||||
|
|
||||||
self.symbol = None # type: Symbol
|
self.symbol = None # type: Symbol
|
||||||
# set by CObject._add_enumerator_to_parent
|
# set by CObject._add_enumerator_to_parent
|
||||||
@ -1303,7 +1353,10 @@ class ASTDeclaration(ASTBaseBase):
|
|||||||
return self.get_id(_max_id, True)
|
return self.get_id(_max_id, True)
|
||||||
|
|
||||||
def _stringify(self, transform: StringifyTransform) -> str:
|
def _stringify(self, transform: StringifyTransform) -> str:
|
||||||
return transform(self.declaration)
|
res = transform(self.declaration)
|
||||||
|
if self.semicolon:
|
||||||
|
res += ';'
|
||||||
|
return res
|
||||||
|
|
||||||
def describe_signature(self, signode: TextElement, mode: str,
|
def describe_signature(self, signode: TextElement, mode: str,
|
||||||
env: "BuildEnvironment", options: Dict) -> None:
|
env: "BuildEnvironment", options: Dict) -> None:
|
||||||
@ -1339,6 +1392,8 @@ class ASTDeclaration(ASTBaseBase):
|
|||||||
else:
|
else:
|
||||||
assert False
|
assert False
|
||||||
self.declaration.describe_signature(mainDeclNode, mode, env, self.symbol)
|
self.declaration.describe_signature(mainDeclNode, mode, env, self.symbol)
|
||||||
|
if self.semicolon:
|
||||||
|
mainDeclNode += nodes.Text(';')
|
||||||
|
|
||||||
|
|
||||||
class SymbolLookupResult:
|
class SymbolLookupResult:
|
||||||
@ -1990,6 +2045,14 @@ class DefinitionParser(BaseParser):
|
|||||||
def language(self) -> str:
|
def language(self) -> str:
|
||||||
return 'C'
|
return 'C'
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id_attributes(self):
|
||||||
|
return self.config.c_id_attributes
|
||||||
|
|
||||||
|
@property
|
||||||
|
def paren_attributes(self):
|
||||||
|
return self.config.c_paren_attributes
|
||||||
|
|
||||||
def _parse_string(self) -> str:
|
def _parse_string(self) -> str:
|
||||||
if self.current_char != '"':
|
if self.current_char != '"':
|
||||||
return None
|
return None
|
||||||
@ -2009,66 +2072,6 @@ class DefinitionParser(BaseParser):
|
|||||||
self.pos += 1
|
self.pos += 1
|
||||||
return self.definition[startPos:self.pos]
|
return self.definition[startPos:self.pos]
|
||||||
|
|
||||||
def _parse_attribute(self) -> Any:
|
|
||||||
return None
|
|
||||||
# self.skip_ws()
|
|
||||||
# # try C++11 style
|
|
||||||
# startPos = self.pos
|
|
||||||
# if self.skip_string_and_ws('['):
|
|
||||||
# if not self.skip_string('['):
|
|
||||||
# self.pos = startPos
|
|
||||||
# else:
|
|
||||||
# # TODO: actually implement the correct grammar
|
|
||||||
# arg = self._parse_balanced_token_seq(end=[']'])
|
|
||||||
# if not self.skip_string_and_ws(']'):
|
|
||||||
# self.fail("Expected ']' in end of attribute.")
|
|
||||||
# if not self.skip_string_and_ws(']'):
|
|
||||||
# self.fail("Expected ']' in end of attribute after [[...]")
|
|
||||||
# return ASTCPPAttribute(arg)
|
|
||||||
#
|
|
||||||
# # try GNU style
|
|
||||||
# if self.skip_word_and_ws('__attribute__'):
|
|
||||||
# if not self.skip_string_and_ws('('):
|
|
||||||
# self.fail("Expected '(' after '__attribute__'.")
|
|
||||||
# if not self.skip_string_and_ws('('):
|
|
||||||
# self.fail("Expected '(' after '__attribute__('.")
|
|
||||||
# attrs = []
|
|
||||||
# while 1:
|
|
||||||
# if self.match(identifier_re):
|
|
||||||
# name = self.matched_text
|
|
||||||
# self.skip_ws()
|
|
||||||
# if self.skip_string_and_ws('('):
|
|
||||||
# self.fail('Parameterized GNU style attribute not yet supported.')
|
|
||||||
# attrs.append(ASTGnuAttribute(name, None))
|
|
||||||
# # TODO: parse arguments for the attribute
|
|
||||||
# if self.skip_string_and_ws(','):
|
|
||||||
# continue
|
|
||||||
# elif self.skip_string_and_ws(')'):
|
|
||||||
# break
|
|
||||||
# else:
|
|
||||||
# self.fail("Expected identifier, ')', or ',' in __attribute__.")
|
|
||||||
# if not self.skip_string_and_ws(')'):
|
|
||||||
# self.fail("Expected ')' after '__attribute__((...)'")
|
|
||||||
# return ASTGnuAttributeList(attrs)
|
|
||||||
#
|
|
||||||
# # try the simple id attributes defined by the user
|
|
||||||
# for id in self.config.cpp_id_attributes:
|
|
||||||
# if self.skip_word_and_ws(id):
|
|
||||||
# return ASTIdAttribute(id)
|
|
||||||
#
|
|
||||||
# # try the paren attributes defined by the user
|
|
||||||
# for id in self.config.cpp_paren_attributes:
|
|
||||||
# if not self.skip_string_and_ws(id):
|
|
||||||
# continue
|
|
||||||
# if not self.skip_string('('):
|
|
||||||
# self.fail("Expected '(' after user-defined paren-attribute.")
|
|
||||||
# arg = self._parse_balanced_token_seq(end=[')'])
|
|
||||||
# if not self.skip_string(')'):
|
|
||||||
# self.fail("Expected ')' to end user-defined paren-attribute.")
|
|
||||||
# return ASTParenAttribute(id, arg)
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _parse_literal(self) -> ASTLiteral:
|
def _parse_literal(self) -> ASTLiteral:
|
||||||
# -> integer-literal
|
# -> integer-literal
|
||||||
# | character-literal
|
# | character-literal
|
||||||
@ -2080,12 +2083,14 @@ class DefinitionParser(BaseParser):
|
|||||||
return ASTBooleanLiteral(True)
|
return ASTBooleanLiteral(True)
|
||||||
if self.skip_word('false'):
|
if self.skip_word('false'):
|
||||||
return ASTBooleanLiteral(False)
|
return ASTBooleanLiteral(False)
|
||||||
for regex in [float_literal_re, binary_literal_re, hex_literal_re,
|
|
||||||
integer_literal_re, octal_literal_re]:
|
|
||||||
pos = self.pos
|
pos = self.pos
|
||||||
|
if self.match(float_literal_re):
|
||||||
|
self.match(float_literal_suffix_re)
|
||||||
|
return ASTNumberLiteral(self.definition[pos:self.pos])
|
||||||
|
for regex in [binary_literal_re, hex_literal_re,
|
||||||
|
integer_literal_re, octal_literal_re]:
|
||||||
if self.match(regex):
|
if self.match(regex):
|
||||||
while self.current_char in 'uUlLfF':
|
self.match(integers_literal_suffix_re)
|
||||||
self.pos += 1
|
|
||||||
return ASTNumberLiteral(self.definition[pos:self.pos])
|
return ASTNumberLiteral(self.definition[pos:self.pos])
|
||||||
|
|
||||||
string = self._parse_string()
|
string = self._parse_string()
|
||||||
@ -2639,18 +2644,45 @@ class DefinitionParser(BaseParser):
|
|||||||
self.skip_ws()
|
self.skip_ws()
|
||||||
if typed and self.skip_string('['):
|
if typed and self.skip_string('['):
|
||||||
self.skip_ws()
|
self.skip_ws()
|
||||||
if self.skip_string(']'):
|
static = False
|
||||||
arrayOps.append(ASTArray(None))
|
const = False
|
||||||
|
volatile = False
|
||||||
|
restrict = False
|
||||||
|
while True:
|
||||||
|
if not static:
|
||||||
|
if self.skip_word_and_ws('static'):
|
||||||
|
static = True
|
||||||
continue
|
continue
|
||||||
|
if not const:
|
||||||
def parser() -> ASTExpression:
|
if self.skip_word_and_ws('const'):
|
||||||
return self._parse_expression()
|
const = True
|
||||||
|
continue
|
||||||
value = self._parse_expression_fallback([']'], parser)
|
if not volatile:
|
||||||
|
if self.skip_word_and_ws('volatile'):
|
||||||
|
volatile = True
|
||||||
|
continue
|
||||||
|
if not restrict:
|
||||||
|
if self.skip_word_and_ws('restrict'):
|
||||||
|
restrict = True
|
||||||
|
continue
|
||||||
|
break
|
||||||
|
vla = False if static else self.skip_string_and_ws('*')
|
||||||
|
if vla:
|
||||||
if not self.skip_string(']'):
|
if not self.skip_string(']'):
|
||||||
self.fail("Expected ']' in end of array operator.")
|
self.fail("Expected ']' in end of array operator.")
|
||||||
arrayOps.append(ASTArray(value))
|
size = None
|
||||||
continue
|
else:
|
||||||
|
if self.skip_string(']'):
|
||||||
|
size = None
|
||||||
|
else:
|
||||||
|
|
||||||
|
def parser():
|
||||||
|
return self._parse_expression()
|
||||||
|
size = self._parse_expression_fallback([']'], parser)
|
||||||
|
self.skip_ws()
|
||||||
|
if not self.skip_string(']'):
|
||||||
|
self.fail("Expected ']' in end of array operator.")
|
||||||
|
arrayOps.append(ASTArray(static, const, volatile, restrict, vla, size))
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
param = self._parse_parameters(paramMode)
|
param = self._parse_parameters(paramMode)
|
||||||
@ -2793,7 +2825,7 @@ class DefinitionParser(BaseParser):
|
|||||||
declSpecs = self._parse_decl_specs(outer=outer, typed=False)
|
declSpecs = self._parse_decl_specs(outer=outer, typed=False)
|
||||||
decl = self._parse_declarator(named=True, paramMode=outer,
|
decl = self._parse_declarator(named=True, paramMode=outer,
|
||||||
typed=False)
|
typed=False)
|
||||||
self.assert_end()
|
self.assert_end(allowSemicolon=True)
|
||||||
except DefinitionError as exUntyped:
|
except DefinitionError as exUntyped:
|
||||||
desc = "If just a name"
|
desc = "If just a name"
|
||||||
prevErrors.append((exUntyped, desc))
|
prevErrors.append((exUntyped, desc))
|
||||||
@ -2926,7 +2958,15 @@ class DefinitionParser(BaseParser):
|
|||||||
declaration = self._parse_type(named=True, outer='type')
|
declaration = self._parse_type(named=True, outer='type')
|
||||||
else:
|
else:
|
||||||
assert False
|
assert False
|
||||||
return ASTDeclaration(objectType, directiveType, declaration)
|
if objectType != 'macro':
|
||||||
|
self.skip_ws()
|
||||||
|
semicolon = self.skip_string(';')
|
||||||
|
else:
|
||||||
|
semicolon = False
|
||||||
|
return ASTDeclaration(objectType, directiveType, declaration, semicolon)
|
||||||
|
|
||||||
|
def parse_namespace_object(self) -> ASTNestedName:
|
||||||
|
return self._parse_nested_name()
|
||||||
|
|
||||||
def parse_xref_object(self) -> ASTNestedName:
|
def parse_xref_object(self) -> ASTNestedName:
|
||||||
name = self._parse_nested_name()
|
name = self._parse_nested_name()
|
||||||
@ -3081,7 +3121,7 @@ class CObject(ObjectDescription):
|
|||||||
def handle_signature(self, sig: str, signode: TextElement) -> ASTDeclaration:
|
def handle_signature(self, sig: str, signode: TextElement) -> ASTDeclaration:
|
||||||
parentSymbol = self.env.temp_data['c:parent_symbol'] # type: Symbol
|
parentSymbol = self.env.temp_data['c:parent_symbol'] # type: Symbol
|
||||||
|
|
||||||
parser = DefinitionParser(sig, location=signode)
|
parser = DefinitionParser(sig, location=signode, config=self.env.config)
|
||||||
try:
|
try:
|
||||||
ast = self.parse_definition(parser)
|
ast = self.parse_definition(parser)
|
||||||
parser.assert_end()
|
parser.assert_end()
|
||||||
@ -3178,6 +3218,97 @@ class CTypeObject(CObject):
|
|||||||
object_type = 'type'
|
object_type = 'type'
|
||||||
|
|
||||||
|
|
||||||
|
class CNamespaceObject(SphinxDirective):
|
||||||
|
"""
|
||||||
|
This directive is just to tell Sphinx that we're documenting stuff in
|
||||||
|
namespace foo.
|
||||||
|
"""
|
||||||
|
|
||||||
|
has_content = False
|
||||||
|
required_arguments = 1
|
||||||
|
optional_arguments = 0
|
||||||
|
final_argument_whitespace = True
|
||||||
|
option_spec = {} # type: Dict
|
||||||
|
|
||||||
|
def run(self) -> List[Node]:
|
||||||
|
rootSymbol = self.env.domaindata['c']['root_symbol']
|
||||||
|
if self.arguments[0].strip() in ('NULL', '0', 'nullptr'):
|
||||||
|
symbol = rootSymbol
|
||||||
|
stack = [] # type: List[Symbol]
|
||||||
|
else:
|
||||||
|
parser = DefinitionParser(self.arguments[0],
|
||||||
|
location=self.get_source_info(),
|
||||||
|
config=self.env.config)
|
||||||
|
try:
|
||||||
|
name = parser.parse_namespace_object()
|
||||||
|
parser.assert_end()
|
||||||
|
except DefinitionError as e:
|
||||||
|
logger.warning(e, location=self.get_source_info())
|
||||||
|
name = _make_phony_error_name()
|
||||||
|
symbol = rootSymbol.add_name(name)
|
||||||
|
stack = [symbol]
|
||||||
|
self.env.temp_data['c:parent_symbol'] = symbol
|
||||||
|
self.env.temp_data['c:namespace_stack'] = stack
|
||||||
|
self.env.ref_context['c:parent_key'] = symbol.get_lookup_key()
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
class CNamespacePushObject(SphinxDirective):
|
||||||
|
has_content = False
|
||||||
|
required_arguments = 1
|
||||||
|
optional_arguments = 0
|
||||||
|
final_argument_whitespace = True
|
||||||
|
option_spec = {} # type: Dict
|
||||||
|
|
||||||
|
def run(self) -> List[Node]:
|
||||||
|
if self.arguments[0].strip() in ('NULL', '0', 'nullptr'):
|
||||||
|
return []
|
||||||
|
parser = DefinitionParser(self.arguments[0],
|
||||||
|
location=self.get_source_info(),
|
||||||
|
config=self.env.config)
|
||||||
|
try:
|
||||||
|
name = parser.parse_namespace_object()
|
||||||
|
parser.assert_end()
|
||||||
|
except DefinitionError as e:
|
||||||
|
logger.warning(e, location=self.get_source_info())
|
||||||
|
name = _make_phony_error_name()
|
||||||
|
oldParent = self.env.temp_data.get('c:parent_symbol', None)
|
||||||
|
if not oldParent:
|
||||||
|
oldParent = self.env.domaindata['c']['root_symbol']
|
||||||
|
symbol = oldParent.add_name(name)
|
||||||
|
stack = self.env.temp_data.get('c:namespace_stack', [])
|
||||||
|
stack.append(symbol)
|
||||||
|
self.env.temp_data['c:parent_symbol'] = symbol
|
||||||
|
self.env.temp_data['c:namespace_stack'] = stack
|
||||||
|
self.env.ref_context['c:parent_key'] = symbol.get_lookup_key()
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
class CNamespacePopObject(SphinxDirective):
|
||||||
|
has_content = False
|
||||||
|
required_arguments = 0
|
||||||
|
optional_arguments = 0
|
||||||
|
final_argument_whitespace = True
|
||||||
|
option_spec = {} # type: Dict
|
||||||
|
|
||||||
|
def run(self) -> List[Node]:
|
||||||
|
stack = self.env.temp_data.get('c:namespace_stack', None)
|
||||||
|
if not stack or len(stack) == 0:
|
||||||
|
logger.warning("C namespace pop on empty stack. Defaulting to gobal scope.",
|
||||||
|
location=self.get_source_info())
|
||||||
|
stack = []
|
||||||
|
else:
|
||||||
|
stack.pop()
|
||||||
|
if len(stack) > 0:
|
||||||
|
symbol = stack[-1]
|
||||||
|
else:
|
||||||
|
symbol = self.env.domaindata['c']['root_symbol']
|
||||||
|
self.env.temp_data['c:parent_symbol'] = symbol
|
||||||
|
self.env.temp_data['c:namespace_stack'] = stack
|
||||||
|
self.env.ref_context['cp:parent_key'] = symbol.get_lookup_key()
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
class CXRefRole(XRefRole):
|
class CXRefRole(XRefRole):
|
||||||
def process_link(self, env: BuildEnvironment, refnode: Element,
|
def process_link(self, env: BuildEnvironment, refnode: Element,
|
||||||
has_explicit_title: bool, title: str, target: str) -> Tuple[str, str]:
|
has_explicit_title: bool, title: str, target: str) -> Tuple[str, str]:
|
||||||
@ -3214,7 +3345,8 @@ class CExprRole(SphinxRole):
|
|||||||
|
|
||||||
def run(self) -> Tuple[List[Node], List[system_message]]:
|
def run(self) -> Tuple[List[Node], List[system_message]]:
|
||||||
text = self.text.replace('\n', ' ')
|
text = self.text.replace('\n', ' ')
|
||||||
parser = DefinitionParser(text, location=self.get_source_info())
|
parser = DefinitionParser(text, location=self.get_source_info(),
|
||||||
|
config=self.env.config)
|
||||||
# attempt to mimic XRefRole classes, except that...
|
# attempt to mimic XRefRole classes, except that...
|
||||||
classes = ['xref', 'c', self.class_type]
|
classes = ['xref', 'c', self.class_type]
|
||||||
try:
|
try:
|
||||||
@ -3256,6 +3388,10 @@ class CDomain(Domain):
|
|||||||
'enum': CEnumObject,
|
'enum': CEnumObject,
|
||||||
'enumerator': CEnumeratorObject,
|
'enumerator': CEnumeratorObject,
|
||||||
'type': CTypeObject,
|
'type': CTypeObject,
|
||||||
|
# scope control
|
||||||
|
'namespace': CNamespaceObject,
|
||||||
|
'namespace-push': CNamespacePushObject,
|
||||||
|
'namespace-pop': CNamespacePopObject,
|
||||||
}
|
}
|
||||||
roles = {
|
roles = {
|
||||||
'member': CXRefRole(),
|
'member': CXRefRole(),
|
||||||
@ -3344,7 +3480,7 @@ class CDomain(Domain):
|
|||||||
def _resolve_xref_inner(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
|
def _resolve_xref_inner(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
|
||||||
typ: str, target: str, node: pending_xref,
|
typ: str, target: str, node: pending_xref,
|
||||||
contnode: Element) -> Tuple[Element, str]:
|
contnode: Element) -> Tuple[Element, str]:
|
||||||
parser = DefinitionParser(target, location=node)
|
parser = DefinitionParser(target, location=node, config=env.config)
|
||||||
try:
|
try:
|
||||||
name = parser.parse_xref_object()
|
name = parser.parse_xref_object()
|
||||||
except DefinitionError as e:
|
except DefinitionError as e:
|
||||||
@ -3401,6 +3537,8 @@ class CDomain(Domain):
|
|||||||
|
|
||||||
def setup(app: Sphinx) -> Dict[str, Any]:
|
def setup(app: Sphinx) -> Dict[str, Any]:
|
||||||
app.add_domain(CDomain)
|
app.add_domain(CDomain)
|
||||||
|
app.add_config_value("c_id_attributes", [], 'env')
|
||||||
|
app.add_config_value("c_paren_attributes", [], 'env')
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'version': 'builtin',
|
'version': 'builtin',
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
import re
|
import re
|
||||||
from typing import (
|
from typing import (
|
||||||
Any, Callable, Dict, Generator, Iterator, List, Tuple, Type, TypeVar, Union
|
Any, Callable, Dict, Generator, Iterator, List, Tuple, Type, TypeVar, Union, Optional
|
||||||
)
|
)
|
||||||
|
|
||||||
from docutils import nodes
|
from docutils import nodes
|
||||||
@ -21,7 +21,6 @@ from sphinx import addnodes
|
|||||||
from sphinx.addnodes import desc_signature, pending_xref
|
from sphinx.addnodes import desc_signature, pending_xref
|
||||||
from sphinx.application import Sphinx
|
from sphinx.application import Sphinx
|
||||||
from sphinx.builders import Builder
|
from sphinx.builders import Builder
|
||||||
from sphinx.config import Config
|
|
||||||
from sphinx.directives import ObjectDescription
|
from sphinx.directives import ObjectDescription
|
||||||
from sphinx.domains import Domain, ObjType
|
from sphinx.domains import Domain, ObjType
|
||||||
from sphinx.environment import BuildEnvironment
|
from sphinx.environment import BuildEnvironment
|
||||||
@ -32,10 +31,11 @@ from sphinx.transforms import SphinxTransform
|
|||||||
from sphinx.transforms.post_transforms import ReferencesResolver
|
from sphinx.transforms.post_transforms import ReferencesResolver
|
||||||
from sphinx.util import logging
|
from sphinx.util import logging
|
||||||
from sphinx.util.cfamily import (
|
from sphinx.util.cfamily import (
|
||||||
NoOldIdError, ASTBaseBase, verify_description_mode, StringifyTransform,
|
NoOldIdError, ASTBaseBase, ASTAttribute, verify_description_mode, StringifyTransform,
|
||||||
BaseParser, DefinitionError, UnsupportedMultiCharacterCharLiteral,
|
BaseParser, DefinitionError, UnsupportedMultiCharacterCharLiteral,
|
||||||
identifier_re, anon_identifier_re, integer_literal_re, octal_literal_re,
|
identifier_re, anon_identifier_re, integer_literal_re, octal_literal_re,
|
||||||
hex_literal_re, binary_literal_re, float_literal_re,
|
hex_literal_re, binary_literal_re, integers_literal_suffix_re,
|
||||||
|
float_literal_re, float_literal_suffix_re,
|
||||||
char_literal_re
|
char_literal_re
|
||||||
)
|
)
|
||||||
from sphinx.util.docfields import Field, GroupedField
|
from sphinx.util.docfields import Field, GroupedField
|
||||||
@ -110,7 +110,8 @@ T = TypeVar('T')
|
|||||||
simple-declaration ->
|
simple-declaration ->
|
||||||
attribute-specifier-seq[opt] decl-specifier-seq[opt]
|
attribute-specifier-seq[opt] decl-specifier-seq[opt]
|
||||||
init-declarator-list[opt] ;
|
init-declarator-list[opt] ;
|
||||||
# Drop the semi-colon. For now: drop the attributes (TODO).
|
# Make the semicolon optional.
|
||||||
|
# For now: drop the attributes (TODO).
|
||||||
# Use at most 1 init-declarator.
|
# Use at most 1 init-declarator.
|
||||||
-> decl-specifier-seq init-declarator
|
-> decl-specifier-seq init-declarator
|
||||||
-> decl-specifier-seq declarator initializer
|
-> decl-specifier-seq declarator initializer
|
||||||
@ -296,6 +297,9 @@ T = TypeVar('T')
|
|||||||
nested-name
|
nested-name
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
udl_identifier_re = re.compile(r'''(?x)
|
||||||
|
[a-zA-Z_][a-zA-Z0-9_]*\b # note, no word boundary in the beginning
|
||||||
|
''')
|
||||||
_string_re = re.compile(r"[LuU8]?('([^'\\]*(?:\\.[^'\\]*)*)'"
|
_string_re = re.compile(r"[LuU8]?('([^'\\]*(?:\\.[^'\\]*)*)'"
|
||||||
r'|"([^"\\]*(?:\\.[^"\\]*)*)")', re.S)
|
r'|"([^"\\]*(?:\\.[^"\\]*)*)")', re.S)
|
||||||
_visibility_re = re.compile(r'\b(public|private|protected)\b')
|
_visibility_re = re.compile(r'\b(public|private|protected)\b')
|
||||||
@ -607,8 +611,7 @@ class ASTIdentifier(ASTBase):
|
|||||||
reftype='identifier',
|
reftype='identifier',
|
||||||
reftarget=targetText, modname=None,
|
reftarget=targetText, modname=None,
|
||||||
classname=None)
|
classname=None)
|
||||||
key = symbol.get_lookup_key()
|
pnode['cpp:parent_key'] = symbol.get_lookup_key()
|
||||||
pnode['cpp:parent_key'] = key
|
|
||||||
if self.is_anon():
|
if self.is_anon():
|
||||||
pnode += nodes.strong(text="[anonymous]")
|
pnode += nodes.strong(text="[anonymous]")
|
||||||
else:
|
else:
|
||||||
@ -624,6 +627,19 @@ class ASTIdentifier(ASTBase):
|
|||||||
signode += nodes.strong(text="[anonymous]")
|
signode += nodes.strong(text="[anonymous]")
|
||||||
else:
|
else:
|
||||||
signode += nodes.Text(self.identifier)
|
signode += nodes.Text(self.identifier)
|
||||||
|
elif mode == 'udl':
|
||||||
|
# the target is 'operator""id' instead of just 'id'
|
||||||
|
assert len(prefix) == 0
|
||||||
|
assert len(templateArgs) == 0
|
||||||
|
assert not self.is_anon()
|
||||||
|
targetText = 'operator""' + self.identifier
|
||||||
|
pnode = addnodes.pending_xref('', refdomain='cpp',
|
||||||
|
reftype='identifier',
|
||||||
|
reftarget=targetText, modname=None,
|
||||||
|
classname=None)
|
||||||
|
pnode['cpp:parent_key'] = symbol.get_lookup_key()
|
||||||
|
pnode += nodes.Text(self.identifier)
|
||||||
|
signode += pnode
|
||||||
else:
|
else:
|
||||||
raise Exception('Unknown description mode: %s' % mode)
|
raise Exception('Unknown description mode: %s' % mode)
|
||||||
|
|
||||||
@ -736,11 +752,17 @@ class ASTNestedName(ASTBase):
|
|||||||
names = self.names[:-1] if mode == 'lastIsName' else self.names
|
names = self.names[:-1] if mode == 'lastIsName' else self.names
|
||||||
# If lastIsName, then wrap all of the prefix in a desc_addname,
|
# If lastIsName, then wrap all of the prefix in a desc_addname,
|
||||||
# else append directly to signode.
|
# else append directly to signode.
|
||||||
# NOTE: Breathe relies on the prefix being in the desc_addname node,
|
# NOTE: Breathe previously relied on the prefix being in the desc_addname node,
|
||||||
# so it can remove it in inner declarations.
|
# so it can remove it in inner declarations.
|
||||||
dest = signode
|
dest = signode
|
||||||
if mode == 'lastIsName':
|
if mode == 'lastIsName':
|
||||||
dest = addnodes.desc_addname()
|
dest = addnodes.desc_addname()
|
||||||
|
if self.rooted:
|
||||||
|
prefix += '::'
|
||||||
|
if mode == 'lastIsName' and len(names) == 0:
|
||||||
|
signode += nodes.Text('::')
|
||||||
|
else:
|
||||||
|
dest += nodes.Text('::')
|
||||||
for i in range(len(names)):
|
for i in range(len(names)):
|
||||||
nne = names[i]
|
nne = names[i]
|
||||||
template = self.templates[i]
|
template = self.templates[i]
|
||||||
@ -769,89 +791,6 @@ class ASTNestedName(ASTBase):
|
|||||||
raise Exception('Unknown description mode: %s' % mode)
|
raise Exception('Unknown description mode: %s' % mode)
|
||||||
|
|
||||||
|
|
||||||
################################################################################
|
|
||||||
# Attributes
|
|
||||||
################################################################################
|
|
||||||
|
|
||||||
class ASTAttribute(ASTBase):
|
|
||||||
def describe_signature(self, signode: TextElement) -> None:
|
|
||||||
raise NotImplementedError(repr(self))
|
|
||||||
|
|
||||||
|
|
||||||
class ASTCPPAttribute(ASTAttribute):
|
|
||||||
def __init__(self, arg: str) -> None:
|
|
||||||
self.arg = arg
|
|
||||||
|
|
||||||
def _stringify(self, transform: StringifyTransform) -> str:
|
|
||||||
return "[[" + self.arg + "]]"
|
|
||||||
|
|
||||||
def describe_signature(self, signode: TextElement) -> None:
|
|
||||||
txt = str(self)
|
|
||||||
signode.append(nodes.Text(txt, txt))
|
|
||||||
|
|
||||||
|
|
||||||
class ASTGnuAttribute(ASTBase):
|
|
||||||
def __init__(self, name: str, args: Any) -> None:
|
|
||||||
self.name = name
|
|
||||||
self.args = args
|
|
||||||
|
|
||||||
def _stringify(self, transform: StringifyTransform) -> str:
|
|
||||||
res = [self.name]
|
|
||||||
if self.args:
|
|
||||||
res.append('(')
|
|
||||||
res.append(transform(self.args))
|
|
||||||
res.append(')')
|
|
||||||
return ''.join(res)
|
|
||||||
|
|
||||||
|
|
||||||
class ASTGnuAttributeList(ASTAttribute):
|
|
||||||
def __init__(self, attrs: List[ASTGnuAttribute]) -> None:
|
|
||||||
self.attrs = attrs
|
|
||||||
|
|
||||||
def _stringify(self, transform: StringifyTransform) -> str:
|
|
||||||
res = ['__attribute__((']
|
|
||||||
first = True
|
|
||||||
for attr in self.attrs:
|
|
||||||
if not first:
|
|
||||||
res.append(', ')
|
|
||||||
first = False
|
|
||||||
res.append(transform(attr))
|
|
||||||
res.append('))')
|
|
||||||
return ''.join(res)
|
|
||||||
|
|
||||||
def describe_signature(self, signode: TextElement) -> None:
|
|
||||||
txt = str(self)
|
|
||||||
signode.append(nodes.Text(txt, txt))
|
|
||||||
|
|
||||||
|
|
||||||
class ASTIdAttribute(ASTAttribute):
|
|
||||||
"""For simple attributes defined by the user."""
|
|
||||||
|
|
||||||
def __init__(self, id: str) -> None:
|
|
||||||
self.id = id
|
|
||||||
|
|
||||||
def _stringify(self, transform: StringifyTransform) -> str:
|
|
||||||
return self.id
|
|
||||||
|
|
||||||
def describe_signature(self, signode: TextElement) -> None:
|
|
||||||
signode.append(nodes.Text(self.id, self.id))
|
|
||||||
|
|
||||||
|
|
||||||
class ASTParenAttribute(ASTAttribute):
|
|
||||||
"""For paren attributes defined by the user."""
|
|
||||||
|
|
||||||
def __init__(self, id: str, arg: str) -> None:
|
|
||||||
self.id = id
|
|
||||||
self.arg = arg
|
|
||||||
|
|
||||||
def _stringify(self, transform: StringifyTransform) -> str:
|
|
||||||
return self.id + '(' + self.arg + ')'
|
|
||||||
|
|
||||||
def describe_signature(self, signode: TextElement) -> None:
|
|
||||||
txt = str(self)
|
|
||||||
signode.append(nodes.Text(txt, txt))
|
|
||||||
|
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# Expressions
|
# Expressions
|
||||||
################################################################################
|
################################################################################
|
||||||
@ -913,6 +852,7 @@ class ASTNumberLiteral(ASTLiteral):
|
|||||||
return self.data
|
return self.data
|
||||||
|
|
||||||
def get_id(self, version: int) -> str:
|
def get_id(self, version: int) -> str:
|
||||||
|
# TODO: floats should be mangled by writing the hex of the binary representation
|
||||||
return "L%sE" % self.data
|
return "L%sE" % self.data
|
||||||
|
|
||||||
def describe_signature(self, signode: TextElement, mode: str,
|
def describe_signature(self, signode: TextElement, mode: str,
|
||||||
@ -957,6 +897,7 @@ class ASTCharLiteral(ASTLiteral):
|
|||||||
return self.prefix + "'" + self.data + "'"
|
return self.prefix + "'" + self.data + "'"
|
||||||
|
|
||||||
def get_id(self, version: int) -> str:
|
def get_id(self, version: int) -> str:
|
||||||
|
# TODO: the ID should be have L E around it
|
||||||
return self.type + str(self.value)
|
return self.type + str(self.value)
|
||||||
|
|
||||||
def describe_signature(self, signode: TextElement, mode: str,
|
def describe_signature(self, signode: TextElement, mode: str,
|
||||||
@ -965,6 +906,26 @@ class ASTCharLiteral(ASTLiteral):
|
|||||||
signode.append(nodes.Text(txt, txt))
|
signode.append(nodes.Text(txt, txt))
|
||||||
|
|
||||||
|
|
||||||
|
class ASTUserDefinedLiteral(ASTLiteral):
|
||||||
|
def __init__(self, literal: ASTLiteral, ident: ASTIdentifier):
|
||||||
|
self.literal = literal
|
||||||
|
self.ident = ident
|
||||||
|
|
||||||
|
def _stringify(self, transform: StringifyTransform) -> str:
|
||||||
|
return transform(self.literal) + transform(self.ident)
|
||||||
|
|
||||||
|
def get_id(self, version: int) -> str:
|
||||||
|
# mangle as if it was a function call: ident(literal)
|
||||||
|
return 'clL_Zli{}E{}E'.format(self.ident.get_id(version), self.literal.get_id(version))
|
||||||
|
|
||||||
|
def describe_signature(self, signode: TextElement, mode: str,
|
||||||
|
env: "BuildEnvironment", symbol: "Symbol") -> None:
|
||||||
|
self.literal.describe_signature(signode, mode, env, symbol)
|
||||||
|
self.ident.describe_signature(signode, "udl", env, "", "", symbol)
|
||||||
|
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
|
||||||
class ASTThisLiteral(ASTExpression):
|
class ASTThisLiteral(ASTExpression):
|
||||||
def _stringify(self, transform: StringifyTransform) -> str:
|
def _stringify(self, transform: StringifyTransform) -> str:
|
||||||
return "this"
|
return "this"
|
||||||
@ -1350,7 +1311,7 @@ class ASTNoexceptExpr(ASTExpression):
|
|||||||
self.expr = expr
|
self.expr = expr
|
||||||
|
|
||||||
def _stringify(self, transform: StringifyTransform) -> str:
|
def _stringify(self, transform: StringifyTransform) -> str:
|
||||||
return "noexcept(" + transform(self.expr) + ")"
|
return 'noexcept(' + transform(self.expr) + ')'
|
||||||
|
|
||||||
def get_id(self, version: int) -> str:
|
def get_id(self, version: int) -> str:
|
||||||
return 'nx' + self.expr.get_id(version)
|
return 'nx' + self.expr.get_id(version)
|
||||||
@ -1896,15 +1857,34 @@ class ASTFunctionParameter(ASTBase):
|
|||||||
self.arg.describe_signature(signode, mode, env, symbol=symbol)
|
self.arg.describe_signature(signode, mode, env, symbol=symbol)
|
||||||
|
|
||||||
|
|
||||||
|
class ASTNoexceptSpec(ASTBase):
|
||||||
|
def __init__(self, expr: Optional[ASTExpression]):
|
||||||
|
self.expr = expr
|
||||||
|
|
||||||
|
def _stringify(self, transform: StringifyTransform) -> str:
|
||||||
|
if self.expr:
|
||||||
|
return 'noexcept(' + transform(self.expr) + ')'
|
||||||
|
return 'noexcept'
|
||||||
|
|
||||||
|
def describe_signature(self, signode: TextElement, mode: str,
|
||||||
|
env: "BuildEnvironment", symbol: "Symbol") -> None:
|
||||||
|
signode += addnodes.desc_annotation('noexcept', 'noexcept')
|
||||||
|
if self.expr:
|
||||||
|
signode.append(nodes.Text('('))
|
||||||
|
self.expr.describe_signature(signode, mode, env, symbol)
|
||||||
|
signode.append(nodes.Text(')'))
|
||||||
|
|
||||||
|
|
||||||
class ASTParametersQualifiers(ASTBase):
|
class ASTParametersQualifiers(ASTBase):
|
||||||
def __init__(self, args: List[ASTFunctionParameter],
|
def __init__(self, args: List[ASTFunctionParameter], volatile: bool, const: bool,
|
||||||
volatile: bool, const: bool, refQual: str,
|
refQual: str, exceptionSpec: ASTNoexceptSpec, trailingReturn: "ASTType",
|
||||||
exceptionSpec: str, override: bool, final: bool, initializer: str) -> None:
|
override: bool, final: bool, initializer: str) -> None:
|
||||||
self.args = args
|
self.args = args
|
||||||
self.volatile = volatile
|
self.volatile = volatile
|
||||||
self.const = const
|
self.const = const
|
||||||
self.refQual = refQual
|
self.refQual = refQual
|
||||||
self.exceptionSpec = exceptionSpec
|
self.exceptionSpec = exceptionSpec
|
||||||
|
self.trailingReturn = trailingReturn
|
||||||
self.override = override
|
self.override = override
|
||||||
self.final = final
|
self.final = final
|
||||||
self.initializer = initializer
|
self.initializer = initializer
|
||||||
@ -1958,7 +1938,10 @@ class ASTParametersQualifiers(ASTBase):
|
|||||||
res.append(self.refQual)
|
res.append(self.refQual)
|
||||||
if self.exceptionSpec:
|
if self.exceptionSpec:
|
||||||
res.append(' ')
|
res.append(' ')
|
||||||
res.append(str(self.exceptionSpec))
|
res.append(transform(self.exceptionSpec))
|
||||||
|
if self.trailingReturn:
|
||||||
|
res.append(' -> ')
|
||||||
|
res.append(transform(self.trailingReturn))
|
||||||
if self.final:
|
if self.final:
|
||||||
res.append(' final')
|
res.append(' final')
|
||||||
if self.override:
|
if self.override:
|
||||||
@ -1995,7 +1978,11 @@ class ASTParametersQualifiers(ASTBase):
|
|||||||
if self.refQual:
|
if self.refQual:
|
||||||
_add_text(signode, self.refQual)
|
_add_text(signode, self.refQual)
|
||||||
if self.exceptionSpec:
|
if self.exceptionSpec:
|
||||||
_add_anno(signode, str(self.exceptionSpec))
|
signode += nodes.Text(' ')
|
||||||
|
self.exceptionSpec.describe_signature(signode, mode, env, symbol)
|
||||||
|
if self.trailingReturn:
|
||||||
|
signode += nodes.Text(' -> ')
|
||||||
|
self.trailingReturn.describe_signature(signode, mode, env, symbol)
|
||||||
if self.final:
|
if self.final:
|
||||||
_add_anno(signode, 'final')
|
_add_anno(signode, 'final')
|
||||||
if self.override:
|
if self.override:
|
||||||
@ -2146,12 +2133,15 @@ class ASTDeclSpecs(ASTBase):
|
|||||||
if self.trailingTypeSpec:
|
if self.trailingTypeSpec:
|
||||||
if addSpace:
|
if addSpace:
|
||||||
signode += nodes.Text(' ')
|
signode += nodes.Text(' ')
|
||||||
|
numChildren = len(signode)
|
||||||
self.trailingTypeSpec.describe_signature(signode, mode, env,
|
self.trailingTypeSpec.describe_signature(signode, mode, env,
|
||||||
symbol=symbol)
|
symbol=symbol)
|
||||||
numChildren = len(signode)
|
addSpace = len(signode) != numChildren
|
||||||
self.rightSpecs.describe_signature(signode)
|
|
||||||
if len(signode) != numChildren:
|
if len(str(self.rightSpecs)) > 0:
|
||||||
|
if addSpace:
|
||||||
signode += nodes.Text(' ')
|
signode += nodes.Text(' ')
|
||||||
|
self.rightSpecs.describe_signature(signode)
|
||||||
|
|
||||||
|
|
||||||
# Declarator
|
# Declarator
|
||||||
@ -2202,6 +2192,10 @@ class ASTDeclarator(ASTBase):
|
|||||||
def function_params(self) -> List[ASTFunctionParameter]:
|
def function_params(self) -> List[ASTFunctionParameter]:
|
||||||
raise NotImplementedError(repr(self))
|
raise NotImplementedError(repr(self))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def trailingReturn(self) -> "ASTType":
|
||||||
|
raise NotImplementedError(repr(self))
|
||||||
|
|
||||||
def require_space_after_declSpecs(self) -> bool:
|
def require_space_after_declSpecs(self) -> bool:
|
||||||
raise NotImplementedError(repr(self))
|
raise NotImplementedError(repr(self))
|
||||||
|
|
||||||
@ -2245,6 +2239,10 @@ class ASTDeclaratorNameParamQual(ASTDeclarator):
|
|||||||
def function_params(self) -> List[ASTFunctionParameter]:
|
def function_params(self) -> List[ASTFunctionParameter]:
|
||||||
return self.paramQual.function_params
|
return self.paramQual.function_params
|
||||||
|
|
||||||
|
@property
|
||||||
|
def trailingReturn(self) -> "ASTType":
|
||||||
|
return self.paramQual.trailingReturn
|
||||||
|
|
||||||
# only the modifiers for a function, e.g.,
|
# only the modifiers for a function, e.g.,
|
||||||
def get_modifiers_id(self, version: int) -> str:
|
def get_modifiers_id(self, version: int) -> str:
|
||||||
# cv-qualifiers
|
# cv-qualifiers
|
||||||
@ -2362,6 +2360,10 @@ class ASTDeclaratorPtr(ASTDeclarator):
|
|||||||
def function_params(self) -> List[ASTFunctionParameter]:
|
def function_params(self) -> List[ASTFunctionParameter]:
|
||||||
return self.next.function_params
|
return self.next.function_params
|
||||||
|
|
||||||
|
@property
|
||||||
|
def trailingReturn(self) -> "ASTType":
|
||||||
|
return self.next.trailingReturn
|
||||||
|
|
||||||
def require_space_after_declSpecs(self) -> bool:
|
def require_space_after_declSpecs(self) -> bool:
|
||||||
return self.next.require_space_after_declSpecs()
|
return self.next.require_space_after_declSpecs()
|
||||||
|
|
||||||
@ -2461,6 +2463,10 @@ class ASTDeclaratorRef(ASTDeclarator):
|
|||||||
def function_params(self) -> List[ASTFunctionParameter]:
|
def function_params(self) -> List[ASTFunctionParameter]:
|
||||||
return self.next.function_params
|
return self.next.function_params
|
||||||
|
|
||||||
|
@property
|
||||||
|
def trailingReturn(self) -> "ASTType":
|
||||||
|
return self.next.trailingReturn
|
||||||
|
|
||||||
def require_space_after_declSpecs(self) -> bool:
|
def require_space_after_declSpecs(self) -> bool:
|
||||||
return self.next.require_space_after_declSpecs()
|
return self.next.require_space_after_declSpecs()
|
||||||
|
|
||||||
@ -2517,6 +2523,10 @@ class ASTDeclaratorParamPack(ASTDeclarator):
|
|||||||
def function_params(self) -> List[ASTFunctionParameter]:
|
def function_params(self) -> List[ASTFunctionParameter]:
|
||||||
return self.next.function_params
|
return self.next.function_params
|
||||||
|
|
||||||
|
@property
|
||||||
|
def trailingReturn(self) -> "ASTType":
|
||||||
|
return self.next.trailingReturn
|
||||||
|
|
||||||
def require_space_after_declSpecs(self) -> bool:
|
def require_space_after_declSpecs(self) -> bool:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -2573,6 +2583,10 @@ class ASTDeclaratorMemPtr(ASTDeclarator):
|
|||||||
def function_params(self) -> List[ASTFunctionParameter]:
|
def function_params(self) -> List[ASTFunctionParameter]:
|
||||||
return self.next.function_params
|
return self.next.function_params
|
||||||
|
|
||||||
|
@property
|
||||||
|
def trailingReturn(self) -> "ASTType":
|
||||||
|
return self.next.trailingReturn
|
||||||
|
|
||||||
def require_space_after_declSpecs(self) -> bool:
|
def require_space_after_declSpecs(self) -> bool:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -2661,6 +2675,10 @@ class ASTDeclaratorParen(ASTDeclarator):
|
|||||||
def function_params(self) -> List[ASTFunctionParameter]:
|
def function_params(self) -> List[ASTFunctionParameter]:
|
||||||
return self.inner.function_params
|
return self.inner.function_params
|
||||||
|
|
||||||
|
@property
|
||||||
|
def trailingReturn(self) -> "ASTType":
|
||||||
|
return self.inner.trailingReturn
|
||||||
|
|
||||||
def require_space_after_declSpecs(self) -> bool:
|
def require_space_after_declSpecs(self) -> bool:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -2789,6 +2807,10 @@ class ASTType(ASTBase):
|
|||||||
def function_params(self) -> List[ASTFunctionParameter]:
|
def function_params(self) -> List[ASTFunctionParameter]:
|
||||||
return self.decl.function_params
|
return self.decl.function_params
|
||||||
|
|
||||||
|
@property
|
||||||
|
def trailingReturn(self) -> "ASTType":
|
||||||
|
return self.decl.trailingReturn
|
||||||
|
|
||||||
def get_id(self, version: int, objectType: str = None,
|
def get_id(self, version: int, objectType: str = None,
|
||||||
symbol: "Symbol" = None) -> str:
|
symbol: "Symbol" = None) -> str:
|
||||||
if version == 1:
|
if version == 1:
|
||||||
@ -2825,6 +2847,9 @@ class ASTType(ASTBase):
|
|||||||
templ = symbol.declaration.templatePrefix
|
templ = symbol.declaration.templatePrefix
|
||||||
if templ is not None:
|
if templ is not None:
|
||||||
typeId = self.decl.get_ptr_suffix_id(version)
|
typeId = self.decl.get_ptr_suffix_id(version)
|
||||||
|
if self.trailingReturn:
|
||||||
|
returnTypeId = self.trailingReturn.get_id(version)
|
||||||
|
else:
|
||||||
returnTypeId = self.declSpecs.get_id(version)
|
returnTypeId = self.declSpecs.get_id(version)
|
||||||
res.append(typeId)
|
res.append(typeId)
|
||||||
res.append(returnTypeId)
|
res.append(returnTypeId)
|
||||||
@ -3546,12 +3571,14 @@ class ASTTemplateDeclarationPrefix(ASTBase):
|
|||||||
|
|
||||||
class ASTDeclaration(ASTBase):
|
class ASTDeclaration(ASTBase):
|
||||||
def __init__(self, objectType: str, directiveType: str, visibility: str,
|
def __init__(self, objectType: str, directiveType: str, visibility: str,
|
||||||
templatePrefix: ASTTemplateDeclarationPrefix, declaration: Any) -> None:
|
templatePrefix: ASTTemplateDeclarationPrefix, declaration: Any,
|
||||||
|
semicolon: bool = False) -> None:
|
||||||
self.objectType = objectType
|
self.objectType = objectType
|
||||||
self.directiveType = directiveType
|
self.directiveType = directiveType
|
||||||
self.visibility = visibility
|
self.visibility = visibility
|
||||||
self.templatePrefix = templatePrefix
|
self.templatePrefix = templatePrefix
|
||||||
self.declaration = declaration
|
self.declaration = declaration
|
||||||
|
self.semicolon = semicolon
|
||||||
|
|
||||||
self.symbol = None # type: Symbol
|
self.symbol = None # type: Symbol
|
||||||
# set by CPPObject._add_enumerator_to_parent
|
# set by CPPObject._add_enumerator_to_parent
|
||||||
@ -3564,7 +3591,7 @@ class ASTDeclaration(ASTBase):
|
|||||||
templatePrefixClone = None
|
templatePrefixClone = None
|
||||||
return ASTDeclaration(self.objectType, self.directiveType,
|
return ASTDeclaration(self.objectType, self.directiveType,
|
||||||
self.visibility, templatePrefixClone,
|
self.visibility, templatePrefixClone,
|
||||||
self.declaration.clone())
|
self.declaration.clone(), self.semicolon)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> ASTNestedName:
|
def name(self) -> ASTNestedName:
|
||||||
@ -3606,6 +3633,8 @@ class ASTDeclaration(ASTBase):
|
|||||||
if self.templatePrefix:
|
if self.templatePrefix:
|
||||||
res.append(transform(self.templatePrefix))
|
res.append(transform(self.templatePrefix))
|
||||||
res.append(transform(self.declaration))
|
res.append(transform(self.declaration))
|
||||||
|
if self.semicolon:
|
||||||
|
res.append(';')
|
||||||
return ''.join(res)
|
return ''.join(res)
|
||||||
|
|
||||||
def describe_signature(self, signode: desc_signature, mode: str,
|
def describe_signature(self, signode: desc_signature, mode: str,
|
||||||
@ -3659,6 +3688,8 @@ class ASTDeclaration(ASTBase):
|
|||||||
else:
|
else:
|
||||||
assert False
|
assert False
|
||||||
self.declaration.describe_signature(mainDeclNode, mode, env, self.symbol)
|
self.declaration.describe_signature(mainDeclNode, mode, env, self.symbol)
|
||||||
|
if self.semicolon:
|
||||||
|
mainDeclNode += nodes.Text(';')
|
||||||
|
|
||||||
|
|
||||||
class ASTNamespace(ASTBase):
|
class ASTNamespace(ASTBase):
|
||||||
@ -3697,8 +3728,8 @@ class LookupKey:
|
|||||||
class Symbol:
|
class Symbol:
|
||||||
debug_indent = 0
|
debug_indent = 0
|
||||||
debug_indent_string = " "
|
debug_indent_string = " "
|
||||||
debug_lookup = False
|
debug_lookup = False # overridden by the corresponding config value
|
||||||
debug_show_tree = False
|
debug_show_tree = False # overridden by the corresponding config value
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def debug_print(*args: Any) -> None:
|
def debug_print(*args: Any) -> None:
|
||||||
@ -4667,16 +4698,18 @@ class DefinitionParser(BaseParser):
|
|||||||
|
|
||||||
_prefix_keys = ('class', 'struct', 'enum', 'union', 'typename')
|
_prefix_keys = ('class', 'struct', 'enum', 'union', 'typename')
|
||||||
|
|
||||||
def __init__(self, definition: str, *,
|
|
||||||
location: Union[nodes.Node, Tuple[str, int]],
|
|
||||||
config: "Config") -> None:
|
|
||||||
super().__init__(definition, location=location)
|
|
||||||
self.config = config
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def language(self) -> str:
|
def language(self) -> str:
|
||||||
return 'C++'
|
return 'C++'
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id_attributes(self):
|
||||||
|
return self.config.cpp_id_attributes
|
||||||
|
|
||||||
|
@property
|
||||||
|
def paren_attributes(self):
|
||||||
|
return self.config.cpp_paren_attributes
|
||||||
|
|
||||||
def _parse_string(self) -> str:
|
def _parse_string(self) -> str:
|
||||||
if self.current_char != '"':
|
if self.current_char != '"':
|
||||||
return None
|
return None
|
||||||
@ -4696,85 +4729,6 @@ class DefinitionParser(BaseParser):
|
|||||||
self.pos += 1
|
self.pos += 1
|
||||||
return self.definition[startPos:self.pos]
|
return self.definition[startPos:self.pos]
|
||||||
|
|
||||||
def _parse_balanced_token_seq(self, end: List[str]) -> str:
|
|
||||||
# TODO: add handling of string literals and similar
|
|
||||||
brackets = {'(': ')', '[': ']', '{': '}'}
|
|
||||||
startPos = self.pos
|
|
||||||
symbols = [] # type: List[str]
|
|
||||||
while not self.eof:
|
|
||||||
if len(symbols) == 0 and self.current_char in end:
|
|
||||||
break
|
|
||||||
if self.current_char in brackets.keys():
|
|
||||||
symbols.append(brackets[self.current_char])
|
|
||||||
elif len(symbols) > 0 and self.current_char == symbols[-1]:
|
|
||||||
symbols.pop()
|
|
||||||
elif self.current_char in ")]}":
|
|
||||||
self.fail("Unexpected '%s' in balanced-token-seq." % self.current_char)
|
|
||||||
self.pos += 1
|
|
||||||
if self.eof:
|
|
||||||
self.fail("Could not find end of balanced-token-seq starting at %d."
|
|
||||||
% startPos)
|
|
||||||
return self.definition[startPos:self.pos]
|
|
||||||
|
|
||||||
def _parse_attribute(self) -> ASTAttribute:
|
|
||||||
self.skip_ws()
|
|
||||||
# try C++11 style
|
|
||||||
startPos = self.pos
|
|
||||||
if self.skip_string_and_ws('['):
|
|
||||||
if not self.skip_string('['):
|
|
||||||
self.pos = startPos
|
|
||||||
else:
|
|
||||||
# TODO: actually implement the correct grammar
|
|
||||||
arg = self._parse_balanced_token_seq(end=[']'])
|
|
||||||
if not self.skip_string_and_ws(']'):
|
|
||||||
self.fail("Expected ']' in end of attribute.")
|
|
||||||
if not self.skip_string_and_ws(']'):
|
|
||||||
self.fail("Expected ']' in end of attribute after [[...]")
|
|
||||||
return ASTCPPAttribute(arg)
|
|
||||||
|
|
||||||
# try GNU style
|
|
||||||
if self.skip_word_and_ws('__attribute__'):
|
|
||||||
if not self.skip_string_and_ws('('):
|
|
||||||
self.fail("Expected '(' after '__attribute__'.")
|
|
||||||
if not self.skip_string_and_ws('('):
|
|
||||||
self.fail("Expected '(' after '__attribute__('.")
|
|
||||||
attrs = []
|
|
||||||
while 1:
|
|
||||||
if self.match(identifier_re):
|
|
||||||
name = self.matched_text
|
|
||||||
self.skip_ws()
|
|
||||||
if self.skip_string_and_ws('('):
|
|
||||||
self.fail('Parameterized GNU style attribute not yet supported.')
|
|
||||||
attrs.append(ASTGnuAttribute(name, None))
|
|
||||||
# TODO: parse arguments for the attribute
|
|
||||||
if self.skip_string_and_ws(','):
|
|
||||||
continue
|
|
||||||
elif self.skip_string_and_ws(')'):
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
self.fail("Expected identifier, ')', or ',' in __attribute__.")
|
|
||||||
if not self.skip_string_and_ws(')'):
|
|
||||||
self.fail("Expected ')' after '__attribute__((...)'")
|
|
||||||
return ASTGnuAttributeList(attrs)
|
|
||||||
|
|
||||||
# try the simple id attributes defined by the user
|
|
||||||
for id in self.config.cpp_id_attributes:
|
|
||||||
if self.skip_word_and_ws(id):
|
|
||||||
return ASTIdAttribute(id)
|
|
||||||
|
|
||||||
# try the paren attributes defined by the user
|
|
||||||
for id in self.config.cpp_paren_attributes:
|
|
||||||
if not self.skip_string_and_ws(id):
|
|
||||||
continue
|
|
||||||
if not self.skip_string('('):
|
|
||||||
self.fail("Expected '(' after user-defined paren-attribute.")
|
|
||||||
arg = self._parse_balanced_token_seq(end=[')'])
|
|
||||||
if not self.skip_string(')'):
|
|
||||||
self.fail("Expected ')' to end user-defined paren-attribute.")
|
|
||||||
return ASTParenAttribute(id, arg)
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _parse_literal(self) -> ASTLiteral:
|
def _parse_literal(self) -> ASTLiteral:
|
||||||
# -> integer-literal
|
# -> integer-literal
|
||||||
# | character-literal
|
# | character-literal
|
||||||
@ -4783,6 +4737,15 @@ class DefinitionParser(BaseParser):
|
|||||||
# | boolean-literal -> "false" | "true"
|
# | boolean-literal -> "false" | "true"
|
||||||
# | pointer-literal -> "nullptr"
|
# | pointer-literal -> "nullptr"
|
||||||
# | user-defined-literal
|
# | user-defined-literal
|
||||||
|
|
||||||
|
def _udl(literal: ASTLiteral) -> ASTLiteral:
|
||||||
|
if not self.match(udl_identifier_re):
|
||||||
|
return literal
|
||||||
|
# hmm, should we care if it's a keyword?
|
||||||
|
# it looks like GCC does not disallow keywords
|
||||||
|
ident = ASTIdentifier(self.matched_text)
|
||||||
|
return ASTUserDefinedLiteral(literal, ident)
|
||||||
|
|
||||||
self.skip_ws()
|
self.skip_ws()
|
||||||
if self.skip_word('nullptr'):
|
if self.skip_word('nullptr'):
|
||||||
return ASTPointerLiteral()
|
return ASTPointerLiteral()
|
||||||
@ -4790,31 +4753,40 @@ class DefinitionParser(BaseParser):
|
|||||||
return ASTBooleanLiteral(True)
|
return ASTBooleanLiteral(True)
|
||||||
if self.skip_word('false'):
|
if self.skip_word('false'):
|
||||||
return ASTBooleanLiteral(False)
|
return ASTBooleanLiteral(False)
|
||||||
for regex in [float_literal_re, binary_literal_re, hex_literal_re,
|
|
||||||
integer_literal_re, octal_literal_re]:
|
|
||||||
pos = self.pos
|
pos = self.pos
|
||||||
|
if self.match(float_literal_re):
|
||||||
|
hasSuffix = self.match(float_literal_suffix_re)
|
||||||
|
floatLit = ASTNumberLiteral(self.definition[pos:self.pos])
|
||||||
|
if hasSuffix:
|
||||||
|
return floatLit
|
||||||
|
else:
|
||||||
|
return _udl(floatLit)
|
||||||
|
for regex in [binary_literal_re, hex_literal_re,
|
||||||
|
integer_literal_re, octal_literal_re]:
|
||||||
if self.match(regex):
|
if self.match(regex):
|
||||||
while self.current_char in 'uUlLfF':
|
hasSuffix = self.match(integers_literal_suffix_re)
|
||||||
self.pos += 1
|
intLit = ASTNumberLiteral(self.definition[pos:self.pos])
|
||||||
return ASTNumberLiteral(self.definition[pos:self.pos])
|
if hasSuffix:
|
||||||
|
return intLit
|
||||||
|
else:
|
||||||
|
return _udl(intLit)
|
||||||
|
|
||||||
string = self._parse_string()
|
string = self._parse_string()
|
||||||
if string is not None:
|
if string is not None:
|
||||||
return ASTStringLiteral(string)
|
return _udl(ASTStringLiteral(string))
|
||||||
|
|
||||||
# character-literal
|
# character-literal
|
||||||
if self.match(char_literal_re):
|
if self.match(char_literal_re):
|
||||||
prefix = self.last_match.group(1) # may be None when no prefix
|
prefix = self.last_match.group(1) # may be None when no prefix
|
||||||
data = self.last_match.group(2)
|
data = self.last_match.group(2)
|
||||||
try:
|
try:
|
||||||
return ASTCharLiteral(prefix, data)
|
charLit = ASTCharLiteral(prefix, data)
|
||||||
except UnicodeDecodeError as e:
|
except UnicodeDecodeError as e:
|
||||||
self.fail("Can not handle character literal. Internal error was: %s" % e)
|
self.fail("Can not handle character literal. Internal error was: %s" % e)
|
||||||
except UnsupportedMultiCharacterCharLiteral:
|
except UnsupportedMultiCharacterCharLiteral:
|
||||||
self.fail("Can not handle character literal"
|
self.fail("Can not handle character literal"
|
||||||
" resulting in multiple decoded characters.")
|
" resulting in multiple decoded characters.")
|
||||||
|
return _udl(charLit)
|
||||||
# TODO: user-defined lit
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _parse_fold_or_paren_expression(self) -> ASTExpression:
|
def _parse_fold_or_paren_expression(self) -> ASTExpression:
|
||||||
@ -5651,15 +5623,22 @@ class DefinitionParser(BaseParser):
|
|||||||
refQual = '&'
|
refQual = '&'
|
||||||
|
|
||||||
exceptionSpec = None
|
exceptionSpec = None
|
||||||
override = None
|
|
||||||
final = None
|
|
||||||
initializer = None
|
|
||||||
self.skip_ws()
|
self.skip_ws()
|
||||||
if self.skip_string('noexcept'):
|
if self.skip_string('noexcept'):
|
||||||
exceptionSpec = 'noexcept'
|
if self.skip_string_and_ws('('):
|
||||||
|
expr = self._parse_constant_expression(False)
|
||||||
self.skip_ws()
|
self.skip_ws()
|
||||||
if self.skip_string('('):
|
if not self.skip_string(')'):
|
||||||
self.fail('Parameterised "noexcept" not yet implemented.')
|
self.fail("Expecting ')' to end 'noexcept'.")
|
||||||
|
exceptionSpec = ASTNoexceptSpec(expr)
|
||||||
|
else:
|
||||||
|
exceptionSpec = ASTNoexceptSpec(None)
|
||||||
|
|
||||||
|
self.skip_ws()
|
||||||
|
if self.skip_string('->'):
|
||||||
|
trailingReturn = self._parse_type(named=False)
|
||||||
|
else:
|
||||||
|
trailingReturn = None
|
||||||
|
|
||||||
self.skip_ws()
|
self.skip_ws()
|
||||||
override = self.skip_word_and_ws('override')
|
override = self.skip_word_and_ws('override')
|
||||||
@ -5669,6 +5648,7 @@ class DefinitionParser(BaseParser):
|
|||||||
'override') # they can be permuted
|
'override') # they can be permuted
|
||||||
|
|
||||||
self.skip_ws()
|
self.skip_ws()
|
||||||
|
initializer = None
|
||||||
if self.skip_string('='):
|
if self.skip_string('='):
|
||||||
self.skip_ws()
|
self.skip_ws()
|
||||||
valid = ('0', 'delete', 'default')
|
valid = ('0', 'delete', 'default')
|
||||||
@ -5682,8 +5662,8 @@ class DefinitionParser(BaseParser):
|
|||||||
% '" or "'.join(valid))
|
% '" or "'.join(valid))
|
||||||
|
|
||||||
return ASTParametersQualifiers(
|
return ASTParametersQualifiers(
|
||||||
args, volatile, const, refQual, exceptionSpec, override, final,
|
args, volatile, const, refQual, exceptionSpec, trailingReturn,
|
||||||
initializer)
|
override, final, initializer)
|
||||||
|
|
||||||
def _parse_decl_specs_simple(self, outer: str, typed: bool) -> ASTDeclSpecsSimple:
|
def _parse_decl_specs_simple(self, outer: str, typed: bool) -> ASTDeclSpecsSimple:
|
||||||
"""Just parse the simple ones."""
|
"""Just parse the simple ones."""
|
||||||
@ -6033,7 +6013,7 @@ class DefinitionParser(BaseParser):
|
|||||||
declSpecs = self._parse_decl_specs(outer=outer, typed=False)
|
declSpecs = self._parse_decl_specs(outer=outer, typed=False)
|
||||||
decl = self._parse_declarator(named=True, paramMode=outer,
|
decl = self._parse_declarator(named=True, paramMode=outer,
|
||||||
typed=False)
|
typed=False)
|
||||||
self.assert_end()
|
self.assert_end(allowSemicolon=True)
|
||||||
except DefinitionError as exUntyped:
|
except DefinitionError as exUntyped:
|
||||||
if outer == 'type':
|
if outer == 'type':
|
||||||
desc = "If just a name"
|
desc = "If just a name"
|
||||||
@ -6444,8 +6424,10 @@ class DefinitionParser(BaseParser):
|
|||||||
templatePrefix,
|
templatePrefix,
|
||||||
fullSpecShorthand=False,
|
fullSpecShorthand=False,
|
||||||
isMember=objectType == 'member')
|
isMember=objectType == 'member')
|
||||||
|
self.skip_ws()
|
||||||
|
semicolon = self.skip_string(';')
|
||||||
return ASTDeclaration(objectType, directiveType, visibility,
|
return ASTDeclaration(objectType, directiveType, visibility,
|
||||||
templatePrefix, declaration)
|
templatePrefix, declaration, semicolon)
|
||||||
|
|
||||||
def parse_namespace_object(self) -> ASTNamespace:
|
def parse_namespace_object(self) -> ASTNamespace:
|
||||||
templatePrefix = self._parse_template_declaration_prefix(objectType="namespace")
|
templatePrefix = self._parse_template_declaration_prefix(objectType="namespace")
|
||||||
@ -7200,8 +7182,7 @@ class CPPDomain(Domain):
|
|||||||
# add parens again for those that could be functions
|
# add parens again for those that could be functions
|
||||||
if typ == 'any' or typ == 'func':
|
if typ == 'any' or typ == 'func':
|
||||||
target += '()'
|
target += '()'
|
||||||
parser = DefinitionParser(target, location=node,
|
parser = DefinitionParser(target, location=node, config=env.config)
|
||||||
config=env.config)
|
|
||||||
try:
|
try:
|
||||||
ast, isShorthand = parser.parse_xref_object()
|
ast, isShorthand = parser.parse_xref_object()
|
||||||
except DefinitionError as e:
|
except DefinitionError as e:
|
||||||
@ -7408,9 +7389,18 @@ def setup(app: Sphinx) -> Dict[str, Any]:
|
|||||||
app.add_config_value("cpp_paren_attributes", [], 'env')
|
app.add_config_value("cpp_paren_attributes", [], 'env')
|
||||||
app.add_post_transform(AliasTransform)
|
app.add_post_transform(AliasTransform)
|
||||||
|
|
||||||
|
# debug stuff
|
||||||
|
app.add_config_value("cpp_debug_lookup", False, '')
|
||||||
|
app.add_config_value("cpp_debug_show_tree", False, '')
|
||||||
|
|
||||||
|
def setDebugFlags(app):
|
||||||
|
Symbol.debug_lookup = app.config.cpp_debug_lookup
|
||||||
|
Symbol.debug_show_tree = app.config.cpp_debug_show_tree
|
||||||
|
app.connect("builder-inited", setDebugFlags)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'version': 'builtin',
|
'version': 'builtin',
|
||||||
'env_version': 2,
|
'env_version': 3,
|
||||||
'parallel_read_safe': True,
|
'parallel_read_safe': True,
|
||||||
'parallel_write_safe': True,
|
'parallel_write_safe': True,
|
||||||
}
|
}
|
||||||
|
@ -142,7 +142,7 @@ class MathDomain(Domain):
|
|||||||
|
|
||||||
def add_equation(self, env: BuildEnvironment, docname: str, labelid: str) -> int:
|
def add_equation(self, env: BuildEnvironment, docname: str, labelid: str) -> int:
|
||||||
warnings.warn('MathDomain.add_equation() is deprecated.',
|
warnings.warn('MathDomain.add_equation() is deprecated.',
|
||||||
RemovedInSphinx40Warning)
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
if labelid in self.equations:
|
if labelid in self.equations:
|
||||||
path = env.doc2path(self.equations[labelid][0])
|
path = env.doc2path(self.equations[labelid][0])
|
||||||
msg = __('duplicate label of equation %s, other instance in %s') % (labelid, path)
|
msg = __('duplicate label of equation %s, other instance in %s') % (labelid, path)
|
||||||
@ -154,7 +154,7 @@ class MathDomain(Domain):
|
|||||||
|
|
||||||
def get_next_equation_number(self, docname: str) -> int:
|
def get_next_equation_number(self, docname: str) -> int:
|
||||||
warnings.warn('MathDomain.get_next_equation_number() is deprecated.',
|
warnings.warn('MathDomain.get_next_equation_number() is deprecated.',
|
||||||
RemovedInSphinx40Warning)
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
targets = [eq for eq in self.equations.values() if eq[0] == docname]
|
targets = [eq for eq in self.equations.values() if eq[0] == docname]
|
||||||
return len(targets) + 1
|
return len(targets) + 1
|
||||||
|
|
||||||
|
@ -77,9 +77,8 @@ ModuleEntry = NamedTuple('ModuleEntry', [('docname', str),
|
|||||||
('deprecated', bool)])
|
('deprecated', bool)])
|
||||||
|
|
||||||
|
|
||||||
def _parse_annotation(annotation: str) -> List[Node]:
|
def type_to_xref(text: str) -> addnodes.pending_xref:
|
||||||
"""Parse type annotation."""
|
"""Convert a type string to a cross reference node."""
|
||||||
def make_xref(text: str) -> addnodes.pending_xref:
|
|
||||||
if text == 'None':
|
if text == 'None':
|
||||||
reftype = 'obj'
|
reftype = 'obj'
|
||||||
else:
|
else:
|
||||||
@ -88,6 +87,9 @@ def _parse_annotation(annotation: str) -> List[Node]:
|
|||||||
return pending_xref('', nodes.Text(text),
|
return pending_xref('', nodes.Text(text),
|
||||||
refdomain='py', reftype=reftype, reftarget=text)
|
refdomain='py', reftype=reftype, reftarget=text)
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_annotation(annotation: str) -> List[Node]:
|
||||||
|
"""Parse type annotation."""
|
||||||
def unparse(node: ast.AST) -> List[Node]:
|
def unparse(node: ast.AST) -> List[Node]:
|
||||||
if isinstance(node, ast.Attribute):
|
if isinstance(node, ast.Attribute):
|
||||||
return [nodes.Text("%s.%s" % (unparse(node.value)[0], node.attr))]
|
return [nodes.Text("%s.%s" % (unparse(node.value)[0], node.attr))]
|
||||||
@ -114,11 +116,16 @@ def _parse_annotation(annotation: str) -> List[Node]:
|
|||||||
result.append(addnodes.desc_sig_punctuation('', ']'))
|
result.append(addnodes.desc_sig_punctuation('', ']'))
|
||||||
return result
|
return result
|
||||||
elif isinstance(node, ast.Tuple):
|
elif isinstance(node, ast.Tuple):
|
||||||
|
if node.elts:
|
||||||
result = []
|
result = []
|
||||||
for elem in node.elts:
|
for elem in node.elts:
|
||||||
result.extend(unparse(elem))
|
result.extend(unparse(elem))
|
||||||
result.append(addnodes.desc_sig_punctuation('', ', '))
|
result.append(addnodes.desc_sig_punctuation('', ', '))
|
||||||
result.pop()
|
result.pop()
|
||||||
|
else:
|
||||||
|
result = [addnodes.desc_sig_punctuation('', '('),
|
||||||
|
addnodes.desc_sig_punctuation('', ')')]
|
||||||
|
|
||||||
return result
|
return result
|
||||||
else:
|
else:
|
||||||
raise SyntaxError # unsupported syntax
|
raise SyntaxError # unsupported syntax
|
||||||
@ -128,10 +135,10 @@ def _parse_annotation(annotation: str) -> List[Node]:
|
|||||||
result = unparse(tree)
|
result = unparse(tree)
|
||||||
for i, node in enumerate(result):
|
for i, node in enumerate(result):
|
||||||
if isinstance(node, nodes.Text):
|
if isinstance(node, nodes.Text):
|
||||||
result[i] = make_xref(str(node))
|
result[i] = type_to_xref(str(node))
|
||||||
return result
|
return result
|
||||||
except SyntaxError:
|
except SyntaxError:
|
||||||
return [make_xref(annotation)]
|
return [type_to_xref(annotation)]
|
||||||
|
|
||||||
|
|
||||||
def _parse_arglist(arglist: str) -> addnodes.desc_parameterlist:
|
def _parse_arglist(arglist: str) -> addnodes.desc_parameterlist:
|
||||||
@ -525,10 +532,11 @@ class PyModulelevel(PyObject):
|
|||||||
if cls.__name__ != 'DirectiveAdapter':
|
if cls.__name__ != 'DirectiveAdapter':
|
||||||
warnings.warn('PyModulelevel is deprecated. '
|
warnings.warn('PyModulelevel is deprecated. '
|
||||||
'Please check the implementation of %s' % cls,
|
'Please check the implementation of %s' % cls,
|
||||||
RemovedInSphinx40Warning)
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
warnings.warn('PyModulelevel is deprecated', RemovedInSphinx40Warning)
|
warnings.warn('PyModulelevel is deprecated',
|
||||||
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
|
|
||||||
return super().run()
|
return super().run()
|
||||||
|
|
||||||
@ -615,7 +623,7 @@ class PyVariable(PyObject):
|
|||||||
|
|
||||||
typ = self.options.get('type')
|
typ = self.options.get('type')
|
||||||
if typ:
|
if typ:
|
||||||
signode += addnodes.desc_annotation(typ, ': ' + typ)
|
signode += addnodes.desc_annotation(typ, '', nodes.Text(': '), type_to_xref(typ))
|
||||||
|
|
||||||
value = self.options.get('value')
|
value = self.options.get('value')
|
||||||
if value:
|
if value:
|
||||||
@ -636,10 +644,18 @@ class PyClasslike(PyObject):
|
|||||||
Description of a class-like object (classes, interfaces, exceptions).
|
Description of a class-like object (classes, interfaces, exceptions).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
option_spec = PyObject.option_spec.copy()
|
||||||
|
option_spec.update({
|
||||||
|
'final': directives.flag,
|
||||||
|
})
|
||||||
|
|
||||||
allow_nesting = True
|
allow_nesting = True
|
||||||
|
|
||||||
def get_signature_prefix(self, sig: str) -> str:
|
def get_signature_prefix(self, sig: str) -> str:
|
||||||
return self.objtype + ' '
|
if 'final' in self.options:
|
||||||
|
return 'final %s ' % self.objtype
|
||||||
|
else:
|
||||||
|
return '%s ' % self.objtype
|
||||||
|
|
||||||
def get_index_text(self, modname: str, name_cls: Tuple[str, str]) -> str:
|
def get_index_text(self, modname: str, name_cls: Tuple[str, str]) -> str:
|
||||||
if self.objtype == 'class':
|
if self.objtype == 'class':
|
||||||
@ -662,10 +678,11 @@ class PyClassmember(PyObject):
|
|||||||
if cls.__name__ != 'DirectiveAdapter':
|
if cls.__name__ != 'DirectiveAdapter':
|
||||||
warnings.warn('PyClassmember is deprecated. '
|
warnings.warn('PyClassmember is deprecated. '
|
||||||
'Please check the implementation of %s' % cls,
|
'Please check the implementation of %s' % cls,
|
||||||
RemovedInSphinx40Warning)
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
warnings.warn('PyClassmember is deprecated', RemovedInSphinx40Warning)
|
warnings.warn('PyClassmember is deprecated',
|
||||||
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
|
|
||||||
return super().run()
|
return super().run()
|
||||||
|
|
||||||
@ -744,6 +761,7 @@ class PyMethod(PyObject):
|
|||||||
'abstractmethod': directives.flag,
|
'abstractmethod': directives.flag,
|
||||||
'async': directives.flag,
|
'async': directives.flag,
|
||||||
'classmethod': directives.flag,
|
'classmethod': directives.flag,
|
||||||
|
'final': directives.flag,
|
||||||
'property': directives.flag,
|
'property': directives.flag,
|
||||||
'staticmethod': directives.flag,
|
'staticmethod': directives.flag,
|
||||||
})
|
})
|
||||||
@ -756,6 +774,8 @@ class PyMethod(PyObject):
|
|||||||
|
|
||||||
def get_signature_prefix(self, sig: str) -> str:
|
def get_signature_prefix(self, sig: str) -> str:
|
||||||
prefix = []
|
prefix = []
|
||||||
|
if 'final' in self.options:
|
||||||
|
prefix.append('final')
|
||||||
if 'abstractmethod' in self.options:
|
if 'abstractmethod' in self.options:
|
||||||
prefix.append('abstract')
|
prefix.append('abstract')
|
||||||
if 'async' in self.options:
|
if 'async' in self.options:
|
||||||
@ -848,7 +868,7 @@ class PyAttribute(PyObject):
|
|||||||
|
|
||||||
typ = self.options.get('type')
|
typ = self.options.get('type')
|
||||||
if typ:
|
if typ:
|
||||||
signode += addnodes.desc_annotation(typ, ': ' + typ)
|
signode += addnodes.desc_annotation(typ, '', nodes.Text(': '), type_to_xref(typ))
|
||||||
|
|
||||||
value = self.options.get('value')
|
value = self.options.get('value')
|
||||||
if value:
|
if value:
|
||||||
@ -880,10 +900,11 @@ class PyDecoratorMixin:
|
|||||||
if cls.__name__ != 'DirectiveAdapter':
|
if cls.__name__ != 'DirectiveAdapter':
|
||||||
warnings.warn('PyDecoratorMixin is deprecated. '
|
warnings.warn('PyDecoratorMixin is deprecated. '
|
||||||
'Please check the implementation of %s' % cls,
|
'Please check the implementation of %s' % cls,
|
||||||
RemovedInSphinx50Warning)
|
RemovedInSphinx50Warning, stacklevel=2)
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
warnings.warn('PyDecoratorMixin is deprecated', RemovedInSphinx50Warning)
|
warnings.warn('PyDecoratorMixin is deprecated',
|
||||||
|
RemovedInSphinx50Warning, stacklevel=2)
|
||||||
|
|
||||||
ret = super().handle_signature(sig, signode) # type: ignore
|
ret = super().handle_signature(sig, signode) # type: ignore
|
||||||
signode.insert(0, addnodes.desc_addname('@', '@'))
|
signode.insert(0, addnodes.desc_addname('@', '@'))
|
||||||
|
@ -292,7 +292,7 @@ def make_glossary_term(env: "BuildEnvironment", textnodes: Iterable[Node], index
|
|||||||
document.note_explicit_target(term)
|
document.note_explicit_target(term)
|
||||||
else:
|
else:
|
||||||
warnings.warn('make_glossary_term() expects document is passed as an argument.',
|
warnings.warn('make_glossary_term() expects document is passed as an argument.',
|
||||||
RemovedInSphinx40Warning)
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
gloss_entries = env.temp_data.setdefault('gloss_entries', set())
|
gloss_entries = env.temp_data.setdefault('gloss_entries', set())
|
||||||
node_id = nodes.make_id('term-' + termtext)
|
node_id = nodes.make_id('term-' + termtext)
|
||||||
if node_id == 'term':
|
if node_id == 'term':
|
||||||
@ -660,7 +660,7 @@ class StandardDomain(Domain):
|
|||||||
|
|
||||||
def add_object(self, objtype: str, name: str, docname: str, labelid: str) -> None:
|
def add_object(self, objtype: str, name: str, docname: str, labelid: str) -> None:
|
||||||
warnings.warn('StandardDomain.add_object() is deprecated.',
|
warnings.warn('StandardDomain.add_object() is deprecated.',
|
||||||
RemovedInSphinx50Warning)
|
RemovedInSphinx50Warning, stacklevel=2)
|
||||||
self.objects[objtype, name] = (docname, labelid)
|
self.objects[objtype, name] = (docname, labelid)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -786,9 +786,11 @@ class StandardDomain(Domain):
|
|||||||
resolver = self._resolve_option_xref
|
resolver = self._resolve_option_xref
|
||||||
elif typ == 'citation':
|
elif typ == 'citation':
|
||||||
warnings.warn('pending_xref(domain=std, type=citation) is deprecated: %r' % node,
|
warnings.warn('pending_xref(domain=std, type=citation) is deprecated: %r' % node,
|
||||||
RemovedInSphinx40Warning)
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
domain = env.get_domain('citation')
|
domain = env.get_domain('citation')
|
||||||
return domain.resolve_xref(env, fromdocname, builder, typ, target, node, contnode)
|
return domain.resolve_xref(env, fromdocname, builder, typ, target, node, contnode)
|
||||||
|
elif typ == 'term':
|
||||||
|
resolver = self._resolve_term_xref
|
||||||
else:
|
else:
|
||||||
resolver = self._resolve_obj_xref
|
resolver = self._resolve_obj_xref
|
||||||
|
|
||||||
@ -923,6 +925,28 @@ class StandardDomain(Domain):
|
|||||||
return make_refnode(builder, fromdocname, docname,
|
return make_refnode(builder, fromdocname, docname,
|
||||||
labelid, contnode)
|
labelid, contnode)
|
||||||
|
|
||||||
|
def _resolve_term_xref(self, env: "BuildEnvironment", fromdocname: str,
|
||||||
|
builder: "Builder", typ: str, target: str,
|
||||||
|
node: pending_xref, contnode: Element) -> Element:
|
||||||
|
result = self._resolve_obj_xref(env, fromdocname, builder, typ,
|
||||||
|
target, node, contnode)
|
||||||
|
if result:
|
||||||
|
return result
|
||||||
|
else:
|
||||||
|
for objtype, term in self.objects:
|
||||||
|
if objtype == 'term' and term.lower() == target.lower():
|
||||||
|
docname, labelid = self.objects[objtype, term]
|
||||||
|
logger.warning(__('term %s not found in case sensitive match.'
|
||||||
|
'made a reference to %s instead.'),
|
||||||
|
target, term, location=node, type='ref', subtype='term')
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
docname, labelid = '', ''
|
||||||
|
if not docname:
|
||||||
|
return None
|
||||||
|
return make_refnode(builder, fromdocname, docname,
|
||||||
|
labelid, contnode)
|
||||||
|
|
||||||
def _resolve_obj_xref(self, env: "BuildEnvironment", fromdocname: str,
|
def _resolve_obj_xref(self, env: "BuildEnvironment", fromdocname: str,
|
||||||
builder: "Builder", typ: str, target: str,
|
builder: "Builder", typ: str, target: str,
|
||||||
node: pending_xref, contnode: Element) -> Element:
|
node: pending_xref, contnode: Element) -> Element:
|
||||||
@ -1058,15 +1082,15 @@ class StandardDomain(Domain):
|
|||||||
|
|
||||||
def note_citations(self, env: "BuildEnvironment", docname: str, document: nodes.document) -> None: # NOQA
|
def note_citations(self, env: "BuildEnvironment", docname: str, document: nodes.document) -> None: # NOQA
|
||||||
warnings.warn('StandardDomain.note_citations() is deprecated.',
|
warnings.warn('StandardDomain.note_citations() is deprecated.',
|
||||||
RemovedInSphinx40Warning)
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
|
|
||||||
def note_citation_refs(self, env: "BuildEnvironment", docname: str, document: nodes.document) -> None: # NOQA
|
def note_citation_refs(self, env: "BuildEnvironment", docname: str, document: nodes.document) -> None: # NOQA
|
||||||
warnings.warn('StandardDomain.note_citation_refs() is deprecated.',
|
warnings.warn('StandardDomain.note_citation_refs() is deprecated.',
|
||||||
RemovedInSphinx40Warning)
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
|
|
||||||
def note_labels(self, env: "BuildEnvironment", docname: str, document: nodes.document) -> None: # NOQA
|
def note_labels(self, env: "BuildEnvironment", docname: str, document: nodes.document) -> None: # NOQA
|
||||||
warnings.warn('StandardDomain.note_labels() is deprecated.',
|
warnings.warn('StandardDomain.note_labels() is deprecated.',
|
||||||
RemovedInSphinx40Warning)
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
|
|
||||||
|
|
||||||
def setup(app: "Sphinx") -> Dict[str, Any]:
|
def setup(app: "Sphinx") -> Dict[str, Any]:
|
||||||
|
@ -331,10 +331,10 @@ class BuildEnvironment:
|
|||||||
"""
|
"""
|
||||||
if suffix:
|
if suffix:
|
||||||
warnings.warn('The suffix argument for doc2path() is deprecated.',
|
warnings.warn('The suffix argument for doc2path() is deprecated.',
|
||||||
RemovedInSphinx40Warning)
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
if base not in (True, False, None):
|
if base not in (True, False, None):
|
||||||
warnings.warn('The string style base argument for doc2path() is deprecated.',
|
warnings.warn('The string style base argument for doc2path() is deprecated.',
|
||||||
RemovedInSphinx40Warning)
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
|
|
||||||
pathname = self.project.doc2path(docname, base is True)
|
pathname = self.project.doc2path(docname, base is True)
|
||||||
if suffix:
|
if suffix:
|
||||||
|
@ -29,7 +29,7 @@ class IndexEntriesCollector(EnvironmentCollector):
|
|||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
warnings.warn('IndexEntriesCollector is deprecated.',
|
warnings.warn('IndexEntriesCollector is deprecated.',
|
||||||
RemovedInSphinx40Warning)
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
|
|
||||||
def clear_doc(self, app: Sphinx, env: BuildEnvironment, docname: str) -> None:
|
def clear_doc(self, app: Sphinx, env: BuildEnvironment, docname: str) -> None:
|
||||||
env.indexentries.pop(docname, None)
|
env.indexentries.pop(docname, None)
|
||||||
|
@ -116,7 +116,8 @@ class PycodeError(Exception):
|
|||||||
|
|
||||||
|
|
||||||
class NoUri(Exception):
|
class NoUri(Exception):
|
||||||
"""Raised by builder.get_relative_uri() if there is no URI available."""
|
"""Raised by builder.get_relative_uri() or from missing-reference handlers
|
||||||
|
if there is no URI available."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@ -13,15 +13,16 @@
|
|||||||
import warnings
|
import warnings
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
from typing import Any, Callable, Dict, List, NamedTuple
|
from typing import Any, Callable, Dict, List, NamedTuple, Tuple
|
||||||
|
|
||||||
from sphinx.deprecation import RemovedInSphinx40Warning
|
from sphinx.deprecation import RemovedInSphinx40Warning
|
||||||
from sphinx.errors import ExtensionError
|
from sphinx.errors import ExtensionError, SphinxError
|
||||||
from sphinx.locale import __
|
from sphinx.locale import __
|
||||||
from sphinx.util import logging
|
from sphinx.util import logging
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
# For type annotation
|
# For type annotation
|
||||||
|
from typing import Type # for python3.5.1
|
||||||
from sphinx.application import Sphinx
|
from sphinx.application import Sphinx
|
||||||
|
|
||||||
|
|
||||||
@ -88,7 +89,8 @@ class EventManager:
|
|||||||
if listener.id == listener_id:
|
if listener.id == listener_id:
|
||||||
listeners.remove(listener)
|
listeners.remove(listener)
|
||||||
|
|
||||||
def emit(self, name: str, *args: Any) -> List:
|
def emit(self, name: str, *args: Any,
|
||||||
|
allowed_exceptions: Tuple["Type[Exception]", ...] = ()) -> List:
|
||||||
"""Emit a Sphinx event."""
|
"""Emit a Sphinx event."""
|
||||||
try:
|
try:
|
||||||
logger.debug('[app] emitting event: %r%s', name, repr(args)[:100])
|
logger.debug('[app] emitting event: %r%s', name, repr(args)[:100])
|
||||||
@ -100,19 +102,29 @@ class EventManager:
|
|||||||
results = []
|
results = []
|
||||||
listeners = sorted(self.listeners[name], key=attrgetter("priority"))
|
listeners = sorted(self.listeners[name], key=attrgetter("priority"))
|
||||||
for listener in listeners:
|
for listener in listeners:
|
||||||
|
try:
|
||||||
if self.app is None:
|
if self.app is None:
|
||||||
# for compatibility; RemovedInSphinx40Warning
|
# for compatibility; RemovedInSphinx40Warning
|
||||||
results.append(listener.handler(*args))
|
results.append(listener.handler(*args))
|
||||||
else:
|
else:
|
||||||
results.append(listener.handler(self.app, *args))
|
results.append(listener.handler(self.app, *args))
|
||||||
|
except allowed_exceptions:
|
||||||
|
# pass through the errors specified as *allowed_exceptions*
|
||||||
|
raise
|
||||||
|
except SphinxError:
|
||||||
|
raise
|
||||||
|
except Exception as exc:
|
||||||
|
raise ExtensionError(__("Handler %r for event %r threw an exception") %
|
||||||
|
(listener.handler, name)) from exc
|
||||||
return results
|
return results
|
||||||
|
|
||||||
def emit_firstresult(self, name: str, *args: Any) -> Any:
|
def emit_firstresult(self, name: str, *args: Any,
|
||||||
|
allowed_exceptions: Tuple["Type[Exception]", ...] = ()) -> Any:
|
||||||
"""Emit a Sphinx event and returns first result.
|
"""Emit a Sphinx event and returns first result.
|
||||||
|
|
||||||
This returns the result of the first handler that doesn't return ``None``.
|
This returns the result of the first handler that doesn't return ``None``.
|
||||||
"""
|
"""
|
||||||
for result in self.emit(name, *args):
|
for result in self.emit(name, *args, allowed_exceptions=allowed_exceptions):
|
||||||
if result is not None:
|
if result is not None:
|
||||||
return result
|
return result
|
||||||
return None
|
return None
|
||||||
|
@ -54,7 +54,7 @@ template_dir = path.join(package_dir, 'templates', 'apidoc')
|
|||||||
def makename(package: str, module: str) -> str:
|
def makename(package: str, module: str) -> str:
|
||||||
"""Join package and module with a dot."""
|
"""Join package and module with a dot."""
|
||||||
warnings.warn('makename() is deprecated.',
|
warnings.warn('makename() is deprecated.',
|
||||||
RemovedInSphinx40Warning)
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
# Both package and module can be None/empty.
|
# Both package and module can be None/empty.
|
||||||
if package:
|
if package:
|
||||||
name = package
|
name = package
|
||||||
@ -112,7 +112,7 @@ def write_file(name: str, text: str, opts: Any) -> None:
|
|||||||
def format_heading(level: int, text: str, escape: bool = True) -> str:
|
def format_heading(level: int, text: str, escape: bool = True) -> str:
|
||||||
"""Create a heading of <level> [1, 2 or 3 supported]."""
|
"""Create a heading of <level> [1, 2 or 3 supported]."""
|
||||||
warnings.warn('format_warning() is deprecated.',
|
warnings.warn('format_warning() is deprecated.',
|
||||||
RemovedInSphinx40Warning)
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
if escape:
|
if escape:
|
||||||
text = rst.escape(text)
|
text = rst.escape(text)
|
||||||
underlining = ['=', '-', '~', ][level - 1] * len(text)
|
underlining = ['=', '-', '~', ][level - 1] * len(text)
|
||||||
@ -122,7 +122,7 @@ def format_heading(level: int, text: str, escape: bool = True) -> str:
|
|||||||
def format_directive(module: str, package: str = None) -> str:
|
def format_directive(module: str, package: str = None) -> str:
|
||||||
"""Create the automodule directive and add the options."""
|
"""Create the automodule directive and add the options."""
|
||||||
warnings.warn('format_directive() is deprecated.',
|
warnings.warn('format_directive() is deprecated.',
|
||||||
RemovedInSphinx40Warning)
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
directive = '.. automodule:: %s\n' % module_join(package, module)
|
directive = '.. automodule:: %s\n' % module_join(package, module)
|
||||||
for option in OPTIONS:
|
for option in OPTIONS:
|
||||||
directive += ' :%s:\n' % option
|
directive += ' :%s:\n' % option
|
||||||
@ -209,7 +209,7 @@ def create_modules_toc_file(modules: List[str], opts: Any, name: str = 'modules'
|
|||||||
def shall_skip(module: str, opts: Any, excludes: List[str] = []) -> bool:
|
def shall_skip(module: str, opts: Any, excludes: List[str] = []) -> bool:
|
||||||
"""Check if we want to skip this module."""
|
"""Check if we want to skip this module."""
|
||||||
warnings.warn('shall_skip() is deprecated.',
|
warnings.warn('shall_skip() is deprecated.',
|
||||||
RemovedInSphinx40Warning)
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
# skip if the file doesn't exist and not using implicit namespaces
|
# skip if the file doesn't exist and not using implicit namespaces
|
||||||
if not opts.implicit_namespaces and not path.exists(module):
|
if not opts.implicit_namespaces and not path.exists(module):
|
||||||
return True
|
return True
|
||||||
|
@ -16,13 +16,12 @@ import warnings
|
|||||||
from inspect import Parameter
|
from inspect import Parameter
|
||||||
from types import ModuleType
|
from types import ModuleType
|
||||||
from typing import Any, Callable, Dict, Iterator, List, Sequence, Set, Tuple, Type, Union
|
from typing import Any, Callable, Dict, Iterator, List, Sequence, Set, Tuple, Type, Union
|
||||||
from unittest.mock import patch
|
|
||||||
|
|
||||||
from docutils.statemachine import StringList
|
from docutils.statemachine import StringList
|
||||||
|
|
||||||
import sphinx
|
import sphinx
|
||||||
from sphinx.application import Sphinx
|
from sphinx.application import Sphinx
|
||||||
from sphinx.config import ENUM
|
from sphinx.config import Config, ENUM
|
||||||
from sphinx.deprecation import RemovedInSphinx40Warning, RemovedInSphinx50Warning
|
from sphinx.deprecation import RemovedInSphinx40Warning, RemovedInSphinx50Warning
|
||||||
from sphinx.environment import BuildEnvironment
|
from sphinx.environment import BuildEnvironment
|
||||||
from sphinx.ext.autodoc.importer import import_object, get_module_members, get_object_members
|
from sphinx.ext.autodoc.importer import import_object, get_module_members, get_object_members
|
||||||
@ -31,7 +30,7 @@ from sphinx.locale import _, __
|
|||||||
from sphinx.pycode import ModuleAnalyzer, PycodeError
|
from sphinx.pycode import ModuleAnalyzer, PycodeError
|
||||||
from sphinx.util import inspect
|
from sphinx.util import inspect
|
||||||
from sphinx.util import logging
|
from sphinx.util import logging
|
||||||
from sphinx.util import rpartition
|
from sphinx.util import split_full_qualified_name
|
||||||
from sphinx.util.docstrings import extract_metadata, prepare_docstring
|
from sphinx.util.docstrings import extract_metadata, prepare_docstring
|
||||||
from sphinx.util.inspect import getdoc, object_description, safe_getattr, stringify_signature
|
from sphinx.util.inspect import getdoc, object_description, safe_getattr, stringify_signature
|
||||||
from sphinx.util.typing import stringify as stringify_typehint
|
from sphinx.util.typing import stringify as stringify_typehint
|
||||||
@ -66,6 +65,7 @@ def identity(x: Any) -> Any:
|
|||||||
|
|
||||||
|
|
||||||
ALL = object()
|
ALL = object()
|
||||||
|
UNINITIALIZED_ATTR = object()
|
||||||
INSTANCEATTR = object()
|
INSTANCEATTR = object()
|
||||||
SLOTSATTR = object()
|
SLOTSATTR = object()
|
||||||
|
|
||||||
@ -311,6 +311,7 @@ class Documenter:
|
|||||||
modname = None
|
modname = None
|
||||||
parents = []
|
parents = []
|
||||||
|
|
||||||
|
with mock(self.env.config.autodoc_mock_imports):
|
||||||
self.modname, self.objpath = self.resolve_name(modname, parents, path, base)
|
self.modname, self.objpath = self.resolve_name(modname, parents, path, base)
|
||||||
|
|
||||||
if not self.modname:
|
if not self.modname:
|
||||||
@ -395,9 +396,9 @@ class Documenter:
|
|||||||
except TypeError:
|
except TypeError:
|
||||||
# retry without arguments for old documenters
|
# retry without arguments for old documenters
|
||||||
args = self.format_args()
|
args = self.format_args()
|
||||||
except Exception as err:
|
except Exception:
|
||||||
logger.warning(__('error while formatting arguments for %s: %s') %
|
logger.warning(__('error while formatting arguments for %s:') %
|
||||||
(self.fullname, err), type='autodoc')
|
self.fullname, type='autodoc', exc_info=True)
|
||||||
args = None
|
args = None
|
||||||
|
|
||||||
retann = self.retann
|
retann = self.retann
|
||||||
@ -419,8 +420,15 @@ class Documenter:
|
|||||||
directive = getattr(self, 'directivetype', self.objtype)
|
directive = getattr(self, 'directivetype', self.objtype)
|
||||||
name = self.format_name()
|
name = self.format_name()
|
||||||
sourcename = self.get_sourcename()
|
sourcename = self.get_sourcename()
|
||||||
self.add_line('.. %s:%s:: %s%s' % (domain, directive, name, sig),
|
|
||||||
|
# one signature per line, indented by column
|
||||||
|
prefix = '.. %s:%s:: ' % (domain, directive)
|
||||||
|
for i, sig_line in enumerate(sig.split("\n")):
|
||||||
|
self.add_line('%s%s%s' % (prefix, name, sig_line),
|
||||||
sourcename)
|
sourcename)
|
||||||
|
if i == 0:
|
||||||
|
prefix = " " * len(prefix)
|
||||||
|
|
||||||
if self.options.noindex:
|
if self.options.noindex:
|
||||||
self.add_line(' :noindex:', sourcename)
|
self.add_line(' :noindex:', sourcename)
|
||||||
if self.objpath:
|
if self.objpath:
|
||||||
@ -428,14 +436,19 @@ class Documenter:
|
|||||||
# etc. don't support a prepended module name
|
# etc. don't support a prepended module name
|
||||||
self.add_line(' :module: %s' % self.modname, sourcename)
|
self.add_line(' :module: %s' % self.modname, sourcename)
|
||||||
|
|
||||||
def get_doc(self, encoding: str = None, ignore: int = 1) -> List[List[str]]:
|
def get_doc(self, encoding: str = None, ignore: int = None) -> List[List[str]]:
|
||||||
"""Decode and return lines of the docstring(s) for the object."""
|
"""Decode and return lines of the docstring(s) for the object."""
|
||||||
if encoding is not None:
|
if encoding is not None:
|
||||||
warnings.warn("The 'encoding' argument to autodoc.%s.get_doc() is deprecated."
|
warnings.warn("The 'encoding' argument to autodoc.%s.get_doc() is deprecated."
|
||||||
% self.__class__.__name__,
|
% self.__class__.__name__,
|
||||||
RemovedInSphinx40Warning)
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
|
if ignore is not None:
|
||||||
|
warnings.warn("The 'ignore' argument to autodoc.%s.get_doc() is deprecated."
|
||||||
|
% self.__class__.__name__,
|
||||||
|
RemovedInSphinx50Warning, stacklevel=2)
|
||||||
docstring = getdoc(self.object, self.get_attr,
|
docstring = getdoc(self.object, self.get_attr,
|
||||||
self.env.config.autodoc_inherit_docstrings)
|
self.env.config.autodoc_inherit_docstrings,
|
||||||
|
self.parent, self.object_name)
|
||||||
if docstring:
|
if docstring:
|
||||||
tab_width = self.directive.state.document.settings.tab_width
|
tab_width = self.directive.state.document.settings.tab_width
|
||||||
return [prepare_docstring(docstring, ignore, tab_width)]
|
return [prepare_docstring(docstring, ignore, tab_width)]
|
||||||
@ -466,7 +479,10 @@ class Documenter:
|
|||||||
key = ('.'.join(self.objpath[:-1]), self.objpath[-1])
|
key = ('.'.join(self.objpath[:-1]), self.objpath[-1])
|
||||||
if key in attr_docs:
|
if key in attr_docs:
|
||||||
no_docstring = True
|
no_docstring = True
|
||||||
docstrings = [attr_docs[key]]
|
# make a copy of docstring for attributes to avoid cache
|
||||||
|
# the change of autodoc-process-docstring event.
|
||||||
|
docstrings = [list(attr_docs[key])]
|
||||||
|
|
||||||
for i, line in enumerate(self.process_doc(docstrings)):
|
for i, line in enumerate(self.process_doc(docstrings)):
|
||||||
self.add_line(line, sourcename, i)
|
self.add_line(line, sourcename, i)
|
||||||
|
|
||||||
@ -556,7 +572,8 @@ class Documenter:
|
|||||||
else:
|
else:
|
||||||
isattr = False
|
isattr = False
|
||||||
|
|
||||||
doc = getdoc(member, self.get_attr, self.env.config.autodoc_inherit_docstrings)
|
doc = getdoc(member, self.get_attr, self.env.config.autodoc_inherit_docstrings,
|
||||||
|
self.parent, self.object_name)
|
||||||
if not isinstance(doc, str):
|
if not isinstance(doc, str):
|
||||||
# Ignore non-string __doc__
|
# Ignore non-string __doc__
|
||||||
doc = None
|
doc = None
|
||||||
@ -574,11 +591,17 @@ class Documenter:
|
|||||||
if 'private' in metadata:
|
if 'private' in metadata:
|
||||||
# consider a member private if docstring has "private" metadata
|
# consider a member private if docstring has "private" metadata
|
||||||
isprivate = True
|
isprivate = True
|
||||||
|
elif 'public' in metadata:
|
||||||
|
# consider a member public if docstring has "public" metadata
|
||||||
|
isprivate = False
|
||||||
else:
|
else:
|
||||||
isprivate = membername.startswith('_')
|
isprivate = membername.startswith('_')
|
||||||
|
|
||||||
keep = False
|
keep = False
|
||||||
if want_all and membername.startswith('__') and \
|
if safe_getattr(member, '__sphinx_mock__', False):
|
||||||
|
# mocked module or object
|
||||||
|
pass
|
||||||
|
elif want_all and membername.startswith('__') and \
|
||||||
membername.endswith('__') and len(membername) > 4:
|
membername.endswith('__') and len(membername) > 4:
|
||||||
# special __methods__
|
# special __methods__
|
||||||
if self.options.special_members is ALL:
|
if self.options.special_members is ALL:
|
||||||
@ -725,7 +748,8 @@ class Documenter:
|
|||||||
# where the attribute documentation would actually be found in.
|
# where the attribute documentation would actually be found in.
|
||||||
# This is used for situations where you have a module that collects the
|
# This is used for situations where you have a module that collects the
|
||||||
# functions and classes of internal submodules.
|
# functions and classes of internal submodules.
|
||||||
self.real_modname = real_modname or self.get_real_modname() # type: str
|
guess_modname = self.get_real_modname()
|
||||||
|
self.real_modname = real_modname or guess_modname
|
||||||
|
|
||||||
# try to also get a source code analyzer for attribute docs
|
# try to also get a source code analyzer for attribute docs
|
||||||
try:
|
try:
|
||||||
@ -733,8 +757,8 @@ class Documenter:
|
|||||||
# parse right now, to get PycodeErrors on parsing (results will
|
# parse right now, to get PycodeErrors on parsing (results will
|
||||||
# be cached anyway)
|
# be cached anyway)
|
||||||
self.analyzer.find_attr_docs()
|
self.analyzer.find_attr_docs()
|
||||||
except PycodeError as err:
|
except PycodeError:
|
||||||
logger.debug('[autodoc] module analyzer failed: %s', err)
|
logger.debug('[autodoc] module analyzer failed:', exc_info=True)
|
||||||
# no source file -- e.g. for builtin and C modules
|
# no source file -- e.g. for builtin and C modules
|
||||||
self.analyzer = None
|
self.analyzer = None
|
||||||
# at least add the module.__file__ as a dependency
|
# at least add the module.__file__ as a dependency
|
||||||
@ -743,6 +767,14 @@ class Documenter:
|
|||||||
else:
|
else:
|
||||||
self.directive.filename_set.add(self.analyzer.srcname)
|
self.directive.filename_set.add(self.analyzer.srcname)
|
||||||
|
|
||||||
|
if self.real_modname != guess_modname:
|
||||||
|
# Add module to dependency list if target object is defined in other module.
|
||||||
|
try:
|
||||||
|
analyzer = ModuleAnalyzer.for_module(guess_modname)
|
||||||
|
self.directive.filename_set.add(analyzer.srcname)
|
||||||
|
except PycodeError:
|
||||||
|
pass
|
||||||
|
|
||||||
# check __module__ of object (for members not given explicitly)
|
# check __module__ of object (for members not given explicitly)
|
||||||
if check_module:
|
if check_module:
|
||||||
if not self.check_module():
|
if not self.check_module():
|
||||||
@ -828,7 +860,7 @@ class ModuleDocumenter(Documenter):
|
|||||||
if self.options.deprecated:
|
if self.options.deprecated:
|
||||||
self.add_line(' :deprecated:', sourcename)
|
self.add_line(' :deprecated:', sourcename)
|
||||||
|
|
||||||
def get_object_members(self, want_all: bool) -> Tuple[bool, List[Tuple[str, object]]]:
|
def get_object_members(self, want_all: bool) -> Tuple[bool, List[Tuple[str, Any]]]:
|
||||||
if want_all:
|
if want_all:
|
||||||
if (self.options.ignore_module_all or not
|
if (self.options.ignore_module_all or not
|
||||||
hasattr(self.object, '__all__')):
|
hasattr(self.object, '__all__')):
|
||||||
@ -873,8 +905,14 @@ class ModuleLevelDocumenter(Documenter):
|
|||||||
) -> Tuple[str, List[str]]:
|
) -> Tuple[str, List[str]]:
|
||||||
if modname is None:
|
if modname is None:
|
||||||
if path:
|
if path:
|
||||||
modname = path.rstrip('.')
|
stripped = path.rstrip('.')
|
||||||
|
modname, qualname = split_full_qualified_name(stripped)
|
||||||
|
if qualname:
|
||||||
|
parents = qualname.split(".")
|
||||||
else:
|
else:
|
||||||
|
parents = []
|
||||||
|
|
||||||
|
if modname is None:
|
||||||
# if documenting a toplevel object without explicit module,
|
# if documenting a toplevel object without explicit module,
|
||||||
# it can be contained in another auto directive ...
|
# it can be contained in another auto directive ...
|
||||||
modname = self.env.temp_data.get('autodoc:module')
|
modname = self.env.temp_data.get('autodoc:module')
|
||||||
@ -907,8 +945,13 @@ class ClassLevelDocumenter(Documenter):
|
|||||||
# ... if still None, there's no way to know
|
# ... if still None, there's no way to know
|
||||||
if mod_cls is None:
|
if mod_cls is None:
|
||||||
return None, []
|
return None, []
|
||||||
modname, cls = rpartition(mod_cls, '.')
|
|
||||||
parents = [cls]
|
try:
|
||||||
|
modname, qualname = split_full_qualified_name(mod_cls)
|
||||||
|
parents = qualname.split(".") if qualname else []
|
||||||
|
except ImportError:
|
||||||
|
parents = mod_cls.split(".")
|
||||||
|
|
||||||
# if the module name is still missing, get it like above
|
# if the module name is still missing, get it like above
|
||||||
if not modname:
|
if not modname:
|
||||||
modname = self.env.temp_data.get('autodoc:module')
|
modname = self.env.temp_data.get('autodoc:module')
|
||||||
@ -928,7 +971,7 @@ class DocstringSignatureMixin:
|
|||||||
if encoding is not None:
|
if encoding is not None:
|
||||||
warnings.warn("The 'encoding' argument to autodoc.%s._find_signature() is "
|
warnings.warn("The 'encoding' argument to autodoc.%s._find_signature() is "
|
||||||
"deprecated." % self.__class__.__name__,
|
"deprecated." % self.__class__.__name__,
|
||||||
RemovedInSphinx40Warning)
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
docstrings = self.get_doc()
|
docstrings = self.get_doc()
|
||||||
self._new_docstrings = docstrings[:]
|
self._new_docstrings = docstrings[:]
|
||||||
result = None
|
result = None
|
||||||
@ -958,11 +1001,11 @@ class DocstringSignatureMixin:
|
|||||||
break
|
break
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def get_doc(self, encoding: str = None, ignore: int = 1) -> List[List[str]]:
|
def get_doc(self, encoding: str = None, ignore: int = None) -> List[List[str]]:
|
||||||
if encoding is not None:
|
if encoding is not None:
|
||||||
warnings.warn("The 'encoding' argument to autodoc.%s.get_doc() is deprecated."
|
warnings.warn("The 'encoding' argument to autodoc.%s.get_doc() is deprecated."
|
||||||
% self.__class__.__name__,
|
% self.__class__.__name__,
|
||||||
RemovedInSphinx40Warning)
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
lines = getattr(self, '_new_docstrings', None)
|
lines = getattr(self, '_new_docstrings', None)
|
||||||
if lines is not None:
|
if lines is not None:
|
||||||
return lines
|
return lines
|
||||||
@ -1014,43 +1057,19 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
|
|||||||
if self.env.config.autodoc_typehints in ('none', 'description'):
|
if self.env.config.autodoc_typehints in ('none', 'description'):
|
||||||
kwargs.setdefault('show_annotation', False)
|
kwargs.setdefault('show_annotation', False)
|
||||||
|
|
||||||
unwrapped = inspect.unwrap(self.object)
|
|
||||||
if ((inspect.isbuiltin(unwrapped) or inspect.ismethoddescriptor(unwrapped)) and
|
|
||||||
not inspect.is_cython_function_or_method(unwrapped)):
|
|
||||||
# cannot introspect arguments of a C function or method
|
|
||||||
return None
|
|
||||||
try:
|
try:
|
||||||
if (not inspect.isfunction(unwrapped) and
|
self.env.app.emit('autodoc-before-process-signature', self.object, False)
|
||||||
not inspect.ismethod(unwrapped) and
|
if inspect.is_singledispatch_function(self.object):
|
||||||
not inspect.isbuiltin(unwrapped) and
|
sig = inspect.signature(self.object, follow_wrapped=True)
|
||||||
not inspect.is_cython_function_or_method(unwrapped) and
|
|
||||||
not inspect.isclass(unwrapped) and
|
|
||||||
hasattr(unwrapped, '__call__')):
|
|
||||||
self.env.app.emit('autodoc-before-process-signature',
|
|
||||||
unwrapped.__call__, False)
|
|
||||||
sig = inspect.signature(unwrapped.__call__)
|
|
||||||
else:
|
else:
|
||||||
self.env.app.emit('autodoc-before-process-signature', unwrapped, False)
|
sig = inspect.signature(self.object)
|
||||||
sig = inspect.signature(unwrapped)
|
|
||||||
args = stringify_signature(sig, **kwargs)
|
args = stringify_signature(sig, **kwargs)
|
||||||
except TypeError:
|
except TypeError as exc:
|
||||||
if (inspect.is_builtin_class_method(unwrapped, '__new__') and
|
logger.warning(__("Failed to get a function signature for %s: %s"),
|
||||||
inspect.is_builtin_class_method(unwrapped, '__init__')):
|
self.fullname, exc)
|
||||||
raise TypeError('%r is a builtin class' % unwrapped)
|
return None
|
||||||
|
except ValueError:
|
||||||
# if a class should be documented as function (yay duck
|
args = ''
|
||||||
# typing) we try to use the constructor signature as function
|
|
||||||
# signature without the first argument.
|
|
||||||
try:
|
|
||||||
self.env.app.emit('autodoc-before-process-signature',
|
|
||||||
unwrapped.__new__, True)
|
|
||||||
sig = inspect.signature(unwrapped.__new__, bound_method=True)
|
|
||||||
args = stringify_signature(sig, show_return_annotation=False, **kwargs)
|
|
||||||
except TypeError:
|
|
||||||
self.env.app.emit('autodoc-before-process-signature',
|
|
||||||
unwrapped.__init__, True)
|
|
||||||
sig = inspect.signature(unwrapped.__init__, bound_method=True)
|
|
||||||
args = stringify_signature(sig, show_return_annotation=False, **kwargs)
|
|
||||||
|
|
||||||
if self.env.config.strip_signature_backslash:
|
if self.env.config.strip_signature_backslash:
|
||||||
# escape backslashes for reST
|
# escape backslashes for reST
|
||||||
@ -1067,36 +1086,12 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
|
|||||||
if inspect.iscoroutinefunction(self.object):
|
if inspect.iscoroutinefunction(self.object):
|
||||||
self.add_line(' :async:', sourcename)
|
self.add_line(' :async:', sourcename)
|
||||||
|
|
||||||
|
def format_signature(self, **kwargs: Any) -> str:
|
||||||
|
sig = super().format_signature(**kwargs)
|
||||||
|
sigs = [sig]
|
||||||
|
|
||||||
class SingledispatchFunctionDocumenter(FunctionDocumenter):
|
if inspect.is_singledispatch_function(self.object):
|
||||||
"""
|
# append signature of singledispatch'ed functions
|
||||||
Specialized Documenter subclass for singledispatch'ed functions.
|
|
||||||
"""
|
|
||||||
objtype = 'singledispatch_function'
|
|
||||||
directivetype = 'function'
|
|
||||||
member_order = 30
|
|
||||||
|
|
||||||
# before FunctionDocumenter
|
|
||||||
priority = FunctionDocumenter.priority + 1
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any
|
|
||||||
) -> bool:
|
|
||||||
return (super().can_document_member(member, membername, isattr, parent) and
|
|
||||||
inspect.is_singledispatch_function(member))
|
|
||||||
|
|
||||||
def add_directive_header(self, sig: str) -> None:
|
|
||||||
sourcename = self.get_sourcename()
|
|
||||||
|
|
||||||
# intercept generated directive headers
|
|
||||||
# TODO: It is very hacky to use mock to intercept header generation
|
|
||||||
with patch.object(self, 'add_line') as add_line:
|
|
||||||
super().add_directive_header(sig)
|
|
||||||
|
|
||||||
# output first line of header
|
|
||||||
self.add_line(*add_line.call_args_list[0][0])
|
|
||||||
|
|
||||||
# inserts signature of singledispatch'ed functions
|
|
||||||
for typ, func in self.object.registry.items():
|
for typ, func in self.object.registry.items():
|
||||||
if typ is object:
|
if typ is object:
|
||||||
pass # default implementation. skipped.
|
pass # default implementation. skipped.
|
||||||
@ -1105,13 +1100,9 @@ class SingledispatchFunctionDocumenter(FunctionDocumenter):
|
|||||||
|
|
||||||
documenter = FunctionDocumenter(self.directive, '')
|
documenter = FunctionDocumenter(self.directive, '')
|
||||||
documenter.object = func
|
documenter.object = func
|
||||||
self.add_line(' %s%s' % (self.format_name(),
|
sigs.append(documenter.format_signature())
|
||||||
documenter.format_signature()),
|
|
||||||
sourcename)
|
|
||||||
|
|
||||||
# output remains of directive header
|
return "\n".join(sigs)
|
||||||
for call in add_line.call_args_list[1:]:
|
|
||||||
self.add_line(*call[0])
|
|
||||||
|
|
||||||
def annotate_to_first_argument(self, func: Callable, typ: Type) -> None:
|
def annotate_to_first_argument(self, func: Callable, typ: Type) -> None:
|
||||||
"""Annotate type hint to the first argument of function if needed."""
|
"""Annotate type hint to the first argument of function if needed."""
|
||||||
@ -1125,6 +1116,14 @@ class SingledispatchFunctionDocumenter(FunctionDocumenter):
|
|||||||
func.__signature__ = sig.replace(parameters=params) # type: ignore
|
func.__signature__ = sig.replace(parameters=params) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
class SingledispatchFunctionDocumenter(FunctionDocumenter):
|
||||||
|
"""
|
||||||
|
Used to be a specialized Documenter subclass for singledispatch'ed functions.
|
||||||
|
|
||||||
|
Retained for backwards compatibility, now does the same as the FunctionDocumenter
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class DecoratorDocumenter(FunctionDocumenter):
|
class DecoratorDocumenter(FunctionDocumenter):
|
||||||
"""
|
"""
|
||||||
Specialized Documenter subclass for decorator functions.
|
Specialized Documenter subclass for decorator functions.
|
||||||
@ -1204,10 +1203,15 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
|
|||||||
return super().format_signature(**kwargs)
|
return super().format_signature(**kwargs)
|
||||||
|
|
||||||
def add_directive_header(self, sig: str) -> None:
|
def add_directive_header(self, sig: str) -> None:
|
||||||
|
sourcename = self.get_sourcename()
|
||||||
|
|
||||||
if self.doc_as_attr:
|
if self.doc_as_attr:
|
||||||
self.directivetype = 'attribute'
|
self.directivetype = 'attribute'
|
||||||
super().add_directive_header(sig)
|
super().add_directive_header(sig)
|
||||||
|
|
||||||
|
if self.analyzer and '.'.join(self.objpath) in self.analyzer.finals:
|
||||||
|
self.add_line(' :final:', sourcename)
|
||||||
|
|
||||||
# add inheritance info, if wanted
|
# add inheritance info, if wanted
|
||||||
if not self.doc_as_attr and self.options.show_inheritance:
|
if not self.doc_as_attr and self.options.show_inheritance:
|
||||||
sourcename = self.get_sourcename()
|
sourcename = self.get_sourcename()
|
||||||
@ -1220,11 +1224,11 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
|
|||||||
self.add_line(' ' + _('Bases: %s') % ', '.join(bases),
|
self.add_line(' ' + _('Bases: %s') % ', '.join(bases),
|
||||||
sourcename)
|
sourcename)
|
||||||
|
|
||||||
def get_doc(self, encoding: str = None, ignore: int = 1) -> List[List[str]]:
|
def get_doc(self, encoding: str = None, ignore: int = None) -> List[List[str]]:
|
||||||
if encoding is not None:
|
if encoding is not None:
|
||||||
warnings.warn("The 'encoding' argument to autodoc.%s.get_doc() is deprecated."
|
warnings.warn("The 'encoding' argument to autodoc.%s.get_doc() is deprecated."
|
||||||
% self.__class__.__name__,
|
% self.__class__.__name__,
|
||||||
RemovedInSphinx40Warning)
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
lines = getattr(self, '_new_docstrings', None)
|
lines = getattr(self, '_new_docstrings', None)
|
||||||
if lines is not None:
|
if lines is not None:
|
||||||
return lines
|
return lines
|
||||||
@ -1241,7 +1245,8 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
|
|||||||
if content in ('both', 'init'):
|
if content in ('both', 'init'):
|
||||||
__init__ = self.get_attr(self.object, '__init__', None)
|
__init__ = self.get_attr(self.object, '__init__', None)
|
||||||
initdocstring = getdoc(__init__, self.get_attr,
|
initdocstring = getdoc(__init__, self.get_attr,
|
||||||
self.env.config.autodoc_inherit_docstrings)
|
self.env.config.autodoc_inherit_docstrings,
|
||||||
|
self.parent, self.object_name)
|
||||||
# for new-style classes, no __init__ means default __init__
|
# for new-style classes, no __init__ means default __init__
|
||||||
if (initdocstring is not None and
|
if (initdocstring is not None and
|
||||||
(initdocstring == object.__init__.__doc__ or # for pypy
|
(initdocstring == object.__init__.__doc__ or # for pypy
|
||||||
@ -1251,7 +1256,8 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
|
|||||||
# try __new__
|
# try __new__
|
||||||
__new__ = self.get_attr(self.object, '__new__', None)
|
__new__ = self.get_attr(self.object, '__new__', None)
|
||||||
initdocstring = getdoc(__new__, self.get_attr,
|
initdocstring = getdoc(__new__, self.get_attr,
|
||||||
self.env.config.autodoc_inherit_docstrings)
|
self.env.config.autodoc_inherit_docstrings,
|
||||||
|
self.parent, self.object_name)
|
||||||
# for new-style classes, no __new__ means default __new__
|
# for new-style classes, no __new__ means default __new__
|
||||||
if (initdocstring is not None and
|
if (initdocstring is not None and
|
||||||
(initdocstring == object.__new__.__doc__ or # for pypy
|
(initdocstring == object.__new__.__doc__ or # for pypy
|
||||||
@ -1345,6 +1351,9 @@ class DataDocumenter(ModuleLevelDocumenter):
|
|||||||
sourcename)
|
sourcename)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
if self.object is UNINITIALIZED_ATTR:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
objrepr = object_description(self.object)
|
objrepr = object_description(self.object)
|
||||||
self.add_line(' :value: ' + objrepr, sourcename)
|
self.add_line(' :value: ' + objrepr, sourcename)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
@ -1387,6 +1396,7 @@ class DataDeclarationDocumenter(DataDocumenter):
|
|||||||
"""Never import anything."""
|
"""Never import anything."""
|
||||||
# disguise as a data
|
# disguise as a data
|
||||||
self.objtype = 'data'
|
self.objtype = 'data'
|
||||||
|
self.object = UNINITIALIZED_ATTR
|
||||||
try:
|
try:
|
||||||
# import module to obtain type annotation
|
# import module to obtain type annotation
|
||||||
self.parent = importlib.import_module(self.modname)
|
self.parent = importlib.import_module(self.modname)
|
||||||
@ -1436,18 +1446,33 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
|
|||||||
if self.env.config.autodoc_typehints in ('none', 'description'):
|
if self.env.config.autodoc_typehints in ('none', 'description'):
|
||||||
kwargs.setdefault('show_annotation', False)
|
kwargs.setdefault('show_annotation', False)
|
||||||
|
|
||||||
unwrapped = inspect.unwrap(self.object)
|
try:
|
||||||
if ((inspect.isbuiltin(unwrapped) or inspect.ismethoddescriptor(unwrapped)) and
|
if self.object == object.__init__ and self.parent != object:
|
||||||
not inspect.is_cython_function_or_method(unwrapped)):
|
# Classes not having own __init__() method are shown as no arguments.
|
||||||
# can never get arguments of a C function or method
|
#
|
||||||
return None
|
# Note: The signature of object.__init__() is (self, /, *args, **kwargs).
|
||||||
if inspect.isstaticmethod(unwrapped, cls=self.parent, name=self.object_name):
|
# But it makes users confused.
|
||||||
self.env.app.emit('autodoc-before-process-signature', unwrapped, False)
|
args = '()'
|
||||||
sig = inspect.signature(unwrapped, bound_method=False)
|
|
||||||
else:
|
else:
|
||||||
self.env.app.emit('autodoc-before-process-signature', unwrapped, True)
|
if inspect.isstaticmethod(self.object, cls=self.parent, name=self.object_name):
|
||||||
sig = inspect.signature(unwrapped, bound_method=True)
|
self.env.app.emit('autodoc-before-process-signature', self.object, False)
|
||||||
|
sig = inspect.signature(self.object, bound_method=False)
|
||||||
|
else:
|
||||||
|
self.env.app.emit('autodoc-before-process-signature', self.object, True)
|
||||||
|
|
||||||
|
meth = self.parent.__dict__.get(self.objpath[-1], None)
|
||||||
|
if meth and inspect.is_singledispatch_method(meth):
|
||||||
|
sig = inspect.signature(self.object, bound_method=True,
|
||||||
|
follow_wrapped=True)
|
||||||
|
else:
|
||||||
|
sig = inspect.signature(self.object, bound_method=True)
|
||||||
args = stringify_signature(sig, **kwargs)
|
args = stringify_signature(sig, **kwargs)
|
||||||
|
except TypeError as exc:
|
||||||
|
logger.warning(__("Failed to get a method signature for %s: %s"),
|
||||||
|
self.fullname, exc)
|
||||||
|
return None
|
||||||
|
except ValueError:
|
||||||
|
args = ''
|
||||||
|
|
||||||
if self.env.config.strip_signature_backslash:
|
if self.env.config.strip_signature_backslash:
|
||||||
# escape backslashes for reST
|
# escape backslashes for reST
|
||||||
@ -1467,44 +1492,19 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
|
|||||||
self.add_line(' :classmethod:', sourcename)
|
self.add_line(' :classmethod:', sourcename)
|
||||||
if inspect.isstaticmethod(obj, cls=self.parent, name=self.object_name):
|
if inspect.isstaticmethod(obj, cls=self.parent, name=self.object_name):
|
||||||
self.add_line(' :staticmethod:', sourcename)
|
self.add_line(' :staticmethod:', sourcename)
|
||||||
|
if self.analyzer and '.'.join(self.objpath) in self.analyzer.finals:
|
||||||
|
self.add_line(' :final:', sourcename)
|
||||||
|
|
||||||
def document_members(self, all_members: bool = False) -> None:
|
def document_members(self, all_members: bool = False) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def format_signature(self, **kwargs: Any) -> str:
|
||||||
|
sig = super().format_signature(**kwargs)
|
||||||
|
sigs = [sig]
|
||||||
|
|
||||||
class SingledispatchMethodDocumenter(MethodDocumenter):
|
|
||||||
"""
|
|
||||||
Specialized Documenter subclass for singledispatch'ed methods.
|
|
||||||
"""
|
|
||||||
objtype = 'singledispatch_method'
|
|
||||||
directivetype = 'method'
|
|
||||||
member_order = 50
|
|
||||||
|
|
||||||
# before MethodDocumenter
|
|
||||||
priority = MethodDocumenter.priority + 1
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any
|
|
||||||
) -> bool:
|
|
||||||
if super().can_document_member(member, membername, isattr, parent) and parent.object:
|
|
||||||
meth = parent.object.__dict__.get(membername)
|
|
||||||
return inspect.is_singledispatch_method(meth)
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def add_directive_header(self, sig: str) -> None:
|
|
||||||
sourcename = self.get_sourcename()
|
|
||||||
|
|
||||||
# intercept generated directive headers
|
|
||||||
# TODO: It is very hacky to use mock to intercept header generation
|
|
||||||
with patch.object(self, 'add_line') as add_line:
|
|
||||||
super().add_directive_header(sig)
|
|
||||||
|
|
||||||
# output first line of header
|
|
||||||
self.add_line(*add_line.call_args_list[0][0])
|
|
||||||
|
|
||||||
# inserts signature of singledispatch'ed functions
|
|
||||||
meth = self.parent.__dict__.get(self.objpath[-1])
|
meth = self.parent.__dict__.get(self.objpath[-1])
|
||||||
|
if inspect.is_singledispatch_method(meth):
|
||||||
|
# append signature of singledispatch'ed functions
|
||||||
for typ, func in meth.dispatcher.registry.items():
|
for typ, func in meth.dispatcher.registry.items():
|
||||||
if typ is object:
|
if typ is object:
|
||||||
pass # default implementation. skipped.
|
pass # default implementation. skipped.
|
||||||
@ -1512,14 +1512,12 @@ class SingledispatchMethodDocumenter(MethodDocumenter):
|
|||||||
self.annotate_to_first_argument(func, typ)
|
self.annotate_to_first_argument(func, typ)
|
||||||
|
|
||||||
documenter = MethodDocumenter(self.directive, '')
|
documenter = MethodDocumenter(self.directive, '')
|
||||||
|
documenter.parent = self.parent
|
||||||
documenter.object = func
|
documenter.object = func
|
||||||
self.add_line(' %s%s' % (self.format_name(),
|
documenter.objpath = [None]
|
||||||
documenter.format_signature()),
|
sigs.append(documenter.format_signature())
|
||||||
sourcename)
|
|
||||||
|
|
||||||
# output remains of directive header
|
return "\n".join(sigs)
|
||||||
for call in add_line.call_args_list[1:]:
|
|
||||||
self.add_line(*call[0])
|
|
||||||
|
|
||||||
def annotate_to_first_argument(self, func: Callable, typ: Type) -> None:
|
def annotate_to_first_argument(self, func: Callable, typ: Type) -> None:
|
||||||
"""Annotate type hint to the first argument of function if needed."""
|
"""Annotate type hint to the first argument of function if needed."""
|
||||||
@ -1533,6 +1531,14 @@ class SingledispatchMethodDocumenter(MethodDocumenter):
|
|||||||
func.__signature__ = sig.replace(parameters=params) # type: ignore
|
func.__signature__ = sig.replace(parameters=params) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
class SingledispatchMethodDocumenter(MethodDocumenter):
|
||||||
|
"""
|
||||||
|
Used to be a specialized Documenter subclass for singledispatch'ed methods.
|
||||||
|
|
||||||
|
Retained for backwards compatibility, now does the same as the MethodDocumenter
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter): # type: ignore
|
class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter): # type: ignore
|
||||||
"""
|
"""
|
||||||
Specialized Documenter subclass for attributes.
|
Specialized Documenter subclass for attributes.
|
||||||
@ -1584,8 +1590,7 @@ class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter):
|
|||||||
super().add_directive_header(sig)
|
super().add_directive_header(sig)
|
||||||
sourcename = self.get_sourcename()
|
sourcename = self.get_sourcename()
|
||||||
if not self.options.annotation:
|
if not self.options.annotation:
|
||||||
if not self._datadescriptor:
|
# obtain type annotation for this attribute
|
||||||
# obtain annotation for this attribute
|
|
||||||
annotations = getattr(self.parent, '__annotations__', {})
|
annotations = getattr(self.parent, '__annotations__', {})
|
||||||
if annotations and self.objpath[-1] in annotations:
|
if annotations and self.objpath[-1] in annotations:
|
||||||
objrepr = stringify_typehint(annotations.get(self.objpath[-1]))
|
objrepr = stringify_typehint(annotations.get(self.objpath[-1]))
|
||||||
@ -1596,7 +1601,12 @@ class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter):
|
|||||||
self.add_line(' :type: ' + self.analyzer.annotations[key],
|
self.add_line(' :type: ' + self.analyzer.annotations[key],
|
||||||
sourcename)
|
sourcename)
|
||||||
|
|
||||||
|
# data descriptors do not have useful values
|
||||||
|
if not self._datadescriptor:
|
||||||
try:
|
try:
|
||||||
|
if self.object is INSTANCEATTR:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
objrepr = object_description(self.object)
|
objrepr = object_description(self.object)
|
||||||
self.add_line(' :value: ' + objrepr, sourcename)
|
self.add_line(' :value: ' + objrepr, sourcename)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
@ -1669,6 +1679,7 @@ class InstanceAttributeDocumenter(AttributeDocumenter):
|
|||||||
"""Never import anything."""
|
"""Never import anything."""
|
||||||
# disguise as an attribute
|
# disguise as an attribute
|
||||||
self.objtype = 'attribute'
|
self.objtype = 'attribute'
|
||||||
|
self.object = INSTANCEATTR
|
||||||
self._datadescriptor = False
|
self._datadescriptor = False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -1713,8 +1724,12 @@ class SlotsAttributeDocumenter(AttributeDocumenter):
|
|||||||
self.env.note_reread()
|
self.env.note_reread()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def get_doc(self, encoding: str = None, ignore: int = 1) -> List[List[str]]:
|
def get_doc(self, encoding: str = None, ignore: int = None) -> List[List[str]]:
|
||||||
"""Decode and return lines of the docstring(s) for the object."""
|
"""Decode and return lines of the docstring(s) for the object."""
|
||||||
|
if ignore is not None:
|
||||||
|
warnings.warn("The 'ignore' argument to autodoc.%s.get_doc() is deprecated."
|
||||||
|
% self.__class__.__name__,
|
||||||
|
RemovedInSphinx50Warning, stacklevel=2)
|
||||||
name = self.objpath[-1]
|
name = self.objpath[-1]
|
||||||
__slots__ = safe_getattr(self.parent, '__slots__', [])
|
__slots__ = safe_getattr(self.parent, '__slots__', [])
|
||||||
if isinstance(__slots__, dict) and isinstance(__slots__.get(name), str):
|
if isinstance(__slots__, dict) and isinstance(__slots__.get(name), str):
|
||||||
@ -1726,7 +1741,7 @@ class SlotsAttributeDocumenter(AttributeDocumenter):
|
|||||||
|
|
||||||
def get_documenters(app: Sphinx) -> Dict[str, "Type[Documenter]"]:
|
def get_documenters(app: Sphinx) -> Dict[str, "Type[Documenter]"]:
|
||||||
"""Returns registered Documenter classes"""
|
"""Returns registered Documenter classes"""
|
||||||
warnings.warn("get_documenters() is deprecated.", RemovedInSphinx50Warning)
|
warnings.warn("get_documenters() is deprecated.", RemovedInSphinx50Warning, stacklevel=2)
|
||||||
return app.registry.documenters
|
return app.registry.documenters
|
||||||
|
|
||||||
|
|
||||||
@ -1739,6 +1754,14 @@ def autodoc_attrgetter(app: Sphinx, obj: Any, name: str, *defargs: Any) -> Any:
|
|||||||
return safe_getattr(obj, name, *defargs)
|
return safe_getattr(obj, name, *defargs)
|
||||||
|
|
||||||
|
|
||||||
|
def migrate_autodoc_member_order(app: Sphinx, config: Config) -> None:
|
||||||
|
if config.autodoc_member_order == 'alphabetic':
|
||||||
|
# RemovedInSphinx50Warning
|
||||||
|
logger.warning(__('autodoc_member_order now accepts "alphabetical" '
|
||||||
|
'instead of "alphabetic". Please update your setting.'))
|
||||||
|
config.autodoc_member_order = 'alphabetical' # type: ignore
|
||||||
|
|
||||||
|
|
||||||
def setup(app: Sphinx) -> Dict[str, Any]:
|
def setup(app: Sphinx) -> Dict[str, Any]:
|
||||||
app.add_autodocumenter(ModuleDocumenter)
|
app.add_autodocumenter(ModuleDocumenter)
|
||||||
app.add_autodocumenter(ClassDocumenter)
|
app.add_autodocumenter(ClassDocumenter)
|
||||||
@ -1746,18 +1769,16 @@ def setup(app: Sphinx) -> Dict[str, Any]:
|
|||||||
app.add_autodocumenter(DataDocumenter)
|
app.add_autodocumenter(DataDocumenter)
|
||||||
app.add_autodocumenter(DataDeclarationDocumenter)
|
app.add_autodocumenter(DataDeclarationDocumenter)
|
||||||
app.add_autodocumenter(FunctionDocumenter)
|
app.add_autodocumenter(FunctionDocumenter)
|
||||||
app.add_autodocumenter(SingledispatchFunctionDocumenter)
|
|
||||||
app.add_autodocumenter(DecoratorDocumenter)
|
app.add_autodocumenter(DecoratorDocumenter)
|
||||||
app.add_autodocumenter(MethodDocumenter)
|
app.add_autodocumenter(MethodDocumenter)
|
||||||
app.add_autodocumenter(SingledispatchMethodDocumenter)
|
|
||||||
app.add_autodocumenter(AttributeDocumenter)
|
app.add_autodocumenter(AttributeDocumenter)
|
||||||
app.add_autodocumenter(PropertyDocumenter)
|
app.add_autodocumenter(PropertyDocumenter)
|
||||||
app.add_autodocumenter(InstanceAttributeDocumenter)
|
app.add_autodocumenter(InstanceAttributeDocumenter)
|
||||||
app.add_autodocumenter(SlotsAttributeDocumenter)
|
app.add_autodocumenter(SlotsAttributeDocumenter)
|
||||||
|
|
||||||
app.add_config_value('autoclass_content', 'class', True)
|
app.add_config_value('autoclass_content', 'class', True, ENUM('both', 'class', 'init'))
|
||||||
app.add_config_value('autodoc_member_order', 'alphabetic', True)
|
app.add_config_value('autodoc_member_order', 'alphabetical', True,
|
||||||
app.add_config_value('autodoc_default_flags', [], True)
|
ENUM('alphabetic', 'alphabetical', 'bysource', 'groupwise'))
|
||||||
app.add_config_value('autodoc_default_options', {}, True)
|
app.add_config_value('autodoc_default_options', {}, True)
|
||||||
app.add_config_value('autodoc_docstring_signature', True, True)
|
app.add_config_value('autodoc_docstring_signature', True, True)
|
||||||
app.add_config_value('autodoc_mock_imports', [], True)
|
app.add_config_value('autodoc_mock_imports', [], True)
|
||||||
@ -1770,6 +1791,8 @@ def setup(app: Sphinx) -> Dict[str, Any]:
|
|||||||
app.add_event('autodoc-process-signature')
|
app.add_event('autodoc-process-signature')
|
||||||
app.add_event('autodoc-skip-member')
|
app.add_event('autodoc-skip-member')
|
||||||
|
|
||||||
|
app.connect('config-inited', migrate_autodoc_member_order)
|
||||||
|
|
||||||
app.setup_extension('sphinx.ext.autodoc.type_comment')
|
app.setup_extension('sphinx.ext.autodoc.type_comment')
|
||||||
app.setup_extension('sphinx.ext.autodoc.typehints')
|
app.setup_extension('sphinx.ext.autodoc.typehints')
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ class DocumenterBridge:
|
|||||||
else:
|
else:
|
||||||
# create fake object for self.state.document.settings.tab_width
|
# create fake object for self.state.document.settings.tab_width
|
||||||
warnings.warn('DocumenterBridge requires a state object on instantiation.',
|
warnings.warn('DocumenterBridge requires a state object on instantiation.',
|
||||||
RemovedInSphinx40Warning)
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
settings = Struct(tab_width=8)
|
settings = Struct(tab_width=8)
|
||||||
document = Struct(settings=settings)
|
document = Struct(settings=settings)
|
||||||
self.state = Struct(document=document)
|
self.state = Struct(document=document)
|
||||||
|
@ -11,10 +11,10 @@
|
|||||||
import importlib
|
import importlib
|
||||||
import traceback
|
import traceback
|
||||||
import warnings
|
import warnings
|
||||||
from collections import namedtuple
|
from typing import Any, Callable, Dict, List, Mapping, NamedTuple, Tuple
|
||||||
from typing import Any, Callable, Dict, List, Mapping, Tuple
|
|
||||||
|
|
||||||
from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
|
from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
|
||||||
|
from sphinx.pycode import ModuleAnalyzer
|
||||||
from sphinx.util import logging
|
from sphinx.util import logging
|
||||||
from sphinx.util.inspect import isclass, isenumclass, safe_getattr
|
from sphinx.util.inspect import isclass, isenumclass, safe_getattr
|
||||||
|
|
||||||
@ -122,11 +122,13 @@ def get_module_members(module: Any) -> List[Tuple[str, Any]]:
|
|||||||
return sorted(list(members.values()))
|
return sorted(list(members.values()))
|
||||||
|
|
||||||
|
|
||||||
Attribute = namedtuple('Attribute', ['name', 'directly_defined', 'value'])
|
Attribute = NamedTuple('Attribute', [('name', str),
|
||||||
|
('directly_defined', bool),
|
||||||
|
('value', Any)])
|
||||||
|
|
||||||
|
|
||||||
def get_object_members(subject: Any, objpath: List[str], attrgetter: Callable,
|
def get_object_members(subject: Any, objpath: List[str], attrgetter: Callable,
|
||||||
analyzer: Any = None) -> Dict[str, Attribute]:
|
analyzer: ModuleAnalyzer = None) -> Dict[str, Attribute]:
|
||||||
"""Get members and attributes of target object."""
|
"""Get members and attributes of target object."""
|
||||||
from sphinx.ext.autodoc import INSTANCEATTR
|
from sphinx.ext.autodoc import INSTANCEATTR
|
||||||
|
|
||||||
@ -142,8 +144,9 @@ def get_object_members(subject: Any, objpath: List[str], attrgetter: Callable,
|
|||||||
members[name] = Attribute(name, True, value)
|
members[name] = Attribute(name, True, value)
|
||||||
|
|
||||||
superclass = subject.__mro__[1]
|
superclass = subject.__mro__[1]
|
||||||
for name, value in obj_dict.items():
|
for name in obj_dict:
|
||||||
if name not in superclass.__dict__:
|
if name not in superclass.__dict__:
|
||||||
|
value = safe_getattr(subject, name)
|
||||||
members[name] = Attribute(name, True, value)
|
members[name] = Attribute(name, True, value)
|
||||||
|
|
||||||
# members in __slots__
|
# members in __slots__
|
||||||
|
@ -25,6 +25,7 @@ class _MockObject:
|
|||||||
"""Used by autodoc_mock_imports."""
|
"""Used by autodoc_mock_imports."""
|
||||||
|
|
||||||
__display_name__ = '_MockObject'
|
__display_name__ = '_MockObject'
|
||||||
|
__sphinx_mock__ = True
|
||||||
|
|
||||||
def __new__(cls, *args: Any, **kwargs: Any) -> Any:
|
def __new__(cls, *args: Any, **kwargs: Any) -> Any:
|
||||||
if len(args) == 3 and isinstance(args[1], tuple):
|
if len(args) == 3 and isinstance(args[1], tuple):
|
||||||
@ -78,6 +79,7 @@ def _make_subclass(name: str, module: str, superclass: Any = _MockObject,
|
|||||||
class _MockModule(ModuleType):
|
class _MockModule(ModuleType):
|
||||||
"""Used by autodoc_mock_imports."""
|
"""Used by autodoc_mock_imports."""
|
||||||
__file__ = os.devnull
|
__file__ = os.devnull
|
||||||
|
__sphinx_mock__ = True
|
||||||
|
|
||||||
def __init__(self, name: str) -> None:
|
def __init__(self, name: str) -> None:
|
||||||
super().__init__(name)
|
super().__init__(name)
|
||||||
|
@ -104,23 +104,15 @@ def modify_field_list(node: nodes.field_list, annotations: Dict[str, str]) -> No
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
arg = arguments.get(name, {})
|
arg = arguments.get(name, {})
|
||||||
|
if not arg.get('type'):
|
||||||
field = nodes.field()
|
field = nodes.field()
|
||||||
if arg.get('param') and arg.get('type'):
|
|
||||||
# both param and type are already filled manually
|
|
||||||
continue
|
|
||||||
elif arg.get('param'):
|
|
||||||
# only param: fill type field
|
|
||||||
field += nodes.field_name('', 'type ' + name)
|
field += nodes.field_name('', 'type ' + name)
|
||||||
field += nodes.field_body('', nodes.paragraph('', annotation))
|
field += nodes.field_body('', nodes.paragraph('', annotation))
|
||||||
elif arg.get('type'):
|
node += field
|
||||||
# only type: It's odd...
|
if not arg.get('param'):
|
||||||
|
field = nodes.field()
|
||||||
field += nodes.field_name('', 'param ' + name)
|
field += nodes.field_name('', 'param ' + name)
|
||||||
field += nodes.field_body('', nodes.paragraph('', ''))
|
field += nodes.field_body('', nodes.paragraph('', ''))
|
||||||
else:
|
|
||||||
# both param and type are not found
|
|
||||||
field += nodes.field_name('', 'param ' + annotation + ' ' + name)
|
|
||||||
field += nodes.field_body('', nodes.paragraph('', ''))
|
|
||||||
|
|
||||||
node += field
|
node += field
|
||||||
|
|
||||||
if 'return' in annotations and 'return' not in arguments:
|
if 'return' in annotations and 'return' not in arguments:
|
||||||
|
@ -228,8 +228,10 @@ class Autosummary(SphinxDirective):
|
|||||||
final_argument_whitespace = False
|
final_argument_whitespace = False
|
||||||
has_content = True
|
has_content = True
|
||||||
option_spec = {
|
option_spec = {
|
||||||
|
'caption': directives.unchanged_required,
|
||||||
'toctree': directives.unchanged,
|
'toctree': directives.unchanged,
|
||||||
'nosignatures': directives.flag,
|
'nosignatures': directives.flag,
|
||||||
|
'recursive': directives.flag,
|
||||||
'template': directives.unchanged,
|
'template': directives.unchanged,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,9 +272,14 @@ class Autosummary(SphinxDirective):
|
|||||||
tocnode['entries'] = [(None, docn) for docn in docnames]
|
tocnode['entries'] = [(None, docn) for docn in docnames]
|
||||||
tocnode['maxdepth'] = -1
|
tocnode['maxdepth'] = -1
|
||||||
tocnode['glob'] = None
|
tocnode['glob'] = None
|
||||||
|
tocnode['caption'] = self.options.get('caption')
|
||||||
|
|
||||||
nodes.append(autosummary_toc('', '', tocnode))
|
nodes.append(autosummary_toc('', '', tocnode))
|
||||||
|
|
||||||
|
if 'toctree' not in self.options and 'caption' in self.options:
|
||||||
|
logger.warning(__('A captioned autosummary requires :toctree: option. ignored.'),
|
||||||
|
location=nodes[-1])
|
||||||
|
|
||||||
return nodes
|
return nodes
|
||||||
|
|
||||||
def get_items(self, names: List[str]) -> List[Tuple[str, str, str, str]]:
|
def get_items(self, names: List[str]) -> List[Tuple[str, str, str, str]]:
|
||||||
@ -295,8 +302,7 @@ class Autosummary(SphinxDirective):
|
|||||||
with mock(self.config.autosummary_mock_imports):
|
with mock(self.config.autosummary_mock_imports):
|
||||||
real_name, obj, parent, modname = import_by_name(name, prefixes=prefixes)
|
real_name, obj, parent, modname = import_by_name(name, prefixes=prefixes)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
logger.warning(__('failed to import %s'), name)
|
logger.warning(__('autosummary: failed to import %s'), name)
|
||||||
items.append((name, '', '', name))
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
self.bridge.result = StringList() # initialize for each documenter
|
self.bridge.result = StringList() # initialize for each documenter
|
||||||
@ -652,7 +658,7 @@ def autolink_role(typ: str, rawtext: str, etext: str, lineno: int, inliner: Inli
|
|||||||
Expands to ':obj:`text`' if `text` is an object that can be imported;
|
Expands to ':obj:`text`' if `text` is an object that can be imported;
|
||||||
otherwise expands to '*text*'.
|
otherwise expands to '*text*'.
|
||||||
"""
|
"""
|
||||||
warnings.warn('autolink_role() is deprecated.', RemovedInSphinx40Warning)
|
warnings.warn('autolink_role() is deprecated.', RemovedInSphinx40Warning, stacklevel=2)
|
||||||
env = inliner.document.settings.env
|
env = inliner.document.settings.env
|
||||||
pyobj_role = env.get_domain('py').role('obj')
|
pyobj_role = env.get_domain('py').role('obj')
|
||||||
objects, msg = pyobj_role('obj', rawtext, etext, lineno, inliner, options, content)
|
objects, msg = pyobj_role('obj', rawtext, etext, lineno, inliner, options, content)
|
||||||
@ -745,8 +751,7 @@ def process_generate_options(app: Sphinx) -> None:
|
|||||||
|
|
||||||
imported_members = app.config.autosummary_imported_members
|
imported_members = app.config.autosummary_imported_members
|
||||||
with mock(app.config.autosummary_mock_imports):
|
with mock(app.config.autosummary_mock_imports):
|
||||||
generate_autosummary_docs(genfiles, builder=app.builder,
|
generate_autosummary_docs(genfiles, suffix=suffix, base_path=app.srcdir,
|
||||||
suffix=suffix, base_path=app.srcdir,
|
|
||||||
app=app, imported_members=imported_members,
|
app=app, imported_members=imported_members,
|
||||||
overwrite=app.config.autosummary_generate_overwrite)
|
overwrite=app.config.autosummary_generate_overwrite)
|
||||||
|
|
||||||
@ -769,6 +774,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
|
|||||||
app.add_directive('autosummary', Autosummary)
|
app.add_directive('autosummary', Autosummary)
|
||||||
app.add_role('autolink', AutoLink())
|
app.add_role('autolink', AutoLink())
|
||||||
app.connect('builder-inited', process_generate_options)
|
app.connect('builder-inited', process_generate_options)
|
||||||
|
app.add_config_value('autosummary_context', {}, True)
|
||||||
app.add_config_value('autosummary_generate', [], True, [bool])
|
app.add_config_value('autosummary_generate', [], True, [bool])
|
||||||
app.add_config_value('autosummary_generate_overwrite', True, False)
|
app.add_config_value('autosummary_generate_overwrite', True, False)
|
||||||
app.add_config_value('autosummary_mock_imports',
|
app.add_config_value('autosummary_mock_imports',
|
||||||
|
@ -20,29 +20,35 @@
|
|||||||
import argparse
|
import argparse
|
||||||
import locale
|
import locale
|
||||||
import os
|
import os
|
||||||
|
import pkgutil
|
||||||
import pydoc
|
import pydoc
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import warnings
|
import warnings
|
||||||
from typing import Any, Callable, Dict, List, NamedTuple, Set, Tuple
|
from gettext import NullTranslations
|
||||||
|
from os import path
|
||||||
|
from typing import Any, Callable, Dict, List, NamedTuple, Set, Tuple, Union
|
||||||
|
|
||||||
from jinja2 import BaseLoader, FileSystemLoader, TemplateNotFound
|
from jinja2 import TemplateNotFound
|
||||||
from jinja2.sandbox import SandboxedEnvironment
|
from jinja2.sandbox import SandboxedEnvironment
|
||||||
|
|
||||||
import sphinx.locale
|
import sphinx.locale
|
||||||
from sphinx import __display_version__
|
from sphinx import __display_version__
|
||||||
from sphinx import package_dir
|
from sphinx import package_dir
|
||||||
|
from sphinx.application import Sphinx
|
||||||
from sphinx.builders import Builder
|
from sphinx.builders import Builder
|
||||||
|
from sphinx.config import Config
|
||||||
from sphinx.deprecation import RemovedInSphinx40Warning, RemovedInSphinx50Warning
|
from sphinx.deprecation import RemovedInSphinx40Warning, RemovedInSphinx50Warning
|
||||||
from sphinx.ext.autodoc import Documenter
|
from sphinx.ext.autodoc import Documenter
|
||||||
from sphinx.ext.autosummary import import_by_name, get_documenter
|
from sphinx.ext.autosummary import import_by_name, get_documenter
|
||||||
from sphinx.jinja2glue import BuiltinTemplateLoader
|
|
||||||
from sphinx.locale import __
|
from sphinx.locale import __
|
||||||
from sphinx.registry import SphinxComponentRegistry
|
from sphinx.registry import SphinxComponentRegistry
|
||||||
from sphinx.util import logging
|
from sphinx.util import logging
|
||||||
from sphinx.util import rst
|
from sphinx.util import rst
|
||||||
|
from sphinx.util import split_full_qualified_name
|
||||||
from sphinx.util.inspect import safe_getattr
|
from sphinx.util.inspect import safe_getattr
|
||||||
from sphinx.util.osutil import ensuredir
|
from sphinx.util.osutil import ensuredir
|
||||||
|
from sphinx.util.template import SphinxTemplateLoader
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
# For type annotation
|
# For type annotation
|
||||||
@ -55,20 +61,27 @@ logger = logging.getLogger(__name__)
|
|||||||
class DummyApplication:
|
class DummyApplication:
|
||||||
"""Dummy Application class for sphinx-autogen command."""
|
"""Dummy Application class for sphinx-autogen command."""
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self, translator: NullTranslations) -> None:
|
||||||
|
self.config = Config()
|
||||||
self.registry = SphinxComponentRegistry()
|
self.registry = SphinxComponentRegistry()
|
||||||
self.messagelog = [] # type: List[str]
|
self.messagelog = [] # type: List[str]
|
||||||
|
self.srcdir = "/"
|
||||||
|
self.translator = translator
|
||||||
self.verbosity = 0
|
self.verbosity = 0
|
||||||
self._warncount = 0
|
self._warncount = 0
|
||||||
self.warningiserror = False
|
self.warningiserror = False
|
||||||
|
|
||||||
|
self.config.add('autosummary_context', {}, True, None)
|
||||||
|
self.config.init_values()
|
||||||
|
|
||||||
def emit_firstresult(self, *args: Any) -> None:
|
def emit_firstresult(self, *args: Any) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
AutosummaryEntry = NamedTuple('AutosummaryEntry', [('name', str),
|
AutosummaryEntry = NamedTuple('AutosummaryEntry', [('name', str),
|
||||||
('path', str),
|
('path', str),
|
||||||
('template', str)])
|
('template', str),
|
||||||
|
('recursive', bool)])
|
||||||
|
|
||||||
|
|
||||||
def setup_documenters(app: Any) -> None:
|
def setup_documenters(app: Any) -> None:
|
||||||
@ -107,25 +120,37 @@ def _underline(title: str, line: str = '=') -> str:
|
|||||||
class AutosummaryRenderer:
|
class AutosummaryRenderer:
|
||||||
"""A helper class for rendering."""
|
"""A helper class for rendering."""
|
||||||
|
|
||||||
def __init__(self, builder: Builder, template_dir: str) -> None:
|
def __init__(self, app: Union[Builder, Sphinx], template_dir: str = None) -> None:
|
||||||
loader = None # type: BaseLoader
|
if isinstance(app, Builder):
|
||||||
template_dirs = [os.path.join(package_dir, 'ext', 'autosummary', 'templates')]
|
warnings.warn('The first argument for AutosummaryRenderer has been '
|
||||||
if builder is None:
|
'changed to Sphinx object',
|
||||||
|
RemovedInSphinx50Warning, stacklevel=2)
|
||||||
if template_dir:
|
if template_dir:
|
||||||
template_dirs.insert(0, template_dir)
|
warnings.warn('template_dir argument for AutosummaryRenderer is deprecated.',
|
||||||
loader = FileSystemLoader(template_dirs)
|
RemovedInSphinx50Warning, stacklevel=2)
|
||||||
else:
|
|
||||||
# allow the user to override the templates
|
system_templates_path = [os.path.join(package_dir, 'ext', 'autosummary', 'templates')]
|
||||||
loader = BuiltinTemplateLoader()
|
loader = SphinxTemplateLoader(app.srcdir, app.config.templates_path,
|
||||||
loader.init(builder, dirs=template_dirs)
|
system_templates_path)
|
||||||
|
|
||||||
self.env = SandboxedEnvironment(loader=loader)
|
self.env = SandboxedEnvironment(loader=loader)
|
||||||
self.env.filters['escape'] = rst.escape
|
self.env.filters['escape'] = rst.escape
|
||||||
self.env.filters['e'] = rst.escape
|
self.env.filters['e'] = rst.escape
|
||||||
self.env.filters['underline'] = _underline
|
self.env.filters['underline'] = _underline
|
||||||
|
|
||||||
|
if isinstance(app, (Sphinx, DummyApplication)):
|
||||||
|
if app.translator:
|
||||||
|
self.env.add_extension("jinja2.ext.i18n")
|
||||||
|
self.env.install_gettext_translations(app.translator) # type: ignore
|
||||||
|
elif isinstance(app, Builder):
|
||||||
|
if app.app.translator:
|
||||||
|
self.env.add_extension("jinja2.ext.i18n")
|
||||||
|
self.env.install_gettext_translations(app.app.translator) # type: ignore
|
||||||
|
|
||||||
def exists(self, template_name: str) -> bool:
|
def exists(self, template_name: str) -> bool:
|
||||||
"""Check if template file exists."""
|
"""Check if template file exists."""
|
||||||
|
warnings.warn('AutosummaryRenderer.exists() is deprecated.',
|
||||||
|
RemovedInSphinx50Warning, stacklevel=2)
|
||||||
try:
|
try:
|
||||||
self.env.get_template(template_name)
|
self.env.get_template(template_name)
|
||||||
return True
|
return True
|
||||||
@ -134,7 +159,17 @@ class AutosummaryRenderer:
|
|||||||
|
|
||||||
def render(self, template_name: str, context: Dict) -> str:
|
def render(self, template_name: str, context: Dict) -> str:
|
||||||
"""Render a template file."""
|
"""Render a template file."""
|
||||||
return self.env.get_template(template_name).render(context)
|
try:
|
||||||
|
template = self.env.get_template(template_name)
|
||||||
|
except TemplateNotFound:
|
||||||
|
try:
|
||||||
|
# objtype is given as template_name
|
||||||
|
template = self.env.get_template('autosummary/%s.rst' % template_name)
|
||||||
|
except TemplateNotFound:
|
||||||
|
# fallback to base.rst
|
||||||
|
template = self.env.get_template('autosummary/base.rst')
|
||||||
|
|
||||||
|
return template.render(context)
|
||||||
|
|
||||||
|
|
||||||
# -- Generating output ---------------------------------------------------------
|
# -- Generating output ---------------------------------------------------------
|
||||||
@ -142,14 +177,10 @@ class AutosummaryRenderer:
|
|||||||
|
|
||||||
def generate_autosummary_content(name: str, obj: Any, parent: Any,
|
def generate_autosummary_content(name: str, obj: Any, parent: Any,
|
||||||
template: AutosummaryRenderer, template_name: str,
|
template: AutosummaryRenderer, template_name: str,
|
||||||
imported_members: bool, app: Any) -> str:
|
imported_members: bool, app: Any,
|
||||||
|
recursive: bool, context: Dict) -> str:
|
||||||
doc = get_documenter(app, obj, parent)
|
doc = get_documenter(app, obj, parent)
|
||||||
|
|
||||||
if template_name is None:
|
|
||||||
template_name = 'autosummary/%s.rst' % doc.objtype
|
|
||||||
if not template.exists(template_name):
|
|
||||||
template_name = 'autosummary/base.rst'
|
|
||||||
|
|
||||||
def skip_member(obj: Any, name: str, objtype: str) -> bool:
|
def skip_member(obj: Any, name: str, objtype: str) -> bool:
|
||||||
try:
|
try:
|
||||||
return app.emit_firstresult('autodoc-skip-member', objtype, name,
|
return app.emit_firstresult('autodoc-skip-member', objtype, name,
|
||||||
@ -187,7 +218,16 @@ def generate_autosummary_content(name: str, obj: Any, parent: Any,
|
|||||||
public.append(name)
|
public.append(name)
|
||||||
return public, items
|
return public, items
|
||||||
|
|
||||||
|
def get_modules(obj: Any) -> Tuple[List[str], List[str]]:
|
||||||
|
items = [] # type: List[str]
|
||||||
|
for _, modname, ispkg in pkgutil.iter_modules(obj.__path__):
|
||||||
|
fullname = name + '.' + modname
|
||||||
|
items.append(fullname)
|
||||||
|
public = [x for x in items if not x.split('.')[-1].startswith('_')]
|
||||||
|
return public, items
|
||||||
|
|
||||||
ns = {} # type: Dict[str, Any]
|
ns = {} # type: Dict[str, Any]
|
||||||
|
ns.update(context)
|
||||||
|
|
||||||
if doc.objtype == 'module':
|
if doc.objtype == 'module':
|
||||||
ns['members'] = dir(obj)
|
ns['members'] = dir(obj)
|
||||||
@ -197,6 +237,9 @@ def generate_autosummary_content(name: str, obj: Any, parent: Any,
|
|||||||
get_members(obj, {'class'}, imported=imported_members)
|
get_members(obj, {'class'}, imported=imported_members)
|
||||||
ns['exceptions'], ns['all_exceptions'] = \
|
ns['exceptions'], ns['all_exceptions'] = \
|
||||||
get_members(obj, {'exception'}, imported=imported_members)
|
get_members(obj, {'exception'}, imported=imported_members)
|
||||||
|
ispackage = hasattr(obj, '__path__')
|
||||||
|
if ispackage and recursive:
|
||||||
|
ns['modules'], ns['all_modules'] = get_modules(obj)
|
||||||
|
|
||||||
# Find module attributes with docstrings
|
# Find module attributes with docstrings
|
||||||
attrs, public = [], []
|
attrs, public = [], []
|
||||||
@ -221,24 +264,27 @@ def generate_autosummary_content(name: str, obj: Any, parent: Any,
|
|||||||
ns['attributes'], ns['all_attributes'] = \
|
ns['attributes'], ns['all_attributes'] = \
|
||||||
get_members(obj, {'attribute', 'property'})
|
get_members(obj, {'attribute', 'property'})
|
||||||
|
|
||||||
parts = name.split('.')
|
modname, qualname = split_full_qualified_name(name)
|
||||||
if doc.objtype in ('method', 'attribute', 'property'):
|
if doc.objtype in ('method', 'attribute', 'property'):
|
||||||
mod_name = '.'.join(parts[:-2])
|
ns['class'] = qualname.rsplit(".", 1)[0]
|
||||||
cls_name = parts[-2]
|
|
||||||
obj_name = '.'.join(parts[-2:])
|
if doc.objtype in ('class',):
|
||||||
ns['class'] = cls_name
|
shortname = qualname
|
||||||
else:
|
else:
|
||||||
mod_name, obj_name = '.'.join(parts[:-1]), parts[-1]
|
shortname = qualname.rsplit(".", 1)[-1]
|
||||||
|
|
||||||
ns['fullname'] = name
|
ns['fullname'] = name
|
||||||
ns['module'] = mod_name
|
ns['module'] = modname
|
||||||
ns['objname'] = obj_name
|
ns['objname'] = qualname
|
||||||
ns['name'] = parts[-1]
|
ns['name'] = shortname
|
||||||
|
|
||||||
ns['objtype'] = doc.objtype
|
ns['objtype'] = doc.objtype
|
||||||
ns['underline'] = len(name) * '='
|
ns['underline'] = len(name) * '='
|
||||||
|
|
||||||
|
if template_name:
|
||||||
return template.render(template_name, ns)
|
return template.render(template_name, ns)
|
||||||
|
else:
|
||||||
|
return template.render(doc.objtype, ns)
|
||||||
|
|
||||||
|
|
||||||
def generate_autosummary_docs(sources: List[str], output_dir: str = None,
|
def generate_autosummary_docs(sources: List[str], output_dir: str = None,
|
||||||
@ -249,18 +295,26 @@ def generate_autosummary_docs(sources: List[str], output_dir: str = None,
|
|||||||
overwrite: bool = True) -> None:
|
overwrite: bool = True) -> None:
|
||||||
if info:
|
if info:
|
||||||
warnings.warn('info argument for generate_autosummary_docs() is deprecated.',
|
warnings.warn('info argument for generate_autosummary_docs() is deprecated.',
|
||||||
RemovedInSphinx40Warning)
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
_info = info
|
_info = info
|
||||||
else:
|
else:
|
||||||
_info = logger.info
|
_info = logger.info
|
||||||
|
|
||||||
if warn:
|
if warn:
|
||||||
warnings.warn('warn argument for generate_autosummary_docs() is deprecated.',
|
warnings.warn('warn argument for generate_autosummary_docs() is deprecated.',
|
||||||
RemovedInSphinx40Warning)
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
_warn = warn
|
_warn = warn
|
||||||
else:
|
else:
|
||||||
_warn = logger.warning
|
_warn = logger.warning
|
||||||
|
|
||||||
|
if builder:
|
||||||
|
warnings.warn('builder argument for generate_autosummary_docs() is deprecated.',
|
||||||
|
RemovedInSphinx50Warning, stacklevel=2)
|
||||||
|
|
||||||
|
if template_dir:
|
||||||
|
warnings.warn('template_dir argument for generate_autosummary_docs() is deprecated.',
|
||||||
|
RemovedInSphinx50Warning, stacklevel=2)
|
||||||
|
|
||||||
showed_sources = list(sorted(sources))
|
showed_sources = list(sorted(sources))
|
||||||
if len(showed_sources) > 20:
|
if len(showed_sources) > 20:
|
||||||
showed_sources = showed_sources[:10] + ['...'] + showed_sources[-10:]
|
showed_sources = showed_sources[:10] + ['...'] + showed_sources[-10:]
|
||||||
@ -273,7 +327,7 @@ def generate_autosummary_docs(sources: List[str], output_dir: str = None,
|
|||||||
if base_path is not None:
|
if base_path is not None:
|
||||||
sources = [os.path.join(base_path, filename) for filename in sources]
|
sources = [os.path.join(base_path, filename) for filename in sources]
|
||||||
|
|
||||||
template = AutosummaryRenderer(builder, template_dir)
|
template = AutosummaryRenderer(app)
|
||||||
|
|
||||||
# read
|
# read
|
||||||
items = find_autosummary_in_files(sources)
|
items = find_autosummary_in_files(sources)
|
||||||
@ -297,8 +351,12 @@ def generate_autosummary_docs(sources: List[str], output_dir: str = None,
|
|||||||
_warn(__('[autosummary] failed to import %r: %s') % (entry.name, e))
|
_warn(__('[autosummary] failed to import %r: %s') % (entry.name, e))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
context = {}
|
||||||
|
if app:
|
||||||
|
context.update(app.config.autosummary_context)
|
||||||
|
|
||||||
content = generate_autosummary_content(name, obj, parent, template, entry.template,
|
content = generate_autosummary_content(name, obj, parent, template, entry.template,
|
||||||
imported_members, app)
|
imported_members, app, entry.recursive, context)
|
||||||
|
|
||||||
filename = os.path.join(path, name + suffix)
|
filename = os.path.join(path, name + suffix)
|
||||||
if os.path.isfile(filename):
|
if os.path.isfile(filename):
|
||||||
@ -320,8 +378,7 @@ def generate_autosummary_docs(sources: List[str], output_dir: str = None,
|
|||||||
if new_files:
|
if new_files:
|
||||||
generate_autosummary_docs(new_files, output_dir=output_dir,
|
generate_autosummary_docs(new_files, output_dir=output_dir,
|
||||||
suffix=suffix, warn=warn, info=info,
|
suffix=suffix, warn=warn, info=info,
|
||||||
base_path=base_path, builder=builder,
|
base_path=base_path,
|
||||||
template_dir=template_dir,
|
|
||||||
imported_members=imported_members, app=app,
|
imported_members=imported_members, app=app,
|
||||||
overwrite=overwrite)
|
overwrite=overwrite)
|
||||||
|
|
||||||
@ -349,7 +406,7 @@ def find_autosummary_in_docstring(name: str, module: str = None, filename: str =
|
|||||||
"""
|
"""
|
||||||
if module:
|
if module:
|
||||||
warnings.warn('module argument for find_autosummary_in_docstring() is deprecated.',
|
warnings.warn('module argument for find_autosummary_in_docstring() is deprecated.',
|
||||||
RemovedInSphinx50Warning)
|
RemovedInSphinx50Warning, stacklevel=2)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
real_name, obj, parent, modname = import_by_name(name)
|
real_name, obj, parent, modname = import_by_name(name)
|
||||||
@ -383,11 +440,13 @@ def find_autosummary_in_lines(lines: List[str], module: str = None, filename: st
|
|||||||
module_re = re.compile(
|
module_re = re.compile(
|
||||||
r'^\s*\.\.\s+(current)?module::\s*([a-zA-Z0-9_.]+)\s*$')
|
r'^\s*\.\.\s+(current)?module::\s*([a-zA-Z0-9_.]+)\s*$')
|
||||||
autosummary_item_re = re.compile(r'^\s+(~?[_a-zA-Z][a-zA-Z0-9_.]*)\s*.*?')
|
autosummary_item_re = re.compile(r'^\s+(~?[_a-zA-Z][a-zA-Z0-9_.]*)\s*.*?')
|
||||||
|
recursive_arg_re = re.compile(r'^\s+:recursive:\s*$')
|
||||||
toctree_arg_re = re.compile(r'^\s+:toctree:\s*(.*?)\s*$')
|
toctree_arg_re = re.compile(r'^\s+:toctree:\s*(.*?)\s*$')
|
||||||
template_arg_re = re.compile(r'^\s+:template:\s*(.*?)\s*$')
|
template_arg_re = re.compile(r'^\s+:template:\s*(.*?)\s*$')
|
||||||
|
|
||||||
documented = [] # type: List[AutosummaryEntry]
|
documented = [] # type: List[AutosummaryEntry]
|
||||||
|
|
||||||
|
recursive = False
|
||||||
toctree = None # type: str
|
toctree = None # type: str
|
||||||
template = None
|
template = None
|
||||||
current_module = module
|
current_module = module
|
||||||
@ -396,6 +455,11 @@ def find_autosummary_in_lines(lines: List[str], module: str = None, filename: st
|
|||||||
|
|
||||||
for line in lines:
|
for line in lines:
|
||||||
if in_autosummary:
|
if in_autosummary:
|
||||||
|
m = recursive_arg_re.match(line)
|
||||||
|
if m:
|
||||||
|
recursive = True
|
||||||
|
continue
|
||||||
|
|
||||||
m = toctree_arg_re.match(line)
|
m = toctree_arg_re.match(line)
|
||||||
if m:
|
if m:
|
||||||
toctree = m.group(1)
|
toctree = m.group(1)
|
||||||
@ -420,7 +484,7 @@ def find_autosummary_in_lines(lines: List[str], module: str = None, filename: st
|
|||||||
if current_module and \
|
if current_module and \
|
||||||
not name.startswith(current_module + '.'):
|
not name.startswith(current_module + '.'):
|
||||||
name = "%s.%s" % (current_module, name)
|
name = "%s.%s" % (current_module, name)
|
||||||
documented.append(AutosummaryEntry(name, toctree, template))
|
documented.append(AutosummaryEntry(name, toctree, template, recursive))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if not line.strip() or line.startswith(base_indent + " "):
|
if not line.strip() or line.startswith(base_indent + " "):
|
||||||
@ -432,6 +496,7 @@ def find_autosummary_in_lines(lines: List[str], module: str = None, filename: st
|
|||||||
if m:
|
if m:
|
||||||
in_autosummary = True
|
in_autosummary = True
|
||||||
base_indent = m.group(1)
|
base_indent = m.group(1)
|
||||||
|
recursive = False
|
||||||
toctree = None
|
toctree = None
|
||||||
template = None
|
template = None
|
||||||
continue
|
continue
|
||||||
@ -497,14 +562,18 @@ The format of the autosummary directive is documented in the
|
|||||||
def main(argv: List[str] = sys.argv[1:]) -> None:
|
def main(argv: List[str] = sys.argv[1:]) -> None:
|
||||||
sphinx.locale.setlocale(locale.LC_ALL, '')
|
sphinx.locale.setlocale(locale.LC_ALL, '')
|
||||||
sphinx.locale.init_console(os.path.join(package_dir, 'locale'), 'sphinx')
|
sphinx.locale.init_console(os.path.join(package_dir, 'locale'), 'sphinx')
|
||||||
|
translator, _ = sphinx.locale.init([], None)
|
||||||
|
|
||||||
app = DummyApplication()
|
app = DummyApplication(translator)
|
||||||
logging.setup(app, sys.stdout, sys.stderr) # type: ignore
|
logging.setup(app, sys.stdout, sys.stderr) # type: ignore
|
||||||
setup_documenters(app)
|
setup_documenters(app)
|
||||||
args = get_parser().parse_args(argv)
|
args = get_parser().parse_args(argv)
|
||||||
|
|
||||||
|
if args.templates:
|
||||||
|
app.config.templates_path.append(path.abspath(args.templates))
|
||||||
|
|
||||||
generate_autosummary_docs(args.source_file, args.output_dir,
|
generate_autosummary_docs(args.source_file, args.output_dir,
|
||||||
'.' + args.suffix,
|
'.' + args.suffix,
|
||||||
template_dir=args.templates,
|
|
||||||
imported_members=args.imported_members,
|
imported_members=args.imported_members,
|
||||||
app=app)
|
app=app)
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
.. automethod:: __init__
|
.. automethod:: __init__
|
||||||
|
|
||||||
{% if methods %}
|
{% if methods %}
|
||||||
.. rubric:: Methods
|
.. rubric:: {{ _('Methods') }}
|
||||||
|
|
||||||
.. autosummary::
|
.. autosummary::
|
||||||
{% for item in methods %}
|
{% for item in methods %}
|
||||||
@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
{% block attributes %}
|
{% block attributes %}
|
||||||
{% if attributes %}
|
{% if attributes %}
|
||||||
.. rubric:: Attributes
|
.. rubric:: {{ _('Attributes') }}
|
||||||
|
|
||||||
.. autosummary::
|
.. autosummary::
|
||||||
{% for item in attributes %}
|
{% for item in attributes %}
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
{% block functions %}
|
{% block functions %}
|
||||||
{% if functions %}
|
{% if functions %}
|
||||||
.. rubric:: Functions
|
.. rubric:: {{ _('Functions') }}
|
||||||
|
|
||||||
.. autosummary::
|
.. autosummary::
|
||||||
{% for item in functions %}
|
{% for item in functions %}
|
||||||
@ -26,7 +26,7 @@
|
|||||||
|
|
||||||
{% block classes %}
|
{% block classes %}
|
||||||
{% if classes %}
|
{% if classes %}
|
||||||
.. rubric:: Classes
|
.. rubric:: {{ _('Classes') }}
|
||||||
|
|
||||||
.. autosummary::
|
.. autosummary::
|
||||||
{% for item in classes %}
|
{% for item in classes %}
|
||||||
@ -37,7 +37,7 @@
|
|||||||
|
|
||||||
{% block exceptions %}
|
{% block exceptions %}
|
||||||
{% if exceptions %}
|
{% if exceptions %}
|
||||||
.. rubric:: Exceptions
|
.. rubric:: {{ _('Exceptions') }}
|
||||||
|
|
||||||
.. autosummary::
|
.. autosummary::
|
||||||
{% for item in exceptions %}
|
{% for item in exceptions %}
|
||||||
@ -45,3 +45,16 @@
|
|||||||
{%- endfor %}
|
{%- endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block modules %}
|
||||||
|
{% if modules %}
|
||||||
|
.. rubric:: Modules
|
||||||
|
|
||||||
|
.. autosummary::
|
||||||
|
:toctree:
|
||||||
|
:recursive:
|
||||||
|
{% for item in modules %}
|
||||||
|
{{ item }}
|
||||||
|
{%- endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
@ -47,7 +47,7 @@ doctestopt_re = re.compile(r'#\s*doctest:.+$', re.MULTILINE)
|
|||||||
|
|
||||||
def doctest_encode(text: str, encoding: str) -> str:
|
def doctest_encode(text: str, encoding: str) -> str:
|
||||||
warnings.warn('doctest_encode() is deprecated.',
|
warnings.warn('doctest_encode() is deprecated.',
|
||||||
RemovedInSphinx40Warning)
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
@ -12,7 +12,6 @@
|
|||||||
import posixpath
|
import posixpath
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
from hashlib import sha1
|
|
||||||
from os import path
|
from os import path
|
||||||
from subprocess import CalledProcessError, PIPE
|
from subprocess import CalledProcessError, PIPE
|
||||||
from typing import Any, Dict, List, Tuple
|
from typing import Any, Dict, List, Tuple
|
||||||
@ -25,7 +24,7 @@ import sphinx
|
|||||||
from sphinx.application import Sphinx
|
from sphinx.application import Sphinx
|
||||||
from sphinx.errors import SphinxError
|
from sphinx.errors import SphinxError
|
||||||
from sphinx.locale import _, __
|
from sphinx.locale import _, __
|
||||||
from sphinx.util import logging
|
from sphinx.util import logging, sha1
|
||||||
from sphinx.util.docutils import SphinxDirective, SphinxTranslator
|
from sphinx.util.docutils import SphinxDirective, SphinxTranslator
|
||||||
from sphinx.util.fileutil import copy_asset
|
from sphinx.util.fileutil import copy_asset
|
||||||
from sphinx.util.i18n import search_image_for_language
|
from sphinx.util.i18n import search_image_for_language
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import sys
|
||||||
from subprocess import CalledProcessError, PIPE
|
from subprocess import CalledProcessError, PIPE
|
||||||
from typing import Any, Dict
|
from typing import Any, Dict
|
||||||
|
|
||||||
@ -74,6 +75,15 @@ class ImagemagickConverter(ImageConverter):
|
|||||||
|
|
||||||
def setup(app: Sphinx) -> Dict[str, Any]:
|
def setup(app: Sphinx) -> Dict[str, Any]:
|
||||||
app.add_post_transform(ImagemagickConverter)
|
app.add_post_transform(ImagemagickConverter)
|
||||||
|
if sys.platform == 'win32':
|
||||||
|
# On Windows, we use Imagemagik v7 by default to avoid the trouble for
|
||||||
|
# convert.exe bundled with Windows.
|
||||||
|
app.add_config_value('image_converter', 'magick', 'env')
|
||||||
|
app.add_config_value('image_converter_args', ['convert'], 'env')
|
||||||
|
else:
|
||||||
|
# On other platform, we use Imagemagick v6 by default. Especially,
|
||||||
|
# Debian/Ubuntu are still based of v6. So we can't use "magick" command
|
||||||
|
# for these platforms.
|
||||||
app.add_config_value('image_converter', 'convert', 'env')
|
app.add_config_value('image_converter', 'convert', 'env')
|
||||||
app.add_config_value('image_converter_args', [], 'env')
|
app.add_config_value('image_converter_args', [], 'env')
|
||||||
|
|
||||||
|
@ -14,7 +14,6 @@ import shutil
|
|||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
from hashlib import sha1
|
|
||||||
from os import path
|
from os import path
|
||||||
from subprocess import CalledProcessError, PIPE
|
from subprocess import CalledProcessError, PIPE
|
||||||
from typing import Any, Dict, List, Tuple
|
from typing import Any, Dict, List, Tuple
|
||||||
@ -30,7 +29,7 @@ from sphinx.config import Config
|
|||||||
from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
|
from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
|
||||||
from sphinx.errors import SphinxError
|
from sphinx.errors import SphinxError
|
||||||
from sphinx.locale import _, __
|
from sphinx.locale import _, __
|
||||||
from sphinx.util import logging
|
from sphinx.util import logging, sha1
|
||||||
from sphinx.util.math import get_node_equation_number, wrap_displaymath
|
from sphinx.util.math import get_node_equation_number, wrap_displaymath
|
||||||
from sphinx.util.osutil import ensuredir
|
from sphinx.util.osutil import ensuredir
|
||||||
from sphinx.util.png import read_png_depth, write_png_depth
|
from sphinx.util.png import read_png_depth, write_png_depth
|
||||||
|
@ -38,7 +38,6 @@ r"""
|
|||||||
import builtins
|
import builtins
|
||||||
import inspect
|
import inspect
|
||||||
import re
|
import re
|
||||||
from hashlib import md5
|
|
||||||
from importlib import import_module
|
from importlib import import_module
|
||||||
from typing import Any, Dict, Iterable, List, Tuple
|
from typing import Any, Dict, Iterable, List, Tuple
|
||||||
from typing import cast
|
from typing import cast
|
||||||
@ -55,6 +54,7 @@ from sphinx.ext.graphviz import (
|
|||||||
graphviz, figure_wrapper,
|
graphviz, figure_wrapper,
|
||||||
render_dot_html, render_dot_latex, render_dot_texinfo
|
render_dot_html, render_dot_latex, render_dot_texinfo
|
||||||
)
|
)
|
||||||
|
from sphinx.util import md5
|
||||||
from sphinx.util.docutils import SphinxDirective
|
from sphinx.util.docutils import SphinxDirective
|
||||||
from sphinx.writers.html import HTMLTranslator
|
from sphinx.writers.html import HTMLTranslator
|
||||||
from sphinx.writers.latex import LaTeXTranslator
|
from sphinx.writers.latex import LaTeXTranslator
|
||||||
|
@ -168,11 +168,10 @@ class Config:
|
|||||||
**If False**::
|
**If False**::
|
||||||
|
|
||||||
.. attribute:: attr1
|
.. attribute:: attr1
|
||||||
|
:type: int
|
||||||
|
|
||||||
Description of `attr1`
|
Description of `attr1`
|
||||||
|
|
||||||
:type: int
|
|
||||||
|
|
||||||
napoleon_use_param : :obj:`bool` (Defaults to True)
|
napoleon_use_param : :obj:`bool` (Defaults to True)
|
||||||
True to use a ``:param:`` role for each function parameter. False to
|
True to use a ``:param:`` role for each function parameter. False to
|
||||||
use a single ``:parameters:`` role for all the parameters.
|
use a single ``:parameters:`` role for all the parameters.
|
||||||
|
@ -30,7 +30,9 @@ _google_section_regex = re.compile(r'^(\s|\w)+:\s*$')
|
|||||||
_google_typed_arg_regex = re.compile(r'\s*(.+?)\s*\(\s*(.*[^\s]+)\s*\)')
|
_google_typed_arg_regex = re.compile(r'\s*(.+?)\s*\(\s*(.*[^\s]+)\s*\)')
|
||||||
_numpy_section_regex = re.compile(r'^[=\-`:\'"~^_*+#<>]{2,}\s*$')
|
_numpy_section_regex = re.compile(r'^[=\-`:\'"~^_*+#<>]{2,}\s*$')
|
||||||
_single_colon_regex = re.compile(r'(?<!:):(?!:)')
|
_single_colon_regex = re.compile(r'(?<!:):(?!:)')
|
||||||
_xref_regex = re.compile(r'(:(?:[a-zA-Z0-9]+[\-_+:.])*[a-zA-Z0-9]+:`.+?`)')
|
_xref_or_code_regex = re.compile(
|
||||||
|
r'((?::(?:[a-zA-Z0-9]+[\-_+:.])*[a-zA-Z0-9]+:`.+?`)|'
|
||||||
|
r'(?:``.+``))')
|
||||||
_bullet_list_regex = re.compile(r'^(\*|\+|\-)(\s+\S|\s*$)')
|
_bullet_list_regex = re.compile(r'^(\*|\+|\-)(\s+\S|\s*$)')
|
||||||
_enumerated_list_regex = re.compile(
|
_enumerated_list_regex = re.compile(
|
||||||
r'^(?P<paren>\()?'
|
r'^(?P<paren>\()?'
|
||||||
@ -586,13 +588,12 @@ class GoogleDocstring:
|
|||||||
lines.append('.. attribute:: ' + _name)
|
lines.append('.. attribute:: ' + _name)
|
||||||
if self._opt and 'noindex' in self._opt:
|
if self._opt and 'noindex' in self._opt:
|
||||||
lines.append(' :noindex:')
|
lines.append(' :noindex:')
|
||||||
|
if _type:
|
||||||
|
lines.extend(self._indent([':type: %s' % _type], 3))
|
||||||
lines.append('')
|
lines.append('')
|
||||||
|
|
||||||
fields = self._format_field('', '', _desc)
|
fields = self._format_field('', '', _desc)
|
||||||
lines.extend(self._indent(fields, 3))
|
lines.extend(self._indent(fields, 3))
|
||||||
if _type:
|
|
||||||
lines.append('')
|
|
||||||
lines.extend(self._indent([':type: %s' % _type], 3))
|
|
||||||
lines.append('')
|
lines.append('')
|
||||||
if self._config.napoleon_use_ivar:
|
if self._config.napoleon_use_ivar:
|
||||||
lines.append('')
|
lines.append('')
|
||||||
@ -728,7 +729,7 @@ class GoogleDocstring:
|
|||||||
after_colon = []
|
after_colon = []
|
||||||
colon = ''
|
colon = ''
|
||||||
found_colon = False
|
found_colon = False
|
||||||
for i, source in enumerate(_xref_regex.split(line)):
|
for i, source in enumerate(_xref_or_code_regex.split(line)):
|
||||||
if found_colon:
|
if found_colon:
|
||||||
after_colon.append(source)
|
after_colon.append(source)
|
||||||
else:
|
else:
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
from typing import Any, Iterable
|
from typing import Any, Iterable, Optional
|
||||||
|
|
||||||
|
|
||||||
class peek_iter:
|
class peek_iter:
|
||||||
@ -62,7 +62,7 @@ class peek_iter:
|
|||||||
def __next__(self, n: int = None) -> Any:
|
def __next__(self, n: int = None) -> Any:
|
||||||
return self.next(n)
|
return self.next(n)
|
||||||
|
|
||||||
def _fillcache(self, n: int) -> None:
|
def _fillcache(self, n: Optional[int]) -> None:
|
||||||
"""Cache `n` items. If `n` is 0 or None, then 1 item is cached."""
|
"""Cache `n` items. If `n` is 0 or None, then 1 item is cached."""
|
||||||
if not n:
|
if not n:
|
||||||
n = 1
|
n = 1
|
||||||
@ -123,7 +123,7 @@ class peek_iter:
|
|||||||
result = [self._cache.popleft() for i in range(n)]
|
result = [self._cache.popleft() for i in range(n)]
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def peek(self, n: int = None) -> Any:
|
def peek(self, n: Optional[int] = None) -> Any:
|
||||||
"""Preview the next item or `n` items of the iterator.
|
"""Preview the next item or `n` items of the iterator.
|
||||||
|
|
||||||
The iterator is not advanced when peek is called.
|
The iterator is not advanced when peek is called.
|
||||||
@ -220,7 +220,7 @@ class modify_iter(peek_iter):
|
|||||||
'modifier must be callable')
|
'modifier must be callable')
|
||||||
super().__init__(*args)
|
super().__init__(*args)
|
||||||
|
|
||||||
def _fillcache(self, n: int) -> None:
|
def _fillcache(self, n: Optional[int]) -> None:
|
||||||
"""Cache `n` modified items. If `n` is 0 or None, 1 item is cached.
|
"""Cache `n` modified items. If `n` is 0 or None, 1 item is cached.
|
||||||
|
|
||||||
Each item returned by the iterator is passed through the
|
Each item returned by the iterator is passed through the
|
||||||
|
@ -105,7 +105,7 @@ class TodoDomain(Domain):
|
|||||||
|
|
||||||
|
|
||||||
def process_todos(app: Sphinx, doctree: nodes.document) -> None:
|
def process_todos(app: Sphinx, doctree: nodes.document) -> None:
|
||||||
warnings.warn('process_todos() is deprecated.', RemovedInSphinx40Warning)
|
warnings.warn('process_todos() is deprecated.', RemovedInSphinx40Warning, stacklevel=2)
|
||||||
# collect all todos in the environment
|
# collect all todos in the environment
|
||||||
# this is not done in the directive itself because it some transformations
|
# this is not done in the directive itself because it some transformations
|
||||||
# must have already been run, e.g. substitutions
|
# must have already been run, e.g. substitutions
|
||||||
@ -221,7 +221,8 @@ def process_todo_nodes(app: Sphinx, doctree: nodes.document, fromdocname: str) -
|
|||||||
"""Replace all todolist nodes with a list of the collected todos.
|
"""Replace all todolist nodes with a list of the collected todos.
|
||||||
Augment each todo with a backlink to the original location.
|
Augment each todo with a backlink to the original location.
|
||||||
"""
|
"""
|
||||||
warnings.warn('process_todo_nodes() is deprecated.', RemovedInSphinx40Warning)
|
warnings.warn('process_todo_nodes() is deprecated.',
|
||||||
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
|
|
||||||
domain = cast(TodoDomain, app.env.get_domain('todo'))
|
domain = cast(TodoDomain, app.env.get_domain('todo'))
|
||||||
todos = sum(domain.todos.values(), []) # type: List[todo_node]
|
todos = sum(domain.todos.values(), []) # type: List[todo_node]
|
||||||
@ -273,7 +274,7 @@ def process_todo_nodes(app: Sphinx, doctree: nodes.document, fromdocname: str) -
|
|||||||
|
|
||||||
|
|
||||||
def purge_todos(app: Sphinx, env: BuildEnvironment, docname: str) -> None:
|
def purge_todos(app: Sphinx, env: BuildEnvironment, docname: str) -> None:
|
||||||
warnings.warn('purge_todos() is deprecated.', RemovedInSphinx40Warning)
|
warnings.warn('purge_todos() is deprecated.', RemovedInSphinx40Warning, stacklevel=2)
|
||||||
if not hasattr(env, 'todo_all_todos'):
|
if not hasattr(env, 'todo_all_todos'):
|
||||||
return
|
return
|
||||||
env.todo_all_todos = [todo for todo in env.todo_all_todos # type: ignore
|
env.todo_all_todos = [todo for todo in env.todo_all_todos # type: ignore
|
||||||
@ -282,7 +283,7 @@ def purge_todos(app: Sphinx, env: BuildEnvironment, docname: str) -> None:
|
|||||||
|
|
||||||
def merge_info(app: Sphinx, env: BuildEnvironment, docnames: Iterable[str],
|
def merge_info(app: Sphinx, env: BuildEnvironment, docnames: Iterable[str],
|
||||||
other: BuildEnvironment) -> None:
|
other: BuildEnvironment) -> None:
|
||||||
warnings.warn('merge_info() is deprecated.', RemovedInSphinx40Warning)
|
warnings.warn('merge_info() is deprecated.', RemovedInSphinx40Warning, stacklevel=2)
|
||||||
if not hasattr(other, 'todo_all_todos'):
|
if not hasattr(other, 'todo_all_todos'):
|
||||||
return
|
return
|
||||||
if not hasattr(env, 'todo_all_todos'):
|
if not hasattr(env, 'todo_all_todos'):
|
||||||
|
@ -131,8 +131,10 @@ def env_merge_info(app: Sphinx, env: BuildEnvironment, docnames: Iterable[str],
|
|||||||
|
|
||||||
def missing_reference(app: Sphinx, env: BuildEnvironment, node: Element, contnode: Node
|
def missing_reference(app: Sphinx, env: BuildEnvironment, node: Element, contnode: Node
|
||||||
) -> Node:
|
) -> Node:
|
||||||
|
if app.builder.format != 'html':
|
||||||
|
return None
|
||||||
|
elif node['reftype'] == 'viewcode':
|
||||||
# resolve our "viewcode" reference nodes -- they need special treatment
|
# resolve our "viewcode" reference nodes -- they need special treatment
|
||||||
if node['reftype'] == 'viewcode':
|
|
||||||
return make_refnode(app.builder, node['refdoc'], node['reftarget'],
|
return make_refnode(app.builder, node['refdoc'], node['reftarget'],
|
||||||
node['refid'], contnode)
|
node['refid'], contnode)
|
||||||
|
|
||||||
|
@ -10,7 +10,6 @@
|
|||||||
import codecs
|
import codecs
|
||||||
import warnings
|
import warnings
|
||||||
from typing import Any, List
|
from typing import Any, List
|
||||||
from typing import Type # for python3.5.1
|
|
||||||
|
|
||||||
from docutils import nodes
|
from docutils import nodes
|
||||||
from docutils.core import Publisher
|
from docutils.core import Publisher
|
||||||
@ -40,6 +39,7 @@ from sphinx.versioning import UIDTransform
|
|||||||
|
|
||||||
if False:
|
if False:
|
||||||
# For type annotation
|
# For type annotation
|
||||||
|
from typing import Type # for python3.5.1
|
||||||
from sphinx.application import Sphinx
|
from sphinx.application import Sphinx
|
||||||
|
|
||||||
|
|
||||||
@ -80,7 +80,7 @@ class SphinxBaseReader(standalone.Reader):
|
|||||||
self._app = app # hold application object only for compatibility
|
self._app = app # hold application object only for compatibility
|
||||||
self._env = app.env
|
self._env = app.env
|
||||||
|
|
||||||
def get_transforms(self) -> List[Type[Transform]]:
|
def get_transforms(self) -> List["Type[Transform]"]:
|
||||||
transforms = super().get_transforms() + self.transforms
|
transforms = super().get_transforms() + self.transforms
|
||||||
|
|
||||||
# remove transforms which is not needed for Sphinx
|
# remove transforms which is not needed for Sphinx
|
||||||
|
@ -12,7 +12,7 @@ import gettext
|
|||||||
import locale
|
import locale
|
||||||
from collections import UserString, defaultdict
|
from collections import UserString, defaultdict
|
||||||
from gettext import NullTranslations
|
from gettext import NullTranslations
|
||||||
from typing import Any, Callable, Dict, Iterable, List, Tuple, Union
|
from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, Union
|
||||||
|
|
||||||
|
|
||||||
class _TranslationProxy(UserString):
|
class _TranslationProxy(UserString):
|
||||||
@ -173,7 +173,7 @@ def init_console(locale_dir: str, catalog: str) -> Tuple[NullTranslations, bool]
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# encoding is ignored
|
# encoding is ignored
|
||||||
language, _ = locale.getlocale(locale.LC_MESSAGES)
|
language, _ = locale.getlocale(locale.LC_MESSAGES) # type: Tuple[Optional[str], Any]
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
# LC_MESSAGES is not always defined. Fallback to the default language
|
# LC_MESSAGES is not always defined. Fallback to the default language
|
||||||
# in case it is not.
|
# in case it is not.
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -64,7 +64,7 @@ class Parser(docutils.parsers.Parser):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def app(self) -> "Sphinx":
|
def app(self) -> "Sphinx":
|
||||||
warnings.warn('parser.app is deprecated.', RemovedInSphinx50Warning)
|
warnings.warn('parser.app is deprecated.', RemovedInSphinx50Warning, stacklevel=2)
|
||||||
return self._app
|
return self._app
|
||||||
|
|
||||||
|
|
||||||
|
@ -133,7 +133,7 @@ class ModuleAnalyzer:
|
|||||||
pos = source.tell()
|
pos = source.tell()
|
||||||
if not decoded:
|
if not decoded:
|
||||||
warnings.warn('decode option for ModuleAnalyzer is deprecated.',
|
warnings.warn('decode option for ModuleAnalyzer is deprecated.',
|
||||||
RemovedInSphinx40Warning)
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
self._encoding, _ = tokenize.detect_encoding(source.readline)
|
self._encoding, _ = tokenize.detect_encoding(source.readline)
|
||||||
source.seek(pos)
|
source.seek(pos)
|
||||||
self.code = source.read().decode(self._encoding)
|
self.code = source.read().decode(self._encoding)
|
||||||
@ -144,6 +144,7 @@ class ModuleAnalyzer:
|
|||||||
# will be filled by parse()
|
# will be filled by parse()
|
||||||
self.annotations = None # type: Dict[Tuple[str, str], str]
|
self.annotations = None # type: Dict[Tuple[str, str], str]
|
||||||
self.attr_docs = None # type: Dict[Tuple[str, str], List[str]]
|
self.attr_docs = None # type: Dict[Tuple[str, str], List[str]]
|
||||||
|
self.finals = None # type: List[str]
|
||||||
self.tagorder = None # type: Dict[str, int]
|
self.tagorder = None # type: Dict[str, int]
|
||||||
self.tags = None # type: Dict[str, Tuple[str, int, int]]
|
self.tags = None # type: Dict[str, Tuple[str, int, int]]
|
||||||
|
|
||||||
@ -161,6 +162,7 @@ class ModuleAnalyzer:
|
|||||||
self.attr_docs[scope] = ['']
|
self.attr_docs[scope] = ['']
|
||||||
|
|
||||||
self.annotations = parser.annotations
|
self.annotations = parser.annotations
|
||||||
|
self.finals = parser.finals
|
||||||
self.tags = parser.definitions
|
self.tags = parser.definitions
|
||||||
self.tagorder = parser.deforders
|
self.tagorder = parser.deforders
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
@ -183,5 +185,5 @@ class ModuleAnalyzer:
|
|||||||
@property
|
@property
|
||||||
def encoding(self) -> str:
|
def encoding(self) -> str:
|
||||||
warnings.warn('ModuleAnalyzer.encoding is deprecated.',
|
warnings.warn('ModuleAnalyzer.encoding is deprecated.',
|
||||||
RemovedInSphinx40Warning)
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
return self._encoding
|
return self._encoding
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
from typing import Dict, List, Type
|
from typing import Dict, List, Type, Optional
|
||||||
|
|
||||||
if sys.version_info > (3, 8):
|
if sys.version_info > (3, 8):
|
||||||
import ast
|
import ast
|
||||||
@ -58,72 +58,40 @@ def parse(code: str, mode: str = 'exec') -> "ast.AST":
|
|||||||
return ast.parse(code, mode=mode)
|
return ast.parse(code, mode=mode)
|
||||||
|
|
||||||
|
|
||||||
def unparse(node: ast.AST) -> str:
|
def unparse(node: Optional[ast.AST]) -> Optional[str]:
|
||||||
"""Unparse an AST to string."""
|
"""Unparse an AST to string."""
|
||||||
if node is None:
|
if node is None:
|
||||||
return None
|
return None
|
||||||
elif isinstance(node, str):
|
elif isinstance(node, str):
|
||||||
return node
|
return node
|
||||||
elif node.__class__ in OPERATORS:
|
return _UnparseVisitor().visit(node)
|
||||||
|
|
||||||
|
|
||||||
|
# a greatly cut-down version of `ast._Unparser`
|
||||||
|
class _UnparseVisitor(ast.NodeVisitor):
|
||||||
|
|
||||||
|
def _visit_op(self, node: ast.AST) -> str:
|
||||||
return OPERATORS[node.__class__]
|
return OPERATORS[node.__class__]
|
||||||
elif isinstance(node, ast.arg):
|
for _op in OPERATORS:
|
||||||
|
locals()['visit_{}'.format(_op.__name__)] = _visit_op
|
||||||
|
|
||||||
|
def visit_arg(self, node: ast.arg) -> str:
|
||||||
if node.annotation:
|
if node.annotation:
|
||||||
return "%s: %s" % (node.arg, unparse(node.annotation))
|
return "%s: %s" % (node.arg, self.visit(node.annotation))
|
||||||
else:
|
else:
|
||||||
return node.arg
|
return node.arg
|
||||||
elif isinstance(node, ast.arguments):
|
|
||||||
return unparse_arguments(node)
|
def _visit_arg_with_default(self, arg: ast.arg, default: Optional[ast.AST]) -> str:
|
||||||
elif isinstance(node, ast.Attribute):
|
"""Unparse a single argument to a string."""
|
||||||
return "%s.%s" % (unparse(node.value), node.attr)
|
name = self.visit(arg)
|
||||||
elif isinstance(node, ast.BinOp):
|
if default:
|
||||||
return " ".join(unparse(e) for e in [node.left, node.op, node.right])
|
if arg.annotation:
|
||||||
elif isinstance(node, ast.BoolOp):
|
name += " = %s" % self.visit(default)
|
||||||
op = " %s " % unparse(node.op)
|
|
||||||
return op.join(unparse(e) for e in node.values)
|
|
||||||
elif isinstance(node, ast.Bytes):
|
|
||||||
return repr(node.s)
|
|
||||||
elif isinstance(node, ast.Call):
|
|
||||||
args = ([unparse(e) for e in node.args] +
|
|
||||||
["%s=%s" % (k.arg, unparse(k.value)) for k in node.keywords])
|
|
||||||
return "%s(%s)" % (unparse(node.func), ", ".join(args))
|
|
||||||
elif isinstance(node, ast.Dict):
|
|
||||||
keys = (unparse(k) for k in node.keys)
|
|
||||||
values = (unparse(v) for v in node.values)
|
|
||||||
items = (k + ": " + v for k, v in zip(keys, values))
|
|
||||||
return "{" + ", ".join(items) + "}"
|
|
||||||
elif isinstance(node, ast.Ellipsis):
|
|
||||||
return "..."
|
|
||||||
elif isinstance(node, ast.Index):
|
|
||||||
return unparse(node.value)
|
|
||||||
elif isinstance(node, ast.Lambda):
|
|
||||||
return "lambda %s: ..." % unparse(node.args)
|
|
||||||
elif isinstance(node, ast.List):
|
|
||||||
return "[" + ", ".join(unparse(e) for e in node.elts) + "]"
|
|
||||||
elif isinstance(node, ast.Name):
|
|
||||||
return node.id
|
|
||||||
elif isinstance(node, ast.NameConstant):
|
|
||||||
return repr(node.value)
|
|
||||||
elif isinstance(node, ast.Num):
|
|
||||||
return repr(node.n)
|
|
||||||
elif isinstance(node, ast.Set):
|
|
||||||
return "{" + ", ".join(unparse(e) for e in node.elts) + "}"
|
|
||||||
elif isinstance(node, ast.Str):
|
|
||||||
return repr(node.s)
|
|
||||||
elif isinstance(node, ast.Subscript):
|
|
||||||
return "%s[%s]" % (unparse(node.value), unparse(node.slice))
|
|
||||||
elif isinstance(node, ast.UnaryOp):
|
|
||||||
return "%s %s" % (unparse(node.op), unparse(node.operand))
|
|
||||||
elif isinstance(node, ast.Tuple):
|
|
||||||
return ", ".join(unparse(e) for e in node.elts)
|
|
||||||
elif sys.version_info > (3, 6) and isinstance(node, ast.Constant):
|
|
||||||
# this branch should be placed at last
|
|
||||||
return repr(node.value)
|
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError('Unable to parse %s object' % type(node).__name__)
|
name += "=%s" % self.visit(default)
|
||||||
|
return name
|
||||||
|
|
||||||
|
def visit_arguments(self, node: ast.arguments) -> str:
|
||||||
def unparse_arguments(node: ast.arguments) -> str:
|
|
||||||
"""Unparse an arguments to string."""
|
|
||||||
defaults = list(node.defaults)
|
defaults = list(node.defaults)
|
||||||
positionals = len(node.args)
|
positionals = len(node.args)
|
||||||
posonlyargs = 0
|
posonlyargs = 0
|
||||||
@ -140,41 +108,98 @@ def unparse_arguments(node: ast.arguments) -> str:
|
|||||||
args = [] # type: List[str]
|
args = [] # type: List[str]
|
||||||
if hasattr(node, "posonlyargs"): # for py38+
|
if hasattr(node, "posonlyargs"): # for py38+
|
||||||
for i, arg in enumerate(node.posonlyargs): # type: ignore
|
for i, arg in enumerate(node.posonlyargs): # type: ignore
|
||||||
name = unparse(arg)
|
args.append(self._visit_arg_with_default(arg, defaults[i]))
|
||||||
if defaults[i]:
|
|
||||||
if arg.annotation:
|
|
||||||
name += " = %s" % unparse(defaults[i])
|
|
||||||
else:
|
|
||||||
name += "=%s" % unparse(defaults[i])
|
|
||||||
args.append(name)
|
|
||||||
|
|
||||||
if node.posonlyargs: # type: ignore
|
if node.posonlyargs: # type: ignore
|
||||||
args.append('/')
|
args.append('/')
|
||||||
|
|
||||||
for i, arg in enumerate(node.args):
|
for i, arg in enumerate(node.args):
|
||||||
name = unparse(arg)
|
args.append(self._visit_arg_with_default(arg, defaults[i + posonlyargs]))
|
||||||
if defaults[i + posonlyargs]:
|
|
||||||
if arg.annotation:
|
|
||||||
name += " = %s" % unparse(defaults[i + posonlyargs])
|
|
||||||
else:
|
|
||||||
name += "=%s" % unparse(defaults[i + posonlyargs])
|
|
||||||
args.append(name)
|
|
||||||
|
|
||||||
if node.vararg:
|
if node.vararg:
|
||||||
args.append("*" + unparse(node.vararg))
|
args.append("*" + self.visit(node.vararg))
|
||||||
|
|
||||||
if node.kwonlyargs and not node.vararg:
|
if node.kwonlyargs and not node.vararg:
|
||||||
args.append('*')
|
args.append('*')
|
||||||
for i, arg in enumerate(node.kwonlyargs):
|
for i, arg in enumerate(node.kwonlyargs):
|
||||||
name = unparse(arg)
|
args.append(self._visit_arg_with_default(arg, kw_defaults[i]))
|
||||||
if kw_defaults[i]:
|
|
||||||
if arg.annotation:
|
|
||||||
name += " = %s" % unparse(kw_defaults[i])
|
|
||||||
else:
|
|
||||||
name += "=%s" % unparse(kw_defaults[i])
|
|
||||||
args.append(name)
|
|
||||||
|
|
||||||
if node.kwarg:
|
if node.kwarg:
|
||||||
args.append("**" + unparse(node.kwarg))
|
args.append("**" + self.visit(node.kwarg))
|
||||||
|
|
||||||
return ", ".join(args)
|
return ", ".join(args)
|
||||||
|
|
||||||
|
def visit_Attribute(self, node: ast.Attribute) -> str:
|
||||||
|
return "%s.%s" % (self.visit(node.value), node.attr)
|
||||||
|
|
||||||
|
def visit_BinOp(self, node: ast.BinOp) -> str:
|
||||||
|
return " ".join(self.visit(e) for e in [node.left, node.op, node.right])
|
||||||
|
|
||||||
|
def visit_BoolOp(self, node: ast.BoolOp) -> str:
|
||||||
|
op = " %s " % self.visit(node.op)
|
||||||
|
return op.join(self.visit(e) for e in node.values)
|
||||||
|
|
||||||
|
def visit_Call(self, node: ast.Call) -> str:
|
||||||
|
args = ([self.visit(e) for e in node.args] +
|
||||||
|
["%s=%s" % (k.arg, self.visit(k.value)) for k in node.keywords])
|
||||||
|
return "%s(%s)" % (self.visit(node.func), ", ".join(args))
|
||||||
|
|
||||||
|
def visit_Dict(self, node: ast.Dict) -> str:
|
||||||
|
keys = (self.visit(k) for k in node.keys)
|
||||||
|
values = (self.visit(v) for v in node.values)
|
||||||
|
items = (k + ": " + v for k, v in zip(keys, values))
|
||||||
|
return "{" + ", ".join(items) + "}"
|
||||||
|
|
||||||
|
def visit_Index(self, node: ast.Index) -> str:
|
||||||
|
return self.visit(node.value)
|
||||||
|
|
||||||
|
def visit_Lambda(self, node: ast.Lambda) -> str:
|
||||||
|
return "lambda %s: ..." % self.visit(node.args)
|
||||||
|
|
||||||
|
def visit_List(self, node: ast.List) -> str:
|
||||||
|
return "[" + ", ".join(self.visit(e) for e in node.elts) + "]"
|
||||||
|
|
||||||
|
def visit_Name(self, node: ast.Name) -> str:
|
||||||
|
return node.id
|
||||||
|
|
||||||
|
def visit_Set(self, node: ast.Set) -> str:
|
||||||
|
return "{" + ", ".join(self.visit(e) for e in node.elts) + "}"
|
||||||
|
|
||||||
|
def visit_Subscript(self, node: ast.Subscript) -> str:
|
||||||
|
return "%s[%s]" % (self.visit(node.value), self.visit(node.slice))
|
||||||
|
|
||||||
|
def visit_UnaryOp(self, node: ast.UnaryOp) -> str:
|
||||||
|
return "%s %s" % (self.visit(node.op), self.visit(node.operand))
|
||||||
|
|
||||||
|
def visit_Tuple(self, node: ast.Tuple) -> str:
|
||||||
|
if node.elts:
|
||||||
|
return ", ".join(self.visit(e) for e in node.elts)
|
||||||
|
else:
|
||||||
|
return "()"
|
||||||
|
|
||||||
|
if sys.version_info >= (3, 6):
|
||||||
|
def visit_Constant(self, node: ast.Constant) -> str:
|
||||||
|
if node.value is Ellipsis:
|
||||||
|
return "..."
|
||||||
|
else:
|
||||||
|
return repr(node.value)
|
||||||
|
|
||||||
|
if sys.version_info < (3, 8):
|
||||||
|
# these ast nodes were deprecated in python 3.8
|
||||||
|
def visit_Bytes(self, node: ast.Bytes) -> str:
|
||||||
|
return repr(node.s)
|
||||||
|
|
||||||
|
def visit_Ellipsis(self, node: ast.Ellipsis) -> str:
|
||||||
|
return "..."
|
||||||
|
|
||||||
|
def visit_NameConstant(self, node: ast.NameConstant) -> str:
|
||||||
|
return repr(node.value)
|
||||||
|
|
||||||
|
def visit_Num(self, node: ast.Num) -> str:
|
||||||
|
return repr(node.n)
|
||||||
|
|
||||||
|
def visit_Str(self, node: ast.Str) -> str:
|
||||||
|
return repr(node.s)
|
||||||
|
|
||||||
|
def generic_visit(self, node):
|
||||||
|
raise NotImplementedError('Unable to parse %s object' % type(node).__name__)
|
||||||
|
@ -14,7 +14,7 @@ import sys
|
|||||||
import tokenize
|
import tokenize
|
||||||
from token import NAME, NEWLINE, INDENT, DEDENT, NUMBER, OP, STRING
|
from token import NAME, NEWLINE, INDENT, DEDENT, NUMBER, OP, STRING
|
||||||
from tokenize import COMMENT, NL
|
from tokenize import COMMENT, NL
|
||||||
from typing import Any, Dict, List, Tuple
|
from typing import Any, Dict, List, Optional, Tuple
|
||||||
|
|
||||||
from sphinx.pycode.ast import ast # for py37 or older
|
from sphinx.pycode.ast import ast # for py37 or older
|
||||||
from sphinx.pycode.ast import parse, unparse
|
from sphinx.pycode.ast import parse, unparse
|
||||||
@ -231,43 +231,59 @@ class VariableCommentPicker(ast.NodeVisitor):
|
|||||||
self.annotations = {} # type: Dict[Tuple[str, str], str]
|
self.annotations = {} # type: Dict[Tuple[str, str], str]
|
||||||
self.previous = None # type: ast.AST
|
self.previous = None # type: ast.AST
|
||||||
self.deforders = {} # type: Dict[str, int]
|
self.deforders = {} # type: Dict[str, int]
|
||||||
|
self.finals = [] # type: List[str]
|
||||||
|
self.typing = None # type: str
|
||||||
|
self.typing_final = None # type: str
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
def add_entry(self, name: str) -> None:
|
def get_qualname_for(self, name: str) -> Optional[List[str]]:
|
||||||
|
"""Get qualified name for given object as a list of string."""
|
||||||
if self.current_function:
|
if self.current_function:
|
||||||
if self.current_classes and self.context[-1] == "__init__":
|
if self.current_classes and self.context[-1] == "__init__":
|
||||||
# store variable comments inside __init__ method of classes
|
# store variable comments inside __init__ method of classes
|
||||||
definition = self.context[:-1] + [name]
|
return self.context[:-1] + [name]
|
||||||
else:
|
else:
|
||||||
return
|
return None
|
||||||
else:
|
else:
|
||||||
definition = self.context + [name]
|
return self.context + [name]
|
||||||
|
|
||||||
self.deforders[".".join(definition)] = next(self.counter)
|
def add_entry(self, name: str) -> None:
|
||||||
|
qualname = self.get_qualname_for(name)
|
||||||
|
if qualname:
|
||||||
|
self.deforders[".".join(qualname)] = next(self.counter)
|
||||||
|
|
||||||
|
def add_final_entry(self, name: str) -> None:
|
||||||
|
qualname = self.get_qualname_for(name)
|
||||||
|
if qualname:
|
||||||
|
self.finals.append(".".join(qualname))
|
||||||
|
|
||||||
def add_variable_comment(self, name: str, comment: str) -> None:
|
def add_variable_comment(self, name: str, comment: str) -> None:
|
||||||
if self.current_function:
|
qualname = self.get_qualname_for(name)
|
||||||
if self.current_classes and self.context[-1] == "__init__":
|
if qualname:
|
||||||
# store variable comments inside __init__ method of classes
|
basename = ".".join(qualname[:-1])
|
||||||
context = ".".join(self.context[:-1])
|
self.comments[(basename, name)] = comment
|
||||||
else:
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
context = ".".join(self.context)
|
|
||||||
|
|
||||||
self.comments[(context, name)] = comment
|
|
||||||
|
|
||||||
def add_variable_annotation(self, name: str, annotation: ast.AST) -> None:
|
def add_variable_annotation(self, name: str, annotation: ast.AST) -> None:
|
||||||
if self.current_function:
|
qualname = self.get_qualname_for(name)
|
||||||
if self.current_classes and self.context[-1] == "__init__":
|
if qualname:
|
||||||
# store variable comments inside __init__ method of classes
|
basename = ".".join(qualname[:-1])
|
||||||
context = ".".join(self.context[:-1])
|
self.annotations[(basename, name)] = unparse(annotation)
|
||||||
else:
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
context = ".".join(self.context)
|
|
||||||
|
|
||||||
self.annotations[(context, name)] = unparse(annotation)
|
def is_final(self, decorators: List[ast.expr]) -> bool:
|
||||||
|
final = []
|
||||||
|
if self.typing:
|
||||||
|
final.append('%s.final' % self.typing)
|
||||||
|
if self.typing_final:
|
||||||
|
final.append(self.typing_final)
|
||||||
|
|
||||||
|
for decorator in decorators:
|
||||||
|
try:
|
||||||
|
if unparse(decorator) in final:
|
||||||
|
return True
|
||||||
|
except NotImplementedError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
def get_self(self) -> ast.arg:
|
def get_self(self) -> ast.arg:
|
||||||
"""Returns the name of first argument if in function."""
|
"""Returns the name of first argument if in function."""
|
||||||
@ -288,18 +304,20 @@ class VariableCommentPicker(ast.NodeVisitor):
|
|||||||
def visit_Import(self, node: ast.Import) -> None:
|
def visit_Import(self, node: ast.Import) -> None:
|
||||||
"""Handles Import node and record it to definition orders."""
|
"""Handles Import node and record it to definition orders."""
|
||||||
for name in node.names:
|
for name in node.names:
|
||||||
if name.asname:
|
self.add_entry(name.asname or name.name)
|
||||||
self.add_entry(name.asname)
|
|
||||||
else:
|
|
||||||
self.add_entry(name.name)
|
|
||||||
|
|
||||||
def visit_ImportFrom(self, node: ast.Import) -> None:
|
if name.name == 'typing':
|
||||||
|
self.typing = name.asname or name.name
|
||||||
|
elif name.name == 'typing.final':
|
||||||
|
self.typing_final = name.asname or name.name
|
||||||
|
|
||||||
|
def visit_ImportFrom(self, node: ast.ImportFrom) -> None:
|
||||||
"""Handles Import node and record it to definition orders."""
|
"""Handles Import node and record it to definition orders."""
|
||||||
for name in node.names:
|
for name in node.names:
|
||||||
if name.asname:
|
self.add_entry(name.asname or name.name)
|
||||||
self.add_entry(name.asname)
|
|
||||||
else:
|
if node.module == 'typing' and name.name == 'final':
|
||||||
self.add_entry(name.name)
|
self.typing_final = name.asname or name.name
|
||||||
|
|
||||||
def visit_Assign(self, node: ast.Assign) -> None:
|
def visit_Assign(self, node: ast.Assign) -> None:
|
||||||
"""Handles Assign node and pick up a variable comment."""
|
"""Handles Assign node and pick up a variable comment."""
|
||||||
@ -384,6 +402,8 @@ class VariableCommentPicker(ast.NodeVisitor):
|
|||||||
"""Handles ClassDef node and set context."""
|
"""Handles ClassDef node and set context."""
|
||||||
self.current_classes.append(node.name)
|
self.current_classes.append(node.name)
|
||||||
self.add_entry(node.name)
|
self.add_entry(node.name)
|
||||||
|
if self.is_final(node.decorator_list):
|
||||||
|
self.add_final_entry(node.name)
|
||||||
self.context.append(node.name)
|
self.context.append(node.name)
|
||||||
self.previous = node
|
self.previous = node
|
||||||
for child in node.body:
|
for child in node.body:
|
||||||
@ -395,6 +415,8 @@ class VariableCommentPicker(ast.NodeVisitor):
|
|||||||
"""Handles FunctionDef node and set context."""
|
"""Handles FunctionDef node and set context."""
|
||||||
if self.current_function is None:
|
if self.current_function is None:
|
||||||
self.add_entry(node.name) # should be called before setting self.current_function
|
self.add_entry(node.name) # should be called before setting self.current_function
|
||||||
|
if self.is_final(node.decorator_list):
|
||||||
|
self.add_final_entry(node.name)
|
||||||
self.context.append(node.name)
|
self.context.append(node.name)
|
||||||
self.current_function = node
|
self.current_function = node
|
||||||
for child in node.body:
|
for child in node.body:
|
||||||
@ -495,6 +517,7 @@ class Parser:
|
|||||||
self.comments = {} # type: Dict[Tuple[str, str], str]
|
self.comments = {} # type: Dict[Tuple[str, str], str]
|
||||||
self.deforders = {} # type: Dict[str, int]
|
self.deforders = {} # type: Dict[str, int]
|
||||||
self.definitions = {} # type: Dict[str, Tuple[str, int, int]]
|
self.definitions = {} # type: Dict[str, Tuple[str, int, int]]
|
||||||
|
self.finals = [] # type: List[str]
|
||||||
|
|
||||||
def parse(self) -> None:
|
def parse(self) -> None:
|
||||||
"""Parse the source code."""
|
"""Parse the source code."""
|
||||||
@ -509,6 +532,7 @@ class Parser:
|
|||||||
self.annotations = picker.annotations
|
self.annotations = picker.annotations
|
||||||
self.comments = picker.comments
|
self.comments = picker.comments
|
||||||
self.deforders = picker.deforders
|
self.deforders = picker.deforders
|
||||||
|
self.finals = picker.finals
|
||||||
|
|
||||||
def parse_definition(self) -> None:
|
def parse_definition(self) -> None:
|
||||||
"""Parse the location of definitions from the code."""
|
"""Parse the location of definitions from the code."""
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
import re
|
import re
|
||||||
import warnings
|
import warnings
|
||||||
from typing import Any, Dict, List, Tuple
|
from typing import Any, Dict, List, Tuple
|
||||||
from typing import Type # for python3.5.1
|
|
||||||
|
|
||||||
from docutils import nodes, utils
|
from docutils import nodes, utils
|
||||||
from docutils.nodes import Element, Node, TextElement, system_message
|
from docutils.nodes import Element, Node, TextElement, system_message
|
||||||
@ -29,6 +28,7 @@ from sphinx.util.typing import RoleFunction
|
|||||||
|
|
||||||
if False:
|
if False:
|
||||||
# For type annotation
|
# For type annotation
|
||||||
|
from typing import Type # for python3.5.1
|
||||||
from sphinx.application import Sphinx
|
from sphinx.application import Sphinx
|
||||||
from sphinx.environment import BuildEnvironment
|
from sphinx.environment import BuildEnvironment
|
||||||
|
|
||||||
@ -77,7 +77,7 @@ class XRefRole(ReferenceRole):
|
|||||||
innernodeclass = nodes.literal # type: Type[TextElement]
|
innernodeclass = nodes.literal # type: Type[TextElement]
|
||||||
|
|
||||||
def __init__(self, fix_parens: bool = False, lowercase: bool = False,
|
def __init__(self, fix_parens: bool = False, lowercase: bool = False,
|
||||||
nodeclass: Type[Element] = None, innernodeclass: Type[TextElement] = None,
|
nodeclass: "Type[Element]" = None, innernodeclass: "Type[TextElement]" = None,
|
||||||
warn_dangling: bool = False) -> None:
|
warn_dangling: bool = False) -> None:
|
||||||
self.fix_parens = fix_parens
|
self.fix_parens = fix_parens
|
||||||
self.lowercase = lowercase
|
self.lowercase = lowercase
|
||||||
@ -574,7 +574,7 @@ def index_role(typ: str, rawtext: str, text: str, lineno: int, inliner: Inliner,
|
|||||||
|
|
||||||
class Index(ReferenceRole):
|
class Index(ReferenceRole):
|
||||||
def run(self) -> Tuple[List[Node], List[system_message]]:
|
def run(self) -> Tuple[List[Node], List[system_message]]:
|
||||||
warnings.warn('Index role is deprecated.', RemovedInSphinx40Warning)
|
warnings.warn('Index role is deprecated.', RemovedInSphinx40Warning, stacklevel=2)
|
||||||
target_id = 'index-%s' % self.env.new_serialno('index')
|
target_id = 'index-%s' % self.env.new_serialno('index')
|
||||||
if self.has_explicit_title:
|
if self.has_explicit_title:
|
||||||
# if an explicit target is given, process it as a full entry
|
# if an explicit target is given, process it as a full entry
|
||||||
|
@ -23,7 +23,7 @@ from sphinx import package_dir
|
|||||||
from sphinx.deprecation import RemovedInSphinx40Warning
|
from sphinx.deprecation import RemovedInSphinx40Warning
|
||||||
from sphinx.environment import BuildEnvironment
|
from sphinx.environment import BuildEnvironment
|
||||||
from sphinx.search.jssplitter import splitter_code
|
from sphinx.search.jssplitter import splitter_code
|
||||||
from sphinx.util import jsdump, rpartition
|
from sphinx.util import jsdump
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
# For type annotation
|
# For type annotation
|
||||||
@ -203,7 +203,7 @@ class WordCollector(nodes.NodeVisitor):
|
|||||||
def is_meta_keywords(self, node: addnodes.meta, nodetype: Any = None) -> bool:
|
def is_meta_keywords(self, node: addnodes.meta, nodetype: Any = None) -> bool:
|
||||||
if nodetype is not None:
|
if nodetype is not None:
|
||||||
warnings.warn('"nodetype" argument for WordCollector.is_meta_keywords() '
|
warnings.warn('"nodetype" argument for WordCollector.is_meta_keywords() '
|
||||||
'is deprecated.', RemovedInSphinx40Warning)
|
'is deprecated.', RemovedInSphinx40Warning, stacklevel=2)
|
||||||
|
|
||||||
if isinstance(node, addnodes.meta) and node.get('name') == 'keywords':
|
if isinstance(node, addnodes.meta) and node.get('name') == 'keywords':
|
||||||
meta_lang = node.get('lang')
|
meta_lang = node.get('lang')
|
||||||
@ -333,7 +333,7 @@ class IndexBuilder:
|
|||||||
continue
|
continue
|
||||||
fullname = html.escape(fullname)
|
fullname = html.escape(fullname)
|
||||||
dispname = html.escape(dispname)
|
dispname = html.escape(dispname)
|
||||||
prefix, name = rpartition(dispname, '.')
|
prefix, _, name = dispname.rpartition('.')
|
||||||
pdict = rv.setdefault(prefix, {})
|
pdict = rv.setdefault(prefix, {})
|
||||||
try:
|
try:
|
||||||
typeindex = otypes[domainname, type]
|
typeindex = otypes[domainname, type]
|
||||||
|
@ -7,6 +7,9 @@
|
|||||||
:copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
#}
|
#}
|
||||||
|
{%- extends "layout.html" %}
|
||||||
|
{% set title = _('Index') %}
|
||||||
|
|
||||||
{% macro indexentries(firstname, links) %}
|
{% macro indexentries(firstname, links) %}
|
||||||
{%- if links -%}
|
{%- if links -%}
|
||||||
<a href="{{ links[0][1] }}">
|
<a href="{{ links[0][1] }}">
|
||||||
@ -26,8 +29,6 @@
|
|||||||
{%- endif %}
|
{%- endif %}
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
{%- extends "layout.html" %}
|
|
||||||
{% set title = _('Index') %}
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
|
|
||||||
<h1 id="index">{{ _('Index') }}</h1>
|
<h1 id="index">{{ _('Index') }}</h1>
|
||||||
|
@ -8,4 +8,4 @@
|
|||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
#}
|
#}
|
||||||
<h3><a href="{{ pathto(master_doc)|e }}">{{ _('Table of Contents') }}</a></h3>
|
<h3><a href="{{ pathto(master_doc)|e }}">{{ _('Table of Contents') }}</a></h3>
|
||||||
{{ toctree() }}
|
{{ toctree(includehidden=theme_globaltoc_includehidden, collapse=theme_globaltoc_collapse) }}
|
||||||
|
@ -42,6 +42,7 @@
|
|||||||
{%- for parent in parents %}
|
{%- for parent in parents %}
|
||||||
<li class="nav-item nav-item-{{ loop.index }}"><a href="{{ parent.link|e }}" {% if loop.last %}{{ accesskey("U") }}{% endif %}>{{ parent.title }}</a>{{ reldelim1 }}</li>
|
<li class="nav-item nav-item-{{ loop.index }}"><a href="{{ parent.link|e }}" {% if loop.last %}{{ accesskey("U") }}{% endif %}>{{ parent.title }}</a>{{ reldelim1 }}</li>
|
||||||
{%- endfor %}
|
{%- endfor %}
|
||||||
|
<li class="nav-item nav-item-this"><a href="{{ link|e }}">{{ title }}</a></li>
|
||||||
{%- block relbaritems %} {% endblock %}
|
{%- block relbaritems %} {% endblock %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@ -131,7 +132,7 @@
|
|||||||
{{- script() }}
|
{{- script() }}
|
||||||
{%- endblock %}
|
{%- endblock %}
|
||||||
{%- if pageurl %}
|
{%- if pageurl %}
|
||||||
<link rel="canonical" href="{{ pageurl }}" />
|
<link rel="canonical" href="{{ pageurl|e }}" />
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
{%- if use_opensearch %}
|
{%- if use_opensearch %}
|
||||||
<link rel="search" type="application/opensearchdescription+xml"
|
<link rel="search" type="application/opensearchdescription+xml"
|
||||||
@ -180,6 +181,7 @@
|
|||||||
{%- endif %}
|
{%- endif %}
|
||||||
<div class="body" role="main">
|
<div class="body" role="main">
|
||||||
{% block body %} {% endblock %}
|
{% block body %} {% endblock %}
|
||||||
|
<div class="clearer"></div>
|
||||||
</div>
|
</div>
|
||||||
{%- if render_sidebar %}
|
{%- if render_sidebar %}
|
||||||
</div>
|
</div>
|
||||||
@ -207,7 +209,7 @@
|
|||||||
{% trans last_updated=last_updated|e %}Last updated on {{ last_updated }}.{% endtrans %}
|
{% trans last_updated=last_updated|e %}Last updated on {{ last_updated }}.{% endtrans %}
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
{%- if show_sphinx %}
|
{%- if show_sphinx %}
|
||||||
{% trans sphinx_version=sphinx_version|e %}Created using <a href="http://sphinx-doc.org/">Sphinx</a> {{ sphinx_version }}.{% endtrans %}
|
{% trans sphinx_version=sphinx_version|e %}Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> {{ sphinx_version }}.{% endtrans %}
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
</div>
|
</div>
|
||||||
{%- endblock %}
|
{%- endblock %}
|
||||||
|
@ -320,18 +320,25 @@ div.sidebar {
|
|||||||
background-color: #ffe;
|
background-color: #ffe;
|
||||||
width: 40%;
|
width: 40%;
|
||||||
float: right;
|
float: right;
|
||||||
|
clear: right;
|
||||||
|
overflow-x: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
p.sidebar-title {
|
p.sidebar-title {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.admonition, div.topic, pre, div[class|="highlight"] {
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
/* -- topics ---------------------------------------------------------------- */
|
/* -- topics ---------------------------------------------------------------- */
|
||||||
|
|
||||||
div.topic {
|
div.topic {
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
padding: 7px 7px 0 7px;
|
padding: 7px 7px 0 7px;
|
||||||
margin: 10px 0 10px 0;
|
margin: 10px 0 10px 0;
|
||||||
|
overflow-x: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
p.topic-title {
|
p.topic-title {
|
||||||
@ -346,6 +353,7 @@ div.admonition {
|
|||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
padding: 7px;
|
padding: 7px;
|
||||||
|
overflow-x: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.admonition dt {
|
div.admonition dt {
|
||||||
@ -369,6 +377,8 @@ div.body p.centered {
|
|||||||
/* -- tables ---------------------------------------------------------------- */
|
/* -- tables ---------------------------------------------------------------- */
|
||||||
|
|
||||||
table.docutils {
|
table.docutils {
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
border: 0;
|
border: 0;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
}
|
}
|
||||||
@ -506,6 +516,7 @@ li > p:last-child {
|
|||||||
dl.footnote > dt,
|
dl.footnote > dt,
|
||||||
dl.citation > dt {
|
dl.citation > dt {
|
||||||
float: left;
|
float: left;
|
||||||
|
margin-right: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
dl.footnote > dd,
|
dl.footnote > dd,
|
||||||
@ -645,21 +656,52 @@ span.pre {
|
|||||||
}
|
}
|
||||||
|
|
||||||
td.linenos pre {
|
td.linenos pre {
|
||||||
padding: 5px 0px;
|
|
||||||
border: 0;
|
border: 0;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
color: #aaa;
|
color: #aaa;
|
||||||
}
|
}
|
||||||
|
|
||||||
table.highlighttable {
|
table.highlighttable {
|
||||||
margin-left: 0.5em;
|
display: block;
|
||||||
|
margin: 1em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.highlighttable tbody {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.highlighttable tr {
|
||||||
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
table.highlighttable td {
|
table.highlighttable td {
|
||||||
padding: 0 0.5em 0 0.5em;
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.highlighttable td.linenos {
|
||||||
|
padding: 0 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.highlighttable td.code {
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight .hll {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.highlighttable pre {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.code-block-caption + div > table.highlighttable {
|
||||||
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.code-block-caption {
|
div.code-block-caption {
|
||||||
|
margin-top: 1em;
|
||||||
padding: 2px 5px;
|
padding: 2px 5px;
|
||||||
font-size: small;
|
font-size: small;
|
||||||
}
|
}
|
||||||
@ -672,6 +714,7 @@ div.code-block-caption + div > div.highlight > pre {
|
|||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table.highlighttable td.linenos,
|
||||||
div.doctest > div.highlight span.gp { /* gp: Generic.Prompt */
|
div.doctest > div.highlight span.gp { /* gp: Generic.Prompt */
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
@ -685,11 +728,7 @@ div.code-block-caption span.caption-text {
|
|||||||
}
|
}
|
||||||
|
|
||||||
div.literal-block-wrapper {
|
div.literal-block-wrapper {
|
||||||
padding: 1em 1em 0;
|
margin: 1em 0;
|
||||||
}
|
|
||||||
|
|
||||||
div.literal-block-wrapper div.highlight {
|
|
||||||
margin: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
code.descname {
|
code.descname {
|
||||||
@ -740,8 +779,7 @@ span.eqno {
|
|||||||
}
|
}
|
||||||
|
|
||||||
span.eqno a.headerlink {
|
span.eqno a.headerlink {
|
||||||
position: relative;
|
position: absolute;
|
||||||
left: 0px;
|
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,3 +10,5 @@ sidebarwidth = 230
|
|||||||
body_min_width = 450
|
body_min_width = 450
|
||||||
body_max_width = 800
|
body_max_width = 800
|
||||||
navigation_with_keys = False
|
navigation_with_keys = False
|
||||||
|
globaltoc_collapse = true
|
||||||
|
globaltoc_includehidden = false
|
||||||
|
@ -23,6 +23,7 @@ from sphinx import addnodes
|
|||||||
from sphinx.config import Config
|
from sphinx.config import Config
|
||||||
from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
|
from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
|
||||||
from sphinx.locale import _, __
|
from sphinx.locale import _, __
|
||||||
|
from sphinx.util import docutils
|
||||||
from sphinx.util import logging
|
from sphinx.util import logging
|
||||||
from sphinx.util.docutils import new_document
|
from sphinx.util.docutils import new_document
|
||||||
from sphinx.util.i18n import format_date
|
from sphinx.util.i18n import format_date
|
||||||
@ -360,12 +361,18 @@ class SphinxSmartQuotes(SmartQuotes, SphinxTransform):
|
|||||||
def get_tokens(self, txtnodes: List[Text]) -> Generator[Tuple[str, str], None, None]:
|
def get_tokens(self, txtnodes: List[Text]) -> Generator[Tuple[str, str], None, None]:
|
||||||
# A generator that yields ``(texttype, nodetext)`` tuples for a list
|
# A generator that yields ``(texttype, nodetext)`` tuples for a list
|
||||||
# of "Text" nodes (interface to ``smartquotes.educate_tokens()``).
|
# of "Text" nodes (interface to ``smartquotes.educate_tokens()``).
|
||||||
|
|
||||||
texttype = {True: 'literal', # "literal" text is not changed:
|
|
||||||
False: 'plain'}
|
|
||||||
for txtnode in txtnodes:
|
for txtnode in txtnodes:
|
||||||
notsmartquotable = not is_smartquotable(txtnode)
|
if is_smartquotable(txtnode):
|
||||||
yield (texttype[notsmartquotable], txtnode.astext())
|
if docutils.__version_info__ >= (0, 16):
|
||||||
|
# SmartQuotes uses backslash escapes instead of null-escapes
|
||||||
|
text = re.sub(r'(?<=\x00)([-\\\'".`])', r'\\\1', str(txtnode))
|
||||||
|
else:
|
||||||
|
text = txtnode.astext()
|
||||||
|
|
||||||
|
yield ('plain', text)
|
||||||
|
else:
|
||||||
|
# skip smart quotes
|
||||||
|
yield ('literal', txtnode.astext())
|
||||||
|
|
||||||
|
|
||||||
class DoctreeReadEvent(SphinxTransform):
|
class DoctreeReadEvent(SphinxTransform):
|
||||||
|
@ -92,7 +92,8 @@ class ReferencesResolver(SphinxPostTransform):
|
|||||||
# no new node found? try the missing-reference event
|
# no new node found? try the missing-reference event
|
||||||
if newnode is None:
|
if newnode is None:
|
||||||
newnode = self.app.emit_firstresult('missing-reference', self.env,
|
newnode = self.app.emit_firstresult('missing-reference', self.env,
|
||||||
node, contnode)
|
node, contnode,
|
||||||
|
allowed_exceptions=(NoUri,))
|
||||||
# still not found? warn if node wishes to be warned about or
|
# still not found? warn if node wishes to be warned about or
|
||||||
# we are in nit-picky mode
|
# we are in nit-picky mode
|
||||||
if newnode is None:
|
if newnode is None:
|
||||||
|
@ -10,7 +10,6 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
from hashlib import sha1
|
|
||||||
from math import ceil
|
from math import ceil
|
||||||
from typing import Any, Dict, List, Tuple
|
from typing import Any, Dict, List, Tuple
|
||||||
|
|
||||||
@ -19,7 +18,7 @@ from docutils import nodes
|
|||||||
from sphinx.application import Sphinx
|
from sphinx.application import Sphinx
|
||||||
from sphinx.locale import __
|
from sphinx.locale import __
|
||||||
from sphinx.transforms import SphinxTransform
|
from sphinx.transforms import SphinxTransform
|
||||||
from sphinx.util import epoch_to_rfc1123, rfc1123_to_epoch
|
from sphinx.util import epoch_to_rfc1123, rfc1123_to_epoch, sha1
|
||||||
from sphinx.util import logging, requests
|
from sphinx.util import logging, requests
|
||||||
from sphinx.util.images import guess_mimetype, get_image_extension, parse_data_uri
|
from sphinx.util.images import guess_mimetype, get_image_extension, parse_data_uri
|
||||||
from sphinx.util.osutil import ensuredir, movefile
|
from sphinx.util.osutil import ensuredir, movefile
|
||||||
@ -194,7 +193,9 @@ class ImageConverter(BaseImageConverter):
|
|||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def match(self, node: nodes.image) -> bool:
|
def match(self, node: nodes.image) -> bool:
|
||||||
if self.available is None:
|
if not self.app.builder.supported_image_types:
|
||||||
|
return False
|
||||||
|
elif self.available is None:
|
||||||
self.available = self.is_available()
|
self.available = self.is_available()
|
||||||
|
|
||||||
if not self.available:
|
if not self.available:
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
import fnmatch
|
import fnmatch
|
||||||
import functools
|
import functools
|
||||||
|
import hashlib
|
||||||
import os
|
import os
|
||||||
import posixpath
|
import posixpath
|
||||||
import re
|
import re
|
||||||
@ -21,14 +22,13 @@ import warnings
|
|||||||
from codecs import BOM_UTF8
|
from codecs import BOM_UTF8
|
||||||
from collections import deque
|
from collections import deque
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from hashlib import md5
|
|
||||||
from importlib import import_module
|
from importlib import import_module
|
||||||
from os import path
|
from os import path
|
||||||
from time import mktime, strptime
|
from time import mktime, strptime
|
||||||
from typing import Any, Callable, Dict, IO, Iterable, Iterator, List, Pattern, Set, Tuple
|
from typing import Any, Callable, Dict, IO, Iterable, Iterator, List, Pattern, Set, Tuple
|
||||||
from urllib.parse import urlsplit, urlunsplit, quote_plus, parse_qsl, urlencode
|
from urllib.parse import urlsplit, urlunsplit, quote_plus, parse_qsl, urlencode
|
||||||
|
|
||||||
from sphinx.deprecation import RemovedInSphinx40Warning
|
from sphinx.deprecation import RemovedInSphinx40Warning, RemovedInSphinx50Warning
|
||||||
from sphinx.errors import (
|
from sphinx.errors import (
|
||||||
PycodeError, SphinxParallelError, ExtensionError, FiletypeNotFoundError
|
PycodeError, SphinxParallelError, ExtensionError, FiletypeNotFoundError
|
||||||
)
|
)
|
||||||
@ -110,7 +110,7 @@ def get_matching_docs(dirname: str, suffixes: List[str],
|
|||||||
Exclude files and dirs matching a pattern in *exclude_patterns*.
|
Exclude files and dirs matching a pattern in *exclude_patterns*.
|
||||||
"""
|
"""
|
||||||
warnings.warn('get_matching_docs() is now deprecated. Use get_matching_files() instead.',
|
warnings.warn('get_matching_docs() is now deprecated. Use get_matching_files() instead.',
|
||||||
RemovedInSphinx40Warning)
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
suffixpatterns = ['*' + s for s in suffixes]
|
suffixpatterns = ['*' + s for s in suffixes]
|
||||||
for filename in get_matching_files(dirname, exclude_matchers):
|
for filename in get_matching_files(dirname, exclude_matchers):
|
||||||
for suffixpattern in suffixpatterns:
|
for suffixpattern in suffixpatterns:
|
||||||
@ -170,6 +170,36 @@ class FilenameUniqDict(dict):
|
|||||||
self._existing = state
|
self._existing = state
|
||||||
|
|
||||||
|
|
||||||
|
def md5(data=b'', **kwargs):
|
||||||
|
"""Wrapper around hashlib.md5
|
||||||
|
|
||||||
|
Attempt call with 'usedforsecurity=False' if we get a ValueError, which happens when
|
||||||
|
OpenSSL FIPS mode is enabled:
|
||||||
|
ValueError: error:060800A3:digital envelope routines:EVP_DigestInit_ex:disabled for fips
|
||||||
|
|
||||||
|
See: https://github.com/sphinx-doc/sphinx/issues/7611
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return hashlib.md5(data, **kwargs) # type: ignore
|
||||||
|
except ValueError:
|
||||||
|
return hashlib.md5(data, **kwargs, usedforsecurity=False) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
def sha1(data=b'', **kwargs):
|
||||||
|
"""Wrapper around hashlib.sha1
|
||||||
|
|
||||||
|
Attempt call with 'usedforsecurity=False' if we get a ValueError
|
||||||
|
|
||||||
|
See: https://github.com/sphinx-doc/sphinx/issues/7611
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return hashlib.sha1(data, **kwargs) # type: ignore
|
||||||
|
except ValueError:
|
||||||
|
return hashlib.sha1(data, **kwargs, usedforsecurity=False) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
class DownloadFiles(dict):
|
class DownloadFiles(dict):
|
||||||
"""A special dictionary for download files.
|
"""A special dictionary for download files.
|
||||||
|
|
||||||
@ -315,7 +345,7 @@ _coding_re = re.compile(r'coding[:=]\s*([-\w.]+)')
|
|||||||
def detect_encoding(readline: Callable[[], bytes]) -> str:
|
def detect_encoding(readline: Callable[[], bytes]) -> str:
|
||||||
"""Like tokenize.detect_encoding() from Py3k, but a bit simplified."""
|
"""Like tokenize.detect_encoding() from Py3k, but a bit simplified."""
|
||||||
warnings.warn('sphinx.util.detect_encoding() is deprecated',
|
warnings.warn('sphinx.util.detect_encoding() is deprecated',
|
||||||
RemovedInSphinx40Warning)
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
|
|
||||||
def read_or_stop() -> bytes:
|
def read_or_stop() -> bytes:
|
||||||
try:
|
try:
|
||||||
@ -467,6 +497,7 @@ class attrdict(dict):
|
|||||||
|
|
||||||
def rpartition(s: str, t: str) -> Tuple[str, str]:
|
def rpartition(s: str, t: str) -> Tuple[str, str]:
|
||||||
"""Similar to str.rpartition from 2.5, but doesn't return the separator."""
|
"""Similar to str.rpartition from 2.5, but doesn't return the separator."""
|
||||||
|
warnings.warn('rpartition() is now deprecated.', RemovedInSphinx50Warning, stacklevel=2)
|
||||||
i = s.rfind(t)
|
i = s.rfind(t)
|
||||||
if i != -1:
|
if i != -1:
|
||||||
return s[:i], s[i + len(t):]
|
return s[:i], s[i + len(t):]
|
||||||
@ -570,6 +601,31 @@ def import_object(objname: str, source: str = None) -> Any:
|
|||||||
raise ExtensionError('Could not import %s' % objname, exc)
|
raise ExtensionError('Could not import %s' % objname, exc)
|
||||||
|
|
||||||
|
|
||||||
|
def split_full_qualified_name(name: str) -> Tuple[str, str]:
|
||||||
|
"""Split full qualified name to a pair of modname and qualname.
|
||||||
|
|
||||||
|
A qualname is an abbreviation for "Qualified name" introduced at PEP-3155
|
||||||
|
(https://www.python.org/dev/peps/pep-3155/). It is a dotted path name
|
||||||
|
from the module top-level.
|
||||||
|
|
||||||
|
A "full" qualified name means a string containing both module name and
|
||||||
|
qualified name.
|
||||||
|
|
||||||
|
.. note:: This function imports module actually to check the exisitence.
|
||||||
|
Therefore you need to mock 3rd party modules if needed before
|
||||||
|
calling this function.
|
||||||
|
"""
|
||||||
|
parts = name.split('.')
|
||||||
|
for i, part in enumerate(parts, 1):
|
||||||
|
try:
|
||||||
|
modname = ".".join(parts[:i])
|
||||||
|
import_module(modname)
|
||||||
|
except ImportError:
|
||||||
|
return ".".join(parts[:i - 1]), ".".join(parts[i - 1:])
|
||||||
|
|
||||||
|
return name, ""
|
||||||
|
|
||||||
|
|
||||||
def encode_uri(uri: str) -> str:
|
def encode_uri(uri: str) -> str:
|
||||||
split = list(urlsplit(uri))
|
split = list(urlsplit(uri))
|
||||||
split[1] = split[1].encode('idna').decode('ascii')
|
split[1] = split[1].encode('idna').decode('ascii')
|
||||||
|
@ -16,7 +16,9 @@ from typing import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from docutils import nodes
|
from docutils import nodes
|
||||||
|
from docutils.nodes import TextElement
|
||||||
|
|
||||||
|
from sphinx.config import Config
|
||||||
from sphinx.deprecation import RemovedInSphinx40Warning
|
from sphinx.deprecation import RemovedInSphinx40Warning
|
||||||
from sphinx.util import logging
|
from sphinx.util import logging
|
||||||
|
|
||||||
@ -39,6 +41,16 @@ integer_literal_re = re.compile(r'[1-9][0-9]*')
|
|||||||
octal_literal_re = re.compile(r'0[0-7]*')
|
octal_literal_re = re.compile(r'0[0-7]*')
|
||||||
hex_literal_re = re.compile(r'0[xX][0-9a-fA-F][0-9a-fA-F]*')
|
hex_literal_re = re.compile(r'0[xX][0-9a-fA-F][0-9a-fA-F]*')
|
||||||
binary_literal_re = re.compile(r'0[bB][01][01]*')
|
binary_literal_re = re.compile(r'0[bB][01][01]*')
|
||||||
|
integers_literal_suffix_re = re.compile(r'''(?x)
|
||||||
|
# unsigned and/or (long) long, in any order, but at least one of them
|
||||||
|
(
|
||||||
|
([uU] ([lL] | (ll) | (LL))?)
|
||||||
|
|
|
||||||
|
(([lL] | (ll) | (LL)) [uU]?)
|
||||||
|
)\b
|
||||||
|
# the ending word boundary is important for distinguishing
|
||||||
|
# between suffixes and UDLs in C++
|
||||||
|
''')
|
||||||
float_literal_re = re.compile(r'''(?x)
|
float_literal_re = re.compile(r'''(?x)
|
||||||
[+-]?(
|
[+-]?(
|
||||||
# decimal
|
# decimal
|
||||||
@ -51,6 +63,8 @@ float_literal_re = re.compile(r'''(?x)
|
|||||||
| (0[xX][0-9a-fA-F]+\.([pP][+-]?[0-9a-fA-F]+)?)
|
| (0[xX][0-9a-fA-F]+\.([pP][+-]?[0-9a-fA-F]+)?)
|
||||||
)
|
)
|
||||||
''')
|
''')
|
||||||
|
float_literal_suffix_re = re.compile(r'[fFlL]\b')
|
||||||
|
# the ending word boundary is important for distinguishing between suffixes and UDLs in C++
|
||||||
char_literal_re = re.compile(r'''(?x)
|
char_literal_re = re.compile(r'''(?x)
|
||||||
((?:u8)|u|U|L)?
|
((?:u8)|u|U|L)?
|
||||||
'(
|
'(
|
||||||
@ -67,7 +81,7 @@ char_literal_re = re.compile(r'''(?x)
|
|||||||
|
|
||||||
|
|
||||||
def verify_description_mode(mode: str) -> None:
|
def verify_description_mode(mode: str) -> None:
|
||||||
if mode not in ('lastIsName', 'noneIsName', 'markType', 'markName', 'param'):
|
if mode not in ('lastIsName', 'noneIsName', 'markType', 'markName', 'param', 'udl'):
|
||||||
raise Exception("Description mode '%s' is invalid." % mode)
|
raise Exception("Description mode '%s' is invalid." % mode)
|
||||||
|
|
||||||
|
|
||||||
@ -112,6 +126,92 @@ class ASTBaseBase:
|
|||||||
return '<%s>' % self.__class__.__name__
|
return '<%s>' % self.__class__.__name__
|
||||||
|
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Attributes
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
class ASTAttribute(ASTBaseBase):
|
||||||
|
def describe_signature(self, signode: TextElement) -> None:
|
||||||
|
raise NotImplementedError(repr(self))
|
||||||
|
|
||||||
|
|
||||||
|
class ASTCPPAttribute(ASTAttribute):
|
||||||
|
def __init__(self, arg: str) -> None:
|
||||||
|
self.arg = arg
|
||||||
|
|
||||||
|
def _stringify(self, transform: StringifyTransform) -> str:
|
||||||
|
return "[[" + self.arg + "]]"
|
||||||
|
|
||||||
|
def describe_signature(self, signode: TextElement) -> None:
|
||||||
|
txt = str(self)
|
||||||
|
signode.append(nodes.Text(txt, txt))
|
||||||
|
|
||||||
|
|
||||||
|
class ASTGnuAttribute(ASTBaseBase):
|
||||||
|
def __init__(self, name: str, args: Any) -> None:
|
||||||
|
self.name = name
|
||||||
|
self.args = args
|
||||||
|
|
||||||
|
def _stringify(self, transform: StringifyTransform) -> str:
|
||||||
|
res = [self.name]
|
||||||
|
if self.args:
|
||||||
|
res.append('(')
|
||||||
|
res.append(transform(self.args))
|
||||||
|
res.append(')')
|
||||||
|
return ''.join(res)
|
||||||
|
|
||||||
|
|
||||||
|
class ASTGnuAttributeList(ASTAttribute):
|
||||||
|
def __init__(self, attrs: List[ASTGnuAttribute]) -> None:
|
||||||
|
self.attrs = attrs
|
||||||
|
|
||||||
|
def _stringify(self, transform: StringifyTransform) -> str:
|
||||||
|
res = ['__attribute__((']
|
||||||
|
first = True
|
||||||
|
for attr in self.attrs:
|
||||||
|
if not first:
|
||||||
|
res.append(', ')
|
||||||
|
first = False
|
||||||
|
res.append(transform(attr))
|
||||||
|
res.append('))')
|
||||||
|
return ''.join(res)
|
||||||
|
|
||||||
|
def describe_signature(self, signode: TextElement) -> None:
|
||||||
|
txt = str(self)
|
||||||
|
signode.append(nodes.Text(txt, txt))
|
||||||
|
|
||||||
|
|
||||||
|
class ASTIdAttribute(ASTAttribute):
|
||||||
|
"""For simple attributes defined by the user."""
|
||||||
|
|
||||||
|
def __init__(self, id: str) -> None:
|
||||||
|
self.id = id
|
||||||
|
|
||||||
|
def _stringify(self, transform: StringifyTransform) -> str:
|
||||||
|
return self.id
|
||||||
|
|
||||||
|
def describe_signature(self, signode: TextElement) -> None:
|
||||||
|
signode.append(nodes.Text(self.id, self.id))
|
||||||
|
|
||||||
|
|
||||||
|
class ASTParenAttribute(ASTAttribute):
|
||||||
|
"""For paren attributes defined by the user."""
|
||||||
|
|
||||||
|
def __init__(self, id: str, arg: str) -> None:
|
||||||
|
self.id = id
|
||||||
|
self.arg = arg
|
||||||
|
|
||||||
|
def _stringify(self, transform: StringifyTransform) -> str:
|
||||||
|
return self.id + '(' + self.arg + ')'
|
||||||
|
|
||||||
|
def describe_signature(self, signode: TextElement) -> None:
|
||||||
|
txt = str(self)
|
||||||
|
signode.append(nodes.Text(txt, txt))
|
||||||
|
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
|
||||||
class UnsupportedMultiCharacterCharLiteral(Exception):
|
class UnsupportedMultiCharacterCharLiteral(Exception):
|
||||||
@property
|
@property
|
||||||
def decoded(self) -> str:
|
def decoded(self) -> str:
|
||||||
@ -132,9 +232,11 @@ class DefinitionError(Exception):
|
|||||||
|
|
||||||
class BaseParser:
|
class BaseParser:
|
||||||
def __init__(self, definition: str, *,
|
def __init__(self, definition: str, *,
|
||||||
location: Union[nodes.Node, Tuple[str, int]]) -> None:
|
location: Union[nodes.Node, Tuple[str, int]],
|
||||||
|
config: "Config") -> None:
|
||||||
self.definition = definition.strip()
|
self.definition = definition.strip()
|
||||||
self.location = location # for warnings
|
self.location = location # for warnings
|
||||||
|
self.config = config
|
||||||
|
|
||||||
self.pos = 0
|
self.pos = 0
|
||||||
self.end = len(self.definition)
|
self.end = len(self.definition)
|
||||||
@ -248,7 +350,100 @@ class BaseParser:
|
|||||||
self.pos = self.end
|
self.pos = self.end
|
||||||
return rv
|
return rv
|
||||||
|
|
||||||
def assert_end(self) -> None:
|
def assert_end(self, *, allowSemicolon: bool = False) -> None:
|
||||||
self.skip_ws()
|
self.skip_ws()
|
||||||
|
if allowSemicolon:
|
||||||
|
if not self.eof and self.definition[self.pos:] != ';':
|
||||||
|
self.fail('Expected end of definition or ;.')
|
||||||
|
else:
|
||||||
if not self.eof:
|
if not self.eof:
|
||||||
self.fail('Expected end of definition.')
|
self.fail('Expected end of definition.')
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id_attributes(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@property
|
||||||
|
def paren_attributes(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def _parse_balanced_token_seq(self, end: List[str]) -> str:
|
||||||
|
# TODO: add handling of string literals and similar
|
||||||
|
brackets = {'(': ')', '[': ']', '{': '}'}
|
||||||
|
startPos = self.pos
|
||||||
|
symbols = [] # type: List[str]
|
||||||
|
while not self.eof:
|
||||||
|
if len(symbols) == 0 and self.current_char in end:
|
||||||
|
break
|
||||||
|
if self.current_char in brackets.keys():
|
||||||
|
symbols.append(brackets[self.current_char])
|
||||||
|
elif len(symbols) > 0 and self.current_char == symbols[-1]:
|
||||||
|
symbols.pop()
|
||||||
|
elif self.current_char in ")]}":
|
||||||
|
self.fail("Unexpected '%s' in balanced-token-seq." % self.current_char)
|
||||||
|
self.pos += 1
|
||||||
|
if self.eof:
|
||||||
|
self.fail("Could not find end of balanced-token-seq starting at %d."
|
||||||
|
% startPos)
|
||||||
|
return self.definition[startPos:self.pos]
|
||||||
|
|
||||||
|
def _parse_attribute(self) -> ASTAttribute:
|
||||||
|
self.skip_ws()
|
||||||
|
# try C++11 style
|
||||||
|
startPos = self.pos
|
||||||
|
if self.skip_string_and_ws('['):
|
||||||
|
if not self.skip_string('['):
|
||||||
|
self.pos = startPos
|
||||||
|
else:
|
||||||
|
# TODO: actually implement the correct grammar
|
||||||
|
arg = self._parse_balanced_token_seq(end=[']'])
|
||||||
|
if not self.skip_string_and_ws(']'):
|
||||||
|
self.fail("Expected ']' in end of attribute.")
|
||||||
|
if not self.skip_string_and_ws(']'):
|
||||||
|
self.fail("Expected ']' in end of attribute after [[...]")
|
||||||
|
return ASTCPPAttribute(arg)
|
||||||
|
|
||||||
|
# try GNU style
|
||||||
|
if self.skip_word_and_ws('__attribute__'):
|
||||||
|
if not self.skip_string_and_ws('('):
|
||||||
|
self.fail("Expected '(' after '__attribute__'.")
|
||||||
|
if not self.skip_string_and_ws('('):
|
||||||
|
self.fail("Expected '(' after '__attribute__('.")
|
||||||
|
attrs = []
|
||||||
|
while 1:
|
||||||
|
if self.match(identifier_re):
|
||||||
|
name = self.matched_text
|
||||||
|
self.skip_ws()
|
||||||
|
if self.skip_string_and_ws('('):
|
||||||
|
self.fail('Parameterized GNU style attribute not yet supported.')
|
||||||
|
attrs.append(ASTGnuAttribute(name, None))
|
||||||
|
# TODO: parse arguments for the attribute
|
||||||
|
if self.skip_string_and_ws(','):
|
||||||
|
continue
|
||||||
|
elif self.skip_string_and_ws(')'):
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
self.fail("Expected identifier, ')', or ',' in __attribute__.")
|
||||||
|
if not self.skip_string_and_ws(')'):
|
||||||
|
self.fail("Expected ')' after '__attribute__((...)'")
|
||||||
|
return ASTGnuAttributeList(attrs)
|
||||||
|
|
||||||
|
# try the simple id attributes defined by the user
|
||||||
|
for id in self.id_attributes:
|
||||||
|
if self.skip_word_and_ws(id):
|
||||||
|
return ASTIdAttribute(id)
|
||||||
|
|
||||||
|
# try the paren attributes defined by the user
|
||||||
|
for id in self.paren_attributes:
|
||||||
|
if not self.skip_string_and_ws(id):
|
||||||
|
continue
|
||||||
|
if not self.skip_string('('):
|
||||||
|
self.fail("Expected '(' after user-defined paren-attribute.")
|
||||||
|
arg = self._parse_balanced_token_seq(end=[')'])
|
||||||
|
if not self.skip_string(')'):
|
||||||
|
self.fail("Expected ')' to end user-defined paren-attribute.")
|
||||||
|
return ASTParenAttribute(id, arg)
|
||||||
|
|
||||||
|
return None
|
||||||
|
@ -46,7 +46,7 @@ class IndexEntriesMigrator(SphinxTransform):
|
|||||||
if len(entries) == 4:
|
if len(entries) == 4:
|
||||||
source, line = get_source_line(node)
|
source, line = get_source_line(node)
|
||||||
warnings.warn('An old styled index node found: %r at (%s:%s)' %
|
warnings.warn('An old styled index node found: %r at (%s:%s)' %
|
||||||
(node, source, line), RemovedInSphinx40Warning)
|
(node, source, line), RemovedInSphinx40Warning, stacklevel=2)
|
||||||
node['entries'][i] = entries + (None,)
|
node['entries'][i] = entries + (None,)
|
||||||
|
|
||||||
|
|
||||||
|
@ -224,12 +224,12 @@ class DocFieldTransformer:
|
|||||||
except Exception:
|
except Exception:
|
||||||
# for 3rd party extensions directly calls this transformer.
|
# for 3rd party extensions directly calls this transformer.
|
||||||
warnings.warn('DocFieldTransformer expects given directive object is a subclass '
|
warnings.warn('DocFieldTransformer expects given directive object is a subclass '
|
||||||
'of ObjectDescription.', RemovedInSphinx40Warning)
|
'of ObjectDescription.', RemovedInSphinx40Warning, stacklevel=2)
|
||||||
self.typemap = self.preprocess_fieldtypes(directive.__class__.doc_field_types)
|
self.typemap = self.preprocess_fieldtypes(directive.__class__.doc_field_types)
|
||||||
|
|
||||||
def preprocess_fieldtypes(self, types: List[Field]) -> Dict[str, Tuple[Field, bool]]:
|
def preprocess_fieldtypes(self, types: List[Field]) -> Dict[str, Tuple[Field, bool]]:
|
||||||
warnings.warn('DocFieldTransformer.preprocess_fieldtypes() is deprecated.',
|
warnings.warn('DocFieldTransformer.preprocess_fieldtypes() is deprecated.',
|
||||||
RemovedInSphinx40Warning)
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
typemap = {}
|
typemap = {}
|
||||||
for fieldtype in types:
|
for fieldtype in types:
|
||||||
for name in fieldtype.names:
|
for name in fieldtype.names:
|
||||||
|
@ -10,10 +10,13 @@
|
|||||||
|
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
import warnings
|
||||||
from typing import Dict, List
|
from typing import Dict, List
|
||||||
|
|
||||||
from docutils.parsers.rst.states import Body
|
from docutils.parsers.rst.states import Body
|
||||||
|
|
||||||
|
from sphinx.deprecation import RemovedInSphinx50Warning
|
||||||
|
|
||||||
|
|
||||||
field_list_item_re = re.compile(Body.patterns['field_marker'])
|
field_list_item_re = re.compile(Body.patterns['field_marker'])
|
||||||
|
|
||||||
@ -42,7 +45,7 @@ def extract_metadata(s: str) -> Dict[str, str]:
|
|||||||
return metadata
|
return metadata
|
||||||
|
|
||||||
|
|
||||||
def prepare_docstring(s: str, ignore: int = 1, tabsize: int = 8) -> List[str]:
|
def prepare_docstring(s: str, ignore: int = None, tabsize: int = 8) -> List[str]:
|
||||||
"""Convert a docstring into lines of parseable reST. Remove common leading
|
"""Convert a docstring into lines of parseable reST. Remove common leading
|
||||||
indentation, where the indentation of a given number of lines (usually just
|
indentation, where the indentation of a given number of lines (usually just
|
||||||
one) is ignored.
|
one) is ignored.
|
||||||
@ -51,6 +54,12 @@ def prepare_docstring(s: str, ignore: int = 1, tabsize: int = 8) -> List[str]:
|
|||||||
ViewList (used as argument of nested_parse().) An empty line is added to
|
ViewList (used as argument of nested_parse().) An empty line is added to
|
||||||
act as a separator between this docstring and following content.
|
act as a separator between this docstring and following content.
|
||||||
"""
|
"""
|
||||||
|
if ignore is None:
|
||||||
|
ignore = 1
|
||||||
|
else:
|
||||||
|
warnings.warn("The 'ignore' argument to parepare_docstring() is deprecated.",
|
||||||
|
RemovedInSphinx50Warning, stacklevel=2)
|
||||||
|
|
||||||
lines = s.expandtabs(tabsize).splitlines()
|
lines = s.expandtabs(tabsize).splitlines()
|
||||||
# Find minimum indentation of any non-blank lines after ignored lines.
|
# Find minimum indentation of any non-blank lines after ignored lines.
|
||||||
margin = sys.maxsize
|
margin = sys.maxsize
|
||||||
|
@ -12,7 +12,7 @@ import base64
|
|||||||
import imghdr
|
import imghdr
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from os import path
|
from os import path
|
||||||
from typing import IO, NamedTuple, Tuple
|
from typing import IO, NamedTuple, Optional, Tuple
|
||||||
|
|
||||||
import imagesize
|
import imagesize
|
||||||
|
|
||||||
@ -36,7 +36,7 @@ DataURI = NamedTuple('DataURI', [('mimetype', str),
|
|||||||
('data', bytes)])
|
('data', bytes)])
|
||||||
|
|
||||||
|
|
||||||
def get_image_size(filename: str) -> Tuple[int, int]:
|
def get_image_size(filename: str) -> Optional[Tuple[int, int]]:
|
||||||
try:
|
try:
|
||||||
size = imagesize.get(filename)
|
size = imagesize.get(filename)
|
||||||
if size[0] == -1:
|
if size[0] == -1:
|
||||||
@ -53,7 +53,7 @@ def get_image_size(filename: str) -> Tuple[int, int]:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def guess_mimetype_for_stream(stream: IO, default: str = None) -> str:
|
def guess_mimetype_for_stream(stream: IO, default: Optional[str] = None) -> Optional[str]:
|
||||||
imgtype = imghdr.what(stream) # type: ignore
|
imgtype = imghdr.what(stream) # type: ignore
|
||||||
if imgtype:
|
if imgtype:
|
||||||
return 'image/' + imgtype
|
return 'image/' + imgtype
|
||||||
@ -61,7 +61,7 @@ def guess_mimetype_for_stream(stream: IO, default: str = None) -> str:
|
|||||||
return default
|
return default
|
||||||
|
|
||||||
|
|
||||||
def guess_mimetype(filename: str = '', default: str = None) -> str:
|
def guess_mimetype(filename: str = '', default: Optional[str] = None) -> Optional[str]:
|
||||||
_, ext = path.splitext(filename.lower())
|
_, ext = path.splitext(filename.lower())
|
||||||
if ext in mime_suffixes:
|
if ext in mime_suffixes:
|
||||||
return mime_suffixes[ext]
|
return mime_suffixes[ext]
|
||||||
@ -72,7 +72,7 @@ def guess_mimetype(filename: str = '', default: str = None) -> str:
|
|||||||
return default
|
return default
|
||||||
|
|
||||||
|
|
||||||
def get_image_extension(mimetype: str) -> str:
|
def get_image_extension(mimetype: str) -> Optional[str]:
|
||||||
for ext, _mimetype in mime_suffixes.items():
|
for ext, _mimetype in mime_suffixes.items():
|
||||||
if mimetype == _mimetype:
|
if mimetype == _mimetype:
|
||||||
return ext
|
return ext
|
||||||
@ -80,7 +80,7 @@ def get_image_extension(mimetype: str) -> str:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def parse_data_uri(uri: str) -> DataURI:
|
def parse_data_uri(uri: str) -> Optional[DataURI]:
|
||||||
if not uri.startswith('data:'):
|
if not uri.startswith('data:'):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -101,7 +101,7 @@ def parse_data_uri(uri: str) -> DataURI:
|
|||||||
return DataURI(mimetype, charset, image_data)
|
return DataURI(mimetype, charset, image_data)
|
||||||
|
|
||||||
|
|
||||||
def test_svg(h: bytes, f: IO) -> str:
|
def test_svg(h: bytes, f: IO) -> Optional[str]:
|
||||||
"""An additional imghdr library helper; test the header is SVG's or not."""
|
"""An additional imghdr library helper; test the header is SVG's or not."""
|
||||||
try:
|
try:
|
||||||
if '<svg' in h.decode().lower():
|
if '<svg' in h.decode().lower():
|
||||||
|
@ -20,7 +20,7 @@ from inspect import ( # NOQA
|
|||||||
Parameter, isclass, ismethod, ismethoddescriptor
|
Parameter, isclass, ismethod, ismethoddescriptor
|
||||||
)
|
)
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from typing import Any, Callable, Mapping, List, Tuple
|
from typing import Any, Callable, Mapping, List, Optional, Tuple
|
||||||
from typing import cast
|
from typing import cast
|
||||||
|
|
||||||
from sphinx.deprecation import RemovedInSphinx40Warning, RemovedInSphinx50Warning
|
from sphinx.deprecation import RemovedInSphinx40Warning, RemovedInSphinx50Warning
|
||||||
@ -58,7 +58,7 @@ def getargspec(func: Callable) -> Any:
|
|||||||
"""Like inspect.getfullargspec but supports bound methods, and wrapped
|
"""Like inspect.getfullargspec but supports bound methods, and wrapped
|
||||||
methods."""
|
methods."""
|
||||||
warnings.warn('sphinx.ext.inspect.getargspec() is deprecated',
|
warnings.warn('sphinx.ext.inspect.getargspec() is deprecated',
|
||||||
RemovedInSphinx50Warning)
|
RemovedInSphinx50Warning, stacklevel=2)
|
||||||
# On 3.5+, signature(int) or similar raises ValueError. On 3.4, it
|
# On 3.5+, signature(int) or similar raises ValueError. On 3.4, it
|
||||||
# succeeds with a bogus signature. We want a TypeError uniformly, to
|
# succeeds with a bogus signature. We want a TypeError uniformly, to
|
||||||
# match historical behavior.
|
# match historical behavior.
|
||||||
@ -125,13 +125,15 @@ def unwrap(obj: Any) -> Any:
|
|||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
|
||||||
def unwrap_all(obj: Any) -> Any:
|
def unwrap_all(obj: Any, *, stop: Callable = None) -> Any:
|
||||||
"""
|
"""
|
||||||
Get an original object from wrapped object (unwrapping partials, wrapped
|
Get an original object from wrapped object (unwrapping partials, wrapped
|
||||||
functions, and other decorators).
|
functions, and other decorators).
|
||||||
"""
|
"""
|
||||||
while True:
|
while True:
|
||||||
if ispartial(obj):
|
if stop and stop(obj):
|
||||||
|
return obj
|
||||||
|
elif ispartial(obj):
|
||||||
obj = obj.func
|
obj = obj.func
|
||||||
elif inspect.isroutine(obj) and hasattr(obj, '__wrapped__'):
|
elif inspect.isroutine(obj) and hasattr(obj, '__wrapped__'):
|
||||||
obj = obj.__wrapped__
|
obj = obj.__wrapped__
|
||||||
@ -287,7 +289,8 @@ def isroutine(obj: Any) -> bool:
|
|||||||
|
|
||||||
def iscoroutinefunction(obj: Any) -> bool:
|
def iscoroutinefunction(obj: Any) -> bool:
|
||||||
"""Check if the object is coroutine-function."""
|
"""Check if the object is coroutine-function."""
|
||||||
obj = unwrap_all(obj)
|
# unwrap staticmethod, classmethod and partial (except wrappers)
|
||||||
|
obj = unwrap_all(obj, stop=lambda o: hasattr(o, '__wrapped__'))
|
||||||
if hasattr(obj, '__code__') and inspect.iscoroutinefunction(obj):
|
if hasattr(obj, '__code__') and inspect.iscoroutinefunction(obj):
|
||||||
# check obj.__code__ because iscoroutinefunction() crashes for custom method-like
|
# check obj.__code__ because iscoroutinefunction() crashes for custom method-like
|
||||||
# objects (see https://github.com/sphinx-doc/sphinx/issues/6605)
|
# objects (see https://github.com/sphinx-doc/sphinx/issues/6605)
|
||||||
@ -326,7 +329,7 @@ def safe_getattr(obj: Any, name: str, *defargs: Any) -> Any:
|
|||||||
def safe_getmembers(object: Any, predicate: Callable[[str], bool] = None,
|
def safe_getmembers(object: Any, predicate: Callable[[str], bool] = None,
|
||||||
attr_getter: Callable = safe_getattr) -> List[Tuple[str, Any]]:
|
attr_getter: Callable = safe_getattr) -> List[Tuple[str, Any]]:
|
||||||
"""A version of inspect.getmembers() that uses safe_getattr()."""
|
"""A version of inspect.getmembers() that uses safe_getattr()."""
|
||||||
warnings.warn('safe_getmembers() is deprecated', RemovedInSphinx40Warning)
|
warnings.warn('safe_getmembers() is deprecated', RemovedInSphinx40Warning, stacklevel=2)
|
||||||
|
|
||||||
results = [] # type: List[Tuple[str, Any]]
|
results = [] # type: List[Tuple[str, Any]]
|
||||||
for key in dir(object):
|
for key in dir(object):
|
||||||
@ -386,26 +389,38 @@ def is_builtin_class_method(obj: Any, attr_name: str) -> bool:
|
|||||||
Why this function needed? CPython implements int.__init__ by Descriptor
|
Why this function needed? CPython implements int.__init__ by Descriptor
|
||||||
but PyPy implements it by pure Python code.
|
but PyPy implements it by pure Python code.
|
||||||
"""
|
"""
|
||||||
classes = [c for c in inspect.getmro(obj) if attr_name in c.__dict__]
|
try:
|
||||||
cls = classes[0] if classes else object
|
mro = inspect.getmro(obj)
|
||||||
|
except AttributeError:
|
||||||
if not hasattr(builtins, safe_getattr(cls, '__name__', '')):
|
# no __mro__, assume the object has no methods as we know them
|
||||||
return False
|
return False
|
||||||
return getattr(builtins, safe_getattr(cls, '__name__', '')) is cls
|
|
||||||
|
try:
|
||||||
|
cls = next(c for c in mro if attr_name in safe_getattr(c, '__dict__', {}))
|
||||||
|
except StopIteration:
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
name = safe_getattr(cls, '__name__')
|
||||||
|
except AttributeError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return getattr(builtins, name, None) is cls
|
||||||
|
|
||||||
|
|
||||||
def signature(subject: Callable, bound_method: bool = False) -> inspect.Signature:
|
def signature(subject: Callable, bound_method: bool = False, follow_wrapped: bool = False
|
||||||
|
) -> inspect.Signature:
|
||||||
"""Return a Signature object for the given *subject*.
|
"""Return a Signature object for the given *subject*.
|
||||||
|
|
||||||
:param bound_method: Specify *subject* is a bound method or not
|
:param bound_method: Specify *subject* is a bound method or not
|
||||||
|
:param follow_wrapped: Same as ``inspect.signature()``.
|
||||||
|
Defaults to ``False`` (get a signature of *subject*).
|
||||||
"""
|
"""
|
||||||
# check subject is not a built-in class (ex. int, str)
|
|
||||||
if (isinstance(subject, type) and
|
|
||||||
is_builtin_class_method(subject, "__new__") and
|
|
||||||
is_builtin_class_method(subject, "__init__")):
|
|
||||||
raise TypeError("can't compute signature for built-in type {}".format(subject))
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
try:
|
||||||
|
signature = inspect.signature(subject, follow_wrapped=follow_wrapped)
|
||||||
|
except ValueError:
|
||||||
|
# follow built-in wrappers up (ex. functools.lru_cache)
|
||||||
signature = inspect.signature(subject)
|
signature = inspect.signature(subject)
|
||||||
parameters = list(signature.parameters.values())
|
parameters = list(signature.parameters.values())
|
||||||
return_annotation = signature.return_annotation
|
return_annotation = signature.return_annotation
|
||||||
@ -527,7 +542,7 @@ def signature_from_str(signature: str) -> inspect.Signature:
|
|||||||
annotation=annotation))
|
annotation=annotation))
|
||||||
|
|
||||||
for i, arg in enumerate(args.kwonlyargs):
|
for i, arg in enumerate(args.kwonlyargs):
|
||||||
default = ast_unparse(args.kw_defaults[i])
|
default = ast_unparse(args.kw_defaults[i]) or Parameter.empty
|
||||||
annotation = ast_unparse(arg.annotation) or Parameter.empty
|
annotation = ast_unparse(arg.annotation) or Parameter.empty
|
||||||
params.append(Parameter(arg.arg, Parameter.KEYWORD_ONLY, default=default,
|
params.append(Parameter(arg.arg, Parameter.KEYWORD_ONLY, default=default,
|
||||||
annotation=annotation))
|
annotation=annotation))
|
||||||
@ -552,7 +567,7 @@ class Signature:
|
|||||||
def __init__(self, subject: Callable, bound_method: bool = False,
|
def __init__(self, subject: Callable, bound_method: bool = False,
|
||||||
has_retval: bool = True) -> None:
|
has_retval: bool = True) -> None:
|
||||||
warnings.warn('sphinx.util.inspect.Signature() is deprecated',
|
warnings.warn('sphinx.util.inspect.Signature() is deprecated',
|
||||||
RemovedInSphinx40Warning)
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
|
|
||||||
# check subject is not a built-in class (ex. int, str)
|
# check subject is not a built-in class (ex. int, str)
|
||||||
if (isinstance(subject, type) and
|
if (isinstance(subject, type) and
|
||||||
@ -565,7 +580,7 @@ class Signature:
|
|||||||
self.partialmethod_with_noargs = False
|
self.partialmethod_with_noargs = False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.signature = inspect.signature(subject)
|
self.signature = inspect.signature(subject) # type: Optional[inspect.Signature]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
# Until python 3.6.4, cpython has been crashed on inspection for
|
# Until python 3.6.4, cpython has been crashed on inspection for
|
||||||
# partialmethods not having any arguments.
|
# partialmethods not having any arguments.
|
||||||
@ -691,13 +706,14 @@ class Signature:
|
|||||||
|
|
||||||
|
|
||||||
def getdoc(obj: Any, attrgetter: Callable = safe_getattr,
|
def getdoc(obj: Any, attrgetter: Callable = safe_getattr,
|
||||||
allow_inherited: bool = False) -> str:
|
allow_inherited: bool = False, cls: Any = None, name: str = None) -> str:
|
||||||
"""Get the docstring for the object.
|
"""Get the docstring for the object.
|
||||||
|
|
||||||
This tries to obtain the docstring for some kind of objects additionally:
|
This tries to obtain the docstring for some kind of objects additionally:
|
||||||
|
|
||||||
* partial functions
|
* partial functions
|
||||||
* inherited docstring
|
* inherited docstring
|
||||||
|
* inherited decorated methods
|
||||||
"""
|
"""
|
||||||
doc = attrgetter(obj, '__doc__', None)
|
doc = attrgetter(obj, '__doc__', None)
|
||||||
if ispartial(obj) and doc == obj.__class__.__doc__:
|
if ispartial(obj) and doc == obj.__class__.__doc__:
|
||||||
@ -705,4 +721,14 @@ def getdoc(obj: Any, attrgetter: Callable = safe_getattr,
|
|||||||
elif doc is None and allow_inherited:
|
elif doc is None and allow_inherited:
|
||||||
doc = inspect.getdoc(obj)
|
doc = inspect.getdoc(obj)
|
||||||
|
|
||||||
|
if doc is None and cls:
|
||||||
|
# inspect.getdoc() does not support some kind of inherited and decorated methods.
|
||||||
|
# This tries to obtain the docstring from super classes.
|
||||||
|
for basecls in getattr(cls, '__mro__', []):
|
||||||
|
meth = safe_getattr(basecls, name, None)
|
||||||
|
if meth:
|
||||||
|
doc = inspect.getdoc(meth)
|
||||||
|
if doc:
|
||||||
|
break
|
||||||
|
|
||||||
return doc
|
return doc
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user