mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge branch '4.x' into HEAD
This commit is contained in:
18
.github/workflows/create-release.yml
vendored
Normal file
18
.github/workflows/create-release.yml
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
name: Create release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "v*.*.*"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
create-release:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Release
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
|
with:
|
||||||
|
body: "Changelog: https://www.sphinx-doc.org/en/master/changes.html"
|
||||||
25
.github/workflows/docutils-latest.yml
vendored
Normal file
25
.github/workflows/docutils-latest.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
name: Test with the HEAD of docutils
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: "0 0 * * SUN"
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v2
|
||||||
|
- name: Check Python version
|
||||||
|
run: python --version
|
||||||
|
- name: Unpin docutils
|
||||||
|
run: sed -i -e "s/'docutils>=.*'/'docutils'/" setup.py
|
||||||
|
- name: Install graphviz
|
||||||
|
run: sudo apt-get install graphviz
|
||||||
|
- name: Install dependencies
|
||||||
|
run: pip install -U tox codecov
|
||||||
|
- name: Run Tox
|
||||||
|
run: tox -e du-latest -- -vv
|
||||||
12
.github/workflows/main.yml
vendored
12
.github/workflows/main.yml
vendored
@@ -8,7 +8,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
name: [py36, py37, py38, py39]
|
name: [py36, py37, py38, py39, py310]
|
||||||
include:
|
include:
|
||||||
- name: py36
|
- name: py36
|
||||||
python: 3.6
|
python: 3.6
|
||||||
@@ -23,9 +23,12 @@ jobs:
|
|||||||
python: 3.9
|
python: 3.9
|
||||||
docutils: du17
|
docutils: du17
|
||||||
coverage: "--cov ./ --cov-append --cov-config setup.cfg"
|
coverage: "--cov ./ --cov-append --cov-config setup.cfg"
|
||||||
- name: py310-dev
|
- name: py310
|
||||||
python: 3.10-dev
|
python: "3.10"
|
||||||
docutils: du17
|
docutils: du17
|
||||||
|
- name: py311-dev
|
||||||
|
python: 3.11-dev
|
||||||
|
docutils: py311
|
||||||
env:
|
env:
|
||||||
PYTEST_ADDOPTS: ${{ matrix.coverage }}
|
PYTEST_ADDOPTS: ${{ matrix.coverage }}
|
||||||
|
|
||||||
@@ -47,6 +50,9 @@ jobs:
|
|||||||
run: sudo apt-get install graphviz
|
run: sudo apt-get install graphviz
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pip install -U tox codecov
|
run: pip install -U tox codecov
|
||||||
|
- name: Install the latest py package (for py3.11-dev)
|
||||||
|
run: pip install -U git+https://github.com/pytest-dev/py
|
||||||
|
if: ${{ matrix.python == '3.11-dev' }}
|
||||||
- name: Run Tox
|
- name: Run Tox
|
||||||
run: tox -e ${{ matrix.docutils }} -- -vv
|
run: tox -e ${{ matrix.docutils }} -- -vv
|
||||||
- name: codecov
|
- name: codecov
|
||||||
|
|||||||
5
.github/workflows/transifex.yml
vendored
5
.github/workflows/transifex.yml
vendored
@@ -15,6 +15,8 @@ jobs:
|
|||||||
ref: 4.x
|
ref: 4.x
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: 3.9 # https://github.com/transifex/transifex-client/pull/330
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pip install -U babel jinja2 transifex-client
|
run: pip install -U babel jinja2 transifex-client
|
||||||
- name: Extract translations from source code
|
- name: Extract translations from source code
|
||||||
@@ -33,6 +35,8 @@ jobs:
|
|||||||
ref: 4.x
|
ref: 4.x
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: 3.9 # https://github.com/transifex/transifex-client/pull/330
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pip install -U babel jinja2 transifex-client
|
run: pip install -U babel jinja2 transifex-client
|
||||||
- name: Extract translations from source code
|
- name: Extract translations from source code
|
||||||
@@ -49,3 +53,4 @@ jobs:
|
|||||||
commit-message: 'Update message catalogs'
|
commit-message: 'Update message catalogs'
|
||||||
branch: bot/pull-translations
|
branch: bot/pull-translations
|
||||||
title: Update message catalogs
|
title: Update message catalogs
|
||||||
|
labels: i18n
|
||||||
|
|||||||
261
CHANGES
261
CHANGES
@@ -1,4 +1,91 @@
|
|||||||
Release 4.3.0 (in development)
|
Release 4.4.0 (in development)
|
||||||
|
==============================
|
||||||
|
|
||||||
|
Dependencies
|
||||||
|
------------
|
||||||
|
|
||||||
|
* #10007: Use ``importlib_metadata`` for python-3.9 or older
|
||||||
|
* #10007: Drop ``setuptools``
|
||||||
|
|
||||||
|
Incompatible changes
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Deprecated
|
||||||
|
----------
|
||||||
|
|
||||||
|
Features added
|
||||||
|
--------------
|
||||||
|
|
||||||
|
* #9075: autodoc: Add a config variable :confval:`autodoc_typehints_format`
|
||||||
|
to suppress the leading module names of typehints of function signatures (ex.
|
||||||
|
``io.StringIO`` -> ``StringIO``)
|
||||||
|
* #9831: Autosummary now documents only the members specified in a module's
|
||||||
|
``__all__`` attribute if :confval:`autosummary_ignore_module_all` is set to
|
||||||
|
``False``. The default behaviour is unchanged. Autogen also now supports
|
||||||
|
this behavior with the ``--respect-module-all`` switch.
|
||||||
|
* #9555: autosummary: Improve error messages on failure to load target object
|
||||||
|
* #9800: extlinks: Emit warning if a hardcoded link is replaceable
|
||||||
|
by an extlink, suggesting a replacement.
|
||||||
|
* #9961: html: Support nested <kbd> HTML elements in other HTML builders
|
||||||
|
* #10013: html: Allow to change the loading method of JS via ``loading_method``
|
||||||
|
parameter for :meth:`Sphinx.add_js_file()`
|
||||||
|
* #9551: html search: "Hide Search Matches" link removes "highlight" parameter
|
||||||
|
from URL
|
||||||
|
* #9815: html theme: Wrap sidebar components in div to allow customizing their
|
||||||
|
layout via CSS
|
||||||
|
* #9827: i18n: Sort items in glossary by translated terms
|
||||||
|
* #9899: py domain: Allows to specify cross-reference specifier (``.`` and
|
||||||
|
``~``) as ``:type:`` option
|
||||||
|
* #9894: linkcheck: add option ``linkcheck_exclude_documents`` to disable link
|
||||||
|
checking in matched documents.
|
||||||
|
* #9793: sphinx-build: Allow to use the parallel build feature in macOS on macOS
|
||||||
|
and Python3.8+
|
||||||
|
* #10055: sphinx-build: Create directories when ``-w`` option given
|
||||||
|
* #9993: std domain: Allow to refer an inline target (ex. ``_`target name```)
|
||||||
|
via :rst:role:`ref` role
|
||||||
|
* #9391: texinfo: improve variable in ``samp`` role
|
||||||
|
* #9578: texinfo: Add :confval:`texinfo_cross_references` to disable cross
|
||||||
|
references for readability with standalone readers
|
||||||
|
|
||||||
|
Bugs fixed
|
||||||
|
----------
|
||||||
|
|
||||||
|
* #9866: autodoc: doccomment for the imported class was ignored
|
||||||
|
* #9883: autodoc: doccomment for the alias to mocked object was ignored
|
||||||
|
* #9908: autodoc: debug message is shown on building document using NewTypes
|
||||||
|
with Python 3.10
|
||||||
|
* #9968: autodoc: instance variables are not shown if __init__ method has
|
||||||
|
position-only-arguments
|
||||||
|
* #9194: autodoc: types under the "typing" module are not hyperlinked
|
||||||
|
* #10009: autodoc: Crashes if target object raises an error on getting docstring
|
||||||
|
* #10058: autosummary: Imported members are not shown when
|
||||||
|
``autodoc_class_signature = 'separated'``
|
||||||
|
* #9947: i18n: topic directive having a bullet list can't be translatable
|
||||||
|
* #9878: mathjax: MathJax configuration is placed after loading MathJax itself
|
||||||
|
* #9857: Generated RFC links use outdated base url
|
||||||
|
* #9909: HTML, prevent line-wrapping in literal text.
|
||||||
|
* #10061: html theme: Configuration values added by themes are not be able to
|
||||||
|
override from conf.py
|
||||||
|
* #10073: imgconverter: Unnecessary availablity check is called for "data" URIs
|
||||||
|
* #9925: LaTeX: prohibit also with ``'xelatex'`` line splitting at dashes of
|
||||||
|
inline and parsed literals
|
||||||
|
* #9944: LaTeX: extra vertical whitespace for some nested declarations
|
||||||
|
* #9940: LaTeX: Multi-function declaration in Python domain has cramped
|
||||||
|
vertical spacing in latexpdf output
|
||||||
|
* #10015: py domain: types under the "typing" module are not hyperlinked defined
|
||||||
|
at info-field-list
|
||||||
|
* #9390: texinfo: Do not emit labels inside footnotes
|
||||||
|
* #9979: Error level messages were displayed as warning messages
|
||||||
|
* #10057: Failed to scan documents if the project is placed onto the root
|
||||||
|
directory
|
||||||
|
* #9636: code-block: ``:dedent:`` handled numeric argument ``0`` like no argument was given
|
||||||
|
* #9636: code-block: ``:dedent:`` without argument did strip newlines
|
||||||
|
|
||||||
|
|
||||||
|
Testing
|
||||||
|
--------
|
||||||
|
|
||||||
|
Release 4.3.3 (in development)
|
||||||
==============================
|
==============================
|
||||||
|
|
||||||
Dependencies
|
Dependencies
|
||||||
@@ -13,7 +100,103 @@ Deprecated
|
|||||||
Features added
|
Features added
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
|
Bugs fixed
|
||||||
|
----------
|
||||||
|
|
||||||
|
Testing
|
||||||
|
--------
|
||||||
|
|
||||||
|
Release 4.3.2 (released Dec 19, 2021)
|
||||||
|
=====================================
|
||||||
|
|
||||||
|
Bugs fixed
|
||||||
|
----------
|
||||||
|
|
||||||
|
* #9917: C and C++, parse fundamental types no matter the order of simple type
|
||||||
|
specifiers.
|
||||||
|
|
||||||
|
Release 4.3.1 (released Nov 28, 2021)
|
||||||
|
=====================================
|
||||||
|
|
||||||
|
Features added
|
||||||
|
--------------
|
||||||
|
|
||||||
|
* #9864: mathjax: Support chnaging the loading method of MathJax to "defer" via
|
||||||
|
:confval:`mathjax_options`
|
||||||
|
|
||||||
|
Bugs fixed
|
||||||
|
----------
|
||||||
|
|
||||||
|
* #9838: autodoc: AttributeError is raised on building document for functions
|
||||||
|
decorated by functools.lru_cache
|
||||||
|
* #9879: autodoc: AttributeError is raised on building document for an object
|
||||||
|
having invalid __doc__ attribute
|
||||||
|
* #9844: autodoc: Failed to process a function wrapped with functools.partial if
|
||||||
|
:confval:`autodoc_preserve_defaults` enabled
|
||||||
|
* #9872: html: Class namespace collision between autodoc signatures and
|
||||||
|
docutils-0.17
|
||||||
|
* #9868: imgmath: Crashed if the dvisvgm command failed to convert equation
|
||||||
|
* #9864: mathjax: Failed to render equations via MathJax v2. The loading method
|
||||||
|
of MathJax is back to "async" method again
|
||||||
|
|
||||||
|
Release 4.3.0 (released Nov 11, 2021)
|
||||||
|
=====================================
|
||||||
|
|
||||||
|
Dependencies
|
||||||
|
------------
|
||||||
|
|
||||||
|
* Support Python 3.10
|
||||||
|
|
||||||
|
Incompatible changes
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
* #9649: ``searchindex.js``: the embedded data has changed format to allow
|
||||||
|
objects with the same name in different domains.
|
||||||
|
* #9672: The rendering of Python domain declarations is implemented
|
||||||
|
with more docutils nodes to allow better CSS styling.
|
||||||
|
It may break existing styling.
|
||||||
|
* #9672: the signature of
|
||||||
|
:py:meth:`domains.py.PyObject.get_signature_prefix` has changed to
|
||||||
|
return a list of nodes instead of a plain string.
|
||||||
|
* #9695: ``domains.js.JSObject.display_prefix`` has been changed into a method
|
||||||
|
``get_display_prefix`` which now returns a list of nodes
|
||||||
|
instead of a plain string.
|
||||||
|
* #9695: The rendering of Javascript domain declarations is implemented
|
||||||
|
with more docutils nodes to allow better CSS styling.
|
||||||
|
It may break existing styling.
|
||||||
|
* #9450: mathjax: Load MathJax via "defer" strategy
|
||||||
|
|
||||||
|
Deprecated
|
||||||
|
----------
|
||||||
|
|
||||||
|
* ``sphinx.ext.autodoc.AttributeDocumenter._datadescriptor``
|
||||||
|
* ``sphinx.writers.html.HTMLTranslator._fieldlist_row_index``
|
||||||
|
* ``sphinx.writers.html.HTMLTranslator._table_row_index``
|
||||||
|
* ``sphinx.writers.html5.HTML5Translator._fieldlist_row_index``
|
||||||
|
* ``sphinx.writers.html5.HTML5Translator._table_row_index``
|
||||||
|
|
||||||
|
Features added
|
||||||
|
--------------
|
||||||
|
|
||||||
* #9639: autodoc: Support asynchronous generator functions
|
* #9639: autodoc: Support asynchronous generator functions
|
||||||
|
* #9664: autodoc: ``autodoc-process-bases`` supports to inject reST snippet as a
|
||||||
|
base class
|
||||||
|
* #9691: C, added new info-field ``retval``
|
||||||
|
for :rst:dir:`c:function` and :rst:dir:`c:macro`.
|
||||||
|
* C++, added new info-field ``retval`` for :rst:dir:`cpp:function`.
|
||||||
|
* #9618: i18n: Add :confval:`gettext_allow_fuzzy_translations` to allow "fuzzy"
|
||||||
|
messages for translation
|
||||||
|
* #9672: More CSS classes on Python domain descriptions
|
||||||
|
* #9695: More CSS classes on Javascript domain descriptions
|
||||||
|
* #9683: Revert the removal of ``add_stylesheet()`` API. It will be kept until
|
||||||
|
the Sphinx-6.0 release
|
||||||
|
* #2068, add :confval:`intersphinx_disabled_reftypes` for disabling
|
||||||
|
interphinx resolution of cross-references that do not have an explicit
|
||||||
|
inventory specification. Specific types of cross-references can be disabled,
|
||||||
|
e.g., ``std:doc`` or all cross-references in a specific domain,
|
||||||
|
e.g., ``std:*``.
|
||||||
|
* #9623: Allow to suppress "toctree contains reference to excluded document"
|
||||||
|
warnings using :confval:`suppress_warnings`
|
||||||
|
|
||||||
Bugs fixed
|
Bugs fixed
|
||||||
----------
|
----------
|
||||||
@@ -21,34 +204,41 @@ Bugs fixed
|
|||||||
* #9630: autodoc: Failed to build cross references if :confval:`primary_domain`
|
* #9630: autodoc: Failed to build cross references if :confval:`primary_domain`
|
||||||
is not 'py'
|
is not 'py'
|
||||||
* #9644: autodoc: Crashed on getting source info from problematic object
|
* #9644: autodoc: Crashed on getting source info from problematic object
|
||||||
|
* #9655: autodoc: mocked object having doc comment is warned unexpectedly
|
||||||
|
* #9651: autodoc: return type field is not generated even if
|
||||||
|
:confval:`autodoc_typehints_description_target` is set to "documented" when
|
||||||
|
its info-field-list contains ``:returns:`` field
|
||||||
|
* #9657: autodoc: The base class for a subclass of mocked object is incorrect
|
||||||
|
* #9607: autodoc: Incorrect base class detection for the subclasses of the
|
||||||
|
generic class
|
||||||
|
* #9755: autodoc: memory addresses are shown for aliases
|
||||||
|
* #9752: autodoc: Failed to detect type annotation for slots attribute
|
||||||
|
* #9756: autodoc: Crashed if classmethod does not have __func__ attribute
|
||||||
|
* #9757: autodoc: :confval:`autodoc_inherit_docstrings` does not effect to
|
||||||
|
overridden classmethods
|
||||||
|
* #9781: autodoc: :confval:`autodoc_preserve_defaults` does not support
|
||||||
|
hexadecimal numeric
|
||||||
* #9630: autosummary: Failed to build summary table if :confval:`primary_domain`
|
* #9630: autosummary: Failed to build summary table if :confval:`primary_domain`
|
||||||
is not 'py'
|
is not 'py'
|
||||||
* #9636: code-block: ``:dedent:`` handled numeric argument ``0`` like no argument was given
|
* #9670: html: Fix download file with special characters
|
||||||
* #9636: code-block: ``:dedent:`` without argument did strip newlines
|
* #9710: html: Wrong styles for even/odd rows in nested tables
|
||||||
|
* #9763: html: parameter name and its type annotation are not separated in HTML
|
||||||
Testing
|
* #9649: HTML search: when objects have the same name but in different domains,
|
||||||
--------
|
return all of them as result instead of just one.
|
||||||
|
* #7634: intersphinx: references on the file in sub directory are broken
|
||||||
Release 4.2.1 (in development)
|
* #9737: LaTeX: hlist is rendered as a list containing "aggedright" text
|
||||||
==============================
|
* #9678: linkcheck: file extension was shown twice in warnings
|
||||||
|
* #9697: py domain: An index entry with parens was registered for ``py:method``
|
||||||
Dependencies
|
directive with ``:property:`` option
|
||||||
------------
|
* #9775: py domain: Literal typehint was converted to a cross reference when
|
||||||
|
:confval:`autodoc_typehints='description'`
|
||||||
Incompatible changes
|
* #9708: needs_extension failed to check double-digit version correctly
|
||||||
--------------------
|
* #9688: Fix :rst:dir:`code`` does not recognize ``:class:`` option
|
||||||
|
* #9733: Fix for logging handler flushing warnings in the middle of the docs
|
||||||
Deprecated
|
build
|
||||||
----------
|
* #9656: Fix warnings without subtype being incorrectly suppressed
|
||||||
|
* Intersphinx, for unresolved references with an explicit inventory,
|
||||||
Features added
|
e.g., ``proj:myFunc``, leave the inventory prefix in the unresolved text.
|
||||||
--------------
|
|
||||||
|
|
||||||
Bugs fixed
|
|
||||||
----------
|
|
||||||
|
|
||||||
Testing
|
|
||||||
--------
|
|
||||||
|
|
||||||
Release 4.2.0 (released Sep 12, 2021)
|
Release 4.2.0 (released Sep 12, 2021)
|
||||||
=====================================
|
=====================================
|
||||||
@@ -120,6 +310,7 @@ Bugs fixed
|
|||||||
with the HEAD of 3.10
|
with the HEAD of 3.10
|
||||||
* #9436, #9471: autodoc: crashed if ``autodoc_class_signature = "separated"``
|
* #9436, #9471: autodoc: crashed if ``autodoc_class_signature = "separated"``
|
||||||
* #9456: html search: html_copy_source can't control the search summaries
|
* #9456: html search: html_copy_source can't control the search summaries
|
||||||
|
* #9500: LaTeX: Failed to build Japanese document on Windows
|
||||||
* #9435: linkcheck: Failed to check anchors in github.com
|
* #9435: linkcheck: Failed to check anchors in github.com
|
||||||
|
|
||||||
Release 4.1.1 (released Jul 15, 2021)
|
Release 4.1.1 (released Jul 15, 2021)
|
||||||
@@ -1555,6 +1746,14 @@ Bugs fixed
|
|||||||
:confval:`intersphinx_mapping` on :event:`config-inited` event
|
:confval:`intersphinx_mapping` on :event:`config-inited` event
|
||||||
* #7343: Sphinx builds has been slower since 2.4.0 on debug mode
|
* #7343: Sphinx builds has been slower since 2.4.0 on debug mode
|
||||||
|
|
||||||
|
Release 2.4.5 (released Nov 18, 2021)
|
||||||
|
=====================================
|
||||||
|
|
||||||
|
Dependencies
|
||||||
|
------------
|
||||||
|
|
||||||
|
* #9807: Restrict docutils to 0.17.x or older
|
||||||
|
|
||||||
Release 2.4.4 (released Mar 05, 2020)
|
Release 2.4.4 (released Mar 05, 2020)
|
||||||
=====================================
|
=====================================
|
||||||
|
|
||||||
@@ -2391,6 +2590,14 @@ Testing
|
|||||||
|
|
||||||
* Add a helper function: ``sphinx.testing.restructuredtext.parse()``
|
* Add a helper function: ``sphinx.testing.restructuredtext.parse()``
|
||||||
|
|
||||||
|
Release 1.8.6 (released Nov 18, 2021)
|
||||||
|
=====================================
|
||||||
|
|
||||||
|
Dependencies
|
||||||
|
------------
|
||||||
|
|
||||||
|
* #9807: Restrict docutils to 0.17.x or older
|
||||||
|
|
||||||
Release 1.8.5 (released Mar 10, 2019)
|
Release 1.8.5 (released Mar 10, 2019)
|
||||||
=====================================
|
=====================================
|
||||||
|
|
||||||
|
|||||||
2
EXAMPLES
2
EXAMPLES
@@ -430,7 +430,7 @@ Books produced using Sphinx
|
|||||||
* `"Theoretical Physics Reference" <https://www.theoretical-physics.net/>`__
|
* `"Theoretical Physics Reference" <https://www.theoretical-physics.net/>`__
|
||||||
* `"The Varnish Book" <https://info.varnish-software.com/the-varnish-book>`__
|
* `"The Varnish Book" <https://info.varnish-software.com/the-varnish-book>`__
|
||||||
|
|
||||||
Theses produced using Sphinx
|
These produced using Sphinx
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
* `"A Web-Based System for Comparative Analysis of OpenStreetMap Data by the Use
|
* `"A Web-Based System for Comparative Analysis of OpenStreetMap Data by the Use
|
||||||
|
|||||||
2
LICENSE
2
LICENSE
@@ -1,7 +1,7 @@
|
|||||||
License for Sphinx
|
License for Sphinx
|
||||||
==================
|
==================
|
||||||
|
|
||||||
Copyright (c) 2007-2021 by the Sphinx team (see AUTHORS file).
|
Copyright (c) 2007-2022 by the Sphinx team (see AUTHORS file).
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
|||||||
8
doc/_static/conf.py.txt
vendored
8
doc/_static/conf.py.txt
vendored
@@ -1,8 +1,8 @@
|
|||||||
# test documentation build configuration file, created by
|
# test documentation build configuration file, created by
|
||||||
# sphinx-quickstart on Sun Jun 26 00:00:43 2016.
|
# sphinx-quickstart on Sun Jun 26 00:00:43 2016.
|
||||||
#
|
#
|
||||||
# This file is execfile()d with the current directory set to its
|
# This file is executed through importlib.import_module with
|
||||||
# containing dir.
|
# the current directory set to its containing dir.
|
||||||
#
|
#
|
||||||
# Note that not all possible configuration values are present in this
|
# Note that not all possible configuration values are present in this
|
||||||
# autogenerated file.
|
# autogenerated file.
|
||||||
@@ -319,6 +319,10 @@ texinfo_documents = [
|
|||||||
#
|
#
|
||||||
# texinfo_no_detailmenu = False
|
# texinfo_no_detailmenu = False
|
||||||
|
|
||||||
|
# If false, do not generate in manual @ref nodes.
|
||||||
|
#
|
||||||
|
# texinfo_cross_references = False
|
||||||
|
|
||||||
# -- A random example -----------------------------------------------------
|
# -- A random example -----------------------------------------------------
|
||||||
|
|
||||||
import sys, os
|
import sys, os
|
||||||
|
|||||||
BIN
doc/_static/tutorial/lumache-autosummary.png
vendored
Normal file
BIN
doc/_static/tutorial/lumache-autosummary.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
BIN
doc/_static/tutorial/lumache-py-function-full.png
vendored
Normal file
BIN
doc/_static/tutorial/lumache-py-function-full.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 70 KiB |
BIN
doc/_static/tutorial/lumache-py-function.png
vendored
Normal file
BIN
doc/_static/tutorial/lumache-py-function.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 41 KiB |
2
doc/_templates/index.html
vendored
2
doc/_templates/index.html
vendored
@@ -118,7 +118,7 @@
|
|||||||
this part of the documentation is for you.{%endtrans%}</p>
|
this part of the documentation is for you.{%endtrans%}</p>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>{%trans path=pathto("internals/contributing")%}<a href="{{ path }}">Sphinx Contributors’s Guide</a></li>{%endtrans%}
|
<li>{%trans path=pathto("internals/contributing")%}<a href="{{ path }}">Sphinx Contributors’ Guide</a></li>{%endtrans%}
|
||||||
<li>{%trans path=pathto("internals/authors")%}<a href="{{ path }}">Sphinx Authors</a></li>{%endtrans%}
|
<li>{%trans path=pathto("internals/authors")%}<a href="{{ path }}">Sphinx Authors</a></li>{%endtrans%}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
|||||||
2
doc/_templates/indexsidebar.html
vendored
2
doc/_templates/indexsidebar.html
vendored
@@ -18,6 +18,6 @@
|
|||||||
<input type="submit" name="sub" value="Subscribe" />
|
<input type="submit" name="sub" value="Subscribe" />
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<p>{%trans%}or come to the <tt>#sphinx-doc</tt> channel on FreeNode.{%endtrans%}</p>
|
<p>{%trans%}or come to the <tt>#sphinx-doc</tt> channel on <a href="https://web.libera.chat/?channel=#sphinx-doc">libera.chat</a>.{%endtrans%}</p>
|
||||||
<p>{%trans%}You can also open an issue at the
|
<p>{%trans%}You can also open an issue at the
|
||||||
<a href="https://github.com/sphinx-doc/sphinx/issues">tracker</a>.{%endtrans%}</p>
|
<a href="https://github.com/sphinx-doc/sphinx/issues">tracker</a>.{%endtrans%}</p>
|
||||||
|
|||||||
2
doc/_themes/sphinx13/theme.conf
vendored
2
doc/_themes/sphinx13/theme.conf
vendored
@@ -1,4 +1,4 @@
|
|||||||
[theme]
|
[theme]
|
||||||
inherit = basic
|
inherit = basic
|
||||||
stylesheet = sphinx13.css
|
stylesheet = sphinx13.css
|
||||||
pygments_style = trac
|
pygments_style = default
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ templates_path = ['_templates']
|
|||||||
exclude_patterns = ['_build']
|
exclude_patterns = ['_build']
|
||||||
|
|
||||||
project = 'Sphinx'
|
project = 'Sphinx'
|
||||||
copyright = '2007-2021, Georg Brandl and the Sphinx team'
|
copyright = '2007-2022, Georg Brandl and the Sphinx team'
|
||||||
version = sphinx.__display_version__
|
version = sphinx.__display_version__
|
||||||
release = version
|
release = version
|
||||||
show_authors = True
|
show_authors = True
|
||||||
@@ -109,6 +109,7 @@ texinfo_documents = [
|
|||||||
intersphinx_mapping = {
|
intersphinx_mapping = {
|
||||||
'python': ('https://docs.python.org/3/', None),
|
'python': ('https://docs.python.org/3/', None),
|
||||||
'requests': ('https://requests.readthedocs.io/en/master', None),
|
'requests': ('https://requests.readthedocs.io/en/master', None),
|
||||||
|
'readthedocs': ('https://docs.readthedocs.io/en/stable', None),
|
||||||
}
|
}
|
||||||
|
|
||||||
# Sphinx document translation with sphinx gettext feature uses these settings:
|
# Sphinx document translation with sphinx gettext feature uses these settings:
|
||||||
|
|||||||
@@ -285,7 +285,7 @@ engine, allowing you to embed variables and control behavior.
|
|||||||
|
|
||||||
For example, the following JavaScript structure:
|
For example, the following JavaScript structure:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: none
|
||||||
|
|
||||||
mymodule/
|
mymodule/
|
||||||
├── _static
|
├── _static
|
||||||
@@ -294,7 +294,7 @@ For example, the following JavaScript structure:
|
|||||||
|
|
||||||
Will result in the following static file placed in your HTML's build output:
|
Will result in the following static file placed in your HTML's build output:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: none
|
||||||
|
|
||||||
_build/
|
_build/
|
||||||
└── html
|
└── html
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from sphinx.ext.autodoc import ClassDocumenter, bool_option
|
|||||||
|
|
||||||
class IntEnumDocumenter(ClassDocumenter):
|
class IntEnumDocumenter(ClassDocumenter):
|
||||||
objtype = 'intenum'
|
objtype = 'intenum'
|
||||||
directivetype = 'class'
|
directivetype = ClassDocumenter.objtype
|
||||||
priority = 10 + ClassDocumenter.priority
|
priority = 10 + ClassDocumenter.priority
|
||||||
option_spec = dict(ClassDocumenter.option_spec)
|
option_spec = dict(ClassDocumenter.option_spec)
|
||||||
option_spec['hex'] = bool_option
|
option_spec['hex'] = bool_option
|
||||||
@@ -18,7 +18,10 @@ class IntEnumDocumenter(ClassDocumenter):
|
|||||||
def can_document_member(cls,
|
def can_document_member(cls,
|
||||||
member: Any, membername: str,
|
member: Any, membername: str,
|
||||||
isattr: bool, parent: Any) -> bool:
|
isattr: bool, parent: Any) -> bool:
|
||||||
return isinstance(member, IntEnum)
|
try:
|
||||||
|
return issubclass(member, IntEnum)
|
||||||
|
except TypeError:
|
||||||
|
return False
|
||||||
|
|
||||||
def add_directive_header(self, sig: str) -> None:
|
def add_directive_header(self, sig: str) -> None:
|
||||||
super().add_directive_header(sig)
|
super().add_directive_header(sig)
|
||||||
@@ -36,14 +39,13 @@ class IntEnumDocumenter(ClassDocumenter):
|
|||||||
use_hex = self.options.hex
|
use_hex = self.options.hex
|
||||||
self.add_line('', source_name)
|
self.add_line('', source_name)
|
||||||
|
|
||||||
for enum_value in enum_object:
|
for the_member_name, enum_member in enum_object.__members__.items():
|
||||||
the_value_name = enum_value.name
|
the_member_value = enum_member.value
|
||||||
the_value_value = enum_value.value
|
|
||||||
if use_hex:
|
if use_hex:
|
||||||
the_value_value = hex(the_value_value)
|
the_member_value = hex(the_member_value)
|
||||||
|
|
||||||
self.add_line(
|
self.add_line(
|
||||||
f"**{the_value_name}**: {the_value_value}", source_name)
|
f"**{the_member_name}**: {the_member_value}", source_name)
|
||||||
self.add_line('', source_name)
|
self.add_line('', source_name)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,31 @@ The following is a list of deprecated interfaces.
|
|||||||
- (will be) Removed
|
- (will be) Removed
|
||||||
- Alternatives
|
- Alternatives
|
||||||
|
|
||||||
|
* - ``sphinx.ext.autodoc.AttributeDocumenter._datadescriptor``
|
||||||
|
- 4.3
|
||||||
|
- 6.0
|
||||||
|
- N/A
|
||||||
|
|
||||||
|
* - ``sphinx.writers.html.HTMLTranslator._fieldlist_row_index``
|
||||||
|
- 4.3
|
||||||
|
- 6.0
|
||||||
|
- ``sphinx.writers.html.HTMLTranslator._fieldlist_row_indices``
|
||||||
|
|
||||||
|
* - ``sphinx.writers.html.HTMLTranslator._table_row_index``
|
||||||
|
- 4.3
|
||||||
|
- 6.0
|
||||||
|
- ``sphinx.writers.html.HTMLTranslator._table_row_indices``
|
||||||
|
|
||||||
|
* - ``sphinx.writers.html5.HTML5Translator._fieldlist_row_index``
|
||||||
|
- 4.3
|
||||||
|
- 6.0
|
||||||
|
- ``sphinx.writers.html5.HTML5Translator._fieldlist_row_indices``
|
||||||
|
|
||||||
|
* - ``sphinx.writers.html5.HTML5Translator._table_row_index``
|
||||||
|
- 4.3
|
||||||
|
- 6.0
|
||||||
|
- ``sphinx.writers.html5.HTML5Translator._table_row_indices``
|
||||||
|
|
||||||
* - The optional argument ``app`` for ``sphinx.environment.BuildEnvironment``
|
* - The optional argument ``app`` for ``sphinx.environment.BuildEnvironment``
|
||||||
- 4.1
|
- 4.1
|
||||||
- 6.0
|
- 6.0
|
||||||
@@ -748,6 +773,11 @@ The following is a list of deprecated interfaces.
|
|||||||
- 4.0
|
- 4.0
|
||||||
- ``sphinx.domains.std.StandardDomain.process_doc()``
|
- ``sphinx.domains.std.StandardDomain.process_doc()``
|
||||||
|
|
||||||
|
* - ``sphinx.domains.js.JSObject.display_prefix``
|
||||||
|
-
|
||||||
|
- 4.3
|
||||||
|
- ``sphinx.domains.js.JSObject.get_display_prefix()``
|
||||||
|
|
||||||
* - ``sphinx.environment.NoUri``
|
* - ``sphinx.environment.NoUri``
|
||||||
- 2.1
|
- 2.1
|
||||||
- 3.0
|
- 3.0
|
||||||
@@ -1207,7 +1237,7 @@ The following is a list of deprecated interfaces.
|
|||||||
|
|
||||||
* - :meth:`~sphinx.application.Sphinx.add_stylesheet()`
|
* - :meth:`~sphinx.application.Sphinx.add_stylesheet()`
|
||||||
- 1.8
|
- 1.8
|
||||||
- 4.0
|
- 6.0
|
||||||
- :meth:`~sphinx.application.Sphinx.add_css_file()`
|
- :meth:`~sphinx.application.Sphinx.add_css_file()`
|
||||||
|
|
||||||
* - :meth:`~sphinx.application.Sphinx.add_javascript()`
|
* - :meth:`~sphinx.application.Sphinx.add_javascript()`
|
||||||
|
|||||||
@@ -13,6 +13,10 @@ Domain API
|
|||||||
.. autoclass:: Index
|
.. autoclass:: Index
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
.. module:: sphinx.directives
|
||||||
|
|
||||||
|
.. autoclass:: ObjectDescription
|
||||||
|
:members:
|
||||||
|
|
||||||
Python Domain
|
Python Domain
|
||||||
-------------
|
-------------
|
||||||
|
|||||||
@@ -299,6 +299,10 @@ appear in the source. Emacs, on the other-hand, will by default replace
|
|||||||
|
|
||||||
:ref:`texinfo-links`
|
:ref:`texinfo-links`
|
||||||
|
|
||||||
|
One can disable generation of the inline references in a document
|
||||||
|
with :confval:`texinfo_cross_references`. That makes
|
||||||
|
an info file more readable with stand-alone reader (``info``).
|
||||||
|
|
||||||
The exact behavior of how Emacs displays references is dependent on the variable
|
The exact behavior of how Emacs displays references is dependent on the variable
|
||||||
``Info-hide-note-references``. If set to the value of ``hide``, Emacs will hide
|
``Info-hide-note-references``. If set to the value of ``hide``, Emacs will hide
|
||||||
both the ``*note:`` part and the ``target-id``. This is generally the best way
|
both the ``*note:`` part and the ``target-id``. This is generally the best way
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ sphinx-users <sphinx-users@googlegroups.com>
|
|||||||
sphinx-dev <sphinx-dev@googlegroups.com>
|
sphinx-dev <sphinx-dev@googlegroups.com>
|
||||||
Mailing list for development related discussions.
|
Mailing list for development related discussions.
|
||||||
|
|
||||||
#sphinx-doc on irc.freenode.net
|
#sphinx-doc on irc.libera.chat
|
||||||
IRC channel for development questions and user support.
|
IRC channel for development questions and user support.
|
||||||
|
|
||||||
.. _python-sphinx: https://stackoverflow.com/questions/tagged/python-sphinx
|
.. _python-sphinx: https://stackoverflow.com/questions/tagged/python-sphinx
|
||||||
|
|||||||
@@ -39,6 +39,10 @@ Options
|
|||||||
|
|
||||||
Document imported members.
|
Document imported members.
|
||||||
|
|
||||||
|
.. option:: -a, --respect-module-all
|
||||||
|
|
||||||
|
Document exactly the members in a module's ``__all__`` attribute.
|
||||||
|
|
||||||
Example
|
Example
|
||||||
-------
|
-------
|
||||||
|
|
||||||
@@ -71,7 +75,7 @@ and assuming ``docs/index.rst`` contained the following:
|
|||||||
|
|
||||||
If you run the following:
|
If you run the following:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: console
|
||||||
|
|
||||||
$ PYTHONPATH=. sphinx-autogen docs/index.rst
|
$ PYTHONPATH=. sphinx-autogen docs/index.rst
|
||||||
|
|
||||||
|
|||||||
166
doc/tutorial/automatic-doc-generation.rst
Normal file
166
doc/tutorial/automatic-doc-generation.rst
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
Automatic documentation generation from code
|
||||||
|
============================================
|
||||||
|
|
||||||
|
In the :ref:`previous section <tutorial-describing-objects>` of the tutorial
|
||||||
|
you manually documented a Python function in Sphinx. However, the description
|
||||||
|
was out of sync with the code itself, since the function signature was not
|
||||||
|
the same. Besides, it would be nice to reuse `Python
|
||||||
|
docstrings <https://www.python.org/dev/peps/pep-0257/#what-is-a-docstring>`_
|
||||||
|
in the documentation, rather than having to write the information in two
|
||||||
|
places.
|
||||||
|
|
||||||
|
Fortunately, :doc:`the autodoc extension </usage/extensions/autodoc>` provides this
|
||||||
|
functionality.
|
||||||
|
|
||||||
|
Reusing signatures and docstrings with autodoc
|
||||||
|
----------------------------------------------
|
||||||
|
|
||||||
|
To use autodoc, first add it to the list of enabled extensions:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
:caption: docs/source/conf.py
|
||||||
|
:emphasize-lines: 4
|
||||||
|
|
||||||
|
extensions = [
|
||||||
|
'sphinx.ext.duration',
|
||||||
|
'sphinx.ext.doctest',
|
||||||
|
'sphinx.ext.autodoc',
|
||||||
|
]
|
||||||
|
|
||||||
|
Next, move the content of the ``.. py:function`` directive to the function
|
||||||
|
docstring in the original Python file, as follows:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
:caption: lumache.py
|
||||||
|
:emphasize-lines: 2-11
|
||||||
|
|
||||||
|
def get_random_ingredients(kind=None):
|
||||||
|
"""
|
||||||
|
Return a list of random ingredients as strings.
|
||||||
|
|
||||||
|
:param kind: Optional "kind" of ingredients.
|
||||||
|
:type kind: list[str] or None
|
||||||
|
:raise lumache.InvalidKindError: If the kind is invalid.
|
||||||
|
:return: The ingredients list.
|
||||||
|
:rtype: list[str]
|
||||||
|
|
||||||
|
"""
|
||||||
|
return ["shells", "gorgonzola", "parsley"]
|
||||||
|
|
||||||
|
Finally, replace the ``.. py:function`` directive from the Sphinx documentation
|
||||||
|
with :rst:dir:`autofunction`:
|
||||||
|
|
||||||
|
.. code-block:: rst
|
||||||
|
:caption: docs/source/usage.rst
|
||||||
|
:emphasize-lines: 3
|
||||||
|
|
||||||
|
you can use the ``lumache.get_random_ingredients()`` function:
|
||||||
|
|
||||||
|
.. autofunction:: lumache.get_random_ingredients
|
||||||
|
|
||||||
|
If you now build the HTML documentation, the output will be the same!
|
||||||
|
With the advantage that it is generated from the code itself.
|
||||||
|
Sphinx took the reStructuredText from the docstring and included it,
|
||||||
|
also generating proper cross-references.
|
||||||
|
|
||||||
|
You can also autogenerate documentation from other objects. For example, add
|
||||||
|
the code for the ``InvalidKindError`` exception:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
:caption: lumache.py
|
||||||
|
|
||||||
|
class InvalidKindError(Exception):
|
||||||
|
"""Raised if the kind is invalid."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
And replace the ``.. py:exception`` directive with :rst:dir:`autoexception`
|
||||||
|
as follows:
|
||||||
|
|
||||||
|
.. code-block:: rst
|
||||||
|
:caption: docs/source/usage.rst
|
||||||
|
:emphasize-lines: 4
|
||||||
|
|
||||||
|
or ``"veggies"``. Otherwise, :py:func:`lumache.get_random_ingredients`
|
||||||
|
will raise an exception.
|
||||||
|
|
||||||
|
.. autoexception:: lumache.InvalidKindError
|
||||||
|
|
||||||
|
And again, after running ``make html``, the output will be the same as before.
|
||||||
|
|
||||||
|
Generating comprehensive API references
|
||||||
|
---------------------------------------
|
||||||
|
|
||||||
|
While using ``sphinx.ext.autodoc`` makes keeping the code and the documentation
|
||||||
|
in sync much easier, it still requires you to write an ``auto*`` directive
|
||||||
|
for every object you want to document. Sphinx provides yet another level of
|
||||||
|
automation: the :doc:`autosummary </usage/extensions/autosummary>` extension.
|
||||||
|
|
||||||
|
The :rst:dir:`autosummary` directive generates documents that contain all the
|
||||||
|
necessary ``autodoc`` directives. To use it, first enable the autosummary
|
||||||
|
extension:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
:caption: docs/source/conf.py
|
||||||
|
:emphasize-lines: 5
|
||||||
|
|
||||||
|
extensions = [
|
||||||
|
'sphinx.ext.duration',
|
||||||
|
'sphinx.ext.doctest',
|
||||||
|
'sphinx.ext.autodoc',
|
||||||
|
'sphinx.ext.autosummary',
|
||||||
|
]
|
||||||
|
|
||||||
|
Next, create a new ``api.rst`` file with these contents:
|
||||||
|
|
||||||
|
.. code-block:: rst
|
||||||
|
:caption: docs/source/api.rst
|
||||||
|
|
||||||
|
API
|
||||||
|
===
|
||||||
|
|
||||||
|
.. autosummary::
|
||||||
|
:toctree: generated
|
||||||
|
|
||||||
|
lumache
|
||||||
|
|
||||||
|
Remember to include the new document in the root toctree:
|
||||||
|
|
||||||
|
.. code-block:: rst
|
||||||
|
:caption: docs/source/index.rst
|
||||||
|
:emphasize-lines: 7
|
||||||
|
|
||||||
|
Contents
|
||||||
|
--------
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
|
||||||
|
usage
|
||||||
|
api
|
||||||
|
|
||||||
|
Finally, after you build the HTML documentation running ``make html``, it will
|
||||||
|
contain two new pages:
|
||||||
|
|
||||||
|
- ``api.html``, corresponding to ``docs/source/api.rst`` and containing a table
|
||||||
|
with the objects you included in the ``autosummary`` directive (in this case,
|
||||||
|
only one).
|
||||||
|
- ``generated/lumache.html``, corresponding to a newly created reST file
|
||||||
|
``generated/lumache.rst`` and containing a summary of members of the module,
|
||||||
|
in this case one function and one exception.
|
||||||
|
|
||||||
|
.. figure:: /_static/tutorial/lumache-autosummary.png
|
||||||
|
:width: 80%
|
||||||
|
:align: center
|
||||||
|
:alt: Summary page created by autosummary
|
||||||
|
|
||||||
|
Summary page created by autosummary
|
||||||
|
|
||||||
|
Each of the links in the summary page will take you to the places where you
|
||||||
|
originally used the corresponding ``autodoc`` directive, in this case in the
|
||||||
|
``usage.rst`` document.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
The generated files are based on `Jinja2
|
||||||
|
templates <https://jinja2docs.readthedocs.io/>`_ that
|
||||||
|
:ref:`can be customized <autosummary-customizing-templates>`,
|
||||||
|
but that is out of scope for this tutorial.
|
||||||
279
doc/tutorial/deploying.rst
Normal file
279
doc/tutorial/deploying.rst
Normal file
@@ -0,0 +1,279 @@
|
|||||||
|
Appendix: Deploying a Sphinx project online
|
||||||
|
===========================================
|
||||||
|
|
||||||
|
When you are ready to show your documentation project to the world, there are
|
||||||
|
many options available to do so. Since the HTML generated by Sphinx is static,
|
||||||
|
you can decouple the process of building your HTML documentation from hosting
|
||||||
|
such files in the platform of your choice. You will not need a sophisticated
|
||||||
|
server running Python: virtually every web hosting service will suffice.
|
||||||
|
|
||||||
|
Therefore, the challenge is less how or where to serve the static HTML, but
|
||||||
|
rather how to pick a workflow that automatically updates the deployed
|
||||||
|
documentation every time there is a change in the source files.
|
||||||
|
|
||||||
|
The following sections describe some of the available options to deploy
|
||||||
|
your online documentation, and give some background information. If you want
|
||||||
|
to go directly to the practical part, you can skip to :ref:`publishing-sources`.
|
||||||
|
|
||||||
|
Sphinx-friendly deployment options
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
|
There are several possible options you have to host your Sphinx documentation.
|
||||||
|
Some of them are:
|
||||||
|
|
||||||
|
**Read the Docs**
|
||||||
|
`Read the Docs`_ is an online service specialized in hosting technical
|
||||||
|
documentation written in Sphinx, as well as MkDocs. They have a
|
||||||
|
number of extra features, such as versioned documentation, traffic and
|
||||||
|
search analytics, custom domains, user-defined redirects, and more.
|
||||||
|
|
||||||
|
**GitHub Pages**
|
||||||
|
`GitHub Pages`_ is a simple static web hosting tightly integrated with
|
||||||
|
`GitHub`_: static HTML is served from one of the branches of a project,
|
||||||
|
and usually sources are stored in another branch so that the output
|
||||||
|
can be updated every time the sources change (for example using `GitHub
|
||||||
|
Actions`_). It is free to use and supports custom domains.
|
||||||
|
|
||||||
|
**GitLab Pages**
|
||||||
|
`GitLab Pages`_ is a similar concept to GitHub Pages, integrated with
|
||||||
|
`GitLab`_ and usually automated with `GitLab CI`_ instead.
|
||||||
|
|
||||||
|
**Netlify**
|
||||||
|
`Netlify`_ is a sophisticated hosting for static sites enhanced by
|
||||||
|
client-side web technologies like JavaScript (so-called `"Jamstack"`_).
|
||||||
|
They offer support for headless content management systems and
|
||||||
|
serverless computing.
|
||||||
|
|
||||||
|
**Your own server**
|
||||||
|
You can always use your own web server to host Sphinx HTML documentation.
|
||||||
|
It is the option that gives more flexibility, but also more complexity.
|
||||||
|
|
||||||
|
All these options have zero cost, with the option of paying for extra features.
|
||||||
|
|
||||||
|
.. _Read the Docs: https://readthedocs.org/
|
||||||
|
.. _GitHub Pages: https://pages.github.com/
|
||||||
|
.. _GitHub: https://github.com/
|
||||||
|
.. _GitHub Actions: https://github.com/features/actions
|
||||||
|
.. _GitLab Pages: https://about.gitlab.com/stages-devops-lifecycle/pages/
|
||||||
|
.. _GitLab: https://gitlab.com/
|
||||||
|
.. _GitLab CI: https://about.gitlab.com/stages-devops-lifecycle/continuous-integration/
|
||||||
|
.. _Netlify: https://www.netlify.com/
|
||||||
|
.. _"Jamstack": https://jamstack.org/
|
||||||
|
|
||||||
|
Embracing the "Docs as Code" philosophy
|
||||||
|
---------------------------------------
|
||||||
|
|
||||||
|
The free offerings of most of the options listed above require your
|
||||||
|
documentation sources to be publicly available. Moreover, these services
|
||||||
|
expect you to use a `Version Control System`_, a technology that tracks the
|
||||||
|
evolution of a collection of files as a series of snapshots ("commits").
|
||||||
|
The practice of writing documentation in plain text files with the same tools
|
||||||
|
as the ones used for software development is commonly known as `"Docs as Code"`_.
|
||||||
|
|
||||||
|
The most popular Version Control System nowadays is Git_, a free and open
|
||||||
|
source tool that is the backbone of services like GitHub and GitLab.
|
||||||
|
Since both Read the Docs and Netlify have integrations with GitHub and GitLab,
|
||||||
|
and both GitHub and GitLab have an integrated Pages product, the most effective
|
||||||
|
way of automatically build your documentation online is to upload your sources
|
||||||
|
to either of these Git hosting services.
|
||||||
|
|
||||||
|
.. _Version Control System: https://en.wikipedia.org/wiki/Version_control
|
||||||
|
.. _"Docs as Code": https://www.writethedocs.org/guide/docs-as-code/
|
||||||
|
.. _Git: https://git-scm.com/
|
||||||
|
|
||||||
|
.. _publishing-sources:
|
||||||
|
|
||||||
|
Publishing your documentation sources
|
||||||
|
-------------------------------------
|
||||||
|
|
||||||
|
GitHub
|
||||||
|
~~~~~~
|
||||||
|
|
||||||
|
The quickest way to upload an existing project to GitHub is to:
|
||||||
|
|
||||||
|
1. `Sign up for a GitHub account <https://github.com/signup>`_.
|
||||||
|
2. `Create a new repository <https://github.com/new>`_.
|
||||||
|
3. Open `the "Upload files" page`_ of your new repository.
|
||||||
|
4. Select the files on your operating system file browser (in your case
|
||||||
|
``README.rst``, ``lumache.py``, the makefiles under the ``docs`` directory,
|
||||||
|
and everything under ``docs/source``) and drag them to the GitHub interface
|
||||||
|
to upload them all.
|
||||||
|
5. Click on the :guilabel:`Commit changes` button.
|
||||||
|
|
||||||
|
.. _the "Upload files" page: https://docs.github.com/en/repositories/working-with-files/managing-files/adding-a-file-to-a-repository
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Make sure you don't upload the ``docs/build`` directory, as it contains the
|
||||||
|
output generated by Sphinx and it will change every time you change the
|
||||||
|
sources, complicating your workflow.
|
||||||
|
|
||||||
|
These steps do not require access to the command line or installing any
|
||||||
|
additional software. To learn more, you can:
|
||||||
|
|
||||||
|
- Follow `this interactive GitHub course`_ to learn more about how the GitHub
|
||||||
|
interface works.
|
||||||
|
- Read `this quickstart tutorial`_ to install extra software on your machine
|
||||||
|
and have more flexibility. You can either use the Git command line, or the
|
||||||
|
GitHub Desktop application.
|
||||||
|
|
||||||
|
.. _this interactive GitHub course: https://lab.github.com/githubtraining/introduction-to-github
|
||||||
|
.. _this quickstart tutorial: https://docs.github.com/en/get-started/quickstart
|
||||||
|
|
||||||
|
GitLab
|
||||||
|
~~~~~~
|
||||||
|
|
||||||
|
Similarly to GitHub, the fastest way to upload your project to GitLab is
|
||||||
|
using the web interface:
|
||||||
|
|
||||||
|
1. `Sign up for a GitLab account <https://gitlab.com/users/sign_up>`_.
|
||||||
|
2. `Create a new blank project <https://gitlab.com/projects/new>`_.
|
||||||
|
3. Upload the project files (in your case ``README.rst``, ``lumache.py``, the
|
||||||
|
makefiles under the ``docs`` directory, and everything under
|
||||||
|
``docs/source``) one by one using the :guilabel:`Upload File` button [#f1]_.
|
||||||
|
|
||||||
|
Again, these steps do not require additional software on your computer. To
|
||||||
|
learn more, you can:
|
||||||
|
|
||||||
|
- Follow `this tutorial`_ to install Git on your machine.
|
||||||
|
- Browse the `GitLab User documentation`_ to understand the possibilities of
|
||||||
|
the platform.
|
||||||
|
|
||||||
|
.. _this tutorial: https://docs.gitlab.com/ee/gitlab-basics/start-using-git.html
|
||||||
|
.. _GitLab User documentation: https://docs.gitlab.com/ee/user/index.html
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Make sure you don't upload the ``docs/build`` directory, as it contains the
|
||||||
|
output generated by Sphinx and it will change every time you change the
|
||||||
|
sources, complicating your workflow.
|
||||||
|
|
||||||
|
.. [#f1] At the time of writing, `uploading whole directories to GitLab using
|
||||||
|
only the web
|
||||||
|
interface <https://gitlab.com/gitlab-org/gitlab/-/issues/228490>`_ is
|
||||||
|
not yet implemented.
|
||||||
|
|
||||||
|
Publishing your HTML documentation
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
|
Read the Docs
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
`Read the Docs`_ offers integration with both GitHub and GitLab. The quickest
|
||||||
|
way of getting started is to follow :doc:`the RTD
|
||||||
|
tutorial <readthedocs:tutorial/index>`, which is loosely based on this one.
|
||||||
|
You can publish your sources on GitHub as explained :ref:`in the previous
|
||||||
|
section <publishing-sources>`, then skip directly to
|
||||||
|
:ref:`readthedocs:tutorial/index:Sign up for Read the Docs`.
|
||||||
|
If you choose GitLab instead, the process is similar.
|
||||||
|
|
||||||
|
GitHub Pages
|
||||||
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
|
`GitHub Pages`_ requires you to :ref:`publish your
|
||||||
|
sources <publishing-sources>` on `GitHub`_. After that, you will need an
|
||||||
|
automated process that performs the ``make html`` step every time the sources
|
||||||
|
change. That can be achieved using `GitHub Actions`_.
|
||||||
|
|
||||||
|
After you have published your sources on GitHub, create a file named
|
||||||
|
``.github/workflows/sphinx.yml`` in your repository with the following
|
||||||
|
contents:
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
:caption: .github/workflows/
|
||||||
|
|
||||||
|
name: Sphinx build
|
||||||
|
|
||||||
|
on: push
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Build HTML
|
||||||
|
uses: ammaraskar/sphinx-action@0.4
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v1
|
||||||
|
with:
|
||||||
|
name: html-docs
|
||||||
|
path: docs/build/html/
|
||||||
|
- name: Deploy
|
||||||
|
uses: peaceiris/actions-gh-pages@v3
|
||||||
|
if: github.ref == 'refs/heads/main'
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
publish_dir: docs/build/html
|
||||||
|
|
||||||
|
This contains a GitHub Actions workflow with a single job of four steps:
|
||||||
|
|
||||||
|
1. Checkout the code.
|
||||||
|
2. Build the HTML documentation using Sphinx.
|
||||||
|
3. Attach the HTML output the artifacts to the GitHub Actions job, for easier
|
||||||
|
inspection.
|
||||||
|
4. If the change happens on the default branch, take the contents of
|
||||||
|
``docs/build/html`` and push it to the ``gh-pages`` branch.
|
||||||
|
|
||||||
|
Next, you need to specify the dependencies for the ``make html`` step to be
|
||||||
|
successful. For that, create a file ``docs/requirements.txt`` and add the
|
||||||
|
following contents:
|
||||||
|
|
||||||
|
.. code-block::
|
||||||
|
:caption: docs/requirements.txt
|
||||||
|
|
||||||
|
furo==2021.11.16
|
||||||
|
|
||||||
|
And finally, you are ready to `enable GitHub Pages on your repository`_. For
|
||||||
|
that, go to :guilabel:`Settings`, then :guilabel:`Pages` on the left sidebar,
|
||||||
|
select the ``gh-pages`` branch in the "Source" dropdown menu, and click
|
||||||
|
:guilabel:`Save`. After a few minutes, you should be able to see your HTML at
|
||||||
|
the designated URL.
|
||||||
|
|
||||||
|
.. _enable GitHub Pages on your repository: https://docs.github.com/en/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site
|
||||||
|
|
||||||
|
GitLab Pages
|
||||||
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
|
`GitLab Pages`_, on the other hand, requires you to :ref:`publish your
|
||||||
|
sources <publishing-sources>` on `GitLab`_. When you are ready, you can
|
||||||
|
automate the process of running ``make html`` using `GitLab CI`_.
|
||||||
|
|
||||||
|
After you have published your sources on GitLab, create a file named
|
||||||
|
``.gitlab-ci.yml`` in your repository with these contents:
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
:caption: .gitlab-ci.yml
|
||||||
|
|
||||||
|
stages:
|
||||||
|
- deploy
|
||||||
|
|
||||||
|
pages:
|
||||||
|
stage: deploy
|
||||||
|
image: python:3.9-slim
|
||||||
|
before_script:
|
||||||
|
- apt-get update && apt-get install make --no-install-recommends -y
|
||||||
|
- python -m pip install sphinx furo
|
||||||
|
script:
|
||||||
|
- cd docs && make html
|
||||||
|
after_script:
|
||||||
|
- mv docs/build/html/ ./public/
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- public
|
||||||
|
rules:
|
||||||
|
- if: $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH
|
||||||
|
|
||||||
|
This contains a GitLab CI workflow with one job of several steps:
|
||||||
|
|
||||||
|
1. Install the necessary dependencies.
|
||||||
|
2. Build the HTML documentation using Sphinx.
|
||||||
|
3. Move the output to a known artifacts location.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
You will need to `validate your account`_ by entering a payment method
|
||||||
|
(you will be charged a small amount that will then be reimbursed).
|
||||||
|
|
||||||
|
.. _validate your account: https://about.gitlab.com/blog/2021/05/17/prevent-crypto-mining-abuse/#validating-an-account
|
||||||
|
|
||||||
|
After that, if the pipeline is successful, you should be able to see your HTML
|
||||||
|
at the designated URL.
|
||||||
275
doc/tutorial/describing-code.rst
Normal file
275
doc/tutorial/describing-code.rst
Normal file
@@ -0,0 +1,275 @@
|
|||||||
|
Describing code in Sphinx
|
||||||
|
=========================
|
||||||
|
|
||||||
|
In the :doc:`previous sections of the tutorial </tutorial/index>` you can read
|
||||||
|
how to write narrative or prose documentation in Sphinx. In this section you
|
||||||
|
will describe code objects instead.
|
||||||
|
|
||||||
|
Sphinx supports documenting code objects in several languages, namely Python,
|
||||||
|
C, C++, JavaScript, and reStructuredText. Each of them can be documented using
|
||||||
|
a series of directives and roles grouped by
|
||||||
|
:doc:`domain </usage/restructuredtext/domains>`. For the remainder of the
|
||||||
|
tutorial you will use the Python domain, but all the concepts seen in this
|
||||||
|
section apply for the other domains as well.
|
||||||
|
|
||||||
|
.. _tutorial-describing-objects:
|
||||||
|
|
||||||
|
Python
|
||||||
|
------
|
||||||
|
|
||||||
|
Documenting Python objects
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Sphinx offers several roles and directives to document Python objects,
|
||||||
|
all grouped together in :ref:`the Python domain <python-domain>`. For example,
|
||||||
|
you can use the :rst:dir:`py:function` directive to document a Python function,
|
||||||
|
as follows:
|
||||||
|
|
||||||
|
.. code-block:: rst
|
||||||
|
:caption: docs/source/usage.rst
|
||||||
|
|
||||||
|
Creating recipes
|
||||||
|
----------------
|
||||||
|
|
||||||
|
To retrieve a list of random ingredients,
|
||||||
|
you can use the ``lumache.get_random_ingredients()`` function:
|
||||||
|
|
||||||
|
.. py:function:: lumache.get_random_ingredients(kind=None)
|
||||||
|
|
||||||
|
Return a list of random ingredients as strings.
|
||||||
|
|
||||||
|
:param kind: Optional "kind" of ingredients.
|
||||||
|
:type kind: list[str] or None
|
||||||
|
:return: The ingredients list.
|
||||||
|
:rtype: list[str]
|
||||||
|
|
||||||
|
Which will render like this:
|
||||||
|
|
||||||
|
.. figure:: /_static/tutorial/lumache-py-function.png
|
||||||
|
:width: 80%
|
||||||
|
:align: center
|
||||||
|
:alt: HTML result of documenting a Python function in Sphinx
|
||||||
|
|
||||||
|
The rendered result of documenting a Python function in Sphinx
|
||||||
|
|
||||||
|
Notice several things:
|
||||||
|
|
||||||
|
- Sphinx parsed the argument of the ``.. py:function`` directive and
|
||||||
|
highlighted the module, the function name, and the parameters appropriately.
|
||||||
|
- The directive content includes a one-line description of the function,
|
||||||
|
as well as a :ref:`info field list <info-field-lists>` containing the function
|
||||||
|
parameter, its expected type, the return value, and the return type.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
The ``py:`` prefix specifies the :term:`domain`. You may configure the
|
||||||
|
default domain so you can omit the prefix, either globally using the
|
||||||
|
:confval:`primary_domain` configuration, or use the
|
||||||
|
:rst:dir:`default-domain` directive to change it from the point it is called
|
||||||
|
until the end of the file.
|
||||||
|
For example, if you set it to ``py`` (the default), you can write
|
||||||
|
``.. function::`` directly.
|
||||||
|
|
||||||
|
Cross-referencing Python objects
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
By default, most of these directives generate entities that can be
|
||||||
|
cross-referenced from any part of the documentation by using
|
||||||
|
:ref:`a corresponding role <python-roles>`. For the case of functions,
|
||||||
|
you can use :rst:role:`py:func` for that, as follows:
|
||||||
|
|
||||||
|
.. code-block:: rst
|
||||||
|
:caption: docs/source/usage.rst
|
||||||
|
|
||||||
|
The ``kind`` parameter should be either ``"meat"``, ``"fish"``,
|
||||||
|
or ``"veggies"``. Otherwise, :py:func:`lumache.get_random_ingredients`
|
||||||
|
will raise an exception.
|
||||||
|
|
||||||
|
When generating code documentation, Sphinx will generate a cross-reference automatically just
|
||||||
|
by using the name of the object, without you having to explicitly use a role
|
||||||
|
for that. For example, you can describe the custom exception raised by the
|
||||||
|
function using the :rst:dir:`py:exception` directive:
|
||||||
|
|
||||||
|
.. code-block:: rst
|
||||||
|
:caption: docs/source/usage.rst
|
||||||
|
|
||||||
|
.. py:exception:: lumache.InvalidKindError
|
||||||
|
|
||||||
|
Raised if the kind is invalid.
|
||||||
|
|
||||||
|
Then, add this exception to the original description of the function:
|
||||||
|
|
||||||
|
.. code-block:: rst
|
||||||
|
:caption: docs/source/usage.rst
|
||||||
|
:emphasize-lines: 7
|
||||||
|
|
||||||
|
.. py:function:: lumache.get_random_ingredients(kind=None)
|
||||||
|
|
||||||
|
Return a list of random ingredients as strings.
|
||||||
|
|
||||||
|
:param kind: Optional "kind" of ingredients.
|
||||||
|
:type kind: list[str] or None
|
||||||
|
:raise lumache.InvalidKindError: If the kind is invalid.
|
||||||
|
:return: The ingredients list.
|
||||||
|
:rtype: list[str]
|
||||||
|
|
||||||
|
And finally, this is how the result would look:
|
||||||
|
|
||||||
|
.. figure:: /_static/tutorial/lumache-py-function-full.png
|
||||||
|
:width: 80%
|
||||||
|
:align: center
|
||||||
|
:alt: HTML result of documenting a Python function in Sphinx
|
||||||
|
with cross-references
|
||||||
|
|
||||||
|
HTML result of documenting a Python function in Sphinx with cross-references
|
||||||
|
|
||||||
|
Beautiful, isn't it?
|
||||||
|
|
||||||
|
Including doctests in your documentation
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Since you are now describing code from a Python library, it will become useful
|
||||||
|
to keep both the documentation and the code as synchronized as possible.
|
||||||
|
One of the ways to do that in Sphinx is to include code snippets in the
|
||||||
|
documentation, called *doctests*, that are executed when the documentation is
|
||||||
|
built.
|
||||||
|
|
||||||
|
To demonstrate doctests and other Sphinx features covered in this tutorial,
|
||||||
|
Sphinx will need to be able to import the code. To achieve that, write this
|
||||||
|
at the beginning of ``conf.py``:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
:caption: docs/source/conf.py
|
||||||
|
:emphasize-lines: 3-5
|
||||||
|
|
||||||
|
# If extensions (or modules to document with autodoc) are in another directory,
|
||||||
|
# add these directories to sys.path here.
|
||||||
|
import pathlib
|
||||||
|
import sys
|
||||||
|
sys.path.insert(0, pathlib.Path(__file__).parents[2].resolve().as_posix())
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
An alternative to changing the :py:data:`sys.path` variable is to create a
|
||||||
|
``pyproject.toml`` file and make the code installable,
|
||||||
|
so it behaves like any other Python library. However, the ``sys.path``
|
||||||
|
approach is simpler.
|
||||||
|
|
||||||
|
Then, before adding doctests to your documentation, enable the
|
||||||
|
:doc:`doctest </usage/extensions/doctest>` extension in ``conf.py``:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
:caption: docs/source/conf.py
|
||||||
|
:emphasize-lines: 3
|
||||||
|
|
||||||
|
extensions = [
|
||||||
|
'sphinx.ext.duration',
|
||||||
|
'sphinx.ext.doctest',
|
||||||
|
]
|
||||||
|
|
||||||
|
Next, write a doctest block as follows:
|
||||||
|
|
||||||
|
.. code-block:: rst
|
||||||
|
:caption: docs/source/usage.rst
|
||||||
|
|
||||||
|
>>> import lumache
|
||||||
|
>>> lumache.get_random_ingredients()
|
||||||
|
['shells', 'gorgonzola', 'parsley']
|
||||||
|
|
||||||
|
Doctests include the Python instructions to be run preceded by ``>>>``,
|
||||||
|
the standard Python interpreter prompt, as well as the expected output
|
||||||
|
of each instruction. This way, Sphinx can check whether the actual output
|
||||||
|
matches the expected one.
|
||||||
|
|
||||||
|
To observe how a doctest failure looks like (rather than a code error as
|
||||||
|
above), let's write the return value incorrectly first. Therefore, add a
|
||||||
|
function ``get_random_ingredients`` like this:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
:caption: lumache.py
|
||||||
|
|
||||||
|
def get_random_ingredients(kind=None):
|
||||||
|
return ["eggs", "bacon", "spam"]
|
||||||
|
|
||||||
|
You can now run ``make doctest`` to execute the doctests of your documentation.
|
||||||
|
Initially this will display an error, since the actual code does not behave
|
||||||
|
as specified:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
(.venv) $ make doctest
|
||||||
|
Running Sphinx v4.2.0
|
||||||
|
loading pickled environment... done
|
||||||
|
...
|
||||||
|
running tests...
|
||||||
|
|
||||||
|
Document: usage
|
||||||
|
---------------
|
||||||
|
**********************************************************************
|
||||||
|
File "usage.rst", line 44, in default
|
||||||
|
Failed example:
|
||||||
|
lumache.get_random_ingredients()
|
||||||
|
Expected:
|
||||||
|
['shells', 'gorgonzola', 'parsley']
|
||||||
|
Got:
|
||||||
|
['eggs', 'bacon', 'spam']
|
||||||
|
**********************************************************************
|
||||||
|
...
|
||||||
|
make: *** [Makefile:20: doctest] Error 1
|
||||||
|
|
||||||
|
As you can see, doctest reports the expected and the actual results,
|
||||||
|
for easy examination. It is now time to fix the function:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
:caption: lumache.py
|
||||||
|
:emphasize-lines: 2
|
||||||
|
|
||||||
|
def get_random_ingredients(kind=None):
|
||||||
|
return ["shells", "gorgonzola", "parsley"]
|
||||||
|
|
||||||
|
And finally, ``make test`` reports success!
|
||||||
|
|
||||||
|
For big projects though, this manual approach can become a bit tedious.
|
||||||
|
In the next section, you will see :doc:`how to automate the
|
||||||
|
process </tutorial/automatic-doc-generation>`.
|
||||||
|
|
||||||
|
Other languages (C, C++, others)
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
Documenting and cross-referencing objects
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Sphinx also supports documenting and cross-referencing objects written in
|
||||||
|
other programming languages. There are four additional built-in domains:
|
||||||
|
C, C++, JavaScript, and reStructuredText. Third-party extensions may
|
||||||
|
define domains for more languages, such as
|
||||||
|
|
||||||
|
- `Fortran <https://sphinx-fortran.readthedocs.io>`_,
|
||||||
|
- `Julia <http://bastikr.github.io/sphinx-julia>`_, or
|
||||||
|
- `PHP <https://github.com/markstory/sphinxcontrib-phpdomain>`_.
|
||||||
|
|
||||||
|
For example, to document a C++ type definition, you would use the built-in
|
||||||
|
:rst:dir:`cpp:type` directive, as follows:
|
||||||
|
|
||||||
|
.. code-block:: rst
|
||||||
|
|
||||||
|
.. cpp:type:: std::vector<int> CustomList
|
||||||
|
|
||||||
|
A typedef-like declaration of a type.
|
||||||
|
|
||||||
|
Which would give the following result:
|
||||||
|
|
||||||
|
.. cpp:type:: std::vector<int> CustomList
|
||||||
|
|
||||||
|
A typedef-like declaration of a type.
|
||||||
|
|
||||||
|
All such directives then generate references that can be
|
||||||
|
cross-referenced by using the corresponding role. For example, to reference
|
||||||
|
the previous type definition, you can use the :rst:role:`cpp:type` role
|
||||||
|
as follows:
|
||||||
|
|
||||||
|
.. code-block:: rst
|
||||||
|
|
||||||
|
Cross reference to :cpp:type:`CustomList`.
|
||||||
|
|
||||||
|
Which would produce a hyperlink to the previous definition: :cpp:type:`CustomList`.
|
||||||
@@ -79,7 +79,8 @@ behavior by adding the following code at the end of your ``conf.py``:
|
|||||||
|
|
||||||
With this configuration value, and after running ``make epub`` again, you will
|
With this configuration value, and after running ``make epub`` again, you will
|
||||||
notice that URLs appear now as footnotes, which avoids cluttering the text.
|
notice that URLs appear now as footnotes, which avoids cluttering the text.
|
||||||
Sweet!
|
Sweet! Read on to explore :doc:`other ways to customize
|
||||||
|
Sphinx </tutorial/more-sphinx-customization>`.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
|
|||||||
@@ -117,3 +117,4 @@ something like this:
|
|||||||
Freshly created documentation of Lumache
|
Freshly created documentation of Lumache
|
||||||
|
|
||||||
There we go! You created your first HTML documentation using Sphinx.
|
There we go! You created your first HTML documentation using Sphinx.
|
||||||
|
Now you can start :doc:`customizing it </tutorial/first-steps>`.
|
||||||
|
|||||||
@@ -33,4 +33,7 @@ project.
|
|||||||
first-steps
|
first-steps
|
||||||
more-sphinx-customization
|
more-sphinx-customization
|
||||||
narrative-documentation
|
narrative-documentation
|
||||||
|
describing-code
|
||||||
|
automatic-doc-generation
|
||||||
|
deploying
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -73,3 +73,6 @@ appearance:
|
|||||||
:alt: HTML documentation of Lumache with the Furo theme
|
:alt: HTML documentation of Lumache with the Furo theme
|
||||||
|
|
||||||
HTML documentation of Lumache with the Furo theme
|
HTML documentation of Lumache with the Furo theme
|
||||||
|
|
||||||
|
It is now time to :doc:`expand the narrative documentation and split it into
|
||||||
|
several documents </tutorial/narrative-documentation>`.
|
||||||
|
|||||||
@@ -85,14 +85,13 @@ introduction paragraph in ``index.rst``:
|
|||||||
|
|
||||||
Check out the :doc:`usage` section for further information.
|
Check out the :doc:`usage` section for further information.
|
||||||
|
|
||||||
The :rst:role:`doc` role you used automatically references a specific document
|
The :rst:role:`doc` :ref:`role <rst-roles-alt>` you used automatically
|
||||||
in the project, in this case the ``usage.rst`` you created earlier.
|
references a specific document in the project, in this case the ``usage.rst``
|
||||||
|
you created earlier.
|
||||||
|
|
||||||
Alternatively, you can also add a cross-reference to an arbitrary part of the
|
Alternatively, you can also add a cross-reference to an arbitrary part of the
|
||||||
project. For that, you need to use the :rst:role:`ref` role, and add an
|
project. For that, you need to use the :rst:role:`ref` role, and add an
|
||||||
explicit *label* that acts as `a target`__.
|
explicit *label* that acts as :duref:`a target <hyperlink-targets>`.
|
||||||
|
|
||||||
__ https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#hyperlink-targets
|
|
||||||
|
|
||||||
For example, to reference the "Installation" subsection, add a label right
|
For example, to reference the "Installation" subsection, add a label right
|
||||||
before the heading, as follows:
|
before the heading, as follows:
|
||||||
@@ -126,3 +125,6 @@ cross-reference to. If you do not include an explicit title, hence using
|
|||||||
``:ref:`installation```, the section title will be used (in this case,
|
``:ref:`installation```, the section title will be used (in this case,
|
||||||
``Installation``). Both the ``:doc:`` and the ``:ref:`` roles will be rendered
|
``Installation``). Both the ``:doc:`` and the ``:ref:`` roles will be rendered
|
||||||
as hyperlinks in the HTML documentation.
|
as hyperlinks in the HTML documentation.
|
||||||
|
|
||||||
|
What about :doc:`documenting code objects in Sphinx </tutorial/describing-code>`?
|
||||||
|
Read on!
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ Options for setuptools integration
|
|||||||
|
|
||||||
This can also be set by passing the `-E` flag to ``setup.py``:
|
This can also be set by passing the `-E` flag to ``setup.py``:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: console
|
||||||
|
|
||||||
$ python setup.py build_sphinx -E
|
$ python setup.py build_sphinx -E
|
||||||
|
|
||||||
@@ -75,7 +75,7 @@ Options for setuptools integration
|
|||||||
|
|
||||||
This can also be set by passing the `-a` flag to ``setup.py``:
|
This can also be set by passing the `-a` flag to ``setup.py``:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: console
|
||||||
|
|
||||||
$ python setup.py build_sphinx -a
|
$ python setup.py build_sphinx -a
|
||||||
|
|
||||||
@@ -88,7 +88,7 @@ Options for setuptools integration
|
|||||||
|
|
||||||
This can also be set by passing the `-s` flag to ``setup.py``:
|
This can also be set by passing the `-s` flag to ``setup.py``:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: console
|
||||||
|
|
||||||
$ python setup.py build_sphinx -s $SOURCE_DIR
|
$ python setup.py build_sphinx -s $SOURCE_DIR
|
||||||
|
|
||||||
@@ -105,7 +105,7 @@ Options for setuptools integration
|
|||||||
|
|
||||||
This can also be set by passing the `-c` flag to ``setup.py``:
|
This can also be set by passing the `-c` flag to ``setup.py``:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: console
|
||||||
|
|
||||||
$ python setup.py build_sphinx -c $CONFIG_DIR
|
$ python setup.py build_sphinx -c $CONFIG_DIR
|
||||||
|
|
||||||
@@ -117,7 +117,7 @@ Options for setuptools integration
|
|||||||
|
|
||||||
This can also be set by passing the `-b` flag to ``setup.py``:
|
This can also be set by passing the `-b` flag to ``setup.py``:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: console
|
||||||
|
|
||||||
$ python setup.py build_sphinx -b $BUILDER
|
$ python setup.py build_sphinx -b $BUILDER
|
||||||
|
|
||||||
@@ -131,7 +131,7 @@ Options for setuptools integration
|
|||||||
|
|
||||||
This can also be set by passing the `-W` flag to ``setup.py``:
|
This can also be set by passing the `-W` flag to ``setup.py``:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: console
|
||||||
|
|
||||||
$ python setup.py build_sphinx -W
|
$ python setup.py build_sphinx -W
|
||||||
|
|
||||||
@@ -169,7 +169,7 @@ Options for setuptools integration
|
|||||||
|
|
||||||
This can also be set by passing the `-i` flag to ``setup.py``:
|
This can also be set by passing the `-i` flag to ``setup.py``:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: console
|
||||||
|
|
||||||
$ python setup.py build_sphinx -i
|
$ python setup.py build_sphinx -i
|
||||||
|
|
||||||
|
|||||||
@@ -22,9 +22,9 @@ and output behavior.
|
|||||||
.. _`docutils.conf`: https://docutils.sourceforge.io/docs/user/config.html
|
.. _`docutils.conf`: https://docutils.sourceforge.io/docs/user/config.html
|
||||||
|
|
||||||
The configuration file is executed as Python code at build time (using
|
The configuration file is executed as Python code at build time (using
|
||||||
:func:`execfile`, and with the current directory set to its containing
|
:func:`importlib.import_module`, and with the current directory set to its
|
||||||
directory), and therefore can execute arbitrarily complex code. Sphinx then
|
containing directory), and therefore can execute arbitrarily complex code.
|
||||||
reads simple names from the file's namespace as its configuration.
|
Sphinx then reads simple names from the file's namespace as its configuration.
|
||||||
|
|
||||||
Important points to note:
|
Important points to note:
|
||||||
|
|
||||||
@@ -329,6 +329,8 @@ General configuration
|
|||||||
* ``ref.python``
|
* ``ref.python``
|
||||||
* ``misc.highlighting_failure``
|
* ``misc.highlighting_failure``
|
||||||
* ``toc.circular``
|
* ``toc.circular``
|
||||||
|
* ``toc.excluded``
|
||||||
|
* ``toc.not_readable``
|
||||||
* ``toc.secnum``
|
* ``toc.secnum``
|
||||||
* ``epub.unknown_project_files``
|
* ``epub.unknown_project_files``
|
||||||
* ``epub.duplicated_toc_entry``
|
* ``epub.duplicated_toc_entry``
|
||||||
@@ -360,6 +362,10 @@ General configuration
|
|||||||
|
|
||||||
Added ``epub.duplicated_toc_entry``
|
Added ``epub.duplicated_toc_entry``
|
||||||
|
|
||||||
|
.. versionchanged:: 4.3
|
||||||
|
|
||||||
|
Added ``toc.excluded`` and ``toc.not_readable``
|
||||||
|
|
||||||
.. confval:: needs_sphinx
|
.. confval:: needs_sphinx
|
||||||
|
|
||||||
If set to a ``major.minor`` version string like ``'1.1'``, Sphinx will
|
If set to a ``major.minor`` version string like ``'1.1'``, Sphinx will
|
||||||
@@ -802,6 +808,13 @@ documentation on :ref:`intl` for details.
|
|||||||
.. versionchanged:: 1.5
|
.. versionchanged:: 1.5
|
||||||
Use ``locales`` directory as a default value
|
Use ``locales`` directory as a default value
|
||||||
|
|
||||||
|
.. confval:: gettext_allow_fuzzy_translations
|
||||||
|
|
||||||
|
If true, "fuzzy" messages in the message catalogs are used for translation.
|
||||||
|
The default is ``False``.
|
||||||
|
|
||||||
|
.. versionadded:: 4.3
|
||||||
|
|
||||||
.. confval:: gettext_compact
|
.. confval:: gettext_compact
|
||||||
|
|
||||||
.. versionadded:: 1.1
|
.. versionadded:: 1.1
|
||||||
@@ -992,7 +1005,7 @@ that use Sphinx's HTMLWriter class.
|
|||||||
to indicate the location of document using `The Canonical Link Relation`_.
|
to indicate the location of document using `The Canonical Link Relation`_.
|
||||||
Default: ``''``.
|
Default: ``''``.
|
||||||
|
|
||||||
.. _The Canonical Link Relation: https://tools.ietf.org/html/rfc6596
|
.. _The Canonical Link Relation: https://datatracker.ietf.org/doc/html/rfc6596
|
||||||
|
|
||||||
.. versionadded:: 1.8
|
.. versionadded:: 1.8
|
||||||
|
|
||||||
@@ -2486,6 +2499,13 @@ These options influence Texinfo output.
|
|||||||
|
|
||||||
.. versionadded:: 1.1
|
.. versionadded:: 1.1
|
||||||
|
|
||||||
|
.. confval:: texinfo_cross_references
|
||||||
|
|
||||||
|
If false, do not generate inline references in a document. That makes
|
||||||
|
an info file more readable with stand-alone reader (``info``).
|
||||||
|
Default is ``True``.
|
||||||
|
|
||||||
|
.. versionadded:: 4.4
|
||||||
|
|
||||||
.. _qthelp-options:
|
.. _qthelp-options:
|
||||||
|
|
||||||
@@ -2637,10 +2657,8 @@ Options for the linkcheck builder
|
|||||||
A regular expression that matches a URI.
|
A regular expression that matches a URI.
|
||||||
*auth_info*
|
*auth_info*
|
||||||
Authentication information to use for that URI. The value can be anything
|
Authentication information to use for that URI. The value can be anything
|
||||||
that is understood by the ``requests`` library (see `requests
|
that is understood by the ``requests`` library (see :ref:`requests
|
||||||
Authentication <requests-auth>`_ for details).
|
Authentication <requests:authentication>` for details).
|
||||||
|
|
||||||
.. _requests-auth: https://requests.readthedocs.io/en/master/user/authentication/
|
|
||||||
|
|
||||||
The ``linkcheck`` builder will use the first matching ``auth_info`` value
|
The ``linkcheck`` builder will use the first matching ``auth_info`` value
|
||||||
it can find in the :confval:`linkcheck_auth` list, so values earlier in the
|
it can find in the :confval:`linkcheck_auth` list, so values earlier in the
|
||||||
@@ -2668,10 +2686,23 @@ Options for the linkcheck builder
|
|||||||
doubling the wait time between attempts until it succeeds or exceeds the
|
doubling the wait time between attempts until it succeeds or exceeds the
|
||||||
``linkcheck_rate_limit_timeout``. By default, the timeout is 5 minutes.
|
``linkcheck_rate_limit_timeout``. By default, the timeout is 5 minutes.
|
||||||
|
|
||||||
.. _Retry-After: https://tools.ietf.org/html/rfc7231#section-7.1.3
|
.. _Retry-After: https://datatracker.ietf.org/doc/html/rfc7231#section-7.1.3
|
||||||
|
|
||||||
.. versionadded:: 3.4
|
.. versionadded:: 3.4
|
||||||
|
|
||||||
|
.. confval:: linkcheck_exclude_documents
|
||||||
|
|
||||||
|
A list of regular expressions that match documents in which Sphinx should
|
||||||
|
not check the validity of links. This can be used for permitting link decay
|
||||||
|
in legacy or historical sections of the documentation.
|
||||||
|
|
||||||
|
Example::
|
||||||
|
|
||||||
|
# ignore all links in documents located in a subfolder named 'legacy'
|
||||||
|
linkcheck_exclude_documents = [r'.*/legacy/.*']
|
||||||
|
|
||||||
|
.. versionadded:: 4.4
|
||||||
|
|
||||||
|
|
||||||
Options for the XML builder
|
Options for the XML builder
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|||||||
@@ -662,6 +662,18 @@ There are also config values that you can set:
|
|||||||
.. __: https://mypy.readthedocs.io/en/latest/kinds_of_types.html#type-aliases
|
.. __: https://mypy.readthedocs.io/en/latest/kinds_of_types.html#type-aliases
|
||||||
.. versionadded:: 3.3
|
.. versionadded:: 3.3
|
||||||
|
|
||||||
|
.. confval:: autodoc_typehints_format
|
||||||
|
|
||||||
|
This value controls the format of typehints. The setting takes the
|
||||||
|
following values:
|
||||||
|
|
||||||
|
* ``'fully-qualified'`` -- Show the module name and its name of typehints
|
||||||
|
(default)
|
||||||
|
* ``'short'`` -- Suppress the leading module names of the typehints
|
||||||
|
(ex. ``io.StringIO`` -> ``StringIO``)
|
||||||
|
|
||||||
|
.. versionadded:: 4.4
|
||||||
|
|
||||||
.. confval:: autodoc_preserve_defaults
|
.. confval:: autodoc_preserve_defaults
|
||||||
|
|
||||||
If True, the default argument values of functions will be not evaluated on
|
If True, the default argument values of functions will be not evaluated on
|
||||||
@@ -768,8 +780,6 @@ needed docstring processing in event :event:`autodoc-process-docstring`:
|
|||||||
|
|
||||||
.. event:: autodoc-process-bases (app, name, obj, options, bases)
|
.. event:: autodoc-process-bases (app, name, obj, options, bases)
|
||||||
|
|
||||||
.. versionadded:: 4.1
|
|
||||||
|
|
||||||
Emitted when autodoc has read and processed a class to determine the
|
Emitted when autodoc has read and processed a class to determine the
|
||||||
base-classes. *bases* is a list of classes that the event handler can
|
base-classes. *bases* is a list of classes that the event handler can
|
||||||
modify **in place** to change what Sphinx puts into the output. It's
|
modify **in place** to change what Sphinx puts into the output. It's
|
||||||
@@ -781,6 +791,12 @@ needed docstring processing in event :event:`autodoc-process-docstring`:
|
|||||||
:param options: the options given to the class directive
|
:param options: the options given to the class directive
|
||||||
:param bases: the list of base classes signature. see above.
|
:param bases: the list of base classes signature. see above.
|
||||||
|
|
||||||
|
.. versionadded:: 4.1
|
||||||
|
.. versionchanged:: 4.3
|
||||||
|
|
||||||
|
``bases`` can contain a string as a base class name. It will be processed
|
||||||
|
as reST mark-up'ed text.
|
||||||
|
|
||||||
|
|
||||||
Skipping members
|
Skipping members
|
||||||
----------------
|
----------------
|
||||||
|
|||||||
@@ -201,6 +201,25 @@ also use these config values:
|
|||||||
|
|
||||||
.. versionadded:: 2.1
|
.. versionadded:: 2.1
|
||||||
|
|
||||||
|
.. versionchanged:: 4.4
|
||||||
|
|
||||||
|
If ``autosummary_ignore_module_all`` is ``False``, this configuration
|
||||||
|
value is ignored for members listed in ``__all__``.
|
||||||
|
|
||||||
|
.. confval:: autosummary_ignore_module_all
|
||||||
|
|
||||||
|
If ``False`` and a module has the ``__all__`` attribute set, autosummary
|
||||||
|
documents every member listed in ``__all__`` and no others. Default is
|
||||||
|
``True``
|
||||||
|
|
||||||
|
Note that if an imported member is listed in ``__all__``, it will be
|
||||||
|
documented regardless of the value of ``autosummary_imported_members``. To
|
||||||
|
match the behaviour of ``from module import *``, set
|
||||||
|
``autosummary_ignore_module_all`` to False and
|
||||||
|
``autosummary_imported_members`` to True.
|
||||||
|
|
||||||
|
.. versionadded:: 4.4
|
||||||
|
|
||||||
.. confval:: autosummary_filename_map
|
.. confval:: autosummary_filename_map
|
||||||
|
|
||||||
A dict mapping object names to filenames. This is necessary to avoid
|
A dict mapping object names to filenames. This is necessary to avoid
|
||||||
@@ -210,6 +229,7 @@ also use these config values:
|
|||||||
|
|
||||||
.. versionadded:: 3.2
|
.. versionadded:: 3.2
|
||||||
|
|
||||||
|
.. _autosummary-customizing-templates:
|
||||||
|
|
||||||
Customizing templates
|
Customizing templates
|
||||||
---------------------
|
---------------------
|
||||||
|
|||||||
@@ -148,6 +148,35 @@ linking:
|
|||||||
exception is raised if the server has not issued a response for timeout
|
exception is raised if the server has not issued a response for timeout
|
||||||
seconds.
|
seconds.
|
||||||
|
|
||||||
|
.. confval:: intersphinx_disabled_reftypes
|
||||||
|
|
||||||
|
.. versionadded:: 4.3
|
||||||
|
|
||||||
|
A list of strings being either:
|
||||||
|
|
||||||
|
- the name of a specific reference type in a domain,
|
||||||
|
e.g., ``std:doc``, ``py:func``, or ``cpp:class``,
|
||||||
|
- the name of a domain, and a wildcard, e.g.,
|
||||||
|
``std:*``, ``py:*``, or ``cpp:*``, or
|
||||||
|
- simply a wildcard ``*``.
|
||||||
|
|
||||||
|
The default value is an empty list.
|
||||||
|
|
||||||
|
When a cross-reference without an explicit inventory specification is being
|
||||||
|
resolved by intersphinx, skip resolution if it matches one of the
|
||||||
|
specifications in this list.
|
||||||
|
|
||||||
|
For example, with ``intersphinx_disabled_reftypes = ['std:doc']``
|
||||||
|
a cross-reference ``:doc:`installation``` will not be attempted to be
|
||||||
|
resolved by intersphinx, but ``:doc:`otherbook:installation``` will be
|
||||||
|
attempted to be resolved in the inventory named ``otherbook`` in
|
||||||
|
:confval:`intersphinx_mapping`.
|
||||||
|
At the same time, all cross-references generated in, e.g., Python,
|
||||||
|
declarations will still be attempted to be resolved by intersphinx.
|
||||||
|
|
||||||
|
If ``*`` is in the list of domains, then no references without an explicit
|
||||||
|
inventory will be resolved by intersphinx.
|
||||||
|
|
||||||
|
|
||||||
Showing all links of an Intersphinx mapping file
|
Showing all links of an Intersphinx mapping file
|
||||||
------------------------------------------------
|
------------------------------------------------
|
||||||
@@ -158,7 +187,7 @@ searching for the root cause of a broken Intersphinx link in a documentation
|
|||||||
project. The following example prints the Intersphinx mapping of the Python 3
|
project. The following example prints the Intersphinx mapping of the Python 3
|
||||||
documentation::
|
documentation::
|
||||||
|
|
||||||
$ python -msphinx.ext.intersphinx https://docs.python.org/3/objects.inv
|
$ python -m sphinx.ext.intersphinx https://docs.python.org/3/objects.inv
|
||||||
|
|
||||||
Using Intersphinx with inventory file under Basic Authorization
|
Using Intersphinx with inventory file under Basic Authorization
|
||||||
---------------------------------------------------------------
|
---------------------------------------------------------------
|
||||||
|
|||||||
@@ -200,6 +200,11 @@ Sphinx but is set to automatically include it from a third-party site.
|
|||||||
|
|
||||||
.. versionadded:: 1.8
|
.. versionadded:: 1.8
|
||||||
|
|
||||||
|
.. versionchanged:: 4.4.1
|
||||||
|
|
||||||
|
Allow to change the loading method (async or defer) of MathJax if "async"
|
||||||
|
or "defer" key is set.
|
||||||
|
|
||||||
.. confval:: mathjax3_config
|
.. confval:: mathjax3_config
|
||||||
|
|
||||||
The configuration options for MathJax v3 (which is used by default).
|
The configuration options for MathJax v3 (which is used by default).
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ Chocolatey
|
|||||||
$ choco install sphinx
|
$ choco install sphinx
|
||||||
|
|
||||||
You would need to `install Chocolatey
|
You would need to `install Chocolatey
|
||||||
<https://chocolatey.org/install/>`_
|
<https://chocolatey.org/install>`_
|
||||||
before running this.
|
before running this.
|
||||||
|
|
||||||
For more information, refer to the `chocolatey page`__.
|
For more information, refer to the `chocolatey page`__.
|
||||||
@@ -239,13 +239,13 @@ PDF builds using LaTeX. Please choose one for your purpose.
|
|||||||
commands. For example, you can use following command to create a Sphinx
|
commands. For example, you can use following command to create a Sphinx
|
||||||
project:
|
project:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: console
|
||||||
|
|
||||||
$ docker run -it --rm -v /path/to/document:/docs sphinxdoc/sphinx sphinx-quickstart
|
$ docker run -it --rm -v /path/to/document:/docs sphinxdoc/sphinx sphinx-quickstart
|
||||||
|
|
||||||
And you can following command this to build HTML document:
|
And you can following command this to build HTML document:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: console
|
||||||
|
|
||||||
$ docker run --rm -v /path/to/document:/docs sphinxdoc/sphinx make html
|
$ docker run --rm -v /path/to/document:/docs sphinxdoc/sphinx make html
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ Sphinx comes with a script called :program:`sphinx-quickstart` that sets up a
|
|||||||
source directory and creates a default :file:`conf.py` with the most useful
|
source directory and creates a default :file:`conf.py` with the most useful
|
||||||
configuration values from a few questions it asks you. To use this, run:
|
configuration values from a few questions it asks you. To use this, run:
|
||||||
|
|
||||||
.. code-block:: shell
|
.. code-block:: console
|
||||||
|
|
||||||
$ sphinx-quickstart
|
$ sphinx-quickstart
|
||||||
|
|
||||||
@@ -128,7 +128,7 @@ Running the build
|
|||||||
Now that you have added some files and content, let's make a first build of the
|
Now that you have added some files and content, let's make a first build of the
|
||||||
docs. A build is started with the :program:`sphinx-build` program:
|
docs. A build is started with the :program:`sphinx-build` program:
|
||||||
|
|
||||||
.. code-block:: shell
|
.. code-block:: console
|
||||||
|
|
||||||
$ sphinx-build -b html sourcedir builddir
|
$ sphinx-build -b html sourcedir builddir
|
||||||
|
|
||||||
@@ -144,7 +144,7 @@ However, :program:`sphinx-quickstart` script creates a :file:`Makefile` and a
|
|||||||
:file:`make.bat` which make life even easier for you. These can be executed by
|
:file:`make.bat` which make life even easier for you. These can be executed by
|
||||||
running :command:`make` with the name of the builder. For example.
|
running :command:`make` with the name of the builder. For example.
|
||||||
|
|
||||||
.. code-block:: shell
|
.. code-block:: console
|
||||||
|
|
||||||
$ make html
|
$ make html
|
||||||
|
|
||||||
|
|||||||
@@ -410,7 +410,27 @@ following the arguments and indicated by the colons). Options must be indented
|
|||||||
to the same level as the directive content.
|
to the same level as the directive content.
|
||||||
|
|
||||||
The directive content follows after a blank line and is indented relative to
|
The directive content follows after a blank line and is indented relative to
|
||||||
the directive start.
|
the directive start or if options are present, by the same amount as the
|
||||||
|
options.
|
||||||
|
|
||||||
|
Be careful as the indent is not a fixed number of whitespace, e.g. three, but
|
||||||
|
any number whitespace. This can be surprising when a fixed indent is used
|
||||||
|
throughout the document and can make a difference for directives which are
|
||||||
|
sensitive to whitespace. Compare::
|
||||||
|
|
||||||
|
.. code-block::
|
||||||
|
:caption: A cool example
|
||||||
|
|
||||||
|
The output of this line starts with four spaces.
|
||||||
|
|
||||||
|
.. code-block::
|
||||||
|
|
||||||
|
The output of this line has no spaces at the beginning.
|
||||||
|
|
||||||
|
In the first code block, the indent for the content was fixated by the option
|
||||||
|
line to three spaces, consequently the content starts with four spaces.
|
||||||
|
In the latter the indent was fixed by the content itself to seven spaces, thus
|
||||||
|
it does not start with a space.
|
||||||
|
|
||||||
|
|
||||||
Images
|
Images
|
||||||
|
|||||||
@@ -832,6 +832,9 @@ Glossary
|
|||||||
.. versionchanged:: 1.4
|
.. versionchanged:: 1.4
|
||||||
Index key for glossary term should be considered *experimental*.
|
Index key for glossary term should be considered *experimental*.
|
||||||
|
|
||||||
|
.. versionchanged:: 4.4
|
||||||
|
In internationalized documentation, the ``:sorted:`` flag sorts
|
||||||
|
according to translated terms.
|
||||||
|
|
||||||
Meta-information markup
|
Meta-information markup
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|||||||
@@ -125,6 +125,7 @@ In short:
|
|||||||
component of the target. For example, ``:py:meth:`~Queue.Queue.get``` will
|
component of the target. For example, ``:py:meth:`~Queue.Queue.get``` will
|
||||||
refer to ``Queue.Queue.get`` but only display ``get`` as the link text.
|
refer to ``Queue.Queue.get`` but only display ``get`` as the link text.
|
||||||
|
|
||||||
|
.. _python-domain:
|
||||||
|
|
||||||
The Python Domain
|
The Python Domain
|
||||||
-----------------
|
-----------------
|
||||||
@@ -677,12 +678,55 @@ The C domain (name **c**) is suited for documentation of C API.
|
|||||||
Note that you don't have to backslash-escape asterisks in the signature, as
|
Note that you don't have to backslash-escape asterisks in the signature, as
|
||||||
it is not parsed by the reST inliner.
|
it is not parsed by the reST inliner.
|
||||||
|
|
||||||
|
In the description of a function you can use the following info fields
|
||||||
|
(see also :ref:`info-field-lists`).
|
||||||
|
|
||||||
|
* ``param``, ``parameter``, ``arg``, ``argument``,
|
||||||
|
Description of a parameter.
|
||||||
|
* ``type``: Type of a parameter,
|
||||||
|
written as if passed to the :rst:role:`c:expr` role.
|
||||||
|
* ``returns``, ``return``: Description of the return value.
|
||||||
|
* ``rtype``: Return type,
|
||||||
|
written as if passed to the :rst:role:`c:expr` role.
|
||||||
|
* ``retval``, ``retvals``: An alternative to ``returns`` for describing
|
||||||
|
the result of the function.
|
||||||
|
|
||||||
|
.. versionadded:: 4.3
|
||||||
|
The ``retval`` field type.
|
||||||
|
|
||||||
|
For example::
|
||||||
|
|
||||||
|
.. c:function:: PyObject *PyType_GenericAlloc(PyTypeObject *type, Py_ssize_t nitems)
|
||||||
|
|
||||||
|
:param type: description of the first parameter.
|
||||||
|
:param nitems: description of the second parameter.
|
||||||
|
:returns: a result.
|
||||||
|
:retval NULL: under some conditions.
|
||||||
|
:retval NULL: under some other conditions as well.
|
||||||
|
|
||||||
|
which renders as
|
||||||
|
|
||||||
|
.. c:function:: PyObject *PyType_GenericAlloc(PyTypeObject *type, Py_ssize_t nitems)
|
||||||
|
|
||||||
|
..
|
||||||
|
** for some editors (e.g., vim) to stop bold-highlighting the source
|
||||||
|
|
||||||
|
:param type: description of the first parameter.
|
||||||
|
:param nitems: description of the second parameter.
|
||||||
|
:returns: a result.
|
||||||
|
:retval NULL: under some conditions.
|
||||||
|
:retval NULL: under some other conditions as well.
|
||||||
|
|
||||||
|
|
||||||
.. rst:directive:: .. c:macro:: name
|
.. rst:directive:: .. c:macro:: name
|
||||||
.. c:macro:: name(arg list)
|
.. c:macro:: name(arg list)
|
||||||
|
|
||||||
Describes a C macro, i.e., a C-language ``#define``, without the replacement
|
Describes a C macro, i.e., a C-language ``#define``, without the replacement
|
||||||
text.
|
text.
|
||||||
|
|
||||||
|
In the description of a macro you can use the same info fields as for the
|
||||||
|
:rst:dir:`c:function` directive.
|
||||||
|
|
||||||
.. versionadded:: 3.0
|
.. versionadded:: 3.0
|
||||||
The function style variant.
|
The function style variant.
|
||||||
|
|
||||||
@@ -1453,14 +1497,23 @@ The ``cpp:namespace-pop`` directive undoes the most recent
|
|||||||
Info field lists
|
Info field lists
|
||||||
~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
The C++ directives support the following info fields (see also
|
All the C++ directives for declaring entities support the following
|
||||||
:ref:`info-field-lists`):
|
info fields (see also :ref:`info-field-lists`):
|
||||||
|
|
||||||
* `param`, `parameter`, `arg`, `argument`: Description of a parameter.
|
* ``tparam``: Description of a template parameter.
|
||||||
* `tparam`: Description of a template parameter.
|
|
||||||
* `returns`, `return`: Description of a return value.
|
The :rst:dir:`cpp:function` directive additionally supports the
|
||||||
|
following fields:
|
||||||
|
|
||||||
|
* ``param``, ``parameter``, ``arg``, ``argument``: Description of a parameter.
|
||||||
|
* ``returns``, ``return``: Description of a return value.
|
||||||
|
* ``retval``, ``retvals``: An alternative to ``returns`` for describing
|
||||||
|
the result of the function.
|
||||||
* `throws`, `throw`, `exception`: Description of a possibly thrown exception.
|
* `throws`, `throw`, `exception`: Description of a possibly thrown exception.
|
||||||
|
|
||||||
|
.. versionadded:: 4.3
|
||||||
|
The ``retval`` field type.
|
||||||
|
|
||||||
.. _cpp-roles:
|
.. _cpp-roles:
|
||||||
|
|
||||||
Cross-referencing
|
Cross-referencing
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ directory containing :file:`conf.py` and use this configuration::
|
|||||||
The third form is a Python package. If a theme you want to use is distributed
|
The third form is a Python package. If a theme you want to use is distributed
|
||||||
as a Python package, you can use it after installing
|
as a Python package, you can use it after installing
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: console
|
||||||
|
|
||||||
# installing theme package
|
# installing theme package
|
||||||
$ pip install sphinxjp.themes.dotted
|
$ pip install sphinxjp.themes.dotted
|
||||||
@@ -127,6 +127,10 @@ Builtin themes
|
|||||||
|
|
||||||
Sphinx comes with a selection of themes to choose from.
|
Sphinx comes with a selection of themes to choose from.
|
||||||
|
|
||||||
|
Note that from these themes only the Alabaster and Scrolls themes are
|
||||||
|
mobile-optimated, the other themes resort to horizontal scrolling
|
||||||
|
if the screen is too narrow.
|
||||||
|
|
||||||
.. cssclass:: clear
|
.. cssclass:: clear
|
||||||
|
|
||||||
These themes are:
|
These themes are:
|
||||||
|
|||||||
28
package-lock.json
generated
28
package-lock.json
generated
@@ -422,30 +422,10 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"follow-redirects": {
|
"follow-redirects": {
|
||||||
"version": "1.7.0",
|
"version": "1.14.7",
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz",
|
||||||
"integrity": "sha512-m/pZQy4Gj287eNy94nivy5wchN3Kp+Q5WgUPNy5lJSZ3sgkVKSYV/ZChMAQVIgx1SqfZ2zBZtPA2YlXIWxxJOQ==",
|
"integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==",
|
||||||
"dev": true,
|
"dev": true
|
||||||
"requires": {
|
|
||||||
"debug": "^3.2.6"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"debug": {
|
|
||||||
"version": "3.2.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
|
|
||||||
"integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"ms": "^2.1.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ms": {
|
|
||||||
"version": "2.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
|
||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
|
||||||
"dev": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"fs-extra": {
|
"fs-extra": {
|
||||||
"version": "7.0.1",
|
"version": "7.0.1",
|
||||||
|
|||||||
11
setup.py
11
setup.py
@@ -1,6 +1,5 @@
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from distutils import log
|
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
|
|
||||||
from setuptools import find_packages, setup
|
from setuptools import find_packages, setup
|
||||||
@@ -29,8 +28,8 @@ install_requires = [
|
|||||||
'alabaster>=0.7,<0.8',
|
'alabaster>=0.7,<0.8',
|
||||||
'imagesize',
|
'imagesize',
|
||||||
'requests>=2.5.0',
|
'requests>=2.5.0',
|
||||||
'setuptools',
|
|
||||||
'packaging',
|
'packaging',
|
||||||
|
"importlib-metadata>=4.4; python_version < '3.10'",
|
||||||
]
|
]
|
||||||
|
|
||||||
extras_require = {
|
extras_require = {
|
||||||
@@ -44,10 +43,9 @@ extras_require = {
|
|||||||
'lint': [
|
'lint': [
|
||||||
'flake8>=3.5.0',
|
'flake8>=3.5.0',
|
||||||
'isort',
|
'isort',
|
||||||
'mypy>=0.900',
|
'mypy>=0.931',
|
||||||
'docutils-stubs',
|
'docutils-stubs',
|
||||||
"types-typed-ast",
|
"types-typed-ast",
|
||||||
"types-pkg_resources",
|
|
||||||
"types-requests",
|
"types-requests",
|
||||||
],
|
],
|
||||||
'test': [
|
'test': [
|
||||||
@@ -149,8 +147,8 @@ else:
|
|||||||
if catalog.fuzzy and not self.use_fuzzy:
|
if catalog.fuzzy and not self.use_fuzzy:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
log.info('writing JavaScript strings in catalog %r to %r',
|
self.log.info('writing JavaScript strings in catalog %r to %r',
|
||||||
po_file, js_file)
|
po_file, js_file)
|
||||||
|
|
||||||
jscatalog = {}
|
jscatalog = {}
|
||||||
for message in catalog:
|
for message in catalog:
|
||||||
@@ -207,6 +205,7 @@ setup(
|
|||||||
'Programming Language :: Python :: 3.7',
|
'Programming Language :: Python :: 3.7',
|
||||||
'Programming Language :: Python :: 3.8',
|
'Programming Language :: Python :: 3.8',
|
||||||
'Programming Language :: Python :: 3.9',
|
'Programming Language :: Python :: 3.9',
|
||||||
|
'Programming Language :: Python :: 3.10',
|
||||||
'Programming Language :: Python :: Implementation :: CPython',
|
'Programming Language :: Python :: Implementation :: CPython',
|
||||||
'Programming Language :: Python :: Implementation :: PyPy',
|
'Programming Language :: Python :: Implementation :: PyPy',
|
||||||
'Framework :: Setuptools Plugin',
|
'Framework :: Setuptools Plugin',
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
The Sphinx documentation toolchain.
|
The Sphinx documentation toolchain.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -27,8 +27,8 @@ if 'PYTHONWARNINGS' not in os.environ:
|
|||||||
warnings.filterwarnings('ignore', "'U' mode is deprecated",
|
warnings.filterwarnings('ignore', "'U' mode is deprecated",
|
||||||
DeprecationWarning, module='docutils.io')
|
DeprecationWarning, module='docutils.io')
|
||||||
|
|
||||||
__version__ = '4.3.0+'
|
__version__ = '4.4.0+'
|
||||||
__released__ = '4.3.0' # used when Sphinx builds its own docs
|
__released__ = '4.4.0' # used when Sphinx builds its own docs
|
||||||
|
|
||||||
#: Version info for better programmatic use.
|
#: Version info for better programmatic use.
|
||||||
#:
|
#:
|
||||||
@@ -38,7 +38,7 @@ __released__ = '4.3.0' # used when Sphinx builds its own docs
|
|||||||
#:
|
#:
|
||||||
#: .. versionadded:: 1.2
|
#: .. versionadded:: 1.2
|
||||||
#: Before version 1.2, check the string ``sphinx.__version__``.
|
#: Before version 1.2, check the string ``sphinx.__version__``.
|
||||||
version_info = (4, 3, 0, 'beta', 0)
|
version_info = (4, 4, 0, 'beta', 0)
|
||||||
|
|
||||||
package_dir = path.abspath(path.dirname(__file__))
|
package_dir = path.abspath(path.dirname(__file__))
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
The Sphinx documentation toolchain.
|
The Sphinx documentation toolchain.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
Additional docutils nodes.
|
Additional docutils nodes.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -16,6 +16,13 @@ from docutils.nodes import Element
|
|||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from sphinx.application import Sphinx
|
from sphinx.application import Sphinx
|
||||||
|
|
||||||
|
try:
|
||||||
|
from docutils.nodes import meta as docutils_meta # type: ignore
|
||||||
|
except ImportError:
|
||||||
|
# docutils-0.17 or older
|
||||||
|
from docutils.parsers.rst.directives.html import MetaBody
|
||||||
|
docutils_meta = MetaBody.meta
|
||||||
|
|
||||||
|
|
||||||
class document(nodes.document):
|
class document(nodes.document):
|
||||||
"""The document root element patched by Sphinx.
|
"""The document root element patched by Sphinx.
|
||||||
@@ -528,6 +535,8 @@ class manpage(nodes.Inline, nodes.FixedTextElement):
|
|||||||
|
|
||||||
|
|
||||||
def setup(app: "Sphinx") -> Dict[str, Any]:
|
def setup(app: "Sphinx") -> Dict[str, Any]:
|
||||||
|
from sphinx.util import docutils # lazy import
|
||||||
|
|
||||||
app.add_node(toctree)
|
app.add_node(toctree)
|
||||||
|
|
||||||
app.add_node(desc)
|
app.add_node(desc)
|
||||||
@@ -563,7 +572,6 @@ def setup(app: "Sphinx") -> Dict[str, Any]:
|
|||||||
app.add_node(start_of_file)
|
app.add_node(start_of_file)
|
||||||
app.add_node(highlightlang)
|
app.add_node(highlightlang)
|
||||||
app.add_node(tabular_col_spec)
|
app.add_node(tabular_col_spec)
|
||||||
app.add_node(meta)
|
|
||||||
app.add_node(pending_xref)
|
app.add_node(pending_xref)
|
||||||
app.add_node(number_reference)
|
app.add_node(number_reference)
|
||||||
app.add_node(download_reference)
|
app.add_node(download_reference)
|
||||||
@@ -571,6 +579,9 @@ def setup(app: "Sphinx") -> Dict[str, Any]:
|
|||||||
app.add_node(literal_strong)
|
app.add_node(literal_strong)
|
||||||
app.add_node(manpage)
|
app.add_node(manpage)
|
||||||
|
|
||||||
|
if docutils.__version_info__ < (0, 18):
|
||||||
|
app.add_node(meta)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'version': 'builtin',
|
'version': 'builtin',
|
||||||
'parallel_read_safe': True,
|
'parallel_read_safe': True,
|
||||||
|
|||||||
@@ -6,13 +6,12 @@
|
|||||||
|
|
||||||
Gracefully adapted from the TextPress system by Armin.
|
Gracefully adapted from the TextPress system by Armin.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import pickle
|
import pickle
|
||||||
import platform
|
|
||||||
import sys
|
import sys
|
||||||
import warnings
|
import warnings
|
||||||
from collections import deque
|
from collections import deque
|
||||||
@@ -195,12 +194,6 @@ class Sphinx:
|
|||||||
# say hello to the world
|
# say hello to the world
|
||||||
logger.info(bold(__('Running Sphinx v%s') % sphinx.__display_version__))
|
logger.info(bold(__('Running Sphinx v%s') % sphinx.__display_version__))
|
||||||
|
|
||||||
# notice for parallel build on macOS and py38+
|
|
||||||
if sys.version_info > (3, 8) and platform.system() == 'Darwin' and parallel > 1:
|
|
||||||
logger.info(bold(__("For security reasons, parallel mode is disabled on macOS and "
|
|
||||||
"python3.8 and above. For more details, please read "
|
|
||||||
"https://github.com/sphinx-doc/sphinx/issues/6803")))
|
|
||||||
|
|
||||||
# status code for command-line application
|
# status code for command-line application
|
||||||
self.statuscode = 0
|
self.statuscode = 0
|
||||||
|
|
||||||
@@ -284,7 +277,8 @@ class Sphinx:
|
|||||||
self.config.language, self.config.source_encoding)
|
self.config.language, self.config.source_encoding)
|
||||||
for catalog in repo.catalogs:
|
for catalog in repo.catalogs:
|
||||||
if catalog.domain == 'sphinx' and catalog.is_outdated():
|
if catalog.domain == 'sphinx' and catalog.is_outdated():
|
||||||
catalog.write_mo(self.config.language)
|
catalog.write_mo(self.config.language,
|
||||||
|
self.config.gettext_allow_fuzzy_translations)
|
||||||
|
|
||||||
locale_dirs: List[Optional[str]] = list(repo.locale_dirs)
|
locale_dirs: List[Optional[str]] = list(repo.locale_dirs)
|
||||||
locale_dirs += [None]
|
locale_dirs += [None]
|
||||||
@@ -936,24 +930,31 @@ class Sphinx:
|
|||||||
"""
|
"""
|
||||||
self.registry.add_post_transform(transform)
|
self.registry.add_post_transform(transform)
|
||||||
|
|
||||||
def add_js_file(self, filename: str, priority: int = 500, **kwargs: Any) -> None:
|
def add_js_file(self, filename: str, priority: int = 500,
|
||||||
|
loading_method: Optional[str] = None, **kwargs: Any) -> None:
|
||||||
"""Register a JavaScript file to include in the HTML output.
|
"""Register a JavaScript file to include in the HTML output.
|
||||||
|
|
||||||
Add *filename* to the list of JavaScript files that the default HTML
|
:param filename: The filename of the JavaScript file. It must be relative to the HTML
|
||||||
template will include in order of *priority* (ascending). The filename
|
static path, a full URI with scheme, or ``None`` value. The ``None``
|
||||||
must be relative to the HTML static path , or a full URI with scheme.
|
value is used to create inline ``<script>`` tag. See the description
|
||||||
If the priority of the JavaScript file is the same as others, the JavaScript
|
of *kwargs* below.
|
||||||
files will be included in order of registration. If the keyword
|
:param priority: The priority to determine the order of ``<script>`` tag for
|
||||||
argument ``body`` is given, its value will be added between the
|
JavaScript files. See list of "prority range for JavaScript
|
||||||
``<script>`` tags. Extra keyword arguments are included as attributes of
|
files" below. If the priority of the JavaScript files it the same
|
||||||
the ``<script>`` tag.
|
as others, the JavaScript files will be loaded in order of
|
||||||
|
registration.
|
||||||
|
:param loading_method: The loading method of the JavaScript file. ``'async'`` or
|
||||||
|
``'defer'`` is allowed.
|
||||||
|
:param kwargs: Extra keyword arguments are included as attributes of the ``<script>``
|
||||||
|
tag. A special keyword argument ``body`` is given, its value will be
|
||||||
|
added between the ``<script>`` tag.
|
||||||
|
|
||||||
Example::
|
Example::
|
||||||
|
|
||||||
app.add_js_file('example.js')
|
app.add_js_file('example.js')
|
||||||
# => <script src="_static/example.js"></script>
|
# => <script src="_static/example.js"></script>
|
||||||
|
|
||||||
app.add_js_file('example.js', async="async")
|
app.add_js_file('example.js', loading_method="async")
|
||||||
# => <script src="_static/example.js" async="async"></script>
|
# => <script src="_static/example.js" async="async"></script>
|
||||||
|
|
||||||
app.add_js_file(None, body="var myVariable = 'foo';")
|
app.add_js_file(None, body="var myVariable = 'foo';")
|
||||||
@@ -982,7 +983,15 @@ class Sphinx:
|
|||||||
|
|
||||||
.. versionchanged:: 3.5
|
.. versionchanged:: 3.5
|
||||||
Take priority argument. Allow to add a JavaScript file to the specific page.
|
Take priority argument. Allow to add a JavaScript file to the specific page.
|
||||||
|
.. versionchanged:: 4.4
|
||||||
|
Take loading_method argument. Allow to change the loading method of the
|
||||||
|
JavaScript file.
|
||||||
"""
|
"""
|
||||||
|
if loading_method == 'async':
|
||||||
|
kwargs['async'] = 'async'
|
||||||
|
elif loading_method == 'defer':
|
||||||
|
kwargs['defer'] = 'defer'
|
||||||
|
|
||||||
self.registry.add_js_file(filename, priority=priority, **kwargs)
|
self.registry.add_js_file(filename, priority=priority, **kwargs)
|
||||||
if hasattr(self.builder, 'add_js_file'):
|
if hasattr(self.builder, 'add_js_file'):
|
||||||
self.builder.add_js_file(filename, priority=priority, **kwargs) # type: ignore
|
self.builder.add_js_file(filename, priority=priority, **kwargs) # type: ignore
|
||||||
@@ -990,12 +999,14 @@ class Sphinx:
|
|||||||
def add_css_file(self, filename: str, priority: int = 500, **kwargs: Any) -> None:
|
def add_css_file(self, filename: str, priority: int = 500, **kwargs: Any) -> None:
|
||||||
"""Register a stylesheet to include in the HTML output.
|
"""Register a stylesheet to include in the HTML output.
|
||||||
|
|
||||||
Add *filename* to the list of CSS files that the default HTML template
|
:param filename: The filename of the CSS file. It must be relative to the HTML
|
||||||
will include in order of *priority* (ascending). The filename must be
|
static path, or a full URI with scheme.
|
||||||
relative to the HTML static path, or a full URI with scheme. If the
|
:param priority: The priority to determine the order of ``<link>`` tag for the
|
||||||
priority of the CSS file is the same as others, the CSS files will be
|
CSS files. See list of "prority range for CSS files" below.
|
||||||
included in order of registration. The keyword arguments are also
|
If the priority of the CSS files it the same as others, the
|
||||||
accepted for attributes of ``<link>`` tag.
|
CSS files will be loaded in order of registration.
|
||||||
|
:param kwargs: Extra keyword arguments are included as attributes of the ``<link>``
|
||||||
|
tag.
|
||||||
|
|
||||||
Example::
|
Example::
|
||||||
|
|
||||||
@@ -1046,6 +1057,26 @@ class Sphinx:
|
|||||||
if hasattr(self.builder, 'add_css_file'):
|
if hasattr(self.builder, 'add_css_file'):
|
||||||
self.builder.add_css_file(filename, priority=priority, **kwargs) # type: ignore
|
self.builder.add_css_file(filename, priority=priority, **kwargs) # type: ignore
|
||||||
|
|
||||||
|
def add_stylesheet(self, filename: str, alternate: bool = False, title: str = None
|
||||||
|
) -> None:
|
||||||
|
"""An alias of :meth:`add_css_file`.
|
||||||
|
|
||||||
|
.. deprecated:: 1.8
|
||||||
|
"""
|
||||||
|
logger.warning('The app.add_stylesheet() is deprecated. '
|
||||||
|
'Please use app.add_css_file() instead.')
|
||||||
|
|
||||||
|
attributes = {} # type: Dict[str, Any]
|
||||||
|
if alternate:
|
||||||
|
attributes['rel'] = 'alternate stylesheet'
|
||||||
|
else:
|
||||||
|
attributes['rel'] = 'stylesheet'
|
||||||
|
|
||||||
|
if title:
|
||||||
|
attributes['title'] = title
|
||||||
|
|
||||||
|
self.add_css_file(filename, **attributes)
|
||||||
|
|
||||||
def add_latex_package(self, packagename: str, options: str = None,
|
def add_latex_package(self, packagename: str, options: str = None,
|
||||||
after_hyperref: bool = False) -> None:
|
after_hyperref: bool = False) -> None:
|
||||||
r"""Register a package to include in the LaTeX source code.
|
r"""Register a package to include in the LaTeX source code.
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
Builder superclass for all builders.
|
Builder superclass for all builders.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -176,7 +176,7 @@ class Builder:
|
|||||||
def post_process_images(self, doctree: Node) -> None:
|
def post_process_images(self, doctree: Node) -> None:
|
||||||
"""Pick the best candidate for all image URIs."""
|
"""Pick the best candidate for all image URIs."""
|
||||||
images = ImageAdapter(self.env)
|
images = ImageAdapter(self.env)
|
||||||
for node in doctree.traverse(nodes.image):
|
for node in doctree.findall(nodes.image):
|
||||||
if '?' in node['candidates']:
|
if '?' in node['candidates']:
|
||||||
# don't rewrite nonlocal image URIs
|
# don't rewrite nonlocal image URIs
|
||||||
continue
|
continue
|
||||||
@@ -217,7 +217,8 @@ class Builder:
|
|||||||
for catalog in status_iterator(catalogs, __('writing output... '), "darkgreen",
|
for catalog in status_iterator(catalogs, __('writing output... '), "darkgreen",
|
||||||
len(catalogs), self.app.verbosity,
|
len(catalogs), self.app.verbosity,
|
||||||
stringify_func=cat2relpath):
|
stringify_func=cat2relpath):
|
||||||
catalog.write_mo(self.config.language)
|
catalog.write_mo(self.config.language,
|
||||||
|
self.config.gettext_allow_fuzzy_translations)
|
||||||
|
|
||||||
def compile_all_catalogs(self) -> None:
|
def compile_all_catalogs(self) -> None:
|
||||||
repo = CatalogRepository(self.srcdir, self.config.locale_dirs,
|
repo = CatalogRepository(self.srcdir, self.config.locale_dirs,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
Base class of epub2/epub3 builders.
|
Base class of epub2/epub3 builders.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -277,7 +277,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
|
|||||||
new_ids.append(new_id)
|
new_ids.append(new_id)
|
||||||
node['ids'] = new_ids
|
node['ids'] = new_ids
|
||||||
|
|
||||||
for reference in tree.traverse(nodes.reference):
|
for reference in tree.findall(nodes.reference):
|
||||||
if 'refuri' in reference:
|
if 'refuri' in reference:
|
||||||
m = self.refuri_re.match(reference['refuri'])
|
m = self.refuri_re.match(reference['refuri'])
|
||||||
if m:
|
if m:
|
||||||
@@ -285,14 +285,14 @@ class EpubBuilder(StandaloneHTMLBuilder):
|
|||||||
if 'refid' in reference:
|
if 'refid' in reference:
|
||||||
reference['refid'] = self.fix_fragment('', reference['refid'])
|
reference['refid'] = self.fix_fragment('', reference['refid'])
|
||||||
|
|
||||||
for target in tree.traverse(nodes.target):
|
for target in tree.findall(nodes.target):
|
||||||
update_node_id(target)
|
update_node_id(target)
|
||||||
|
|
||||||
next_node: Node = target.next_node(ascend=True)
|
next_node: Node = target.next_node(ascend=True)
|
||||||
if isinstance(next_node, nodes.Element):
|
if isinstance(next_node, nodes.Element):
|
||||||
update_node_id(next_node)
|
update_node_id(next_node)
|
||||||
|
|
||||||
for desc_signature in tree.traverse(addnodes.desc_signature):
|
for desc_signature in tree.findall(addnodes.desc_signature):
|
||||||
update_node_id(desc_signature)
|
update_node_id(desc_signature)
|
||||||
|
|
||||||
def add_visible_links(self, tree: nodes.document, show_urls: str = 'inline') -> None:
|
def add_visible_links(self, tree: nodes.document, show_urls: str = 'inline') -> None:
|
||||||
@@ -323,14 +323,14 @@ class EpubBuilder(StandaloneHTMLBuilder):
|
|||||||
# a) place them after the last existing footnote
|
# a) place them after the last existing footnote
|
||||||
# b) place them after an (empty) Footnotes rubric
|
# b) place them after an (empty) Footnotes rubric
|
||||||
# c) create an empty Footnotes rubric at the end of the document
|
# c) create an empty Footnotes rubric at the end of the document
|
||||||
fns = tree.traverse(nodes.footnote)
|
fns = list(tree.findall(nodes.footnote))
|
||||||
if fns:
|
if fns:
|
||||||
fn = fns[-1]
|
fn = fns[-1]
|
||||||
return fn.parent, fn.parent.index(fn) + 1
|
return fn.parent, fn.parent.index(fn) + 1
|
||||||
for node in tree.traverse(nodes.rubric):
|
for node in tree.findall(nodes.rubric):
|
||||||
if len(node) == 1 and node.astext() == FOOTNOTES_RUBRIC_NAME:
|
if len(node) == 1 and node.astext() == FOOTNOTES_RUBRIC_NAME:
|
||||||
return node.parent, node.parent.index(node) + 1
|
return node.parent, node.parent.index(node) + 1
|
||||||
doc = tree.traverse(nodes.document)[0]
|
doc = next(tree.findall(nodes.document))
|
||||||
rub = nodes.rubric()
|
rub = nodes.rubric()
|
||||||
rub.append(nodes.Text(FOOTNOTES_RUBRIC_NAME))
|
rub.append(nodes.Text(FOOTNOTES_RUBRIC_NAME))
|
||||||
doc.append(rub)
|
doc.append(rub)
|
||||||
@@ -339,10 +339,10 @@ class EpubBuilder(StandaloneHTMLBuilder):
|
|||||||
if show_urls == 'no':
|
if show_urls == 'no':
|
||||||
return
|
return
|
||||||
if show_urls == 'footnote':
|
if show_urls == 'footnote':
|
||||||
doc = tree.traverse(nodes.document)[0]
|
doc = next(tree.findall(nodes.document))
|
||||||
fn_spot, fn_idx = footnote_spot(tree)
|
fn_spot, fn_idx = footnote_spot(tree)
|
||||||
nr = 1
|
nr = 1
|
||||||
for node in tree.traverse(nodes.reference):
|
for node in list(tree.findall(nodes.reference)):
|
||||||
uri = node.get('refuri', '')
|
uri = node.get('refuri', '')
|
||||||
if (uri.startswith('http:') or uri.startswith('https:') or
|
if (uri.startswith('http:') or uri.startswith('https:') or
|
||||||
uri.startswith('ftp:')) and uri not in node.astext():
|
uri.startswith('ftp:')) and uri not in node.astext():
|
||||||
@@ -703,7 +703,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
|
|||||||
epub_filename = path.join(self.outdir, outname)
|
epub_filename = path.join(self.outdir, outname)
|
||||||
with ZipFile(epub_filename, 'w', ZIP_DEFLATED) as epub:
|
with ZipFile(epub_filename, 'w', ZIP_DEFLATED) as epub:
|
||||||
epub.write(path.join(self.outdir, 'mimetype'), 'mimetype', ZIP_STORED)
|
epub.write(path.join(self.outdir, 'mimetype'), 'mimetype', ZIP_STORED)
|
||||||
for filename in ['META-INF/container.xml', 'content.opf', 'toc.ncx']:
|
for filename in ('META-INF/container.xml', 'content.opf', 'toc.ncx'):
|
||||||
epub.write(path.join(self.outdir, filename), filename, ZIP_DEFLATED)
|
epub.write(path.join(self.outdir, filename), filename, ZIP_DEFLATED)
|
||||||
for filename in self.files:
|
for filename in self.files:
|
||||||
epub.write(path.join(self.outdir, filename), filename, ZIP_DEFLATED)
|
epub.write(path.join(self.outdir, filename), filename, ZIP_DEFLATED)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
Changelog builder.
|
Changelog builder.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -148,7 +148,7 @@ class ChangesBuilder(Builder):
|
|||||||
|
|
||||||
def hl(self, text: str, version: str) -> str:
|
def hl(self, text: str, version: str) -> str:
|
||||||
text = html.escape(text)
|
text = html.escape(text)
|
||||||
for directive in ['versionchanged', 'versionadded', 'deprecated']:
|
for directive in ('versionchanged', 'versionadded', 'deprecated'):
|
||||||
text = text.replace('.. %s:: %s' % (directive, version),
|
text = text.replace('.. %s:: %s' % (directive, version),
|
||||||
'<b>.. %s:: %s</b>' % (directive, version))
|
'<b>.. %s:: %s</b>' % (directive, version))
|
||||||
return text
|
return text
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
Directory HTML builders.
|
Directory HTML builders.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
Do syntax checks, but no writing.
|
Do syntax checks, but no writing.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
Build epub3 files.
|
Build epub3 files.
|
||||||
Originally derived from epub.py.
|
Originally derived from epub.py.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
The MessageCatalogBuilder class.
|
The MessageCatalogBuilder class.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -146,7 +146,7 @@ class I18nBuilder(Builder):
|
|||||||
def write_doc(self, docname: str, doctree: nodes.document) -> None:
|
def write_doc(self, docname: str, doctree: nodes.document) -> None:
|
||||||
catalog = self.catalogs[docname_to_domain(docname, self.config.gettext_compact)]
|
catalog = self.catalogs[docname_to_domain(docname, self.config.gettext_compact)]
|
||||||
|
|
||||||
for toctree in self.env.tocs[docname].traverse(addnodes.toctree):
|
for toctree in self.env.tocs[docname].findall(addnodes.toctree):
|
||||||
for node, msg in extract_messages(toctree):
|
for node, msg in extract_messages(toctree):
|
||||||
node.uid = '' # type: ignore # Hack UUID model
|
node.uid = '' # type: ignore # Hack UUID model
|
||||||
catalog.add(msg, node)
|
catalog.add(msg, node)
|
||||||
@@ -180,7 +180,7 @@ if source_date_epoch is not None:
|
|||||||
|
|
||||||
class LocalTimeZone(tzinfo):
|
class LocalTimeZone(tzinfo):
|
||||||
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
||||||
super().__init__(*args, **kwargs) # type: ignore
|
super().__init__(*args, **kwargs)
|
||||||
self.tzdelta = tzdelta
|
self.tzdelta = tzdelta
|
||||||
|
|
||||||
def utcoffset(self, dt: datetime) -> timedelta:
|
def utcoffset(self, dt: datetime) -> timedelta:
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
Several HTML builders.
|
Several HTML builders.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -866,7 +866,7 @@ class StandaloneHTMLBuilder(Builder):
|
|||||||
Builder.post_process_images(self, doctree)
|
Builder.post_process_images(self, doctree)
|
||||||
|
|
||||||
if self.config.html_scaled_image_link and self.html_scaled_image_link:
|
if self.config.html_scaled_image_link and self.html_scaled_image_link:
|
||||||
for node in doctree.traverse(nodes.image):
|
for node in doctree.findall(nodes.image):
|
||||||
if not any((key in node) for key in ['scale', 'width', 'height']):
|
if not any((key in node) for key in ['scale', 'width', 'height']):
|
||||||
# resizing options are not given. scaled image link is available
|
# resizing options are not given. scaled image link is available
|
||||||
# only for resized images.
|
# only for resized images.
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
Transforms for HTML builder.
|
Transforms for HTML builder.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ class KeyboardTransform(SphinxPostTransform):
|
|||||||
x
|
x
|
||||||
"""
|
"""
|
||||||
default_priority = 400
|
default_priority = 400
|
||||||
builders = ('html',)
|
formats = ('html',)
|
||||||
pattern = re.compile(r'(?<=.)(-|\+|\^|\s+)(?=.)')
|
pattern = re.compile(r'(?<=.)(-|\+|\^|\s+)(?=.)')
|
||||||
multiwords_keys = (('caps', 'lock'),
|
multiwords_keys = (('caps', 'lock'),
|
||||||
('page' 'down'),
|
('page' 'down'),
|
||||||
@@ -48,7 +48,7 @@ class KeyboardTransform(SphinxPostTransform):
|
|||||||
|
|
||||||
def run(self, **kwargs: Any) -> None:
|
def run(self, **kwargs: Any) -> None:
|
||||||
matcher = NodeMatcher(nodes.literal, classes=["kbd"])
|
matcher = NodeMatcher(nodes.literal, classes=["kbd"])
|
||||||
for node in self.document.traverse(matcher): # type: nodes.literal
|
for node in self.document.findall(matcher): # type: nodes.literal
|
||||||
parts = self.pattern.split(node[-1].astext())
|
parts = self.pattern.split(node[-1].astext())
|
||||||
if len(parts) == 1 or self.is_multiwords_key(parts):
|
if len(parts) == 1 or self.is_multiwords_key(parts):
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
LaTeX builder.
|
LaTeX builder.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -280,7 +280,7 @@ class LaTeXBuilder(Builder):
|
|||||||
encoding='utf-8', overwrite_if_changed=True)
|
encoding='utf-8', overwrite_if_changed=True)
|
||||||
with progress_message(__("processing %s") % targetname):
|
with progress_message(__("processing %s") % targetname):
|
||||||
doctree = self.env.get_doctree(docname)
|
doctree = self.env.get_doctree(docname)
|
||||||
toctree = next(iter(doctree.traverse(addnodes.toctree)), None)
|
toctree = next(doctree.findall(addnodes.toctree), None)
|
||||||
if toctree and toctree.get('maxdepth') > 0:
|
if toctree and toctree.get('maxdepth') > 0:
|
||||||
tocdepth = toctree.get('maxdepth')
|
tocdepth = toctree.get('maxdepth')
|
||||||
else:
|
else:
|
||||||
@@ -310,7 +310,7 @@ class LaTeXBuilder(Builder):
|
|||||||
def get_contentsname(self, indexfile: str) -> str:
|
def get_contentsname(self, indexfile: str) -> str:
|
||||||
tree = self.env.get_doctree(indexfile)
|
tree = self.env.get_doctree(indexfile)
|
||||||
contentsname = None
|
contentsname = None
|
||||||
for toctree in tree.traverse(addnodes.toctree):
|
for toctree in tree.findall(addnodes.toctree):
|
||||||
if 'caption' in toctree:
|
if 'caption' in toctree:
|
||||||
contentsname = toctree['caption']
|
contentsname = toctree['caption']
|
||||||
break
|
break
|
||||||
@@ -338,7 +338,7 @@ class LaTeXBuilder(Builder):
|
|||||||
new_sect += nodes.title('<Set title in conf.py>',
|
new_sect += nodes.title('<Set title in conf.py>',
|
||||||
'<Set title in conf.py>')
|
'<Set title in conf.py>')
|
||||||
new_tree += new_sect
|
new_tree += new_sect
|
||||||
for node in tree.traverse(addnodes.toctree):
|
for node in tree.findall(addnodes.toctree):
|
||||||
new_sect += node
|
new_sect += node
|
||||||
tree = new_tree
|
tree = new_tree
|
||||||
largetree = inline_all_toctrees(self, self.docnames, indexfile, tree,
|
largetree = inline_all_toctrees(self, self.docnames, indexfile, tree,
|
||||||
@@ -353,7 +353,7 @@ class LaTeXBuilder(Builder):
|
|||||||
self.env.resolve_references(largetree, indexfile, self)
|
self.env.resolve_references(largetree, indexfile, self)
|
||||||
# resolve :ref:s to distant tex files -- we can't add a cross-reference,
|
# resolve :ref:s to distant tex files -- we can't add a cross-reference,
|
||||||
# but append the document name
|
# but append the document name
|
||||||
for pendingnode in largetree.traverse(addnodes.pending_xref):
|
for pendingnode in largetree.findall(addnodes.pending_xref):
|
||||||
docname = pendingnode['refdocname']
|
docname = pendingnode['refdocname']
|
||||||
sectname = pendingnode['refsectname']
|
sectname = pendingnode['refsectname']
|
||||||
newnodes: List[Node] = [nodes.emphasis(sectname, sectname)]
|
newnodes: List[Node] = [nodes.emphasis(sectname, sectname)]
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
consntants for LaTeX builder.
|
consntants for LaTeX builder.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
Additional nodes for LaTeX writer.
|
Additional nodes for LaTeX writer.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
Theming support for LaTeX builder.
|
Theming support for LaTeX builder.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
Transforms for LaTeX builder.
|
Transforms for LaTeX builder.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@ class FootnoteDocnameUpdater(SphinxTransform):
|
|||||||
|
|
||||||
def apply(self, **kwargs: Any) -> None:
|
def apply(self, **kwargs: Any) -> None:
|
||||||
matcher = NodeMatcher(*self.TARGET_NODES)
|
matcher = NodeMatcher(*self.TARGET_NODES)
|
||||||
for node in self.document.traverse(matcher): # type: Element
|
for node in self.document.findall(matcher): # type: Element
|
||||||
node['docname'] = self.env.docname
|
node['docname'] = self.env.docname
|
||||||
|
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ class SubstitutionDefinitionsRemover(SphinxPostTransform):
|
|||||||
formats = ('latex',)
|
formats = ('latex',)
|
||||||
|
|
||||||
def run(self, **kwargs: Any) -> None:
|
def run(self, **kwargs: Any) -> None:
|
||||||
for node in self.document.traverse(nodes.substitution_definition):
|
for node in list(self.document.findall(nodes.substitution_definition)):
|
||||||
node.parent.remove(node)
|
node.parent.remove(node)
|
||||||
|
|
||||||
|
|
||||||
@@ -81,7 +81,7 @@ class ShowUrlsTransform(SphinxPostTransform):
|
|||||||
if show_urls is False or show_urls == 'no':
|
if show_urls is False or show_urls == 'no':
|
||||||
return
|
return
|
||||||
|
|
||||||
for node in self.document.traverse(nodes.reference):
|
for node in list(self.document.findall(nodes.reference)):
|
||||||
uri = node.get('refuri', '')
|
uri = node.get('refuri', '')
|
||||||
if uri.startswith(URI_SCHEMES):
|
if uri.startswith(URI_SCHEMES):
|
||||||
if uri.startswith('mailto:'):
|
if uri.startswith('mailto:'):
|
||||||
@@ -348,7 +348,7 @@ class LaTeXFootnoteTransform(SphinxPostTransform):
|
|||||||
formats = ('latex',)
|
formats = ('latex',)
|
||||||
|
|
||||||
def run(self, **kwargs: Any) -> None:
|
def run(self, **kwargs: Any) -> None:
|
||||||
footnotes = list(self.document.traverse(nodes.footnote))
|
footnotes = list(self.document.findall(nodes.footnote))
|
||||||
for node in footnotes:
|
for node in footnotes:
|
||||||
node.parent.remove(node)
|
node.parent.remove(node)
|
||||||
|
|
||||||
@@ -423,7 +423,7 @@ class LaTeXFootnoteVisitor(nodes.NodeVisitor):
|
|||||||
self.unrestrict(node)
|
self.unrestrict(node)
|
||||||
|
|
||||||
def depart_table(self, node: nodes.table) -> None:
|
def depart_table(self, node: nodes.table) -> None:
|
||||||
tbody = list(node.traverse(nodes.tbody))[0]
|
tbody = next(node.findall(nodes.tbody))
|
||||||
for footnote in reversed(self.table_footnotes):
|
for footnote in reversed(self.table_footnotes):
|
||||||
fntext = footnotetext('', *footnote.children, ids=footnote['ids'])
|
fntext = footnotetext('', *footnote.children, ids=footnote['ids'])
|
||||||
tbody.insert(0, fntext)
|
tbody.insert(0, fntext)
|
||||||
@@ -501,7 +501,7 @@ class BibliographyTransform(SphinxPostTransform):
|
|||||||
|
|
||||||
def run(self, **kwargs: Any) -> None:
|
def run(self, **kwargs: Any) -> None:
|
||||||
citations = thebibliography()
|
citations = thebibliography()
|
||||||
for node in self.document.traverse(nodes.citation):
|
for node in list(self.document.findall(nodes.citation)):
|
||||||
node.parent.remove(node)
|
node.parent.remove(node)
|
||||||
citations += node
|
citations += node
|
||||||
|
|
||||||
@@ -521,7 +521,7 @@ class CitationReferenceTransform(SphinxPostTransform):
|
|||||||
def run(self, **kwargs: Any) -> None:
|
def run(self, **kwargs: Any) -> None:
|
||||||
domain = cast(CitationDomain, self.env.get_domain('citation'))
|
domain = cast(CitationDomain, self.env.get_domain('citation'))
|
||||||
matcher = NodeMatcher(addnodes.pending_xref, refdomain='citation', reftype='ref')
|
matcher = NodeMatcher(addnodes.pending_xref, refdomain='citation', reftype='ref')
|
||||||
for node in self.document.traverse(matcher): # type: addnodes.pending_xref
|
for node in self.document.findall(matcher): # type: addnodes.pending_xref
|
||||||
docname, labelid, _ = domain.citations.get(node['reftarget'], ('', '', 0))
|
docname, labelid, _ = domain.citations.get(node['reftarget'], ('', '', 0))
|
||||||
if docname:
|
if docname:
|
||||||
citation_ref = nodes.citation_reference('', '', *node.children,
|
citation_ref = nodes.citation_reference('', '', *node.children,
|
||||||
@@ -540,7 +540,7 @@ class MathReferenceTransform(SphinxPostTransform):
|
|||||||
|
|
||||||
def run(self, **kwargs: Any) -> None:
|
def run(self, **kwargs: Any) -> None:
|
||||||
equations = self.env.get_domain('math').data['objects']
|
equations = self.env.get_domain('math').data['objects']
|
||||||
for node in self.document.traverse(addnodes.pending_xref):
|
for node in self.document.findall(addnodes.pending_xref):
|
||||||
if node['refdomain'] == 'math' and node['reftype'] in ('eq', 'numref'):
|
if node['refdomain'] == 'math' and node['reftype'] in ('eq', 'numref'):
|
||||||
docname, _ = equations.get(node['reftarget'], (None, None))
|
docname, _ = equations.get(node['reftarget'], (None, None))
|
||||||
if docname:
|
if docname:
|
||||||
@@ -555,7 +555,7 @@ class LiteralBlockTransform(SphinxPostTransform):
|
|||||||
|
|
||||||
def run(self, **kwargs: Any) -> None:
|
def run(self, **kwargs: Any) -> None:
|
||||||
matcher = NodeMatcher(nodes.container, literal_block=True)
|
matcher = NodeMatcher(nodes.container, literal_block=True)
|
||||||
for node in self.document.traverse(matcher): # type: nodes.container
|
for node in self.document.findall(matcher): # type: nodes.container
|
||||||
newnode = captioned_literal_block('', *node.children, **node.attributes)
|
newnode = captioned_literal_block('', *node.children, **node.attributes)
|
||||||
node.replace_self(newnode)
|
node.replace_self(newnode)
|
||||||
|
|
||||||
@@ -566,7 +566,7 @@ class DocumentTargetTransform(SphinxPostTransform):
|
|||||||
formats = ('latex',)
|
formats = ('latex',)
|
||||||
|
|
||||||
def run(self, **kwargs: Any) -> None:
|
def run(self, **kwargs: Any) -> None:
|
||||||
for node in self.document.traverse(addnodes.start_of_file):
|
for node in self.document.findall(addnodes.start_of_file):
|
||||||
section = node.next_node(nodes.section)
|
section = node.next_node(nodes.section)
|
||||||
if section:
|
if section:
|
||||||
section['ids'].append(':doc') # special label for :doc:
|
section['ids'].append(':doc') # special label for :doc:
|
||||||
@@ -602,9 +602,9 @@ class IndexInSectionTitleTransform(SphinxPostTransform):
|
|||||||
formats = ('latex',)
|
formats = ('latex',)
|
||||||
|
|
||||||
def run(self, **kwargs: Any) -> None:
|
def run(self, **kwargs: Any) -> None:
|
||||||
for node in self.document.traverse(nodes.title):
|
for node in list(self.document.findall(nodes.title)):
|
||||||
if isinstance(node.parent, nodes.section):
|
if isinstance(node.parent, nodes.section):
|
||||||
for i, index in enumerate(node.traverse(addnodes.index)):
|
for i, index in enumerate(node.findall(addnodes.index)):
|
||||||
# move the index node next to the section title
|
# move the index node next to the section title
|
||||||
node.remove(index)
|
node.remove(index)
|
||||||
node.parent.insert(i + 1, index)
|
node.parent.insert(i + 1, index)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
Utilities for LaTeX builder.
|
Utilities for LaTeX builder.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
The CheckExternalLinksBuilder class.
|
The CheckExternalLinksBuilder class.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -43,18 +43,31 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
uri_re = re.compile('([a-z]+:)?//') # matches to foo:// and // (a protocol relative URL)
|
uri_re = re.compile('([a-z]+:)?//') # matches to foo:// and // (a protocol relative URL)
|
||||||
|
|
||||||
Hyperlink = NamedTuple('Hyperlink', (('uri', str),
|
|
||||||
('docname', str),
|
class Hyperlink(NamedTuple):
|
||||||
('lineno', Optional[int])))
|
uri: str
|
||||||
CheckRequest = NamedTuple('CheckRequest', (('next_check', float),
|
docname: str
|
||||||
('hyperlink', Optional[Hyperlink])))
|
lineno: Optional[int]
|
||||||
CheckResult = NamedTuple('CheckResult', (('uri', str),
|
|
||||||
('docname', str),
|
|
||||||
('lineno', int),
|
class CheckRequest(NamedTuple):
|
||||||
('status', str),
|
next_check: float
|
||||||
('message', str),
|
hyperlink: Optional[Hyperlink]
|
||||||
('code', int)))
|
|
||||||
RateLimit = NamedTuple('RateLimit', (('delay', float), ('next_check', float)))
|
|
||||||
|
class CheckResult(NamedTuple):
|
||||||
|
uri: str
|
||||||
|
docname: str
|
||||||
|
lineno: int
|
||||||
|
status: str
|
||||||
|
message: str
|
||||||
|
code: int
|
||||||
|
|
||||||
|
|
||||||
|
class RateLimit(NamedTuple):
|
||||||
|
delay: float
|
||||||
|
next_check: float
|
||||||
|
|
||||||
|
|
||||||
# Tuple is old styled CheckRequest
|
# Tuple is old styled CheckRequest
|
||||||
CheckRequestType = Union[CheckRequest, Tuple[float, str, str, int]]
|
CheckRequestType = Union[CheckRequest, Tuple[float, str, str, int]]
|
||||||
@@ -255,7 +268,7 @@ class CheckExternalLinksBuilder(DummyBuilder):
|
|||||||
elif result.status == 'broken':
|
elif result.status == 'broken':
|
||||||
if self.app.quiet or self.app.warningiserror:
|
if self.app.quiet or self.app.warningiserror:
|
||||||
logger.warning(__('broken link: %s (%s)'), result.uri, result.message,
|
logger.warning(__('broken link: %s (%s)'), result.uri, result.message,
|
||||||
location=(filename, result.lineno))
|
location=(result.docname, result.lineno))
|
||||||
else:
|
else:
|
||||||
logger.info(red('broken ') + result.uri + red(' - ' + result.message))
|
logger.info(red('broken ') + result.uri + red(' - ' + result.message))
|
||||||
self.write_entry('broken', result.docname, filename, result.lineno,
|
self.write_entry('broken', result.docname, filename, result.lineno,
|
||||||
@@ -274,7 +287,7 @@ class CheckExternalLinksBuilder(DummyBuilder):
|
|||||||
linkstat['text'] = text
|
linkstat['text'] = text
|
||||||
if self.config.linkcheck_allowed_redirects:
|
if self.config.linkcheck_allowed_redirects:
|
||||||
logger.warning('redirect ' + result.uri + ' - ' + text + ' to ' +
|
logger.warning('redirect ' + result.uri + ' - ' + text + ' to ' +
|
||||||
result.message, location=(filename, result.lineno))
|
result.message, location=(result.docname, result.lineno))
|
||||||
else:
|
else:
|
||||||
logger.info(color('redirect ') + result.uri +
|
logger.info(color('redirect ') + result.uri +
|
||||||
color(' - ' + text + ' to ' + result.message))
|
color(' - ' + text + ' to ' + result.message))
|
||||||
@@ -378,6 +391,8 @@ class HyperlinkAvailabilityCheckWorker(Thread):
|
|||||||
|
|
||||||
self.anchors_ignore = [re.compile(x)
|
self.anchors_ignore = [re.compile(x)
|
||||||
for x in self.config.linkcheck_anchors_ignore]
|
for x in self.config.linkcheck_anchors_ignore]
|
||||||
|
self.documents_exclude = [re.compile(doc)
|
||||||
|
for doc in self.config.linkcheck_exclude_documents]
|
||||||
self.auth = [(re.compile(pattern), auth_info) for pattern, auth_info
|
self.auth = [(re.compile(pattern), auth_info) for pattern, auth_info
|
||||||
in self.config.linkcheck_auth]
|
in self.config.linkcheck_auth]
|
||||||
|
|
||||||
@@ -519,6 +534,15 @@ class HyperlinkAvailabilityCheckWorker(Thread):
|
|||||||
|
|
||||||
def check(docname: str) -> Tuple[str, str, int]:
|
def check(docname: str) -> Tuple[str, str, int]:
|
||||||
# check for various conditions without bothering the network
|
# check for various conditions without bothering the network
|
||||||
|
|
||||||
|
for doc_matcher in self.documents_exclude:
|
||||||
|
if doc_matcher.match(docname):
|
||||||
|
info = (
|
||||||
|
f'{docname} matched {doc_matcher.pattern} from '
|
||||||
|
'linkcheck_exclude_documents'
|
||||||
|
)
|
||||||
|
return 'ignored', info, 0
|
||||||
|
|
||||||
if len(uri) == 0 or uri.startswith(('#', 'mailto:', 'tel:')):
|
if len(uri) == 0 or uri.startswith(('#', 'mailto:', 'tel:')):
|
||||||
return 'unchecked', '', 0
|
return 'unchecked', '', 0
|
||||||
elif not uri.startswith(('http:', 'https:')):
|
elif not uri.startswith(('http:', 'https:')):
|
||||||
@@ -639,7 +663,7 @@ class HyperlinkCollector(SphinxPostTransform):
|
|||||||
hyperlinks = builder.hyperlinks
|
hyperlinks = builder.hyperlinks
|
||||||
|
|
||||||
# reference nodes
|
# reference nodes
|
||||||
for refnode in self.document.traverse(nodes.reference):
|
for refnode in self.document.findall(nodes.reference):
|
||||||
if 'refuri' not in refnode:
|
if 'refuri' not in refnode:
|
||||||
continue
|
continue
|
||||||
uri = refnode['refuri']
|
uri = refnode['refuri']
|
||||||
@@ -653,7 +677,7 @@ class HyperlinkCollector(SphinxPostTransform):
|
|||||||
hyperlinks[uri] = uri_info
|
hyperlinks[uri] = uri_info
|
||||||
|
|
||||||
# image nodes
|
# image nodes
|
||||||
for imgnode in self.document.traverse(nodes.image):
|
for imgnode in self.document.findall(nodes.image):
|
||||||
uri = imgnode['candidates'].get('?')
|
uri = imgnode['candidates'].get('?')
|
||||||
if uri and '://' in uri:
|
if uri and '://' in uri:
|
||||||
newuri = self.app.emit_firstresult('linkcheck-process-uri', uri)
|
newuri = self.app.emit_firstresult('linkcheck-process-uri', uri)
|
||||||
@@ -699,6 +723,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
|
|||||||
app.add_post_transform(HyperlinkCollector)
|
app.add_post_transform(HyperlinkCollector)
|
||||||
|
|
||||||
app.add_config_value('linkcheck_ignore', [], None)
|
app.add_config_value('linkcheck_ignore', [], None)
|
||||||
|
app.add_config_value('linkcheck_exclude_documents', [], None)
|
||||||
app.add_config_value('linkcheck_allowed_redirects', {}, None)
|
app.add_config_value('linkcheck_allowed_redirects', {}, None)
|
||||||
app.add_config_value('linkcheck_auth', [], None)
|
app.add_config_value('linkcheck_auth', [], None)
|
||||||
app.add_config_value('linkcheck_request_headers', {}, None)
|
app.add_config_value('linkcheck_request_headers', {}, None)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
Manual pages builder.
|
Manual pages builder.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -98,7 +98,7 @@ class ManualPageBuilder(Builder):
|
|||||||
logger.info('} ', nonl=True)
|
logger.info('} ', nonl=True)
|
||||||
self.env.resolve_references(largetree, docname, self)
|
self.env.resolve_references(largetree, docname, self)
|
||||||
# remove pending_xref nodes
|
# remove pending_xref nodes
|
||||||
for pendingnode in largetree.traverse(addnodes.pending_xref):
|
for pendingnode in largetree.findall(addnodes.pending_xref):
|
||||||
pendingnode.replace_self(pendingnode.children)
|
pendingnode.replace_self(pendingnode.children)
|
||||||
|
|
||||||
docwriter.write(largetree, destination)
|
docwriter.write(largetree, destination)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
Single HTML builders.
|
Single HTML builders.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
|
|||||||
def fix_refuris(self, tree: Node) -> None:
|
def fix_refuris(self, tree: Node) -> None:
|
||||||
# fix refuris with double anchor
|
# fix refuris with double anchor
|
||||||
fname = self.config.root_doc + self.out_suffix
|
fname = self.config.root_doc + self.out_suffix
|
||||||
for refnode in tree.traverse(nodes.reference):
|
for refnode in tree.findall(nodes.reference):
|
||||||
if 'refuri' not in refnode:
|
if 'refuri' not in refnode:
|
||||||
continue
|
continue
|
||||||
refuri = refnode['refuri']
|
refuri = refnode['refuri']
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
Texinfo builder.
|
Texinfo builder.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -138,7 +138,7 @@ class TexinfoBuilder(Builder):
|
|||||||
new_sect += nodes.title('<Set title in conf.py>',
|
new_sect += nodes.title('<Set title in conf.py>',
|
||||||
'<Set title in conf.py>')
|
'<Set title in conf.py>')
|
||||||
new_tree += new_sect
|
new_tree += new_sect
|
||||||
for node in tree.traverse(addnodes.toctree):
|
for node in tree.findall(addnodes.toctree):
|
||||||
new_sect += node
|
new_sect += node
|
||||||
tree = new_tree
|
tree = new_tree
|
||||||
largetree = inline_all_toctrees(self, self.docnames, indexfile, tree,
|
largetree = inline_all_toctrees(self, self.docnames, indexfile, tree,
|
||||||
@@ -152,7 +152,7 @@ class TexinfoBuilder(Builder):
|
|||||||
logger.info(__("resolving references..."))
|
logger.info(__("resolving references..."))
|
||||||
self.env.resolve_references(largetree, indexfile, self)
|
self.env.resolve_references(largetree, indexfile, self)
|
||||||
# TODO: add support for external :ref:s
|
# TODO: add support for external :ref:s
|
||||||
for pendingnode in largetree.traverse(addnodes.pending_xref):
|
for pendingnode in largetree.findall(addnodes.pending_xref):
|
||||||
docname = pendingnode['refdocname']
|
docname = pendingnode['refdocname']
|
||||||
sectname = pendingnode['refsectname']
|
sectname = pendingnode['refsectname']
|
||||||
newnodes: List[Node] = [nodes.emphasis(sectname, sectname)]
|
newnodes: List[Node] = [nodes.emphasis(sectname, sectname)]
|
||||||
@@ -211,6 +211,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
|
|||||||
app.add_config_value('texinfo_domain_indices', True, None, [list])
|
app.add_config_value('texinfo_domain_indices', True, None, [list])
|
||||||
app.add_config_value('texinfo_show_urls', 'footnote', None)
|
app.add_config_value('texinfo_show_urls', 'footnote', None)
|
||||||
app.add_config_value('texinfo_no_detailmenu', False, None)
|
app.add_config_value('texinfo_no_detailmenu', False, None)
|
||||||
|
app.add_config_value('texinfo_cross_references', True, None)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'version': 'builtin',
|
'version': 'builtin',
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
Plain-text Sphinx builder.
|
Plain-text Sphinx builder.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
Docutils-native XML and pseudo-XML builders.
|
Docutils-native XML and pseudo-XML builders.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -71,7 +71,7 @@ class XMLBuilder(Builder):
|
|||||||
# work around multiple string % tuple issues in docutils;
|
# work around multiple string % tuple issues in docutils;
|
||||||
# replace tuples in attribute values with lists
|
# replace tuples in attribute values with lists
|
||||||
doctree = doctree.deepcopy()
|
doctree = doctree.deepcopy()
|
||||||
for node in doctree.traverse(nodes.Element):
|
for node in doctree.findall(nodes.Element):
|
||||||
for att, value in node.attributes.items():
|
for att, value in node.attributes.items():
|
||||||
if isinstance(value, tuple):
|
if isinstance(value, tuple):
|
||||||
node.attributes[att] = list(value)
|
node.attributes[att] = list(value)
|
||||||
|
|||||||
@@ -4,6 +4,6 @@
|
|||||||
|
|
||||||
Modules for command line executables.
|
Modules for command line executables.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
Build documentation from a provided source.
|
Build documentation from a provided source.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -16,6 +16,7 @@ import os
|
|||||||
import pdb
|
import pdb
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
|
from os import path
|
||||||
from typing import IO, Any, List
|
from typing import IO, Any, List
|
||||||
|
|
||||||
from docutils.utils import SystemMessage
|
from docutils.utils import SystemMessage
|
||||||
@@ -28,6 +29,7 @@ from sphinx.locale import __
|
|||||||
from sphinx.util import Tee, format_exception_cut_frames, save_traceback
|
from sphinx.util import Tee, format_exception_cut_frames, save_traceback
|
||||||
from sphinx.util.console import color_terminal, nocolor, red, terminal_safe # type: ignore
|
from sphinx.util.console import color_terminal, nocolor, red, terminal_safe # type: ignore
|
||||||
from sphinx.util.docutils import docutils_namespace, patch_docutils
|
from sphinx.util.docutils import docutils_namespace, patch_docutils
|
||||||
|
from sphinx.util.osutil import abspath, ensuredir
|
||||||
|
|
||||||
|
|
||||||
def handle_exception(app: Sphinx, args: Any, exception: BaseException, stderr: IO = sys.stderr) -> None: # NOQA
|
def handle_exception(app: Sphinx, args: Any, exception: BaseException, stderr: IO = sys.stderr) -> None: # NOQA
|
||||||
@@ -240,6 +242,8 @@ def build_main(argv: List[str] = sys.argv[1:]) -> int:
|
|||||||
|
|
||||||
if warning and args.warnfile:
|
if warning and args.warnfile:
|
||||||
try:
|
try:
|
||||||
|
warnfile = abspath(args.warnfile)
|
||||||
|
ensuredir(path.dirname(warnfile))
|
||||||
warnfp = open(args.warnfile, 'w')
|
warnfp = open(args.warnfile, 'w')
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
parser.error(__('cannot open warning file %r: %s') % (
|
parser.error(__('cannot open warning file %r: %s') % (
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
This is in its own module so that importing it is fast. It should not
|
This is in its own module so that importing it is fast. It should not
|
||||||
import the main Sphinx modules (like sphinx.applications, sphinx.builders).
|
import the main Sphinx modules (like sphinx.applications, sphinx.builders).
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
Quickly setup documentation source to work with Sphinx.
|
Quickly setup documentation source to work with Sphinx.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
Build configuration file handling.
|
Build configuration file handling.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -103,6 +103,7 @@ class Config:
|
|||||||
'language': (None, 'env', [str]),
|
'language': (None, 'env', [str]),
|
||||||
'locale_dirs': (['locales'], 'env', []),
|
'locale_dirs': (['locales'], 'env', []),
|
||||||
'figure_language_filename': ('{root}.{language}{ext}', 'env', [str]),
|
'figure_language_filename': ('{root}.{language}{ext}', 'env', [str]),
|
||||||
|
'gettext_allow_fuzzy_translations': (False, 'gettext', []),
|
||||||
|
|
||||||
'master_doc': ('index', 'env', []),
|
'master_doc': ('index', 'env', []),
|
||||||
'root_doc': (lambda config: config.master_doc, 'env', []),
|
'root_doc': (lambda config: config.master_doc, 'env', []),
|
||||||
@@ -205,7 +206,7 @@ class Config:
|
|||||||
except ValueError as exc:
|
except ValueError as exc:
|
||||||
raise ValueError(__('invalid number %r for config value %r, ignoring') %
|
raise ValueError(__('invalid number %r for config value %r, ignoring') %
|
||||||
(value, name)) from exc
|
(value, name)) from exc
|
||||||
elif hasattr(defvalue, '__call__'):
|
elif callable(defvalue):
|
||||||
return value
|
return value
|
||||||
elif defvalue is not None and not isinstance(defvalue, str):
|
elif defvalue is not None and not isinstance(defvalue, str):
|
||||||
raise ValueError(__('cannot override config setting %r with unsupported '
|
raise ValueError(__('cannot override config setting %r with unsupported '
|
||||||
@@ -250,13 +251,24 @@ class Config:
|
|||||||
if name in self.values:
|
if name in self.values:
|
||||||
self.__dict__[name] = config[name]
|
self.__dict__[name] = config[name]
|
||||||
|
|
||||||
|
def post_init_values(self) -> None:
|
||||||
|
"""
|
||||||
|
Initialize additional config variables that are added after init_values() called.
|
||||||
|
"""
|
||||||
|
config = self._raw_config
|
||||||
|
for name in config:
|
||||||
|
if name not in self.__dict__ and name in self.values:
|
||||||
|
self.__dict__[name] = config[name]
|
||||||
|
|
||||||
|
check_confval_types(None, self)
|
||||||
|
|
||||||
def __getattr__(self, name: str) -> Any:
|
def __getattr__(self, name: str) -> Any:
|
||||||
if name.startswith('_'):
|
if name.startswith('_'):
|
||||||
raise AttributeError(name)
|
raise AttributeError(name)
|
||||||
if name not in self.values:
|
if name not in self.values:
|
||||||
raise AttributeError(__('No such config value: %s') % name)
|
raise AttributeError(__('No such config value: %s') % name)
|
||||||
default = self.values[name][0]
|
default = self.values[name][0]
|
||||||
if hasattr(default, '__call__'):
|
if callable(default):
|
||||||
return default(self)
|
return default(self)
|
||||||
return default
|
return default
|
||||||
|
|
||||||
@@ -412,7 +424,7 @@ def check_confval_types(app: "Sphinx", config: Config) -> None:
|
|||||||
for confval in config:
|
for confval in config:
|
||||||
default, rebuild, annotations = config.values[confval.name]
|
default, rebuild, annotations = config.values[confval.name]
|
||||||
|
|
||||||
if hasattr(default, '__call__'):
|
if callable(default):
|
||||||
default = default(config) # evaluate default value
|
default = default(config) # evaluate default value
|
||||||
if default is None and not annotations:
|
if default is None and not annotations:
|
||||||
continue # neither inferable nor expliclitly annotated types
|
continue # neither inferable nor expliclitly annotated types
|
||||||
@@ -426,7 +438,7 @@ def check_confval_types(app: "Sphinx", config: Config) -> None:
|
|||||||
"but `{current}` is given.")
|
"but `{current}` is given.")
|
||||||
logger.warning(msg.format(name=confval.name,
|
logger.warning(msg.format(name=confval.name,
|
||||||
current=confval.value,
|
current=confval.value,
|
||||||
candidates=annotations.candidates))
|
candidates=annotations.candidates), once=True)
|
||||||
else:
|
else:
|
||||||
if type(confval.value) is type(default):
|
if type(confval.value) is type(default):
|
||||||
continue
|
continue
|
||||||
@@ -451,13 +463,13 @@ def check_confval_types(app: "Sphinx", config: Config) -> None:
|
|||||||
permitted = " or ".join(wrapped_annotations)
|
permitted = " or ".join(wrapped_annotations)
|
||||||
logger.warning(msg.format(name=confval.name,
|
logger.warning(msg.format(name=confval.name,
|
||||||
current=type(confval.value),
|
current=type(confval.value),
|
||||||
permitted=permitted))
|
permitted=permitted), once=True)
|
||||||
else:
|
else:
|
||||||
msg = __("The config value `{name}' has type `{current.__name__}', "
|
msg = __("The config value `{name}' has type `{current.__name__}', "
|
||||||
"defaults to `{default.__name__}'.")
|
"defaults to `{default.__name__}'.")
|
||||||
logger.warning(msg.format(name=confval.name,
|
logger.warning(msg.format(name=confval.name,
|
||||||
current=type(confval.value),
|
current=type(confval.value),
|
||||||
default=type(default)))
|
default=type(default)), once=True)
|
||||||
|
|
||||||
|
|
||||||
def check_primary_domain(app: "Sphinx", config: Config) -> None:
|
def check_primary_domain(app: "Sphinx", config: Config) -> None:
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
Sphinx deprecation classes and utilities.
|
Sphinx deprecation classes and utilities.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
Handlers for additional ReST directives.
|
Handlers for additional ReST directives.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
sphinx.directives.code
|
sphinx.directives.code
|
||||||
~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
sphinx.directives.other
|
sphinx.directives.other
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -18,8 +18,8 @@ from docutils.parsers.rst.directives.misc import Include as BaseInclude
|
|||||||
|
|
||||||
from sphinx import addnodes
|
from sphinx import addnodes
|
||||||
from sphinx.domains.changeset import VersionChange # NOQA # for compatibility
|
from sphinx.domains.changeset import VersionChange # NOQA # for compatibility
|
||||||
from sphinx.locale import _
|
from sphinx.locale import _, __
|
||||||
from sphinx.util import docname_join, url_re
|
from sphinx.util import docname_join, logging, url_re
|
||||||
from sphinx.util.docutils import SphinxDirective
|
from sphinx.util.docutils import SphinxDirective
|
||||||
from sphinx.util.matching import Matcher, patfilter
|
from sphinx.util.matching import Matcher, patfilter
|
||||||
from sphinx.util.nodes import explicit_title_re
|
from sphinx.util.nodes import explicit_title_re
|
||||||
@@ -30,6 +30,7 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
|
|
||||||
glob_re = re.compile(r'.*[*?\[].*')
|
glob_re = re.compile(r'.*[*?\[].*')
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def int_or_nothing(argument: str) -> int:
|
def int_or_nothing(argument: str) -> int:
|
||||||
@@ -106,9 +107,8 @@ class TocTree(SphinxDirective):
|
|||||||
toctree['entries'].append((None, docname))
|
toctree['entries'].append((None, docname))
|
||||||
toctree['includefiles'].append(docname)
|
toctree['includefiles'].append(docname)
|
||||||
if not docnames:
|
if not docnames:
|
||||||
ret.append(self.state.document.reporter.warning(
|
logger.warning(__('toctree glob pattern %r didn\'t match any documents'),
|
||||||
'toctree glob pattern %r didn\'t match any documents'
|
entry, location=toctree)
|
||||||
% entry, line=self.lineno))
|
|
||||||
else:
|
else:
|
||||||
if explicit:
|
if explicit:
|
||||||
ref = explicit.group(2)
|
ref = explicit.group(2)
|
||||||
@@ -128,20 +128,21 @@ class TocTree(SphinxDirective):
|
|||||||
toctree['entries'].append((title, ref))
|
toctree['entries'].append((title, ref))
|
||||||
elif docname not in self.env.found_docs:
|
elif docname not in self.env.found_docs:
|
||||||
if excluded(self.env.doc2path(docname, None)):
|
if excluded(self.env.doc2path(docname, None)):
|
||||||
message = 'toctree contains reference to excluded document %r'
|
message = __('toctree contains reference to excluded document %r')
|
||||||
|
subtype = 'excluded'
|
||||||
else:
|
else:
|
||||||
message = 'toctree contains reference to nonexisting document %r'
|
message = __('toctree contains reference to nonexisting document %r')
|
||||||
|
subtype = 'not_readable'
|
||||||
|
|
||||||
ret.append(self.state.document.reporter.warning(message % docname,
|
logger.warning(message, docname, type='toc', subtype=subtype,
|
||||||
line=self.lineno))
|
location=toctree)
|
||||||
self.env.note_reread()
|
self.env.note_reread()
|
||||||
else:
|
else:
|
||||||
if docname in all_docnames:
|
if docname in all_docnames:
|
||||||
all_docnames.remove(docname)
|
all_docnames.remove(docname)
|
||||||
else:
|
else:
|
||||||
message = 'duplicated entry found in toctree: %s'
|
logger.warning(__('duplicated entry found in toctree: %s'), docname,
|
||||||
ret.append(self.state.document.reporter.warning(message % docname,
|
location=toctree)
|
||||||
line=self.lineno))
|
|
||||||
|
|
||||||
toctree['entries'].append((title, docname))
|
toctree['entries'].append((title, docname))
|
||||||
toctree['includefiles'].append(docname)
|
toctree['includefiles'].append(docname)
|
||||||
@@ -250,8 +251,9 @@ class Acks(SphinxDirective):
|
|||||||
self.state.nested_parse(self.content, self.content_offset, node)
|
self.state.nested_parse(self.content, self.content_offset, node)
|
||||||
if len(node.children) != 1 or not isinstance(node.children[0],
|
if len(node.children) != 1 or not isinstance(node.children[0],
|
||||||
nodes.bullet_list):
|
nodes.bullet_list):
|
||||||
reporter = self.state.document.reporter
|
logger.warning(__('.. acks content is not a list'),
|
||||||
return [reporter.warning('.. acks content is not a list', line=self.lineno)]
|
location=(self.env.docname, self.lineno))
|
||||||
|
return []
|
||||||
return [node]
|
return [node]
|
||||||
|
|
||||||
|
|
||||||
@@ -274,8 +276,9 @@ class HList(SphinxDirective):
|
|||||||
self.state.nested_parse(self.content, self.content_offset, node)
|
self.state.nested_parse(self.content, self.content_offset, node)
|
||||||
if len(node.children) != 1 or not isinstance(node.children[0],
|
if len(node.children) != 1 or not isinstance(node.children[0],
|
||||||
nodes.bullet_list):
|
nodes.bullet_list):
|
||||||
reporter = self.state.document.reporter
|
logger.warning(__('.. hlist content is not a list'),
|
||||||
return [reporter.warning('.. hlist content is not a list', line=self.lineno)]
|
location=(self.env.docname, self.lineno))
|
||||||
|
return []
|
||||||
fulllist = node.children[0]
|
fulllist = node.children[0]
|
||||||
# create a hlist node where the items are distributed
|
# create a hlist node where the items are distributed
|
||||||
npercol, nmore = divmod(len(fulllist), ncolumns)
|
npercol, nmore = divmod(len(fulllist), ncolumns)
|
||||||
|
|||||||
@@ -2,19 +2,20 @@
|
|||||||
sphinx.directives.patches
|
sphinx.directives.patches
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import warnings
|
import warnings
|
||||||
from os import path
|
from os import path
|
||||||
from typing import TYPE_CHECKING, Any, Dict, List, Tuple, cast
|
from typing import TYPE_CHECKING, Any, Dict, List, Sequence, Tuple, cast
|
||||||
|
|
||||||
from docutils import nodes
|
from docutils import nodes
|
||||||
from docutils.nodes import Node, make_id, system_message
|
from docutils.nodes import Node, make_id, system_message
|
||||||
from docutils.parsers.rst import directives
|
from docutils.parsers.rst import directives
|
||||||
from docutils.parsers.rst.directives import html, images, tables
|
from docutils.parsers.rst.directives import images, tables
|
||||||
|
from docutils.parsers.rst.roles import set_classes
|
||||||
|
|
||||||
from sphinx import addnodes
|
from sphinx import addnodes
|
||||||
from sphinx.deprecation import RemovedInSphinx60Warning
|
from sphinx.deprecation import RemovedInSphinx60Warning
|
||||||
@@ -27,6 +28,12 @@ from sphinx.util.nodes import set_source_info
|
|||||||
from sphinx.util.osutil import SEP, os_path, relpath
|
from sphinx.util.osutil import SEP, os_path, relpath
|
||||||
from sphinx.util.typing import OptionSpec
|
from sphinx.util.typing import OptionSpec
|
||||||
|
|
||||||
|
try:
|
||||||
|
from docutils.parsers.rst.directives.misc import Meta as MetaBase # type: ignore
|
||||||
|
except ImportError:
|
||||||
|
# docutils-0.17 or older
|
||||||
|
from docutils.parsers.rst.directives.html import Meta as MetaBase
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from sphinx.application import Sphinx
|
from sphinx.application import Sphinx
|
||||||
|
|
||||||
@@ -60,19 +67,21 @@ class Figure(images.Figure):
|
|||||||
return [figure_node]
|
return [figure_node]
|
||||||
|
|
||||||
|
|
||||||
class Meta(html.Meta, SphinxDirective):
|
class Meta(MetaBase, SphinxDirective):
|
||||||
def run(self) -> List[Node]:
|
def run(self) -> Sequence[Node]:
|
||||||
result = super().run()
|
result = super().run()
|
||||||
for node in result:
|
for node in result:
|
||||||
|
# for docutils-0.17 or older. Since docutils-0.18, patching is no longer needed
|
||||||
|
# because it uses picklable node; ``docutils.nodes.meta``.
|
||||||
if (isinstance(node, nodes.pending) and
|
if (isinstance(node, nodes.pending) and
|
||||||
isinstance(node.details['nodes'][0], html.MetaBody.meta)):
|
isinstance(node.details['nodes'][0], addnodes.docutils_meta)):
|
||||||
meta = node.details['nodes'][0]
|
meta = node.details['nodes'][0]
|
||||||
meta.source = self.env.doc2path(self.env.docname)
|
meta.source = self.env.doc2path(self.env.docname)
|
||||||
meta.line = self.lineno
|
meta.line = self.lineno
|
||||||
meta.rawcontent = meta['content'] # type: ignore
|
meta.rawcontent = meta['content']
|
||||||
|
|
||||||
# docutils' meta nodes aren't picklable because the class is nested
|
# docutils' meta nodes aren't picklable because the class is nested
|
||||||
meta.__class__ = addnodes.meta # type: ignore
|
meta.__class__ = addnodes.meta
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@@ -152,6 +161,7 @@ class Code(SphinxDirective):
|
|||||||
def run(self) -> List[Node]:
|
def run(self) -> List[Node]:
|
||||||
self.assert_has_content()
|
self.assert_has_content()
|
||||||
|
|
||||||
|
set_classes(self.options)
|
||||||
code = '\n'.join(self.content)
|
code = '\n'.join(self.content)
|
||||||
node = nodes.literal_block(code, code,
|
node = nodes.literal_block(code, code,
|
||||||
classes=self.options.get('classes', []),
|
classes=self.options.get('classes', []),
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
Support for domains, which are groupings of description directives
|
Support for domains, which are groupings of description directives
|
||||||
and roles describing e.g. constructs of one programming language.
|
and roles describing e.g. constructs of one programming language.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
The C language domain.
|
The C language domain.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ from sphinx.util.cfamily import (ASTAttribute, ASTBaseBase, ASTBaseParenExprList
|
|||||||
float_literal_suffix_re, hex_literal_re, identifier_re,
|
float_literal_suffix_re, hex_literal_re, identifier_re,
|
||||||
integer_literal_re, integers_literal_suffix_re,
|
integer_literal_re, integers_literal_suffix_re,
|
||||||
octal_literal_re, verify_description_mode)
|
octal_literal_re, verify_description_mode)
|
||||||
from sphinx.util.docfields import Field, TypedField
|
from sphinx.util.docfields import Field, GroupedField, TypedField
|
||||||
from sphinx.util.docutils import SphinxDirective
|
from sphinx.util.docutils import SphinxDirective
|
||||||
from sphinx.util.nodes import make_refnode
|
from sphinx.util.nodes import make_refnode
|
||||||
from sphinx.util.typing import OptionSpec
|
from sphinx.util.typing import OptionSpec
|
||||||
@@ -92,31 +92,22 @@ _id_prefix = [None, 'c.', 'Cv2.']
|
|||||||
_string_re = re.compile(r"[LuU8]?('([^'\\]*(?:\\.[^'\\]*)*)'"
|
_string_re = re.compile(r"[LuU8]?('([^'\\]*(?:\\.[^'\\]*)*)'"
|
||||||
r'|"([^"\\]*(?:\\.[^"\\]*)*)")', re.S)
|
r'|"([^"\\]*(?:\\.[^"\\]*)*)")', re.S)
|
||||||
|
|
||||||
_simple_type_sepcifiers_re = re.compile(r"""(?x)
|
# bool, complex, and imaginary are macro "keywords", so they are handled seperately
|
||||||
|
_simple_type_specifiers_re = re.compile(r"""(?x)
|
||||||
\b(
|
\b(
|
||||||
void|_Bool|bool
|
void|_Bool
|
||||||
# Integer
|
|
||||||
# -------
|
|
||||||
|((signed|unsigned)\s+)?(char|(
|
|
||||||
((long\s+long|long|short)\s+)?int
|
|
||||||
))
|
|
||||||
|__uint128|__int128
|
|
||||||
# extensions
|
|
||||||
|((signed|unsigned)\s+)?__int(8|16|32|64|128)
|
|
||||||
# Floating-point
|
|
||||||
# --------------
|
|
||||||
|(float|double|long\s+double)(\s+(_Complex|complex|_Imaginary|imaginary))?
|
|
||||||
|(_Complex|complex|_Imaginary|imaginary)\s+(float|double|long\s+double)
|
|
||||||
|_Decimal(32|64|128)
|
|
||||||
# extensions
|
|
||||||
|__float80|_Float64x|__float128|_Float128|__ibm128
|
|
||||||
|__fp16
|
|
||||||
# Fixed-point, extension
|
|
||||||
|(_Sat\s+)?((signed|unsigned)\s+)?((short|long|long\s+long)\s+)?(_Fract|fract|_Accum|accum)
|
|
||||||
# Integer types that could be prefixes of the previous ones
|
|
||||||
# ---------------------------------------------------------
|
|
||||||
|((signed|unsigned)\s+)?(long\s+long|long|short)
|
|
||||||
|signed|unsigned
|
|signed|unsigned
|
||||||
|
|short|long
|
||||||
|
|char
|
||||||
|
|int
|
||||||
|
|__uint128|__int128
|
||||||
|
|__int(8|16|32|64|128) # extension
|
||||||
|
|float|double
|
||||||
|
|_Decimal(32|64|128)
|
||||||
|
|_Complex|_Imaginary
|
||||||
|
|__float80|_Float64x|__float128|_Float128|__ibm128 # extension
|
||||||
|
|__fp16 # extension
|
||||||
|
|_Sat|_Fract|fract|_Accum|accum # extension
|
||||||
)\b
|
)\b
|
||||||
""")
|
""")
|
||||||
|
|
||||||
@@ -226,7 +217,7 @@ class ASTNestedName(ASTBase):
|
|||||||
assert not self.rooted, str(self)
|
assert not self.rooted, str(self)
|
||||||
assert len(self.names) == 1
|
assert len(self.names) == 1
|
||||||
self.names[0].describe_signature(signode, 'noneIsName', env, '', symbol)
|
self.names[0].describe_signature(signode, 'noneIsName', env, '', symbol)
|
||||||
elif mode == 'markType' or mode == 'lastIsName' or mode == 'markName':
|
elif mode in ('markType', 'lastIsName', 'markName'):
|
||||||
# Each element should be a pending xref targeting the complete
|
# Each element should be a pending xref targeting the complete
|
||||||
# prefix.
|
# prefix.
|
||||||
prefix = ''
|
prefix = ''
|
||||||
@@ -636,8 +627,9 @@ class ASTTrailingTypeSpec(ASTBase):
|
|||||||
|
|
||||||
|
|
||||||
class ASTTrailingTypeSpecFundamental(ASTTrailingTypeSpec):
|
class ASTTrailingTypeSpecFundamental(ASTTrailingTypeSpec):
|
||||||
def __init__(self, name: str) -> None:
|
def __init__(self, names: List[str]) -> None:
|
||||||
self.names = name.split()
|
assert len(names) != 0
|
||||||
|
self.names = names
|
||||||
|
|
||||||
def _stringify(self, transform: StringifyTransform) -> str:
|
def _stringify(self, transform: StringifyTransform) -> str:
|
||||||
return ' '.join(self.names)
|
return ' '.join(self.names)
|
||||||
@@ -2580,12 +2572,36 @@ class DefinitionParser(BaseParser):
|
|||||||
break
|
break
|
||||||
return ASTNestedName(names, rooted)
|
return ASTNestedName(names, rooted)
|
||||||
|
|
||||||
|
def _parse_simple_type_specifier(self) -> Optional[str]:
|
||||||
|
if self.match(_simple_type_specifiers_re):
|
||||||
|
return self.matched_text
|
||||||
|
for t in ('bool', 'complex', 'imaginary'):
|
||||||
|
if t in self.config.c_extra_keywords:
|
||||||
|
if self.skip_word(t):
|
||||||
|
return t
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _parse_simple_type_specifiers(self) -> ASTTrailingTypeSpecFundamental:
|
||||||
|
names: List[str] = []
|
||||||
|
|
||||||
|
self.skip_ws()
|
||||||
|
while True:
|
||||||
|
t = self._parse_simple_type_specifier()
|
||||||
|
if t is None:
|
||||||
|
break
|
||||||
|
names.append(t)
|
||||||
|
self.skip_ws()
|
||||||
|
if len(names) == 0:
|
||||||
|
return None
|
||||||
|
return ASTTrailingTypeSpecFundamental(names)
|
||||||
|
|
||||||
def _parse_trailing_type_spec(self) -> ASTTrailingTypeSpec:
|
def _parse_trailing_type_spec(self) -> ASTTrailingTypeSpec:
|
||||||
# fundamental types, https://en.cppreference.com/w/c/language/type
|
# fundamental types, https://en.cppreference.com/w/c/language/type
|
||||||
# and extensions
|
# and extensions
|
||||||
self.skip_ws()
|
self.skip_ws()
|
||||||
if self.match(_simple_type_sepcifiers_re):
|
res = self._parse_simple_type_specifiers()
|
||||||
return ASTTrailingTypeSpecFundamental(self.matched_text)
|
if res is not None:
|
||||||
|
return res
|
||||||
|
|
||||||
# prefixed
|
# prefixed
|
||||||
prefix = None
|
prefix = None
|
||||||
@@ -3130,16 +3146,6 @@ class CObject(ObjectDescription[ASTDeclaration]):
|
|||||||
Description of a C language object.
|
Description of a C language object.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
doc_field_types = [
|
|
||||||
TypedField('parameter', label=_('Parameters'),
|
|
||||||
names=('param', 'parameter', 'arg', 'argument'),
|
|
||||||
typerolename='expr', typenames=('type',)),
|
|
||||||
Field('returnvalue', label=_('Returns'), has_arg=False,
|
|
||||||
names=('returns', 'return')),
|
|
||||||
Field('returntype', label=_('Return type'), has_arg=False,
|
|
||||||
names=('rtype',)),
|
|
||||||
]
|
|
||||||
|
|
||||||
option_spec: OptionSpec = {
|
option_spec: OptionSpec = {
|
||||||
'noindexentry': directives.flag,
|
'noindexentry': directives.flag,
|
||||||
}
|
}
|
||||||
@@ -3342,13 +3348,31 @@ class CMemberObject(CObject):
|
|||||||
return self.objtype
|
return self.objtype
|
||||||
|
|
||||||
|
|
||||||
|
_function_doc_field_types = [
|
||||||
|
TypedField('parameter', label=_('Parameters'),
|
||||||
|
names=('param', 'parameter', 'arg', 'argument'),
|
||||||
|
typerolename='expr', typenames=('type',)),
|
||||||
|
GroupedField('retval', label=_('Return values'),
|
||||||
|
names=('retvals', 'retval'),
|
||||||
|
can_collapse=True),
|
||||||
|
Field('returnvalue', label=_('Returns'), has_arg=False,
|
||||||
|
names=('returns', 'return')),
|
||||||
|
Field('returntype', label=_('Return type'), has_arg=False,
|
||||||
|
names=('rtype',)),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class CFunctionObject(CObject):
|
class CFunctionObject(CObject):
|
||||||
object_type = 'function'
|
object_type = 'function'
|
||||||
|
|
||||||
|
doc_field_types = _function_doc_field_types.copy()
|
||||||
|
|
||||||
|
|
||||||
class CMacroObject(CObject):
|
class CMacroObject(CObject):
|
||||||
object_type = 'macro'
|
object_type = 'macro'
|
||||||
|
|
||||||
|
doc_field_types = _function_doc_field_types.copy()
|
||||||
|
|
||||||
|
|
||||||
class CStructObject(CObject):
|
class CStructObject(CObject):
|
||||||
object_type = 'struct'
|
object_type = 'struct'
|
||||||
@@ -3532,7 +3556,7 @@ class AliasTransform(SphinxTransform):
|
|||||||
return nodes
|
return nodes
|
||||||
|
|
||||||
def apply(self, **kwargs: Any) -> None:
|
def apply(self, **kwargs: Any) -> None:
|
||||||
for node in self.document.traverse(AliasNode):
|
for node in self.document.findall(AliasNode):
|
||||||
node = cast(AliasNode, node)
|
node = cast(AliasNode, node)
|
||||||
sig = node.sig
|
sig = node.sig
|
||||||
parentKey = node.parentKey
|
parentKey = node.parentKey
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
The changeset domain.
|
The changeset domain.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
The citation domain.
|
The citation domain.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -112,7 +112,7 @@ class CitationDefinitionTransform(SphinxTransform):
|
|||||||
|
|
||||||
def apply(self, **kwargs: Any) -> None:
|
def apply(self, **kwargs: Any) -> None:
|
||||||
domain = cast(CitationDomain, self.env.get_domain('citation'))
|
domain = cast(CitationDomain, self.env.get_domain('citation'))
|
||||||
for node in self.document.traverse(nodes.citation):
|
for node in self.document.findall(nodes.citation):
|
||||||
# register citation node to domain
|
# register citation node to domain
|
||||||
node['docname'] = self.env.docname
|
node['docname'] = self.env.docname
|
||||||
domain.note_citation(node)
|
domain.note_citation(node)
|
||||||
@@ -131,7 +131,7 @@ class CitationReferenceTransform(SphinxTransform):
|
|||||||
|
|
||||||
def apply(self, **kwargs: Any) -> None:
|
def apply(self, **kwargs: Any) -> None:
|
||||||
domain = cast(CitationDomain, self.env.get_domain('citation'))
|
domain = cast(CitationDomain, self.env.get_domain('citation'))
|
||||||
for node in self.document.traverse(nodes.citation_reference):
|
for node in self.document.findall(nodes.citation_reference):
|
||||||
target = node.astext()
|
target = node.astext()
|
||||||
ref = pending_xref(target, refdomain='citation', reftype='ref',
|
ref = pending_xref(target, refdomain='citation', reftype='ref',
|
||||||
reftarget=target, refwarn=True,
|
reftarget=target, refwarn=True,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
The C++ language domain.
|
The C++ language domain.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -335,27 +335,17 @@ _keywords = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
_simple_type_sepcifiers_re = re.compile(r"""(?x)
|
_simple_type_specifiers_re = re.compile(r"""(?x)
|
||||||
\b(
|
\b(
|
||||||
auto|void|bool
|
auto|void|bool
|
||||||
# Integer
|
|
||||||
# -------
|
|
||||||
|((signed|unsigned)\s+)?(char|__int128|(
|
|
||||||
((long\s+long|long|short)\s+)?int
|
|
||||||
))
|
|
||||||
|wchar_t|char(8|16|32)_t
|
|
||||||
# extensions
|
|
||||||
|((signed|unsigned)\s+)?__int(64|128)
|
|
||||||
# Floating-point
|
|
||||||
# --------------
|
|
||||||
|(float|double|long\s+double)(\s+(_Complex|_Imaginary))?
|
|
||||||
|(_Complex|_Imaginary)\s+(float|double|long\s+double)
|
|
||||||
# extensions
|
|
||||||
|__float80|_Float64x|__float128|_Float128
|
|
||||||
# Integer types that could be prefixes of the previous ones
|
|
||||||
# ---------------------------------------------------------
|
|
||||||
|((signed|unsigned)\s+)?(long\s+long|long|short)
|
|
||||||
|signed|unsigned
|
|signed|unsigned
|
||||||
|
|short|long
|
||||||
|
|char|wchar_t|char(8|16|32)_t
|
||||||
|
|int
|
||||||
|
|__int(64|128) # extension
|
||||||
|
|float|double
|
||||||
|
|__float80|_Float64x|__float128|_Float128 # extension
|
||||||
|
|_Complex|_Imaginary # extension
|
||||||
)\b
|
)\b
|
||||||
""")
|
""")
|
||||||
|
|
||||||
@@ -485,12 +475,12 @@ _id_fundamental_v2 = {
|
|||||||
'long double': 'e',
|
'long double': 'e',
|
||||||
'__float80': 'e', '_Float64x': 'e',
|
'__float80': 'e', '_Float64x': 'e',
|
||||||
'__float128': 'g', '_Float128': 'g',
|
'__float128': 'g', '_Float128': 'g',
|
||||||
'float _Complex': 'Cf', '_Complex float': 'Cf',
|
'_Complex float': 'Cf',
|
||||||
'double _Complex': 'Cd', '_Complex double': 'Cd',
|
'_Complex double': 'Cd',
|
||||||
'long double _Complex': 'Ce', '_Complex long double': 'Ce',
|
'_Complex long double': 'Ce',
|
||||||
'float _Imaginary': 'f', '_Imaginary float': 'f',
|
'_Imaginary float': 'f',
|
||||||
'double _Imaginary': 'd', '_Imaginary double': 'd',
|
'_Imaginary double': 'd',
|
||||||
'long double _Imaginary': 'e', '_Imaginary long double': 'e',
|
'_Imaginary long double': 'e',
|
||||||
'auto': 'Da',
|
'auto': 'Da',
|
||||||
'decltype(auto)': 'Dc',
|
'decltype(auto)': 'Dc',
|
||||||
'std::nullptr_t': 'Dn'
|
'std::nullptr_t': 'Dn'
|
||||||
@@ -786,7 +776,7 @@ class ASTNestedName(ASTBase):
|
|||||||
assert len(self.names) == 1
|
assert len(self.names) == 1
|
||||||
assert not self.templates[0]
|
assert not self.templates[0]
|
||||||
self.names[0].describe_signature(signode, 'param', env, '', symbol)
|
self.names[0].describe_signature(signode, 'param', env, '', symbol)
|
||||||
elif mode == 'markType' or mode == 'lastIsName' or mode == 'markName':
|
elif mode in ('markType', 'lastIsName', 'markName'):
|
||||||
# Each element should be a pending xref targeting the complete
|
# Each element should be a pending xref targeting the complete
|
||||||
# prefix. however, only the identifier part should be a link, such
|
# prefix. however, only the identifier part should be a link, such
|
||||||
# that template args can be a link as well.
|
# that template args can be a link as well.
|
||||||
@@ -1853,8 +1843,12 @@ class ASTTrailingTypeSpec(ASTBase):
|
|||||||
|
|
||||||
|
|
||||||
class ASTTrailingTypeSpecFundamental(ASTTrailingTypeSpec):
|
class ASTTrailingTypeSpecFundamental(ASTTrailingTypeSpec):
|
||||||
def __init__(self, name: str) -> None:
|
def __init__(self, names: List[str], canonNames: List[str]) -> None:
|
||||||
self.names = name.split()
|
assert len(names) != 0
|
||||||
|
assert len(names) == len(canonNames), (names, canonNames)
|
||||||
|
self.names = names
|
||||||
|
# the canonical name list is for ID lookup
|
||||||
|
self.canonNames = canonNames
|
||||||
|
|
||||||
def _stringify(self, transform: StringifyTransform) -> str:
|
def _stringify(self, transform: StringifyTransform) -> str:
|
||||||
return ' '.join(self.names)
|
return ' '.join(self.names)
|
||||||
@@ -1862,14 +1856,14 @@ class ASTTrailingTypeSpecFundamental(ASTTrailingTypeSpec):
|
|||||||
def get_id(self, version: int) -> str:
|
def get_id(self, version: int) -> str:
|
||||||
if version == 1:
|
if version == 1:
|
||||||
res = []
|
res = []
|
||||||
for a in self.names:
|
for a in self.canonNames:
|
||||||
if a in _id_fundamental_v1:
|
if a in _id_fundamental_v1:
|
||||||
res.append(_id_fundamental_v1[a])
|
res.append(_id_fundamental_v1[a])
|
||||||
else:
|
else:
|
||||||
res.append(a)
|
res.append(a)
|
||||||
return '-'.join(res)
|
return '-'.join(res)
|
||||||
|
|
||||||
txt = str(self)
|
txt = ' '.join(self.canonNames)
|
||||||
if txt not in _id_fundamental_v2:
|
if txt not in _id_fundamental_v2:
|
||||||
raise Exception(
|
raise Exception(
|
||||||
'Semi-internal error: Fundamental type "%s" can not be mapped '
|
'Semi-internal error: Fundamental type "%s" can not be mapped '
|
||||||
@@ -5391,7 +5385,7 @@ class DefinitionParser(BaseParser):
|
|||||||
postFixes: List[ASTPostfixOp] = []
|
postFixes: List[ASTPostfixOp] = []
|
||||||
while True:
|
while True:
|
||||||
self.skip_ws()
|
self.skip_ws()
|
||||||
if prefixType in ['expr', 'cast', 'typeid']:
|
if prefixType in ('expr', 'cast', 'typeid'):
|
||||||
if self.skip_string_and_ws('['):
|
if self.skip_string_and_ws('['):
|
||||||
expr = self._parse_expression()
|
expr = self._parse_expression()
|
||||||
self.skip_ws()
|
self.skip_ws()
|
||||||
@@ -5855,12 +5849,102 @@ class DefinitionParser(BaseParser):
|
|||||||
|
|
||||||
# ==========================================================================
|
# ==========================================================================
|
||||||
|
|
||||||
|
def _parse_simple_type_specifiers(self) -> ASTTrailingTypeSpecFundamental:
|
||||||
|
modifier: Optional[str] = None
|
||||||
|
signedness: Optional[str] = None
|
||||||
|
width: List[str] = []
|
||||||
|
typ: Optional[str] = None
|
||||||
|
names: List[str] = [] # the parsed sequence
|
||||||
|
|
||||||
|
self.skip_ws()
|
||||||
|
while self.match(_simple_type_specifiers_re):
|
||||||
|
t = self.matched_text
|
||||||
|
names.append(t)
|
||||||
|
if t in ('auto', 'void', 'bool',
|
||||||
|
'char', 'wchar_t', 'char8_t', 'char16_t', 'char32_t',
|
||||||
|
'int', '__int64', '__int128',
|
||||||
|
'float', 'double',
|
||||||
|
'__float80', '_Float64x', '__float128', '_Float128'):
|
||||||
|
if typ is not None:
|
||||||
|
self.fail("Can not have both {} and {}.".format(t, typ))
|
||||||
|
typ = t
|
||||||
|
elif t in ('signed', 'unsigned'):
|
||||||
|
if signedness is not None:
|
||||||
|
self.fail("Can not have both {} and {}.".format(t, signedness))
|
||||||
|
signedness = t
|
||||||
|
elif t == 'short':
|
||||||
|
if len(width) != 0:
|
||||||
|
self.fail("Can not have both {} and {}.".format(t, width[0]))
|
||||||
|
width.append(t)
|
||||||
|
elif t == 'long':
|
||||||
|
if len(width) != 0 and width[0] != 'long':
|
||||||
|
self.fail("Can not have both {} and {}.".format(t, width[0]))
|
||||||
|
width.append(t)
|
||||||
|
elif t in ('_Imaginary', '_Complex'):
|
||||||
|
if modifier is not None:
|
||||||
|
self.fail("Can not have both {} and {}.".format(t, modifier))
|
||||||
|
modifier = t
|
||||||
|
self.skip_ws()
|
||||||
|
if len(names) == 0:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if typ in ('auto', 'void', 'bool',
|
||||||
|
'wchar_t', 'char8_t', 'char16_t', 'char32_t',
|
||||||
|
'__float80', '_Float64x', '__float128', '_Float128'):
|
||||||
|
if modifier is not None:
|
||||||
|
self.fail("Can not have both {} and {}.".format(typ, modifier))
|
||||||
|
if signedness is not None:
|
||||||
|
self.fail("Can not have both {} and {}.".format(typ, signedness))
|
||||||
|
if len(width) != 0:
|
||||||
|
self.fail("Can not have both {} and {}.".format(typ, ' '.join(width)))
|
||||||
|
elif typ == 'char':
|
||||||
|
if modifier is not None:
|
||||||
|
self.fail("Can not have both {} and {}.".format(typ, modifier))
|
||||||
|
if len(width) != 0:
|
||||||
|
self.fail("Can not have both {} and {}.".format(typ, ' '.join(width)))
|
||||||
|
elif typ == 'int':
|
||||||
|
if modifier is not None:
|
||||||
|
self.fail("Can not have both {} and {}.".format(typ, modifier))
|
||||||
|
elif typ in ('__int64', '__int128'):
|
||||||
|
if modifier is not None:
|
||||||
|
self.fail("Can not have both {} and {}.".format(typ, modifier))
|
||||||
|
if len(width) != 0:
|
||||||
|
self.fail("Can not have both {} and {}.".format(typ, ' '.join(width)))
|
||||||
|
elif typ == 'float':
|
||||||
|
if signedness is not None:
|
||||||
|
self.fail("Can not have both {} and {}.".format(typ, signedness))
|
||||||
|
if len(width) != 0:
|
||||||
|
self.fail("Can not have both {} and {}.".format(typ, ' '.join(width)))
|
||||||
|
elif typ == 'double':
|
||||||
|
if signedness is not None:
|
||||||
|
self.fail("Can not have both {} and {}.".format(typ, signedness))
|
||||||
|
if len(width) > 1:
|
||||||
|
self.fail("Can not have both {} and {}.".format(typ, ' '.join(width)))
|
||||||
|
if len(width) == 1 and width[0] != 'long':
|
||||||
|
self.fail("Can not have both {} and {}.".format(typ, ' '.join(width)))
|
||||||
|
elif typ is None:
|
||||||
|
if modifier is not None:
|
||||||
|
self.fail("Can not have {} without a floating point type.".format(modifier))
|
||||||
|
else:
|
||||||
|
assert False, "Unhandled type {}".format(typ)
|
||||||
|
|
||||||
|
canonNames: List[str] = []
|
||||||
|
if modifier is not None:
|
||||||
|
canonNames.append(modifier)
|
||||||
|
if signedness is not None:
|
||||||
|
canonNames.append(signedness)
|
||||||
|
canonNames.extend(width)
|
||||||
|
if typ is not None:
|
||||||
|
canonNames.append(typ)
|
||||||
|
return ASTTrailingTypeSpecFundamental(names, canonNames)
|
||||||
|
|
||||||
def _parse_trailing_type_spec(self) -> ASTTrailingTypeSpec:
|
def _parse_trailing_type_spec(self) -> ASTTrailingTypeSpec:
|
||||||
# fundamental types, https://en.cppreference.com/w/cpp/language/type
|
# fundamental types, https://en.cppreference.com/w/cpp/language/type
|
||||||
# and extensions
|
# and extensions
|
||||||
self.skip_ws()
|
self.skip_ws()
|
||||||
if self.match(_simple_type_sepcifiers_re):
|
res = self._parse_simple_type_specifiers()
|
||||||
return ASTTrailingTypeSpecFundamental(self.matched_text)
|
if res is not None:
|
||||||
|
return res
|
||||||
|
|
||||||
# decltype
|
# decltype
|
||||||
self.skip_ws()
|
self.skip_ws()
|
||||||
@@ -6541,7 +6625,7 @@ class DefinitionParser(BaseParser):
|
|||||||
|
|
||||||
# ==========================================================================
|
# ==========================================================================
|
||||||
|
|
||||||
def _parse_template_paramter(self) -> ASTTemplateParam:
|
def _parse_template_parameter(self) -> ASTTemplateParam:
|
||||||
self.skip_ws()
|
self.skip_ws()
|
||||||
if self.skip_word('template'):
|
if self.skip_word('template'):
|
||||||
# declare a tenplate template parameter
|
# declare a tenplate template parameter
|
||||||
@@ -6613,7 +6697,7 @@ class DefinitionParser(BaseParser):
|
|||||||
pos = self.pos
|
pos = self.pos
|
||||||
err = None
|
err = None
|
||||||
try:
|
try:
|
||||||
param = self._parse_template_paramter()
|
param = self._parse_template_parameter()
|
||||||
templateParams.append(param)
|
templateParams.append(param)
|
||||||
except DefinitionError as eParam:
|
except DefinitionError as eParam:
|
||||||
self.pos = pos
|
self.pos = pos
|
||||||
@@ -6934,18 +7018,10 @@ def _make_phony_error_name() -> ASTNestedName:
|
|||||||
class CPPObject(ObjectDescription[ASTDeclaration]):
|
class CPPObject(ObjectDescription[ASTDeclaration]):
|
||||||
"""Description of a C++ language object."""
|
"""Description of a C++ language object."""
|
||||||
|
|
||||||
doc_field_types = [
|
doc_field_types: List[Field] = [
|
||||||
GroupedField('parameter', label=_('Parameters'),
|
|
||||||
names=('param', 'parameter', 'arg', 'argument'),
|
|
||||||
can_collapse=True),
|
|
||||||
GroupedField('template parameter', label=_('Template Parameters'),
|
GroupedField('template parameter', label=_('Template Parameters'),
|
||||||
names=('tparam', 'template parameter'),
|
names=('tparam', 'template parameter'),
|
||||||
can_collapse=True),
|
can_collapse=True),
|
||||||
GroupedField('exceptions', label=_('Throws'), rolename='expr',
|
|
||||||
names=('throws', 'throw', 'exception'),
|
|
||||||
can_collapse=True),
|
|
||||||
Field('returnvalue', label=_('Returns'), has_arg=False,
|
|
||||||
names=('returns', 'return')),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
option_spec: OptionSpec = {
|
option_spec: OptionSpec = {
|
||||||
@@ -7181,6 +7257,20 @@ class CPPMemberObject(CPPObject):
|
|||||||
class CPPFunctionObject(CPPObject):
|
class CPPFunctionObject(CPPObject):
|
||||||
object_type = 'function'
|
object_type = 'function'
|
||||||
|
|
||||||
|
doc_field_types = CPPObject.doc_field_types + [
|
||||||
|
GroupedField('parameter', label=_('Parameters'),
|
||||||
|
names=('param', 'parameter', 'arg', 'argument'),
|
||||||
|
can_collapse=True),
|
||||||
|
GroupedField('exceptions', label=_('Throws'), rolename='expr',
|
||||||
|
names=('throws', 'throw', 'exception'),
|
||||||
|
can_collapse=True),
|
||||||
|
GroupedField('retval', label=_('Return values'),
|
||||||
|
names=('retvals', 'retval'),
|
||||||
|
can_collapse=True),
|
||||||
|
Field('returnvalue', label=_('Returns'), has_arg=False,
|
||||||
|
names=('returns', 'return')),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class CPPClassObject(CPPObject):
|
class CPPClassObject(CPPObject):
|
||||||
object_type = 'class'
|
object_type = 'class'
|
||||||
@@ -7369,7 +7459,7 @@ class AliasTransform(SphinxTransform):
|
|||||||
return nodes
|
return nodes
|
||||||
|
|
||||||
def apply(self, **kwargs: Any) -> None:
|
def apply(self, **kwargs: Any) -> None:
|
||||||
for node in self.document.traverse(AliasNode):
|
for node in self.document.findall(AliasNode):
|
||||||
node = cast(AliasNode, node)
|
node = cast(AliasNode, node)
|
||||||
sig = node.sig
|
sig = node.sig
|
||||||
parentKey = node.parentKey
|
parentKey = node.parentKey
|
||||||
@@ -7691,7 +7781,7 @@ class CPPDomain(Domain):
|
|||||||
typ: str, target: str, node: pending_xref,
|
typ: str, target: str, node: pending_xref,
|
||||||
contnode: Element) -> Tuple[Optional[Element], Optional[str]]:
|
contnode: Element) -> Tuple[Optional[Element], Optional[str]]:
|
||||||
# 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 in ('any', 'func'):
|
||||||
target += '()'
|
target += '()'
|
||||||
parser = DefinitionParser(target, location=node, config=env.config)
|
parser = DefinitionParser(target, location=node, config=env.config)
|
||||||
try:
|
try:
|
||||||
@@ -7812,7 +7902,7 @@ class CPPDomain(Domain):
|
|||||||
if (env.config.add_function_parentheses and typ == 'func' and
|
if (env.config.add_function_parentheses and typ == 'func' and
|
||||||
title.endswith('operator()')):
|
title.endswith('operator()')):
|
||||||
addParen += 1
|
addParen += 1
|
||||||
if ((typ == 'any' or typ == 'func') and
|
if (typ in ('any', 'func') and
|
||||||
title.endswith('operator') and
|
title.endswith('operator') and
|
||||||
displayName.endswith('operator()')):
|
displayName.endswith('operator()')):
|
||||||
addParen += 1
|
addParen += 1
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
The index domain.
|
The index domain.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ class IndexDomain(Domain):
|
|||||||
def process_doc(self, env: BuildEnvironment, docname: str, document: Node) -> None:
|
def process_doc(self, env: BuildEnvironment, docname: str, document: Node) -> None:
|
||||||
"""Process a document after it is read by the environment."""
|
"""Process a document after it is read by the environment."""
|
||||||
entries = self.entries.setdefault(env.docname, [])
|
entries = self.entries.setdefault(env.docname, [])
|
||||||
for node in document.traverse(addnodes.index):
|
for node in list(document.findall(addnodes.index)):
|
||||||
try:
|
try:
|
||||||
for entry in node['entries']:
|
for entry in node['entries']:
|
||||||
split_index_msg(entry[0], entry[1])
|
split_index_msg(entry[0], entry[1])
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
The JavaScript domain.
|
The JavaScript domain.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -41,9 +41,6 @@ class JSObject(ObjectDescription[Tuple[str, str]]):
|
|||||||
#: added
|
#: added
|
||||||
has_arguments = False
|
has_arguments = False
|
||||||
|
|
||||||
#: what is displayed right before the documentation entry
|
|
||||||
display_prefix: str = None
|
|
||||||
|
|
||||||
#: If ``allow_nesting`` is ``True``, the object prefixes will be accumulated
|
#: If ``allow_nesting`` is ``True``, the object prefixes will be accumulated
|
||||||
#: based on directive nesting
|
#: based on directive nesting
|
||||||
allow_nesting = False
|
allow_nesting = False
|
||||||
@@ -53,6 +50,10 @@ class JSObject(ObjectDescription[Tuple[str, str]]):
|
|||||||
'noindexentry': directives.flag,
|
'noindexentry': directives.flag,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def get_display_prefix(self) -> List[Node]:
|
||||||
|
#: what is displayed right before the documentation entry
|
||||||
|
return []
|
||||||
|
|
||||||
def handle_signature(self, sig: str, signode: desc_signature) -> Tuple[str, str]:
|
def handle_signature(self, sig: str, signode: desc_signature) -> Tuple[str, str]:
|
||||||
"""Breaks down construct signatures
|
"""Breaks down construct signatures
|
||||||
|
|
||||||
@@ -71,6 +72,7 @@ class JSObject(ObjectDescription[Tuple[str, str]]):
|
|||||||
# If construct is nested, prefix the current prefix
|
# If construct is nested, prefix the current prefix
|
||||||
prefix = self.env.ref_context.get('js:object', None)
|
prefix = self.env.ref_context.get('js:object', None)
|
||||||
mod_name = self.env.ref_context.get('js:module')
|
mod_name = self.env.ref_context.get('js:module')
|
||||||
|
|
||||||
name = member
|
name = member
|
||||||
try:
|
try:
|
||||||
member_prefix, member_name = member.rsplit('.', 1)
|
member_prefix, member_name = member.rsplit('.', 1)
|
||||||
@@ -91,14 +93,22 @@ class JSObject(ObjectDescription[Tuple[str, str]]):
|
|||||||
signode['object'] = prefix
|
signode['object'] = prefix
|
||||||
signode['fullname'] = fullname
|
signode['fullname'] = fullname
|
||||||
|
|
||||||
if self.display_prefix:
|
display_prefix = self.get_display_prefix()
|
||||||
signode += addnodes.desc_annotation(self.display_prefix,
|
if display_prefix:
|
||||||
self.display_prefix)
|
signode += addnodes.desc_annotation('', '', *display_prefix)
|
||||||
|
|
||||||
|
actual_prefix = None
|
||||||
if prefix:
|
if prefix:
|
||||||
signode += addnodes.desc_addname(prefix + '.', prefix + '.')
|
actual_prefix = prefix
|
||||||
elif mod_name:
|
elif mod_name:
|
||||||
signode += addnodes.desc_addname(mod_name + '.', mod_name + '.')
|
actual_prefix = mod_name
|
||||||
signode += addnodes.desc_name(name, name)
|
if actual_prefix:
|
||||||
|
addName = addnodes.desc_addname('', '')
|
||||||
|
for p in actual_prefix.split('.'):
|
||||||
|
addName += addnodes.desc_sig_name(p, p)
|
||||||
|
addName += addnodes.desc_sig_punctuation('.', '.')
|
||||||
|
signode += addName
|
||||||
|
signode += addnodes.desc_name('', '', addnodes.desc_sig_name(name, name))
|
||||||
if self.has_arguments:
|
if self.has_arguments:
|
||||||
if not arglist:
|
if not arglist:
|
||||||
signode += addnodes.desc_parameterlist()
|
signode += addnodes.desc_parameterlist()
|
||||||
@@ -227,9 +237,13 @@ class JSCallable(JSObject):
|
|||||||
|
|
||||||
class JSConstructor(JSCallable):
|
class JSConstructor(JSCallable):
|
||||||
"""Like a callable but with a different prefix."""
|
"""Like a callable but with a different prefix."""
|
||||||
display_prefix = 'class '
|
|
||||||
allow_nesting = True
|
allow_nesting = True
|
||||||
|
|
||||||
|
def get_display_prefix(self) -> List[Node]:
|
||||||
|
return [addnodes.desc_sig_keyword('class', 'class'),
|
||||||
|
addnodes.desc_sig_space()]
|
||||||
|
|
||||||
|
|
||||||
class JSModule(SphinxDirective):
|
class JSModule(SphinxDirective):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
The math domain.
|
The math domain.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -78,7 +78,7 @@ class MathDomain(Domain):
|
|||||||
def math_node(node: Node) -> bool:
|
def math_node(node: Node) -> bool:
|
||||||
return isinstance(node, (nodes.math, nodes.math_block))
|
return isinstance(node, (nodes.math, nodes.math_block))
|
||||||
|
|
||||||
self.data['has_equations'][docname] = any(document.traverse(math_node))
|
self.data['has_equations'][docname] = any(document.findall(math_node))
|
||||||
|
|
||||||
def clear_doc(self, docname: str) -> None:
|
def clear_doc(self, docname: str) -> None:
|
||||||
for equation_id, (doc, eqno) in list(self.equations.items()):
|
for equation_id, (doc, eqno) in list(self.equations.items()):
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
The Python domain.
|
The Python domain.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ from sphinx import addnodes
|
|||||||
from sphinx.addnodes import desc_signature, pending_xref, pending_xref_condition
|
from sphinx.addnodes import desc_signature, pending_xref, pending_xref_condition
|
||||||
from sphinx.application import Sphinx
|
from sphinx.application import Sphinx
|
||||||
from sphinx.builders import Builder
|
from sphinx.builders import Builder
|
||||||
from sphinx.deprecation import RemovedInSphinx50Warning
|
from sphinx.deprecation import RemovedInSphinx50Warning, RemovedInSphinx60Warning
|
||||||
from sphinx.directives import ObjectDescription
|
from sphinx.directives import ObjectDescription
|
||||||
from sphinx.domains import Domain, Index, IndexEntry, ObjType
|
from sphinx.domains import Domain, Index, IndexEntry, ObjType
|
||||||
from sphinx.environment import BuildEnvironment
|
from sphinx.environment import BuildEnvironment
|
||||||
@@ -80,31 +80,57 @@ class ModuleEntry(NamedTuple):
|
|||||||
deprecated: bool
|
deprecated: bool
|
||||||
|
|
||||||
|
|
||||||
def type_to_xref(text: str, env: BuildEnvironment = None) -> addnodes.pending_xref:
|
def parse_reftarget(reftarget: str, suppress_prefix: bool = False
|
||||||
"""Convert a type string to a cross reference node."""
|
) -> Tuple[str, str, str, bool]:
|
||||||
if text == 'None':
|
"""Parse a type string and return (reftype, reftarget, title, refspecific flag)"""
|
||||||
|
refspecific = False
|
||||||
|
if reftarget.startswith('.'):
|
||||||
|
reftarget = reftarget[1:]
|
||||||
|
title = reftarget
|
||||||
|
refspecific = True
|
||||||
|
elif reftarget.startswith('~'):
|
||||||
|
reftarget = reftarget[1:]
|
||||||
|
title = reftarget.split('.')[-1]
|
||||||
|
elif suppress_prefix:
|
||||||
|
title = reftarget.split('.')[-1]
|
||||||
|
elif reftarget.startswith('typing.'):
|
||||||
|
title = reftarget[7:]
|
||||||
|
else:
|
||||||
|
title = reftarget
|
||||||
|
|
||||||
|
if reftarget == 'None' or reftarget.startswith('typing.'):
|
||||||
|
# typing module provides non-class types. Obj reference is good to refer them.
|
||||||
reftype = 'obj'
|
reftype = 'obj'
|
||||||
else:
|
else:
|
||||||
reftype = 'class'
|
reftype = 'class'
|
||||||
|
|
||||||
|
return reftype, reftarget, title, refspecific
|
||||||
|
|
||||||
|
|
||||||
|
def type_to_xref(target: str, env: BuildEnvironment = None, suppress_prefix: bool = False
|
||||||
|
) -> addnodes.pending_xref:
|
||||||
|
"""Convert a type string to a cross reference node."""
|
||||||
if env:
|
if env:
|
||||||
kwargs = {'py:module': env.ref_context.get('py:module'),
|
kwargs = {'py:module': env.ref_context.get('py:module'),
|
||||||
'py:class': env.ref_context.get('py:class')}
|
'py:class': env.ref_context.get('py:class')}
|
||||||
else:
|
else:
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
|
|
||||||
|
reftype, target, title, refspecific = parse_reftarget(target, suppress_prefix)
|
||||||
|
|
||||||
if env.config.python_use_unqualified_type_names:
|
if env.config.python_use_unqualified_type_names:
|
||||||
# Note: It would be better to use qualname to describe the object to support support
|
# Note: It would be better to use qualname to describe the object to support support
|
||||||
# nested classes. But python domain can't access the real python object because this
|
# nested classes. But python domain can't access the real python object because this
|
||||||
# module should work not-dynamically.
|
# module should work not-dynamically.
|
||||||
shortname = text.split('.')[-1]
|
shortname = title.split('.')[-1]
|
||||||
contnodes: List[Node] = [pending_xref_condition('', shortname, condition='resolved'),
|
contnodes: List[Node] = [pending_xref_condition('', shortname, condition='resolved'),
|
||||||
pending_xref_condition('', text, condition='*')]
|
pending_xref_condition('', title, condition='*')]
|
||||||
else:
|
else:
|
||||||
contnodes = [nodes.Text(text)]
|
contnodes = [nodes.Text(title)]
|
||||||
|
|
||||||
return pending_xref('', *contnodes,
|
return pending_xref('', *contnodes,
|
||||||
refdomain='py', reftype=reftype, reftarget=text, **kwargs)
|
refdomain='py', reftype=reftype, reftarget=target,
|
||||||
|
refspecific=refspecific, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def _parse_annotation(annotation: str, env: BuildEnvironment = None) -> List[Node]:
|
def _parse_annotation(annotation: str, env: BuildEnvironment = None) -> List[Node]:
|
||||||
@@ -118,16 +144,28 @@ def _parse_annotation(annotation: str, env: BuildEnvironment = None) -> List[Nod
|
|||||||
result.extend(unparse(node.right))
|
result.extend(unparse(node.right))
|
||||||
return result
|
return result
|
||||||
elif isinstance(node, ast.BitOr):
|
elif isinstance(node, ast.BitOr):
|
||||||
return [nodes.Text(' '), addnodes.desc_sig_punctuation('', '|'), nodes.Text(' ')]
|
return [addnodes.desc_sig_space(),
|
||||||
|
addnodes.desc_sig_punctuation('', '|'),
|
||||||
|
addnodes.desc_sig_space()]
|
||||||
elif isinstance(node, ast.Constant): # type: ignore
|
elif isinstance(node, ast.Constant): # type: ignore
|
||||||
if node.value is Ellipsis:
|
if node.value is Ellipsis:
|
||||||
return [addnodes.desc_sig_punctuation('', "...")]
|
return [addnodes.desc_sig_punctuation('', "...")]
|
||||||
|
elif isinstance(node.value, bool):
|
||||||
|
return [addnodes.desc_sig_keyword('', repr(node.value))]
|
||||||
|
elif isinstance(node.value, int):
|
||||||
|
return [addnodes.desc_sig_literal_number('', repr(node.value))]
|
||||||
|
elif isinstance(node.value, str):
|
||||||
|
return [addnodes.desc_sig_literal_string('', repr(node.value))]
|
||||||
else:
|
else:
|
||||||
|
# handles None, which is further handled by type_to_xref later
|
||||||
|
# and fallback for other types that should be converted
|
||||||
return [nodes.Text(repr(node.value))]
|
return [nodes.Text(repr(node.value))]
|
||||||
elif isinstance(node, ast.Expr):
|
elif isinstance(node, ast.Expr):
|
||||||
return unparse(node.value)
|
return unparse(node.value)
|
||||||
elif isinstance(node, ast.Index):
|
elif isinstance(node, ast.Index):
|
||||||
return unparse(node.value)
|
return unparse(node.value)
|
||||||
|
elif isinstance(node, ast.Invert):
|
||||||
|
return [addnodes.desc_sig_punctuation('', '~')]
|
||||||
elif isinstance(node, ast.List):
|
elif isinstance(node, ast.List):
|
||||||
result = [addnodes.desc_sig_punctuation('', '[')]
|
result = [addnodes.desc_sig_punctuation('', '[')]
|
||||||
if node.elts:
|
if node.elts:
|
||||||
@@ -136,7 +174,9 @@ def _parse_annotation(annotation: str, env: BuildEnvironment = None) -> List[Nod
|
|||||||
# once
|
# once
|
||||||
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.append(addnodes.desc_sig_space())
|
||||||
|
result.pop()
|
||||||
result.pop()
|
result.pop()
|
||||||
result.append(addnodes.desc_sig_punctuation('', ']'))
|
result.append(addnodes.desc_sig_punctuation('', ']'))
|
||||||
return result
|
return result
|
||||||
@@ -156,12 +196,16 @@ def _parse_annotation(annotation: str, env: BuildEnvironment = None) -> List[Nod
|
|||||||
if isinstance(subnode, nodes.Text):
|
if isinstance(subnode, nodes.Text):
|
||||||
result[i] = nodes.literal('', '', subnode)
|
result[i] = nodes.literal('', '', subnode)
|
||||||
return result
|
return result
|
||||||
|
elif isinstance(node, ast.UnaryOp):
|
||||||
|
return unparse(node.op) + unparse(node.operand)
|
||||||
elif isinstance(node, ast.Tuple):
|
elif isinstance(node, ast.Tuple):
|
||||||
if node.elts:
|
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.append(addnodes.desc_sig_space())
|
||||||
|
result.pop()
|
||||||
result.pop()
|
result.pop()
|
||||||
else:
|
else:
|
||||||
result = [addnodes.desc_sig_punctuation('', '('),
|
result = [addnodes.desc_sig_punctuation('', '('),
|
||||||
@@ -170,10 +214,16 @@ def _parse_annotation(annotation: str, env: BuildEnvironment = None) -> List[Nod
|
|||||||
return result
|
return result
|
||||||
else:
|
else:
|
||||||
if sys.version_info < (3, 8):
|
if sys.version_info < (3, 8):
|
||||||
if isinstance(node, ast.Ellipsis):
|
if isinstance(node, ast.Bytes):
|
||||||
|
return [addnodes.desc_sig_literal_string('', repr(node.s))]
|
||||||
|
elif isinstance(node, ast.Ellipsis):
|
||||||
return [addnodes.desc_sig_punctuation('', "...")]
|
return [addnodes.desc_sig_punctuation('', "...")]
|
||||||
elif isinstance(node, ast.NameConstant):
|
elif isinstance(node, ast.NameConstant):
|
||||||
return [nodes.Text(node.value)]
|
return [nodes.Text(node.value)]
|
||||||
|
elif isinstance(node, ast.Num):
|
||||||
|
return [addnodes.desc_sig_literal_string('', repr(node.n))]
|
||||||
|
elif isinstance(node, ast.Str):
|
||||||
|
return [addnodes.desc_sig_literal_string('', repr(node.s))]
|
||||||
|
|
||||||
raise SyntaxError # unsupported syntax
|
raise SyntaxError # unsupported syntax
|
||||||
|
|
||||||
@@ -183,12 +233,19 @@ def _parse_annotation(annotation: str, env: BuildEnvironment = None) -> List[Nod
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
tree = ast_parse(annotation)
|
tree = ast_parse(annotation)
|
||||||
result = unparse(tree)
|
result: List[Node] = []
|
||||||
for i, node in enumerate(result):
|
for node in unparse(tree):
|
||||||
if isinstance(node, nodes.literal):
|
if isinstance(node, nodes.literal):
|
||||||
result[i] = node[0]
|
result.append(node[0])
|
||||||
elif isinstance(node, nodes.Text) and node.strip():
|
elif isinstance(node, nodes.Text) and node.strip():
|
||||||
result[i] = type_to_xref(str(node), env)
|
if (result and isinstance(result[-1], addnodes.desc_sig_punctuation) and
|
||||||
|
result[-1].astext() == '~'):
|
||||||
|
result.pop()
|
||||||
|
result.append(type_to_xref(str(node), env, suppress_prefix=True))
|
||||||
|
else:
|
||||||
|
result.append(type_to_xref(str(node), env))
|
||||||
|
else:
|
||||||
|
result.append(node)
|
||||||
return result
|
return result
|
||||||
except SyntaxError:
|
except SyntaxError:
|
||||||
return [type_to_xref(annotation, env)]
|
return [type_to_xref(annotation, env)]
|
||||||
@@ -222,13 +279,13 @@ def _parse_arglist(arglist: str, env: BuildEnvironment = None) -> addnodes.desc_
|
|||||||
if param.annotation is not param.empty:
|
if param.annotation is not param.empty:
|
||||||
children = _parse_annotation(param.annotation, env)
|
children = _parse_annotation(param.annotation, env)
|
||||||
node += addnodes.desc_sig_punctuation('', ':')
|
node += addnodes.desc_sig_punctuation('', ':')
|
||||||
node += nodes.Text(' ')
|
node += addnodes.desc_sig_space()
|
||||||
node += addnodes.desc_sig_name('', '', *children) # type: ignore
|
node += addnodes.desc_sig_name('', '', *children) # type: ignore
|
||||||
if param.default is not param.empty:
|
if param.default is not param.empty:
|
||||||
if param.annotation is not param.empty:
|
if param.annotation is not param.empty:
|
||||||
node += nodes.Text(' ')
|
node += addnodes.desc_sig_space()
|
||||||
node += addnodes.desc_sig_operator('', '=')
|
node += addnodes.desc_sig_operator('', '=')
|
||||||
node += nodes.Text(' ')
|
node += addnodes.desc_sig_space()
|
||||||
else:
|
else:
|
||||||
node += addnodes.desc_sig_operator('', '=')
|
node += addnodes.desc_sig_operator('', '=')
|
||||||
node += nodes.inline('', param.default, classes=['default_value'],
|
node += nodes.inline('', param.default, classes=['default_value'],
|
||||||
@@ -271,7 +328,8 @@ def _pseudo_parse_arglist(signode: desc_signature, arglist: str) -> None:
|
|||||||
ends_open += 1
|
ends_open += 1
|
||||||
argument = argument[:-1].strip()
|
argument = argument[:-1].strip()
|
||||||
if argument:
|
if argument:
|
||||||
stack[-1] += addnodes.desc_parameter(argument, argument)
|
stack[-1] += addnodes.desc_parameter(
|
||||||
|
'', '', addnodes.desc_sig_name(argument, argument))
|
||||||
while ends_open:
|
while ends_open:
|
||||||
stack.append(addnodes.desc_optional())
|
stack.append(addnodes.desc_optional())
|
||||||
stack[-2] += stack[-1]
|
stack[-2] += stack[-1]
|
||||||
@@ -304,27 +362,27 @@ class PyXrefMixin:
|
|||||||
result = super().make_xref(rolename, domain, target, # type: ignore
|
result = super().make_xref(rolename, domain, target, # type: ignore
|
||||||
innernode, contnode,
|
innernode, contnode,
|
||||||
env, inliner=None, location=None)
|
env, inliner=None, location=None)
|
||||||
result['refspecific'] = True
|
if isinstance(result, pending_xref):
|
||||||
result['py:module'] = env.ref_context.get('py:module')
|
result['refspecific'] = True
|
||||||
result['py:class'] = env.ref_context.get('py:class')
|
result['py:module'] = env.ref_context.get('py:module')
|
||||||
if target.startswith(('.', '~')):
|
result['py:class'] = env.ref_context.get('py:class')
|
||||||
prefix, result['reftarget'] = target[0], target[1:]
|
|
||||||
if prefix == '.':
|
|
||||||
text = target[1:]
|
|
||||||
elif prefix == '~':
|
|
||||||
text = target.split('.')[-1]
|
|
||||||
for node in result.traverse(nodes.Text):
|
|
||||||
node.parent[node.parent.index(node)] = nodes.Text(text)
|
|
||||||
break
|
|
||||||
elif isinstance(result, pending_xref) and env.config.python_use_unqualified_type_names:
|
|
||||||
children = result.children
|
|
||||||
result.clear()
|
|
||||||
|
|
||||||
shortname = target.split('.')[-1]
|
reftype, reftarget, reftitle, _ = parse_reftarget(target)
|
||||||
textnode = innernode('', shortname)
|
if reftarget != reftitle:
|
||||||
contnodes = [pending_xref_condition('', '', textnode, condition='resolved'),
|
result['reftype'] = reftype
|
||||||
pending_xref_condition('', '', *children, condition='*')]
|
result['reftarget'] = reftarget
|
||||||
result.extend(contnodes)
|
|
||||||
|
result.clear()
|
||||||
|
result += innernode(reftitle, reftitle)
|
||||||
|
elif env.config.python_use_unqualified_type_names:
|
||||||
|
children = result.children
|
||||||
|
result.clear()
|
||||||
|
|
||||||
|
shortname = target.split('.')[-1]
|
||||||
|
textnode = innernode('', shortname)
|
||||||
|
contnodes = [pending_xref_condition('', '', textnode, condition='resolved'),
|
||||||
|
pending_xref_condition('', '', *children, condition='*')]
|
||||||
|
result.extend(contnodes)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@@ -338,31 +396,26 @@ class PyXrefMixin:
|
|||||||
|
|
||||||
split_contnode = bool(contnode and contnode.astext() == target)
|
split_contnode = bool(contnode and contnode.astext() == target)
|
||||||
|
|
||||||
|
in_literal = False
|
||||||
results = []
|
results = []
|
||||||
for sub_target in filter(None, sub_targets):
|
for sub_target in filter(None, sub_targets):
|
||||||
if split_contnode:
|
if split_contnode:
|
||||||
contnode = nodes.Text(sub_target)
|
contnode = nodes.Text(sub_target)
|
||||||
|
|
||||||
if delims_re.match(sub_target):
|
if in_literal or delims_re.match(sub_target):
|
||||||
results.append(contnode or innernode(sub_target, sub_target))
|
results.append(contnode or innernode(sub_target, sub_target))
|
||||||
else:
|
else:
|
||||||
results.append(self.make_xref(rolename, domain, sub_target,
|
results.append(self.make_xref(rolename, domain, sub_target,
|
||||||
innernode, contnode, env, inliner, location))
|
innernode, contnode, env, inliner, location))
|
||||||
|
|
||||||
|
if sub_target in ('Literal', 'typing.Literal'):
|
||||||
|
in_literal = True
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
class PyField(PyXrefMixin, Field):
|
class PyField(PyXrefMixin, Field):
|
||||||
def make_xref(self, rolename: str, domain: str, target: str,
|
pass
|
||||||
innernode: Type[TextlikeNode] = nodes.emphasis,
|
|
||||||
contnode: Node = None, env: BuildEnvironment = None,
|
|
||||||
inliner: Inliner = None, location: Node = None) -> Node:
|
|
||||||
if rolename == 'class' and target == 'None':
|
|
||||||
# None is not a type, so use obj role instead.
|
|
||||||
rolename = 'obj'
|
|
||||||
|
|
||||||
return super().make_xref(rolename, domain, target, innernode, contnode,
|
|
||||||
env, inliner, location)
|
|
||||||
|
|
||||||
|
|
||||||
class PyGroupedField(PyXrefMixin, GroupedField):
|
class PyGroupedField(PyXrefMixin, GroupedField):
|
||||||
@@ -370,16 +423,7 @@ class PyGroupedField(PyXrefMixin, GroupedField):
|
|||||||
|
|
||||||
|
|
||||||
class PyTypedField(PyXrefMixin, TypedField):
|
class PyTypedField(PyXrefMixin, TypedField):
|
||||||
def make_xref(self, rolename: str, domain: str, target: str,
|
pass
|
||||||
innernode: Type[TextlikeNode] = nodes.emphasis,
|
|
||||||
contnode: Node = None, env: BuildEnvironment = None,
|
|
||||||
inliner: Inliner = None, location: Node = None) -> Node:
|
|
||||||
if rolename == 'class' and target == 'None':
|
|
||||||
# None is not a type, so use obj role instead.
|
|
||||||
rolename = 'obj'
|
|
||||||
|
|
||||||
return super().make_xref(rolename, domain, target, innernode, contnode,
|
|
||||||
env, inliner, location)
|
|
||||||
|
|
||||||
|
|
||||||
class PyObject(ObjectDescription[Tuple[str, str]]):
|
class PyObject(ObjectDescription[Tuple[str, str]]):
|
||||||
@@ -418,11 +462,11 @@ class PyObject(ObjectDescription[Tuple[str, str]]):
|
|||||||
|
|
||||||
allow_nesting = False
|
allow_nesting = False
|
||||||
|
|
||||||
def get_signature_prefix(self, sig: str) -> str:
|
def get_signature_prefix(self, sig: str) -> List[nodes.Node]:
|
||||||
"""May return a prefix to put before the object name in the
|
"""May return a prefix to put before the object name in the
|
||||||
signature.
|
signature.
|
||||||
"""
|
"""
|
||||||
return ''
|
return []
|
||||||
|
|
||||||
def needs_arglist(self) -> bool:
|
def needs_arglist(self) -> bool:
|
||||||
"""May return true if an empty argument list is to be generated even if
|
"""May return true if an empty argument list is to be generated even if
|
||||||
@@ -476,7 +520,17 @@ class PyObject(ObjectDescription[Tuple[str, str]]):
|
|||||||
|
|
||||||
sig_prefix = self.get_signature_prefix(sig)
|
sig_prefix = self.get_signature_prefix(sig)
|
||||||
if sig_prefix:
|
if sig_prefix:
|
||||||
signode += addnodes.desc_annotation(sig_prefix, sig_prefix)
|
if type(sig_prefix) is str:
|
||||||
|
warnings.warn(
|
||||||
|
"Python directive method get_signature_prefix()"
|
||||||
|
" returning a string is deprecated."
|
||||||
|
" It must now return a list of nodes."
|
||||||
|
" Return value was '{}'.".format(sig_prefix),
|
||||||
|
RemovedInSphinx60Warning)
|
||||||
|
signode += addnodes.desc_annotation(sig_prefix, '', # type: ignore
|
||||||
|
nodes.Text(sig_prefix)) # type: ignore
|
||||||
|
else:
|
||||||
|
signode += addnodes.desc_annotation(str(sig_prefix), '', *sig_prefix)
|
||||||
|
|
||||||
if prefix:
|
if prefix:
|
||||||
signode += addnodes.desc_addname(prefix, prefix)
|
signode += addnodes.desc_addname(prefix, prefix)
|
||||||
@@ -507,7 +561,9 @@ class PyObject(ObjectDescription[Tuple[str, str]]):
|
|||||||
|
|
||||||
anno = self.options.get('annotation')
|
anno = self.options.get('annotation')
|
||||||
if anno:
|
if anno:
|
||||||
signode += addnodes.desc_annotation(' ' + anno, ' ' + anno)
|
signode += addnodes.desc_annotation(' ' + anno, '',
|
||||||
|
addnodes.desc_sig_space(),
|
||||||
|
nodes.Text(anno))
|
||||||
|
|
||||||
return fullname, prefix
|
return fullname, prefix
|
||||||
|
|
||||||
@@ -609,11 +665,12 @@ class PyFunction(PyObject):
|
|||||||
'async': directives.flag,
|
'async': directives.flag,
|
||||||
})
|
})
|
||||||
|
|
||||||
def get_signature_prefix(self, sig: str) -> str:
|
def get_signature_prefix(self, sig: str) -> List[nodes.Node]:
|
||||||
if 'async' in self.options:
|
if 'async' in self.options:
|
||||||
return 'async '
|
return [addnodes.desc_sig_keyword('', 'async'),
|
||||||
|
addnodes.desc_sig_space()]
|
||||||
else:
|
else:
|
||||||
return ''
|
return []
|
||||||
|
|
||||||
def needs_arglist(self) -> bool:
|
def needs_arglist(self) -> bool:
|
||||||
return True
|
return True
|
||||||
@@ -670,11 +727,17 @@ class PyVariable(PyObject):
|
|||||||
typ = self.options.get('type')
|
typ = self.options.get('type')
|
||||||
if typ:
|
if typ:
|
||||||
annotations = _parse_annotation(typ, self.env)
|
annotations = _parse_annotation(typ, self.env)
|
||||||
signode += addnodes.desc_annotation(typ, '', nodes.Text(': '), *annotations)
|
signode += addnodes.desc_annotation(typ, '',
|
||||||
|
addnodes.desc_sig_punctuation('', ':'),
|
||||||
|
addnodes.desc_sig_space(), *annotations)
|
||||||
|
|
||||||
value = self.options.get('value')
|
value = self.options.get('value')
|
||||||
if value:
|
if value:
|
||||||
signode += addnodes.desc_annotation(value, ' = ' + value)
|
signode += addnodes.desc_annotation(value, '',
|
||||||
|
addnodes.desc_sig_space(),
|
||||||
|
addnodes.desc_sig_punctuation('', '='),
|
||||||
|
addnodes.desc_sig_space(),
|
||||||
|
nodes.Text(value))
|
||||||
|
|
||||||
return fullname, prefix
|
return fullname, prefix
|
||||||
|
|
||||||
@@ -698,11 +761,12 @@ class PyClasslike(PyObject):
|
|||||||
|
|
||||||
allow_nesting = True
|
allow_nesting = True
|
||||||
|
|
||||||
def get_signature_prefix(self, sig: str) -> str:
|
def get_signature_prefix(self, sig: str) -> List[nodes.Node]:
|
||||||
if 'final' in self.options:
|
if 'final' in self.options:
|
||||||
return 'final %s ' % self.objtype
|
return [nodes.Text('final'), addnodes.desc_sig_space(),
|
||||||
|
nodes.Text(self.objtype), addnodes.desc_sig_space()]
|
||||||
else:
|
else:
|
||||||
return '%s ' % self.objtype
|
return [nodes.Text(self.objtype), addnodes.desc_sig_space()]
|
||||||
|
|
||||||
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':
|
||||||
@@ -734,25 +798,27 @@ class PyMethod(PyObject):
|
|||||||
else:
|
else:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def get_signature_prefix(self, sig: str) -> str:
|
def get_signature_prefix(self, sig: str) -> List[nodes.Node]:
|
||||||
prefix = []
|
prefix: List[nodes.Node] = []
|
||||||
if 'final' in self.options:
|
if 'final' in self.options:
|
||||||
prefix.append('final')
|
prefix.append(nodes.Text('final'))
|
||||||
|
prefix.append(addnodes.desc_sig_space())
|
||||||
if 'abstractmethod' in self.options:
|
if 'abstractmethod' in self.options:
|
||||||
prefix.append('abstract')
|
prefix.append(nodes.Text('abstract'))
|
||||||
|
prefix.append(addnodes.desc_sig_space())
|
||||||
if 'async' in self.options:
|
if 'async' in self.options:
|
||||||
prefix.append('async')
|
prefix.append(nodes.Text('async'))
|
||||||
|
prefix.append(addnodes.desc_sig_space())
|
||||||
if 'classmethod' in self.options:
|
if 'classmethod' in self.options:
|
||||||
prefix.append('classmethod')
|
prefix.append(nodes.Text('classmethod'))
|
||||||
|
prefix.append(addnodes.desc_sig_space())
|
||||||
if 'property' in self.options:
|
if 'property' in self.options:
|
||||||
prefix.append('property')
|
prefix.append(nodes.Text('property'))
|
||||||
|
prefix.append(addnodes.desc_sig_space())
|
||||||
if 'staticmethod' in self.options:
|
if 'staticmethod' in self.options:
|
||||||
prefix.append('static')
|
prefix.append(nodes.Text('static'))
|
||||||
|
prefix.append(addnodes.desc_sig_space())
|
||||||
if prefix:
|
return prefix
|
||||||
return ' '.join(prefix) + ' '
|
|
||||||
else:
|
|
||||||
return ''
|
|
||||||
|
|
||||||
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:
|
||||||
name, cls = name_cls
|
name, cls = name_cls
|
||||||
@@ -769,7 +835,7 @@ class PyMethod(PyObject):
|
|||||||
if 'classmethod' in self.options:
|
if 'classmethod' in self.options:
|
||||||
return _('%s() (%s class method)') % (methname, clsname)
|
return _('%s() (%s class method)') % (methname, clsname)
|
||||||
elif 'property' in self.options:
|
elif 'property' in self.options:
|
||||||
return _('%s() (%s property)') % (methname, clsname)
|
return _('%s (%s property)') % (methname, clsname)
|
||||||
elif 'staticmethod' in self.options:
|
elif 'staticmethod' in self.options:
|
||||||
return _('%s() (%s static method)') % (methname, clsname)
|
return _('%s() (%s static method)') % (methname, clsname)
|
||||||
else:
|
else:
|
||||||
@@ -831,11 +897,18 @@ class PyAttribute(PyObject):
|
|||||||
typ = self.options.get('type')
|
typ = self.options.get('type')
|
||||||
if typ:
|
if typ:
|
||||||
annotations = _parse_annotation(typ, self.env)
|
annotations = _parse_annotation(typ, self.env)
|
||||||
signode += addnodes.desc_annotation(typ, '', nodes.Text(': '), *annotations)
|
signode += addnodes.desc_annotation(typ, '',
|
||||||
|
addnodes.desc_sig_punctuation('', ':'),
|
||||||
|
addnodes.desc_sig_space(),
|
||||||
|
*annotations)
|
||||||
|
|
||||||
value = self.options.get('value')
|
value = self.options.get('value')
|
||||||
if value:
|
if value:
|
||||||
signode += addnodes.desc_annotation(value, ' = ' + value)
|
signode += addnodes.desc_annotation(value, '',
|
||||||
|
addnodes.desc_sig_space(),
|
||||||
|
addnodes.desc_sig_punctuation('', '='),
|
||||||
|
addnodes.desc_sig_space(),
|
||||||
|
nodes.Text(value))
|
||||||
|
|
||||||
return fullname, prefix
|
return fullname, prefix
|
||||||
|
|
||||||
@@ -870,19 +943,25 @@ class PyProperty(PyObject):
|
|||||||
typ = self.options.get('type')
|
typ = self.options.get('type')
|
||||||
if typ:
|
if typ:
|
||||||
annotations = _parse_annotation(typ, self.env)
|
annotations = _parse_annotation(typ, self.env)
|
||||||
signode += addnodes.desc_annotation(typ, '', nodes.Text(': '), *annotations)
|
signode += addnodes.desc_annotation(typ, '',
|
||||||
|
addnodes.desc_sig_punctuation('', ':'),
|
||||||
|
addnodes.desc_sig_space(),
|
||||||
|
*annotations)
|
||||||
|
|
||||||
return fullname, prefix
|
return fullname, prefix
|
||||||
|
|
||||||
def get_signature_prefix(self, sig: str) -> str:
|
def get_signature_prefix(self, sig: str) -> List[nodes.Node]:
|
||||||
prefix = []
|
prefix: List[nodes.Node] = []
|
||||||
if 'abstractmethod' in self.options:
|
if 'abstractmethod' in self.options:
|
||||||
prefix.append('abstract')
|
prefix.append(nodes.Text('abstract'))
|
||||||
|
prefix.append(addnodes.desc_sig_space())
|
||||||
if 'classmethod' in self.options:
|
if 'classmethod' in self.options:
|
||||||
prefix.append('class')
|
prefix.append(nodes.Text('class'))
|
||||||
|
prefix.append(addnodes.desc_sig_space())
|
||||||
|
|
||||||
prefix.append('property')
|
prefix.append(nodes.Text('property'))
|
||||||
return ' '.join(prefix) + ' '
|
prefix.append(addnodes.desc_sig_space())
|
||||||
|
return prefix
|
||||||
|
|
||||||
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:
|
||||||
name, cls = name_cls
|
name, cls = name_cls
|
||||||
@@ -1401,7 +1480,7 @@ def builtin_resolver(app: Sphinx, env: BuildEnvironment,
|
|||||||
return None
|
return None
|
||||||
elif node.get('reftype') in ('class', 'obj') and node.get('reftarget') == 'None':
|
elif node.get('reftype') in ('class', 'obj') and node.get('reftarget') == 'None':
|
||||||
return contnode
|
return contnode
|
||||||
elif node.get('reftype') in ('class', 'exc'):
|
elif node.get('reftype') in ('class', 'obj', 'exc'):
|
||||||
reftarget = node.get('reftarget')
|
reftarget = node.get('reftarget')
|
||||||
if inspect.isclass(getattr(builtins, reftarget, None)):
|
if inspect.isclass(getattr(builtins, reftarget, None)):
|
||||||
# built-in class
|
# built-in class
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
The reStructuredText domain.
|
The reStructuredText domain.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,11 @@
|
|||||||
|
|
||||||
The standard domain.
|
The standard domain.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import unicodedata
|
|
||||||
import warnings
|
import warnings
|
||||||
from copy import copy
|
from copy import copy
|
||||||
from typing import (TYPE_CHECKING, Any, Callable, Dict, Iterable, Iterator, List, Optional,
|
from typing import (TYPE_CHECKING, Any, Callable, Dict, Iterable, Iterator, List, Optional,
|
||||||
@@ -336,6 +335,7 @@ class Glossary(SphinxDirective):
|
|||||||
def run(self) -> List[Node]:
|
def run(self) -> List[Node]:
|
||||||
node = addnodes.glossary()
|
node = addnodes.glossary()
|
||||||
node.document = self.state.document
|
node.document = self.state.document
|
||||||
|
node['sorted'] = ('sorted' in self.options)
|
||||||
|
|
||||||
# This directive implements a custom format of the reST definition list
|
# This directive implements a custom format of the reST definition list
|
||||||
# that allows multiple lines of terms before the definition. This is
|
# that allows multiple lines of terms before the definition. This is
|
||||||
@@ -400,9 +400,8 @@ class Glossary(SphinxDirective):
|
|||||||
was_empty = False
|
was_empty = False
|
||||||
|
|
||||||
# now, parse all the entries into a big definition list
|
# now, parse all the entries into a big definition list
|
||||||
items = []
|
items: List[nodes.definition_list_item] = []
|
||||||
for terms, definition in entries:
|
for terms, definition in entries:
|
||||||
termtexts: List[str] = []
|
|
||||||
termnodes: List[Node] = []
|
termnodes: List[Node] = []
|
||||||
system_messages: List[Node] = []
|
system_messages: List[Node] = []
|
||||||
for line, source, lineno in terms:
|
for line, source, lineno in terms:
|
||||||
@@ -416,7 +415,6 @@ class Glossary(SphinxDirective):
|
|||||||
node_id=None, document=self.state.document)
|
node_id=None, document=self.state.document)
|
||||||
term.rawsource = line
|
term.rawsource = line
|
||||||
system_messages.extend(sysmsg)
|
system_messages.extend(sysmsg)
|
||||||
termtexts.append(term.astext())
|
|
||||||
termnodes.append(term)
|
termnodes.append(term)
|
||||||
|
|
||||||
termnodes.extend(system_messages)
|
termnodes.extend(system_messages)
|
||||||
@@ -426,16 +424,10 @@ class Glossary(SphinxDirective):
|
|||||||
self.state.nested_parse(definition, definition.items[0][1],
|
self.state.nested_parse(definition, definition.items[0][1],
|
||||||
defnode)
|
defnode)
|
||||||
termnodes.append(defnode)
|
termnodes.append(defnode)
|
||||||
items.append((termtexts,
|
items.append(nodes.definition_list_item('', *termnodes))
|
||||||
nodes.definition_list_item('', *termnodes)))
|
|
||||||
|
|
||||||
if 'sorted' in self.options:
|
dlist = nodes.definition_list('', *items)
|
||||||
items.sort(key=lambda x:
|
|
||||||
unicodedata.normalize('NFD', x[0][0].lower()))
|
|
||||||
|
|
||||||
dlist = nodes.definition_list()
|
|
||||||
dlist['classes'].append('glossary')
|
dlist['classes'].append('glossary')
|
||||||
dlist.extend(item[1] for item in items)
|
|
||||||
node += dlist
|
node += dlist
|
||||||
return messages + [node]
|
return messages + [node]
|
||||||
|
|
||||||
@@ -770,18 +762,20 @@ class StandardDomain(Domain):
|
|||||||
sectname = clean_astext(title)
|
sectname = clean_astext(title)
|
||||||
elif node.tagname == 'rubric':
|
elif node.tagname == 'rubric':
|
||||||
sectname = clean_astext(node)
|
sectname = clean_astext(node)
|
||||||
|
elif node.tagname == 'target' and len(node) > 0:
|
||||||
|
# inline target (ex: blah _`blah` blah)
|
||||||
|
sectname = clean_astext(node)
|
||||||
elif self.is_enumerable_node(node):
|
elif self.is_enumerable_node(node):
|
||||||
sectname = self.get_numfig_title(node)
|
sectname = self.get_numfig_title(node)
|
||||||
if not sectname:
|
|
||||||
continue
|
|
||||||
else:
|
else:
|
||||||
toctree = next(iter(node.traverse(addnodes.toctree)), None)
|
toctree = next(node.findall(addnodes.toctree), None)
|
||||||
if toctree and toctree.get('caption'):
|
if toctree and toctree.get('caption'):
|
||||||
sectname = toctree.get('caption')
|
sectname = toctree.get('caption')
|
||||||
else:
|
else:
|
||||||
# anonymous-only labels
|
# anonymous-only labels
|
||||||
continue
|
continue
|
||||||
self.labels[name] = docname, labelid, sectname
|
if sectname:
|
||||||
|
self.labels[name] = docname, labelid, sectname
|
||||||
|
|
||||||
def add_program_option(self, program: str, name: str, docname: str, labelid: str) -> None:
|
def add_program_option(self, program: str, name: str, docname: str, labelid: str) -> None:
|
||||||
self.progoptions[program, name] = (docname, labelid)
|
self.progoptions[program, name] = (docname, labelid)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
Global creation environment.
|
Global creation environment.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -45,15 +45,18 @@ if TYPE_CHECKING:
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
default_settings: Dict[str, Any] = {
|
default_settings: Dict[str, Any] = {
|
||||||
|
'auto_id_prefix': 'id',
|
||||||
|
'embed_images': False,
|
||||||
'embed_stylesheet': False,
|
'embed_stylesheet': False,
|
||||||
'cloak_email_addresses': True,
|
'cloak_email_addresses': True,
|
||||||
'pep_base_url': 'https://www.python.org/dev/peps/',
|
'pep_base_url': 'https://www.python.org/dev/peps/',
|
||||||
'pep_references': None,
|
'pep_references': None,
|
||||||
'rfc_base_url': 'https://tools.ietf.org/html/',
|
'rfc_base_url': 'https://datatracker.ietf.org/doc/html/',
|
||||||
'rfc_references': None,
|
'rfc_references': None,
|
||||||
'input_encoding': 'utf-8-sig',
|
'input_encoding': 'utf-8-sig',
|
||||||
'doctitle_xform': False,
|
'doctitle_xform': False,
|
||||||
'sectsubtitle_xform': False,
|
'sectsubtitle_xform': False,
|
||||||
|
'section_self_link': False,
|
||||||
'halt_level': 5,
|
'halt_level': 5,
|
||||||
'file_insertion_enabled': True,
|
'file_insertion_enabled': True,
|
||||||
'smartquotes_locales': [],
|
'smartquotes_locales': [],
|
||||||
@@ -532,7 +535,7 @@ class BuildEnvironment:
|
|||||||
self.apply_post_transforms(doctree, docname)
|
self.apply_post_transforms(doctree, docname)
|
||||||
|
|
||||||
# now, resolve all toctree nodes
|
# now, resolve all toctree nodes
|
||||||
for toctreenode in doctree.traverse(addnodes.toctree):
|
for toctreenode in doctree.findall(addnodes.toctree):
|
||||||
result = TocTree(self).resolve(docname, builder, toctreenode,
|
result = TocTree(self).resolve(docname, builder, toctreenode,
|
||||||
prune=prune_toctrees,
|
prune=prune_toctrees,
|
||||||
includehidden=includehidden)
|
includehidden=includehidden)
|
||||||
@@ -618,7 +621,7 @@ class BuildEnvironment:
|
|||||||
|
|
||||||
def check_consistency(self) -> None:
|
def check_consistency(self) -> None:
|
||||||
"""Do consistency checks."""
|
"""Do consistency checks."""
|
||||||
included = set().union(*self.included.values()) # type: ignore
|
included = set().union(*self.included.values())
|
||||||
for docname in sorted(self.all_docs):
|
for docname in sorted(self.all_docs):
|
||||||
if docname not in self.files_to_rebuild:
|
if docname not in self.files_to_rebuild:
|
||||||
if docname == self.config.root_doc:
|
if docname == self.config.root_doc:
|
||||||
|
|||||||
@@ -4,6 +4,6 @@
|
|||||||
|
|
||||||
Sphinx environment adapters
|
Sphinx environment adapters
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
Assets adapter for sphinx.environment.
|
Assets adapter for sphinx.environment.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
Index entries adapters for sphinx.environment.
|
Index entries adapters for sphinx.environment.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
Toctree adapter for sphinx.environment.
|
Toctree adapter for sphinx.environment.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -161,7 +161,7 @@ class TocTree:
|
|||||||
process_only_nodes(toc, builder.tags)
|
process_only_nodes(toc, builder.tags)
|
||||||
if title and toc.children and len(toc.children) == 1:
|
if title and toc.children and len(toc.children) == 1:
|
||||||
child = toc.children[0]
|
child = toc.children[0]
|
||||||
for refnode in child.traverse(nodes.reference):
|
for refnode in child.findall(nodes.reference):
|
||||||
if refnode['refuri'] == ref and \
|
if refnode['refuri'] == ref and \
|
||||||
not refnode['anchorname']:
|
not refnode['anchorname']:
|
||||||
refnode.children = [nodes.Text(title)]
|
refnode.children = [nodes.Text(title)]
|
||||||
@@ -193,13 +193,13 @@ class TocTree:
|
|||||||
for toplevel in children:
|
for toplevel in children:
|
||||||
# nodes with length 1 don't have any children anyway
|
# nodes with length 1 don't have any children anyway
|
||||||
if len(toplevel) > 1:
|
if len(toplevel) > 1:
|
||||||
subtrees = toplevel.traverse(addnodes.toctree)
|
subtrees = list(toplevel.findall(addnodes.toctree))
|
||||||
if subtrees:
|
if subtrees:
|
||||||
toplevel[1][:] = subtrees # type: ignore
|
toplevel[1][:] = subtrees # type: ignore
|
||||||
else:
|
else:
|
||||||
toplevel.pop(1)
|
toplevel.pop(1)
|
||||||
# resolve all sub-toctrees
|
# resolve all sub-toctrees
|
||||||
for subtocnode in toc.traverse(addnodes.toctree):
|
for subtocnode in list(toc.findall(addnodes.toctree)):
|
||||||
if not (subtocnode.get('hidden', False) and
|
if not (subtocnode.get('hidden', False) and
|
||||||
not includehidden):
|
not includehidden):
|
||||||
i = subtocnode.parent.index(subtocnode) + 1
|
i = subtocnode.parent.index(subtocnode) + 1
|
||||||
@@ -257,7 +257,7 @@ class TocTree:
|
|||||||
|
|
||||||
# set the target paths in the toctrees (they are not known at TOC
|
# set the target paths in the toctrees (they are not known at TOC
|
||||||
# generation time)
|
# generation time)
|
||||||
for refnode in newnode.traverse(nodes.reference):
|
for refnode in newnode.findall(nodes.reference):
|
||||||
if not url_re.match(refnode['refuri']):
|
if not url_re.match(refnode['refuri']):
|
||||||
refnode['refuri'] = builder.get_relative_uri(
|
refnode['refuri'] = builder.get_relative_uri(
|
||||||
docname, refnode['refuri']) + refnode['anchorname']
|
docname, refnode['refuri']) + refnode['anchorname']
|
||||||
@@ -308,7 +308,7 @@ class TocTree:
|
|||||||
# renders to nothing
|
# renders to nothing
|
||||||
return nodes.paragraph()
|
return nodes.paragraph()
|
||||||
process_only_nodes(toc, builder.tags)
|
process_only_nodes(toc, builder.tags)
|
||||||
for node in toc.traverse(nodes.reference):
|
for node in toc.findall(nodes.reference):
|
||||||
node['refuri'] = node['anchorname'] or '#'
|
node['refuri'] = node['anchorname'] or '#'
|
||||||
return toc
|
return toc
|
||||||
|
|
||||||
@@ -324,7 +324,7 @@ class TocTree:
|
|||||||
else:
|
else:
|
||||||
kwargs['maxdepth'] = int(kwargs['maxdepth'])
|
kwargs['maxdepth'] = int(kwargs['maxdepth'])
|
||||||
kwargs['collapse'] = collapse
|
kwargs['collapse'] = collapse
|
||||||
for toctreenode in doctree.traverse(addnodes.toctree):
|
for toctreenode in doctree.findall(addnodes.toctree):
|
||||||
toctree = self.resolve(docname, builder, toctreenode, prune=True, **kwargs)
|
toctree = self.resolve(docname, builder, toctreenode, prune=True, **kwargs)
|
||||||
if toctree:
|
if toctree:
|
||||||
toctrees.append(toctree)
|
toctrees.append(toctree)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
The data collector components for sphinx.environment.
|
The data collector components for sphinx.environment.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
The image collector for sphinx.environment.
|
The image collector for sphinx.environment.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ class ImageCollector(EnvironmentCollector):
|
|||||||
"""Process and rewrite image URIs."""
|
"""Process and rewrite image URIs."""
|
||||||
docname = app.env.docname
|
docname = app.env.docname
|
||||||
|
|
||||||
for node in doctree.traverse(nodes.image):
|
for node in doctree.findall(nodes.image):
|
||||||
# Map the mimetype to the corresponding image. The writer may
|
# Map the mimetype to the corresponding image. The writer may
|
||||||
# choose the best image from these candidates. The special key * is
|
# choose the best image from these candidates. The special key * is
|
||||||
# set if there is only single candidate to be used by a writer.
|
# set if there is only single candidate to be used by a writer.
|
||||||
@@ -124,7 +124,7 @@ class DownloadFileCollector(EnvironmentCollector):
|
|||||||
|
|
||||||
def process_doc(self, app: Sphinx, doctree: nodes.document) -> None:
|
def process_doc(self, app: Sphinx, doctree: nodes.document) -> None:
|
||||||
"""Process downloadable file paths. """
|
"""Process downloadable file paths. """
|
||||||
for node in doctree.traverse(addnodes.download_reference):
|
for node in doctree.findall(addnodes.download_reference):
|
||||||
targetname = node['reftarget']
|
targetname = node['reftarget']
|
||||||
if '://' in targetname:
|
if '://' in targetname:
|
||||||
node['refuri'] = targetname
|
node['refuri'] = targetname
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
The dependencies collector components for sphinx.environment.
|
The dependencies collector components for sphinx.environment.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
The metadata collector components for sphinx.environment.
|
The metadata collector components for sphinx.environment.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -33,9 +33,12 @@ class MetadataCollector(EnvironmentCollector):
|
|||||||
|
|
||||||
Keep processing minimal -- just return what docutils says.
|
Keep processing minimal -- just return what docutils says.
|
||||||
"""
|
"""
|
||||||
if len(doctree) > 0 and isinstance(doctree[0], nodes.docinfo):
|
index = doctree.first_child_not_matching_class(nodes.PreBibliographic)
|
||||||
|
if index is None:
|
||||||
|
return
|
||||||
|
elif isinstance(doctree[index], nodes.docinfo):
|
||||||
md = app.env.metadata[app.env.docname]
|
md = app.env.metadata[app.env.docname]
|
||||||
for node in doctree[0]:
|
for node in doctree[index]: # type: ignore
|
||||||
# nodes are multiply inherited...
|
# nodes are multiply inherited...
|
||||||
if isinstance(node, nodes.authors):
|
if isinstance(node, nodes.authors):
|
||||||
authors = cast(List[nodes.author], node)
|
authors = cast(List[nodes.author], node)
|
||||||
@@ -58,7 +61,7 @@ class MetadataCollector(EnvironmentCollector):
|
|||||||
value = 0
|
value = 0
|
||||||
md[name] = value
|
md[name] = value
|
||||||
|
|
||||||
doctree.pop(0)
|
doctree.pop(index)
|
||||||
|
|
||||||
|
|
||||||
def setup(app: Sphinx) -> Dict[str, Any]:
|
def setup(app: Sphinx) -> Dict[str, Any]:
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
The title collector components for sphinx.environment.
|
The title collector components for sphinx.environment.
|
||||||
|
|
||||||
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ class TitleCollector(EnvironmentCollector):
|
|||||||
longtitlenode = nodes.title()
|
longtitlenode = nodes.title()
|
||||||
longtitlenode += nodes.Text(doctree['title'])
|
longtitlenode += nodes.Text(doctree['title'])
|
||||||
# look for first section title and use that as the title
|
# look for first section title and use that as the title
|
||||||
for node in doctree.traverse(nodes.section):
|
for node in doctree.findall(nodes.section):
|
||||||
visitor = SphinxContentsFilter(doctree)
|
visitor = SphinxContentsFilter(doctree)
|
||||||
node[0].walkabout(visitor)
|
node[0].walkabout(visitor)
|
||||||
titlenode += visitor.get_entry_text()
|
titlenode += visitor.get_entry_text()
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user