Merge branch '5.x' into autodoc_force_undocumented_rtype

This commit is contained in:
Takeshi KOMIYA 2022-04-03 22:29:22 +09:00
commit f731bf1999
567 changed files with 58006 additions and 54131 deletions

View File

@ -7,6 +7,7 @@ on:
jobs: jobs:
test: test:
if: github.repository_owner == 'sphinx-doc'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:

View File

@ -10,6 +10,7 @@ permissions:
jobs: jobs:
action: action:
if: github.repository_owner == 'sphinx-doc'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: dessant/lock-threads@v2 - uses: dessant/lock-threads@v2

View File

@ -60,7 +60,7 @@ jobs:
if: matrix.coverage if: matrix.coverage
windows: windows:
runs-on: windows-latest runs-on: windows-2019
strategy: strategy:
matrix: matrix:
architecture: [x86, x64] architecture: [x86, x64]

View File

@ -6,14 +6,15 @@ jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
env: env:
node-version: 10.7 node-version: 16
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Use Node.js ${{ env.node-version }} - name: Use Node.js ${{ env.node-version }}
uses: actions/setup-node@v1 uses: actions/setup-node@v2
with: with:
node-version: ${{ env.node-version }} node-version: ${{ env.node-version }}
cache: "npm"
- run: npm install - run: npm install
- name: Run headless test - name: Run headless test
uses: GabrielBB/xvfb-action@v1 uses: GabrielBB/xvfb-action@v1

View File

@ -7,12 +7,13 @@ on:
jobs: jobs:
push: push:
if: github.repository_owner == 'sphinx-doc'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
with: with:
ref: 4.x ref: 5.x
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v2 uses: actions/setup-python@v2
with: with:
@ -27,12 +28,13 @@ jobs:
TX_TOKEN: ${{ secrets.TX_TOKEN }} TX_TOKEN: ${{ secrets.TX_TOKEN }}
pull: pull:
if: github.repository_owner == 'sphinx-doc'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
with: with:
ref: 4.x ref: 5.x
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v2 uses: actions/setup-python@v2
with: with:

View File

@ -18,6 +18,7 @@ Other co-maintainers:
Other contributors, listed alphabetically, are: Other contributors, listed alphabetically, are:
* Adam Turner -- JavaScript improvements
* Alastair Houghton -- Apple Help builder * Alastair Houghton -- Apple Help builder
* Alexander Todorov -- inheritance_diagram tests and improvements * Alexander Todorov -- inheritance_diagram tests and improvements
* Andi Albrecht -- agogo theme * Andi Albrecht -- agogo theme
@ -73,6 +74,7 @@ Other contributors, listed alphabetically, are:
* Pauli Virtanen -- autodoc improvements, autosummary extension * Pauli Virtanen -- autodoc improvements, autosummary extension
* Eric N. Vander Weele -- autodoc improvements * Eric N. Vander Weele -- autodoc improvements
* Stefan van der Walt -- autosummary extension * Stefan van der Walt -- autosummary extension
* Hugo van Kemenade -- support FORCE_COLOR and NO_COLOR
* Thomas Waldmann -- apidoc module fixes * Thomas Waldmann -- apidoc module fixes
* John Waltman -- Texinfo builder * John Waltman -- Texinfo builder
* Barry Warsaw -- setup command improvements * Barry Warsaw -- setup command improvements

248
CHANGES
View File

@ -1,4 +1,94 @@
Release 4.4.0 (in development) Release 5.0.0 (in development)
==============================
Dependencies
------------
Incompatible changes
--------------------
* #10031: autosummary: ``sphinx.ext.autosummary.import_by_name()`` now raises
``ImportExceptionGroup`` instead of ``ImportError`` when it failed to import
target object. Please handle the exception if your extension uses the
function to import Python object. As a workaround, you can disable the
behavior via ``grouped_exception=False`` keyword argument until v7.0.
* #9962: texinfo: Customizing styles of emphasized text via ``@definfoenclose``
command was not supported because the command was deprecated since texinfo 6.8
* #2068: :confval:`intersphinx_disabled_reftypes` has changed default value
from an empty list to ``['std:doc']`` as avoid too surprising silent
intersphinx resolutions.
To migrate: either add an explicit inventory name to the references
intersphinx should resolve, or explicitly set the value of this configuration
variable to an empty list.
* #9999: LaTeX: separate terms from their definitions by a CR (refs: #9985)
* #10062: Change the default language to ``'en'`` if any language is not set in
``conf.py``
Deprecated
----------
* #10028: jQuery and underscore.js will no longer be automatically injected into
themes from Sphinx 6.0. If you develop a theme or extension that uses the
``jQuery``, ``$``, or ``$u`` global objects, you need to update your
JavaScript or use the mitigation below.
To re-add jQuery and underscore.js, you will need to copy ``jquery.js`` and
``underscore.js`` from `the Sphinx repository`_ to your ``static`` directory,
and add the following to your ``layout.html``:
.. _the Sphinx repository: https://github.com/sphinx-doc/sphinx/tree/v4.3.2/sphinx/themes/basic/static
.. code-block:: html+jinja
{%- block scripts %}
<script src="{{ pathto('_static/jquery.js', resource=True) }}"></script>
<script src="{{ pathto('_static/underscore.js', resource=True) }}"></script>
{{ super() }}
{%- endblock %}
* setuptools integration. The ``build_sphinx`` sub-command for setup.py is
marked as deprecated to follow the policy of setuptools team.
* The ``locale`` argument of ``sphinx.util.i18n:babel_format_date()`` becomes
required
* The ``language`` argument of ``sphinx.util.i18n:format_date()`` becomes
required
* ``sphinx.writers.latex.LaTeXWriter.docclasses``
Features added
--------------
* #9075: autodoc: The default value of :confval:`autodoc_typehints_format` is
changed to ``'smart'``. It will suppress the leading module names of
typehints (ex. ``io.StringIO`` -> ``StringIO``).
* #9792: autodoc: Add new option for ``autodoc_typehints_description_target`` to
include undocumented return values but not undocumented parameters.
* #10028: Removed internal usages of JavaScript frameworks (jQuery and
underscore.js) and modernised ``doctools.js`` and ``searchtools.js`` to
EMCAScript 2018.
Bugs fixed
----------
* #10279: autodoc: Default values for keyword only arguments in overloaded
functions are rendered as a string literal
* #10280: autodoc: :confval:`autodoc_docstring_signature` unexpectedly generates
return value typehint for constructors if docstring has multiple signatures
* #10266: autodoc: :confval:`autodoc_preserve_defaults` does not work for
mixture of keyword only arguments with/without defaults
* #10310: autodoc: class methods are not documented when decorated with mocked
function
* #10214: html: invalid language tag was generated if :confval:`language`
contains a country code (ex. zh_CN)
* #10236: html search: objects are duplicated in search result
* #9962: texinfo: Deprecation message for ``@definfoenclose`` command on
bulding texinfo document
* #10000: LaTeX: glossary terms with common definition are rendered with
too much vertical whitespace
* #10318: ``:prepend:`` option of :rst:dir:`literalinclude` directive does not
work with ``:dedent:`` option
Testing
--------
Release 4.5.1 (in development)
============================== ==============================
Dependencies Dependencies
@ -13,18 +103,118 @@ Deprecated
Features added Features added
-------------- --------------
Bugs fixed
----------
Testing
--------
Release 4.5.0 (released Mar 28, 2022)
=====================================
Incompatible changes
--------------------
* #10112: extlinks: Disable hardcoded links detector by default
* #9993, #10177: std domain: Disallow to refer an inline target via
:rst:role:`ref` role
Deprecated
----------
* ``sphinx.ext.napoleon.docstring.GoogleDocstring._qualify_name()``
Features added
--------------
* #10260: Enable ``FORCE_COLOR`` and ``NO_COLOR`` for terminal colouring
* #10234: autosummary: Add "autosummary" CSS class to summary tables
* #10125: extlinks: Improve suggestion message for a reference having title
* #10112: extlinks: Add :confval:`extlinks_detect_hardcoded_links` to enable
hardcoded links detector feature
* #9494, #9456: html search: Add a config variable
:confval:`html_show_search_summary` to enable/disable the search summaries
* #9337: HTML theme, add option ``enable_search_shortcuts`` that enables :kbd:'/' as
a Quick search shortcut and :kbd:`Esc` shortcut that
removes search highlighting.
* #10107: i18n: Allow to suppress translation warnings by adding ``#noqa``
comment to the tail of each translation message
* #10252: C++, support attributes on classes, unions, and enums.
* #10253: :rst:dir:`pep` role now generates URLs based on peps.python.org
Bugs fixed
----------
* #9876: autodoc: Failed to document an imported class that is built from native
binary module
* #10133: autodoc: Crashed when mocked module is used for type annotation
* #10146: autodoc: :confval:`autodoc_default_options` does not support
``no-value`` option
* #9971: autodoc: TypeError is raised when the target object is annotated by
unhashable object
* #10205: extlinks: Failed to compile regexp on checking hardcoded links
* #10277: html search: Could not search short words (ex. "use")
* #9529: LaTeX: named auto numbered footnote (ex. ``[#named]``) that is referred
multiple times was rendered to a question mark
* #9924: LaTeX: multi-line :rst:dir:`cpp:function` directive has big vertical
spacing in Latexpdf
* #10158: LaTeX: excessive whitespace since v4.4.0 for undocumented
variables/structure members
* #10175: LaTeX: named footnote reference is linked to an incorrect footnote if
the name is also used in the different document
* #10269: manpage: Failed to resolve the title of :ref: cross references
* #10179: i18n: suppress "rST localization" warning
* #10118: imgconverter: Unnecessary availablity check is called for remote URIs
* #10181: napoleon: attributes are displayed like class attributes for google
style docstrings when :confval:`napoleon_use_ivar` is enabled
* #10122: sphinx-build: make.bat does not check the installation of sphinx-build
command before showing help
Release 4.4.0 (released Jan 17, 2022)
=====================================
Dependencies
------------
* #10007: Use ``importlib_metadata`` for python-3.9 or older
* #10007: Drop ``setuptools``
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 * #9831: Autosummary now documents only the members specified in a module's
``__all__`` attribute if :confval:`autosummary_ignore_module_all` is set to ``__all__`` attribute if :confval:`autosummary_ignore_module_all` is set to
``False``. The default behaviour is unchanged. Autogen also now supports ``False``. The default behaviour is unchanged. Autogen also now supports
this behavior with the ``--respect-module-all`` switch. 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 * #9800: extlinks: Emit warning if a hardcoded link is replaceable
by an extlink, suggesting a replacement. 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 * #9815: html theme: Wrap sidebar components in div to allow customizing their
layout via CSS layout via CSS
* #9827: i18n: Sort items in glossary by translated terms
* #9899: py domain: Allows to specify cross-reference specifier (``.`` and * #9899: py domain: Allows to specify cross-reference specifier (``.`` and
``~``) as ``:type:`` option ``~``) as ``:type:`` option
* #9792: autodoc: Add new option for ``autodoc_typehints_description_target`` to * #9894: linkcheck: add option ``linkcheck_exclude_documents`` to disable link
include undocumented return values but not undocumented parameters. 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
* #9981: std domain: Strip value part of the option directive from general index
* #9391: texinfo: improve variable in ``samp`` role
* #9578: texinfo: Add :confval:`texinfo_cross_references` to disable cross
references for readability with standalone readers
* #9822 (and #9062), add new Intersphinx role :rst:role:`external` for explict
lookup in the external projects, without resolving to the local project.
Bugs fixed Bugs fixed
---------- ----------
@ -33,32 +223,42 @@ Bugs fixed
* #9883: autodoc: doccomment for the alias to mocked object was ignored * #9883: autodoc: doccomment for the alias to mocked object was ignored
* #9908: autodoc: debug message is shown on building document using NewTypes * #9908: autodoc: debug message is shown on building document using NewTypes
with Python 3.10 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 * #9878: mathjax: MathJax configuration is placed after loading MathJax itself
* #9932: napoleon: empty "returns" section is generated even if no description
* #9857: Generated RFC links use outdated base url * #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
* #9413: xml: Invalid XML was generated when cross referencing python objects
* #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:`` without argument did strip newlines
Testing Release 4.3.2 (released Dec 19, 2021)
-------- =====================================
Release 4.3.2 (in development)
==============================
Dependencies
------------
Incompatible changes
--------------------
Deprecated
----------
Features added
--------------
Bugs fixed Bugs fixed
---------- ----------
Testing * #9917: C and C++, parse fundamental types no matter the order of simple type
-------- specifiers.
Release 4.3.1 (released Nov 28, 2021) Release 4.3.1 (released Nov 28, 2021)
===================================== =====================================
@ -75,7 +275,7 @@ Bugs fixed
* #9838: autodoc: AttributeError is raised on building document for functions * #9838: autodoc: AttributeError is raised on building document for functions
decorated by functools.lru_cache decorated by functools.lru_cache
* #9879: autodoc: AttributeError is raised on building document for an object * #9879: autodoc: AttributeError is raised on building document for an object
having invalid __doc__ atribute having invalid __doc__ attribute
* #9844: autodoc: Failed to process a function wrapped with functools.partial if * #9844: autodoc: Failed to process a function wrapped with functools.partial if
:confval:`autodoc_preserve_defaults` enabled :confval:`autodoc_preserve_defaults` enabled
* #9872: html: Class namespace collision between autodoc signatures and * #9872: html: Class namespace collision between autodoc signatures and
@ -160,7 +360,7 @@ Bugs fixed
* #9752: autodoc: Failed to detect type annotation for slots attribute * #9752: autodoc: Failed to detect type annotation for slots attribute
* #9756: autodoc: Crashed if classmethod does not have __func__ attribute * #9756: autodoc: Crashed if classmethod does not have __func__ attribute
* #9757: autodoc: :confval:`autodoc_inherit_docstrings` does not effect to * #9757: autodoc: :confval:`autodoc_inherit_docstrings` does not effect to
overriden classmethods overridden classmethods
* #9781: autodoc: :confval:`autodoc_preserve_defaults` does not support * #9781: autodoc: :confval:`autodoc_preserve_defaults` does not support
hexadecimal numeric hexadecimal numeric
* #9630: autosummary: Failed to build summary table if :confval:`primary_domain` * #9630: autosummary: Failed to build summary table if :confval:`primary_domain`

View File

@ -15,9 +15,11 @@ Documentation using the alabaster theme
* `Alabaster <https://alabaster.readthedocs.io/>`__ * `Alabaster <https://alabaster.readthedocs.io/>`__
* `Blinker <https://pythonhosted.org/blinker/>`__ * `Blinker <https://pythonhosted.org/blinker/>`__
* `Calibre <https://manual.calibre-ebook.com/>`__ * `Calibre <https://manual.calibre-ebook.com/>`__
* `CherryPy <https://cherrypy.readthedocs.io/>`__
* `Click <https://click.palletsprojects.com/>`__ (customized) * `Click <https://click.palletsprojects.com/>`__ (customized)
* `coala <https://docs.coala.io/>`__ (customized) * `coala <https://docs.coala.io/>`__ (customized)
* `CodePy <https://documen.tician.de/codepy/>`__ * `CodePy <https://documen.tician.de/codepy/>`__
* `Django Q <https://django-q.readthedocs.io/>`__
* `Eve <https://docs.python-eve.org/>`__ (Python REST API framework) * `Eve <https://docs.python-eve.org/>`__ (Python REST API framework)
* `Fabric <https://docs.fabfile.org/>`__ * `Fabric <https://docs.fabfile.org/>`__
* `Fityk <https://fityk.nieto.pl/>`__ * `Fityk <https://fityk.nieto.pl/>`__
@ -30,19 +32,21 @@ Documentation using the alabaster theme
* `MDAnalysis <https://www.mdanalysis.org/docs/>`__ (customized) * `MDAnalysis <https://www.mdanalysis.org/docs/>`__ (customized)
* `MeshPy <https://documen.tician.de/meshpy/>`__ * `MeshPy <https://documen.tician.de/meshpy/>`__
* `Molecule <https://molecule.readthedocs.io/>`__ * `Molecule <https://molecule.readthedocs.io/>`__
* `Momotor LTI <https://momotor.org/doc/lti/canvas/>`__
* `Podman <https://docs.podman.io/>`__
* `PyCUDA <https://documen.tician.de/pycuda/>`__ * `PyCUDA <https://documen.tician.de/pycuda/>`__
* `PyOpenCL <https://documen.tician.de/pyopencl/>`__ * `PyOpenCL <https://documen.tician.de/pyopencl/>`__
* `PyLangAcq <https://pylangacq.org/>`__ * `PyLangAcq <https://pylangacq.org/>`__
* `pytest <https://docs.pytest.org/>`__ (customized) * `pytest <https://docs.pytest.org/>`__ (customized)
* `python-apt <https://apt.alioth.debian.org/python-apt-doc/>`__ * `python-apt <https://apt-team.pages.debian.net/python-apt/>`__
* `PyVisfile <https://documen.tician.de/pyvisfile/>`__ * `PyVisfile <https://documen.tician.de/pyvisfile/>`__
* `Requests <http://www.python-requests.org/>`__ * `Requests <https://docs.python-requests.org/>`__
* `searx <https://asciimoo.github.io/searx/>`__ * `searx <https://asciimoo.github.io/searx/>`__
* `Spyder <https://docs.spyder-ide.org/>`__ (customized) * `Spyder <https://docs.spyder-ide.org/>`__ (customized)
* `Tablib <http://docs.python-tablib.org/>`__ * `Tablib <http://docs.python-tablib.org/>`__
* `urllib3 <https://urllib3.readthedocs.io/>`__ (customized) * `urllib3 <https://urllib3.readthedocs.io/>`__ (customized)
* `Werkzeug <https://werkzeug.palletsprojects.com/>`__ * `Werkzeug <https://werkzeug.palletsprojects.com/>`__
* `Write the Docs <https://writethedocs-www.readthedocs.io/>`__ * `Write the Docs <https://www.writethedocs.org/>`__
Documentation using the classic theme Documentation using the classic theme
------------------------------------- -------------------------------------
@ -58,8 +62,6 @@ Documentation using the classic theme
* `Buildbot <https://docs.buildbot.net/latest/>`__ * `Buildbot <https://docs.buildbot.net/latest/>`__
* `CMake <https://cmake.org/documentation/>`__ (customized) * `CMake <https://cmake.org/documentation/>`__ (customized)
* `Chaco <https://docs.enthought.com/chaco/>`__ (customized) * `Chaco <https://docs.enthought.com/chaco/>`__ (customized)
* `CORE <https://downloads.pf.itd.nrl.navy.mil/docs/core/core-html/>`__
* `CORE Python modules <https://downloads.pf.itd.nrl.navy.mil/docs/core/core-python-html/>`__
* `Cormoran <http://cormoran.nhopkg.org/docs/>`__ * `Cormoran <http://cormoran.nhopkg.org/docs/>`__
* `DEAP <https://deap.readthedocs.io/>`__ (customized) * `DEAP <https://deap.readthedocs.io/>`__ (customized)
* `Director <https://pythonhosted.org/director/>`__ * `Director <https://pythonhosted.org/director/>`__
@ -67,27 +69,26 @@ Documentation using the classic theme
* `F2py <http://f2py.sourceforge.net/docs/>`__ * `F2py <http://f2py.sourceforge.net/docs/>`__
* `Generic Mapping Tools (GMT) <https://gmt.soest.hawaii.edu/doc/latest/>`__ (customized) * `Generic Mapping Tools (GMT) <https://gmt.soest.hawaii.edu/doc/latest/>`__ (customized)
* `Genomedata <https://noble.gs.washington.edu/proj/genomedata/doc/1.3.3/>`__ * `Genomedata <https://noble.gs.washington.edu/proj/genomedata/doc/1.3.3/>`__
* `GetFEM++ <http://getfem.org/>`__ (customized) * `GetFEM++ <https://getfem.org/>`__ (customized)
* `Glasgow Haskell Compiler <https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/>`__ (customized) * `Glasgow Haskell Compiler <https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/>`__ (customized)
* `Grok <http://grok.zope.org/doc/current/>`__ (customized) * `Grok <http://grok.zope.org/doc/current/>`__ (customized)
* `GROMACS <https://manual.gromacs.org/documentation/>`__ * `GROMACS <https://manual.gromacs.org/documentation/>`__
* `GSL Shell <https://www.nongnu.org/gsl-shell/>`__ * `GSL Shell <https://www.nongnu.org/gsl-shell/>`__
* `Hands-on Python Tutorial <https://anh.cs.luc.edu/python/hands-on/3.1/handsonHtml/>`__ * `Hands-on Python Tutorial <http://anh.cs.luc.edu/python/hands-on/3.1/handsonHtml/>`__
* `Kaa <https://api.freevo.org/kaa-base/>`__ (customized) * `Kaa <https://freevo.github.io/kaa-base/>`__ (customized)
* `Leo <https://leoeditor.com/>`__ * `Leo <https://leoeditor.com/>`__ (customized)
* `LEPL <http://www.acooke.org/lepl/>`__ (customized)
* `Mayavi <https://docs.enthought.com/mayavi/mayavi/>`__ (customized) * `Mayavi <https://docs.enthought.com/mayavi/mayavi/>`__ (customized)
* `MediaGoblin <https://mediagoblin.readthedocs.io/>`__ (customized) * `MediaGoblin <https://mediagoblin.readthedocs.io/>`__ (customized)
* `mpmath <https://mpmath.org/doc/current/>`__ * `mpmath <https://mpmath.org/doc/current/>`__
* `OpenCV <https://docs.opencv.org/>`__ (customized) * `OpenCV <https://docs.opencv.org/>`__ (customized)
* `OpenEXR <https://excamera.com/articles/26/doc/index.html>`__ * `OpenEXR <https://excamera.com/articles/26/doc/index.html>`__
* `OpenGDA <http://www.opengda.org/gdadoc/html/>`__ * `OpenGDA <http://www.opengda.org/documentation/>`__
* `Peach^3 <https://peach3.nl/doc/latest/userdoc/>`__ (customized) * `phpDocumentor <https://docs.phpdoc.org/>`__ (customized)
* `Plone <https://docs.plone.org/>`__ (customized) * `Plone <https://docs.plone.org/>`__ (customized)
* `PyEMD <https://pyemd.readthedocs.io/>`__ * `PyEMD <https://pyemd.readthedocs.io/>`__
* `Pyevolve <http://pyevolve.sourceforge.net/>`__ * `Pyevolve <http://pyevolve.sourceforge.net/>`__
* `Pygame <https://www.pygame.org/docs/>`__ (customized) * `Pygame <https://www.pygame.org/docs/>`__ (customized)
* `PyMQI <https://pythonhosted.org/pymqi/>`__ * `PyMQI <https://dsuch.github.io/pymqi/>`__
* `PyQt4 <http://pyqt.sourceforge.net/Docs/PyQt4/>`__ (customized) * `PyQt4 <http://pyqt.sourceforge.net/Docs/PyQt4/>`__ (customized)
* `PyQt5 <http://pyqt.sourceforge.net/Docs/PyQt5/>`__ (customized) * `PyQt5 <http://pyqt.sourceforge.net/Docs/PyQt5/>`__ (customized)
* `Python 2 <https://docs.python.org/2/>`__ * `Python 2 <https://docs.python.org/2/>`__
@ -120,7 +121,8 @@ Documentation using the sphinxdoc theme
* `MDAnalysis Tutorial <https://www.mdanalysis.org/MDAnalysisTutorial/>`__ * `MDAnalysis Tutorial <https://www.mdanalysis.org/MDAnalysisTutorial/>`__
* `NetworkX <https://networkx.github.io/>`__ * `NetworkX <https://networkx.github.io/>`__
* `PyCantonese <https://pycantonese.org/>`__ * `PyCantonese <https://pycantonese.org/>`__
* `Pyre <https://docs.danse.us/pyre/sphinx/>`__ * `PyRe <https://hackl.science/pyre/>`__
* `Pyre <https://pyre.readthedocs.io/>`__
* `pySPACE <https://pyspace.github.io/pyspace/>`__ * `pySPACE <https://pyspace.github.io/pyspace/>`__
* `Pysparse <http://pysparse.sourceforge.net/>`__ * `Pysparse <http://pysparse.sourceforge.net/>`__
* `PyTango <https://www.esrf.eu/computing/cs/tango/tango_doc/kernel_doc/pytango/latest/>`__ * `PyTango <https://www.esrf.eu/computing/cs/tango/tango_doc/kernel_doc/pytango/latest/>`__
@ -136,8 +138,7 @@ Documentation using the nature theme
* `Alembic <https://alembic.sqlalchemy.org/>`__ * `Alembic <https://alembic.sqlalchemy.org/>`__
* `Cython <https://docs.cython.org/>`__ * `Cython <https://docs.cython.org/>`__
* `easybuild <https://easybuild.readthedocs.io/>`__ * `easybuild <https://easybuild.readthedocs.io/>`__
* `jsFiddle <http://doc.jsfiddle.net/>`__ * `libLAS <https://liblas.org/>`__ (customized)
* `libLAS <https://www.liblas.org/>`__ (customized)
* `Lmod <https://lmod.readthedocs.io/>`__ * `Lmod <https://lmod.readthedocs.io/>`__
* `MapServer <https://mapserver.org/>`__ (customized) * `MapServer <https://mapserver.org/>`__ (customized)
* `Pandas <https://pandas.pydata.org/pandas-docs/stable/>`__ * `Pandas <https://pandas.pydata.org/pandas-docs/stable/>`__
@ -154,11 +155,10 @@ Documentation using another builtin theme
* `Breathe <https://breathe.readthedocs.io/>`__ (haiku) * `Breathe <https://breathe.readthedocs.io/>`__ (haiku)
* `MPipe <https://vmlaker.github.io/mpipe/>`__ (sphinx13) * `MPipe <https://vmlaker.github.io/mpipe/>`__ (sphinx13)
* `NLTK <https://www.nltk.org/>`__ (agogo) * `NLTK <https://www.nltk.org/>`__ (agogo)
* `Programmieren mit PyGTK und Glade (German) <https://www.florian-diesch.de/doc/python-und-glade/online/>`__ (agogo, customized)
* `PyPubSub <https://pypubsub.readthedocs.io/>`__ (bizstyle) * `PyPubSub <https://pypubsub.readthedocs.io/>`__ (bizstyle)
* `Pylons <https://docs.pylonsproject.org/projects/pylons-webframework/>`__ (pyramid) * `Pylons <https://docs.pylonsproject.org/projects/pylons-webframework/>`__ (pyramid)
* `Pyramid web framework <https://docs.pylonsproject.org/projects/pyramid/>`__ (pyramid) * `Pyramid web framework <https://docs.pylonsproject.org/projects/pyramid/>`__ (pyramid)
* `RxDock <https://www.rxdock.org/documentation/html/devel/>`__ * `RxDock <https://www.rxdock.org/documentation/devel/html/>`__
* `Sphinx <https://www.sphinx-doc.org/>`__ (sphinx13) :-) * `Sphinx <https://www.sphinx-doc.org/>`__ (sphinx13) :-)
* `Valence <https://docs.valence.desire2learn.com/>`__ (haiku, customized) * `Valence <https://docs.valence.desire2learn.com/>`__ (haiku, customized)
@ -176,9 +176,12 @@ Documentation using sphinx_rtd_theme
* `Blender Reference Manual <https://docs.blender.org/manual/>`__ * `Blender Reference Manual <https://docs.blender.org/manual/>`__
* `Blocks <https://blocks.readthedocs.io/>`__ * `Blocks <https://blocks.readthedocs.io/>`__
* `bootstrap-datepicker <https://bootstrap-datepicker.readthedocs.io/>`__ * `bootstrap-datepicker <https://bootstrap-datepicker.readthedocs.io/>`__
* `Certbot <https://letsencrypt.readthedocs.io/>`__ * `Certbot <https://certbot.eff.org/docs/>`__
* `CKAN <https://docs.ckan.org/>`__
* `Copr Buildsystem <https://docs.pagure.org/copr.copr/>`__ (customized)
* `Coreboot <https://doc.coreboot.org/>`__
* `Chainer <https://docs.chainer.org/>`__ (customized) * `Chainer <https://docs.chainer.org/>`__ (customized)
* `CherryPy <https://docs.cherrypy.org/>`__ * `citeproc-js <https://citeproc-js.readthedocs.io/>`__
* `cloud-init <https://cloudinit.readthedocs.io/>`__ * `cloud-init <https://cloudinit.readthedocs.io/>`__
* `CodeIgniter <https://www.codeigniter.com/user_guide/>`__ * `CodeIgniter <https://www.codeigniter.com/user_guide/>`__
* `Conda <https://conda.io/docs/>`__ * `Conda <https://conda.io/docs/>`__
@ -187,7 +190,9 @@ Documentation using sphinx_rtd_theme
* `Databricks <https://docs.databricks.com/>`__ (customized) * `Databricks <https://docs.databricks.com/>`__ (customized)
* `Dataiku DSS <https://doc.dataiku.com/>`__ * `Dataiku DSS <https://doc.dataiku.com/>`__
* `DNF <https://dnf.readthedocs.io/>`__ * `DNF <https://dnf.readthedocs.io/>`__
* `Distro Tracker <https://qa.pages.debian.net/distro-tracker/>`__
* `Django-cas-ng <https://djangocas.dev/docs/>`__ * `Django-cas-ng <https://djangocas.dev/docs/>`__
* `dj-stripe <https://dj-stripe.readthedocs.io/>`__
* `edX <https://docs.edx.org/>`__ * `edX <https://docs.edx.org/>`__
* `Electrum <https://docs.electrum.org/>`__ * `Electrum <https://docs.electrum.org/>`__
* `Elemental <https://libelemental.org/documentation/dev/>`__ * `Elemental <https://libelemental.org/documentation/dev/>`__
@ -207,13 +212,15 @@ Documentation using sphinx_rtd_theme
* `Graylog <https://docs.graylog.org/>`__ * `Graylog <https://docs.graylog.org/>`__
* `GPAW <https://wiki.fysik.dtu.dk/gpaw/>`__ (customized) * `GPAW <https://wiki.fysik.dtu.dk/gpaw/>`__ (customized)
* `HDF5 for Python (h5py) <https://docs.h5py.org/>`__ * `HDF5 for Python (h5py) <https://docs.h5py.org/>`__
* `HyperKitty <https://hyperkitty.readthedocs.io/>`__
* `Hyperledger Fabric <https://hyperledger-fabric.readthedocs.io/>`__ * `Hyperledger Fabric <https://hyperledger-fabric.readthedocs.io/>`__
* `Hyperledger Sawtooth <https://intelledger.github.io/>`__ * `Hyperledger Sawtooth <https://sawtooth.hyperledger.org/docs/>`__
* `IdentityServer <https://docs.identityserver.io/>`__ * `IdentityServer <https://docs.identityserver.io/>`__
* `Idris <https://docs.idris-lang.org/>`__ * `Idris <https://docs.idris-lang.org/>`__
* `Inkscape <https://inkscape-manuals.readthedocs.io/>`__ (customized)
* `javasphinx <https://bronto-javasphinx.readthedocs.io/>`__ * `javasphinx <https://bronto-javasphinx.readthedocs.io/>`__
* `Julia <https://julia.readthedocs.io/>`__
* `Jupyter Notebook <https://jupyter-notebook.readthedocs.io/>`__ * `Jupyter Notebook <https://jupyter-notebook.readthedocs.io/>`__
* `Kanboard <https://docs.kanboard.org/>`__
* `Lasagne <https://lasagne.readthedocs.io/>`__ * `Lasagne <https://lasagne.readthedocs.io/>`__
* `latexindent.pl <https://latexindentpl.readthedocs.io/>`__ * `latexindent.pl <https://latexindentpl.readthedocs.io/>`__
* `Learning Apache Spark with Python <https://runawayhorse001.github.io/LearningApacheSpark>`__ * `Learning Apache Spark with Python <https://runawayhorse001.github.io/LearningApacheSpark>`__
@ -226,7 +233,6 @@ Documentation using sphinx_rtd_theme
* `Mesa 3D <https://docs.mesa3d.org/>`__ * `Mesa 3D <https://docs.mesa3d.org/>`__
* `micca - MICrobial Community Analysis <https://micca.readthedocs.io/>`__ * `micca - MICrobial Community Analysis <https://micca.readthedocs.io/>`__
* `MicroPython <https://docs.micropython.org/>`__ * `MicroPython <https://docs.micropython.org/>`__
* `Minds <https://www.minds.org/docs/>`__ (customized)
* `Mink <https://mink.behat.org/>`__ * `Mink <https://mink.behat.org/>`__
* `Mockery <https://docs.mockery.io/>`__ * `Mockery <https://docs.mockery.io/>`__
* `mod_wsgi <https://modwsgi.readthedocs.io/>`__ * `mod_wsgi <https://modwsgi.readthedocs.io/>`__
@ -234,9 +240,14 @@ Documentation using sphinx_rtd_theme
* `Mopidy <https://docs.mopidy.com/>`__ * `Mopidy <https://docs.mopidy.com/>`__
* `mpi4py <https://mpi4py.readthedocs.io/>`__ * `mpi4py <https://mpi4py.readthedocs.io/>`__
* `MyHDL <https://docs.myhdl.org/>`__ * `MyHDL <https://docs.myhdl.org/>`__
* `Mypy <https://mypy.readthedocs.io/>`__
* `Netgate Docs <https://docs.netgate.com/>`__
* `Nextcloud Server <https://docs.nextcloud.com/#server>`__
* `Nextflow <https://www.nextflow.io/docs/latest/index.html>`__ * `Nextflow <https://www.nextflow.io/docs/latest/index.html>`__
* `nghttp2 <https://nghttp2.org/documentation/>`__
* `NICOS <https://forge.frm2.tum.de/nicos/doc/nicos-master/>`__ (customized) * `NICOS <https://forge.frm2.tum.de/nicos/doc/nicos-master/>`__ (customized)
* `OpenFAST <https://openfast.readthedocs.io/>`__ * `OpenFAST <https://openfast.readthedocs.io/>`__
* `Panda3D <https://docs.panda3d.org/>`__ (customized)
* `Pelican <https://docs.getpelican.com/>`__ * `Pelican <https://docs.getpelican.com/>`__
* `picamera <https://picamera.readthedocs.io/>`__ * `picamera <https://picamera.readthedocs.io/>`__
* `Pillow <https://pillow.readthedocs.io/>`__ * `Pillow <https://pillow.readthedocs.io/>`__
@ -245,15 +256,21 @@ Documentation using sphinx_rtd_theme
* `peewee <https://docs.peewee-orm.com/>`__ * `peewee <https://docs.peewee-orm.com/>`__
* `Phinx <https://docs.phinx.org/>`__ * `Phinx <https://docs.phinx.org/>`__
* `phpMyAdmin <https://docs.phpmyadmin.net/>`__ * `phpMyAdmin <https://docs.phpmyadmin.net/>`__
* `PHPUnit <https://phpunit.readthedocs.io/>`__
* `PHPWord <https://phpword.readthedocs.io/>`__
* `PROS <https://pros.cs.purdue.edu/v5/>`__ (customized) * `PROS <https://pros.cs.purdue.edu/v5/>`__ (customized)
* `Pushkin <http://docs.pushkin.io/>`__ * `Pushkin <http://docs.pushkin.io/>`__
* `Pweave <https://mpastell.com/pweave/>`__ * `Pweave <https://mpastell.com/pweave/>`__
* `pyca/cryptograhpy <https://cryptography.io/>`__
* `PyNaCl <https://pynacl.readthedocs.io/>`__
* `pyOpenSSL <https://www.pyopenssl.org/>`__
* `PyPy <https://doc.pypy.org/>`__ * `PyPy <https://doc.pypy.org/>`__
* `python-sqlparse <https://sqlparse.readthedocs.io/>`__ * `python-sqlparse <https://sqlparse.readthedocs.io/>`__
* `PyVISA <https://pyvisa.readthedocs.io/>`__ * `PyVISA <https://pyvisa.readthedocs.io/>`__
* `pyvista <https://docs.pyvista.org/>`__ * `pyvista <https://docs.pyvista.org/>`__
* `Read The Docs <https://docs.readthedocs.io/>`__ * `Read The Docs <https://docs.readthedocs.io/>`__
* `ROCm Platform <https://rocm-documentation.readthedocs.io/>`__ * `RenderDoc <https://renderdoc.org/docs/>`__
* `ROCm Platform <https://rocmdocs.amd.com/>`__
* `Free your information from their silos (French) <https://redaction-technique.org/>`__ (customized) * `Free your information from their silos (French) <https://redaction-technique.org/>`__ (customized)
* `Releases Sphinx extension <https://releases.readthedocs.io/>`__ * `Releases Sphinx extension <https://releases.readthedocs.io/>`__
* `Qtile <https://docs.qtile.org/>`__ * `Qtile <https://docs.qtile.org/>`__
@ -269,6 +286,7 @@ Documentation using sphinx_rtd_theme
* `Sonos Controller (SoCo) <https://docs.python-soco.com/>`__ * `Sonos Controller (SoCo) <https://docs.python-soco.com/>`__
* `Sphinx AutoAPI <https://sphinx-autoapi.readthedocs.io/>`__ * `Sphinx AutoAPI <https://sphinx-autoapi.readthedocs.io/>`__
* `sphinx-argparse <https://sphinx-argparse.readthedocs.io/>`__ * `sphinx-argparse <https://sphinx-argparse.readthedocs.io/>`__
* `sphinx-tabs <https://sphinx-tabs.readthedocs.io/>`__
* `Sphinx-Gallery <https://sphinx-gallery.readthedocs.io/>`__ (customized) * `Sphinx-Gallery <https://sphinx-gallery.readthedocs.io/>`__ (customized)
* `Sphinx with Github Webpages <https://runawayhorse001.github.io/SphinxGithub>`__ * `Sphinx with Github Webpages <https://runawayhorse001.github.io/SphinxGithub>`__
* `SpotBugs <https://spotbugs.readthedocs.io/>`__ * `SpotBugs <https://spotbugs.readthedocs.io/>`__
@ -281,15 +299,18 @@ Documentation using sphinx_rtd_theme
* `Topshelf <https://docs.topshelf-project.com/>`__ * `Topshelf <https://docs.topshelf-project.com/>`__
* `Theano <http://www.deeplearning.net/software/theano/>`__ * `Theano <http://www.deeplearning.net/software/theano/>`__
* `ThreatConnect <https://docs.threatconnect.com/>`__ * `ThreatConnect <https://docs.threatconnect.com/>`__
* `TrueNAS <https://www.ixsystems.com/documentation/truenas/>`__ (customized)
* `Tuleap <https://tuleap.net/doc/en/>`__ * `Tuleap <https://tuleap.net/doc/en/>`__
* `TYPO3 <https://docs.typo3.org/>`__ (customized) * `TYPO3 <https://docs.typo3.org/>`__ (customized)
* `Veyon <https://docs.veyon.io/>`__ * `Veyon <https://docs.veyon.io/>`__
* `Ubiquity <https://micro-framework.readthedocs.io/>`__
* `uWSGI <https://uwsgi-docs.readthedocs.io/>`__ * `uWSGI <https://uwsgi-docs.readthedocs.io/>`__
* `virtualenv <https://virtualenv.readthedocs.io/>`__ * `virtualenv <https://virtualenv.readthedocs.io/>`__
* `Wagtail <https://docs.wagtail.io/>`__ * `Wagtail <https://docs.wagtail.io/>`__
* `Web Application Attack and Audit Framework (w3af) <https://docs.w3af.org/>`__ * `Web Application Attack and Audit Framework (w3af) <https://docs.w3af.org/>`__
* `Weblate <https://docs.weblate.org/>`__ * `Weblate <https://docs.weblate.org/>`__
* `x265 <https://x265.readthedocs.io/>`__ * `x265 <https://x265.readthedocs.io/>`__
* `Zeek <https://docs.zeek.org/>`__
* `Zulip <https://zulip.readthedocs.io/>`__ * `Zulip <https://zulip.readthedocs.io/>`__
Documentation using sphinx_bootstrap_theme Documentation using sphinx_bootstrap_theme
@ -317,12 +338,12 @@ Documentation using a custom theme or integrated in a website
* `Bokeh <https://bokeh.pydata.org/>`__ * `Bokeh <https://bokeh.pydata.org/>`__
* `Boto 3 <https://boto3.readthedocs.io/>`__ * `Boto 3 <https://boto3.readthedocs.io/>`__
* `CakePHP <https://book.cakephp.org/>`__ * `CakePHP <https://book.cakephp.org/>`__
* `CasperJS <http://docs.casperjs.org/>`__
* `Ceph <https://docs.ceph.com/docs/master/>`__ * `Ceph <https://docs.ceph.com/docs/master/>`__
* `Chef <https://docs.chef.io/>`__ * `Chef <https://docs.chef.io/>`__
* `CKAN <https://docs.ckan.org/>`__ * `CKAN <https://docs.ckan.org/>`__
* `Confluent Platform <https://docs.confluent.io/>`__ * `Confluent Platform <https://docs.confluent.io/>`__
* `Django <https://docs.djangoproject.com/>`__ * `Django <https://docs.djangoproject.com/>`__
* `django CMS <https://docs.django-cms.org/>`__
* `Doctrine <https://www.doctrine-project.org/>`__ * `Doctrine <https://www.doctrine-project.org/>`__
* `Enterprise Toolkit for Acrobat products <https://www.adobe.com/devnet-docs/acrobatetk/>`__ * `Enterprise Toolkit for Acrobat products <https://www.adobe.com/devnet-docs/acrobatetk/>`__
* `FreeFEM <https://doc.freefem.org/introduction/>`__ * `FreeFEM <https://doc.freefem.org/introduction/>`__
@ -335,7 +356,7 @@ Documentation using a custom theme or integrated in a website
* `Guzzle <https://docs.guzzlephp.org/>`__ * `Guzzle <https://docs.guzzlephp.org/>`__
* `H2O.ai <https://docs.h2o.ai/>`__ * `H2O.ai <https://docs.h2o.ai/>`__
* `Heka <https://hekad.readthedocs.io/>`__ * `Heka <https://hekad.readthedocs.io/>`__
* `Istihza (Turkish Python documentation project) <https://belgeler.yazbel.com/python-istihza/>`__ * `Istihza (Turkish Python documentation project) <https://python-istihza.yazbel.com/>`__
* `JupyterHub <https://jupyterhub.readthedocs.io/>`__ * `JupyterHub <https://jupyterhub.readthedocs.io/>`__
* `Kombu <http://docs.kombu.me/>`__ * `Kombu <http://docs.kombu.me/>`__
* `Lasso <https://lassoguide.com/>`__ * `Lasso <https://lassoguide.com/>`__
@ -356,6 +377,7 @@ Documentation using a custom theme or integrated in a website
* `OpenTURNS <https://openturns.github.io/openturns/master/>`__ * `OpenTURNS <https://openturns.github.io/openturns/master/>`__
* `Open vSwitch <https://docs.openvswitch.org/>`__ * `Open vSwitch <https://docs.openvswitch.org/>`__
* `PlatformIO <https://docs.platformio.org/>`__ * `PlatformIO <https://docs.platformio.org/>`__
* `Psycopg <https://www.psycopg.org/docs/>`__
* `PyEphem <https://rhodesmill.org/pyephem/>`__ * `PyEphem <https://rhodesmill.org/pyephem/>`__
* `Pygments <https://pygments.org/docs/>`__ * `Pygments <https://pygments.org/docs/>`__
* `Plone User Manual (German) <https://www.hasecke.com/plone-benutzerhandbuch/4.0/>`__ * `Plone User Manual (German) <https://www.hasecke.com/plone-benutzerhandbuch/4.0/>`__
@ -384,14 +406,16 @@ Documentation using a custom theme or integrated in a website
Homepages and other non-documentation sites Homepages and other non-documentation sites
------------------------------------------- -------------------------------------------
* `Alan Crosswell's Using the Django REST Framework and DRF-JSONAPI <http://www.columbia.edu/~alan/django-jsonapi-training/>`__
* `Arizona State University PHY494/PHY598/CHM598 Simulation approaches to Bio-and Nanophysics <https://becksteinlab.physics.asu.edu/pages/courses/2013/SimBioNano/>`__ (classic) * `Arizona State University PHY494/PHY598/CHM598 Simulation approaches to Bio-and Nanophysics <https://becksteinlab.physics.asu.edu/pages/courses/2013/SimBioNano/>`__ (classic)
* `Benoit Boissinot <https://bboissin.appspot.com/>`__ (classic, customized) * `Benoit Boissinot <https://bboissin.appspot.com/>`__ (classic, customized)
* `Computer Networks, Parallelization, and Simulation Laboratory (CNPSLab) <https://lab.miletic.net/>`__ (sphinx_rtd_theme) * `EBI Cloud Consultancy Team <https://tsi-ccdoc.readthedocs.io/>`__ (sphinx_rtd_theme)
* `Deep Learning Tutorials <http://www.deeplearning.net/tutorial/>`__ (sphinxdoc)
* `Eric Holscher <https://ericholscher.com/>`__ (alabaster) * `Eric Holscher <https://ericholscher.com/>`__ (alabaster)
* `Florian Diesch <https://www.florian-diesch.de/>`__
* `Institute for the Design of Advanced Energy Systems (IDAES) <https://idaes-pse.readthedocs.io/>`__ (sphinx_rtd_theme)
* `IDAES Examples <https://idaes.github.io/examples-pse/>`__ (sphinx_rtd_theme)
* `Lei Ma's Statistical Mechanics lecture notes <http://statisticalphysics.openmetric.org/>`__ (sphinx_bootstrap_theme) * `Lei Ma's Statistical Mechanics lecture notes <http://statisticalphysics.openmetric.org/>`__ (sphinx_bootstrap_theme)
* `Loyola University Chicago COMP 339-439 Distributed Systems course <https://books.cs.luc.edu/distributedsystems/>`__ (sphinx_bootstrap_theme) * `Loyola University Chicago CS Academic Programs <https://academics.cs.luc.edu/index.html>`__ (sphinx_rtd_theme, customized)
* `Pylearn2 <http://www.deeplearning.net/software/pylearn2/>`__ (sphinxdoc, customized)
* `PyXLL <https://www.pyxll.com/>`__ (sphinx_bootstrap_theme, customized) * `PyXLL <https://www.pyxll.com/>`__ (sphinx_bootstrap_theme, customized)
* `SciPy Cookbook <https://scipy-cookbook.readthedocs.io/>`__ (sphinx_rtd_theme) * `SciPy Cookbook <https://scipy-cookbook.readthedocs.io/>`__ (sphinx_rtd_theme)
* `Tech writer at work blog <https://blog.documatt.com/>`__ (custom theme) * `Tech writer at work blog <https://blog.documatt.com/>`__ (custom theme)
@ -444,6 +468,6 @@ Projects integrating Sphinx functionality
* `Read the Docs <https://readthedocs.org/>`__, a software-as-a-service documentation hosting platform, uses * `Read the Docs <https://readthedocs.org/>`__, a software-as-a-service documentation hosting platform, uses
Sphinx to automatically build documentation updates that are pushed to GitHub. Sphinx to automatically build documentation updates that are pushed to GitHub.
* `Spyder <https://docs.spyder-ide.org/help.html>`__, the Scientific Python Development Environment, uses Sphinx in its * `Spyder <https://docs.spyder-ide.org/current/panes/help.html>`__, the Scientific Python Development
help pane to render rich documentation for functions, classes and methods Environment, uses Sphinx in its help pane to render rich documentation for functions, classes and methods
automatically or on-demand. automatically or on-demand.

View File

@ -1,7 +1,10 @@
License for Sphinx License for Sphinx
================== ==================
Copyright (c) 2007-2021 by the Sphinx team (see AUTHORS file). Unless otherwise indicated, all code in the Sphinx project is licenced under the
two clause BSD licence below.
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

View 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

View File

@ -14,10 +14,10 @@
<form action="https://groups.google.com/group/sphinx-users/boxsubscribe" <form action="https://groups.google.com/group/sphinx-users/boxsubscribe"
class="subscribeform"> class="subscribeform">
<input type="text" name="email" value="your@email" <input type="text" name="email" value="your@email"
onfocus="$(this).val('');" /> onfocus="this.value = ''" />
<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>

View File

@ -27,31 +27,28 @@
</style> </style>
<script> <script>
// intelligent scrolling of the sidebar content // intelligent scrolling of the sidebar content
$(window).scroll(function() { window.onscroll = () => {
var sb = $('.sphinxsidebarwrapper'); const sb = document.getElementsByClassName('sphinxsidebarwrapper')[0]
var win = $(window); const sbh = sb.offsetHeight
var sbh = sb.height(); const offset = document.getElementsByClassName('sphinxsidebar')[0].offsetTop;
var offset = $('.sphinxsidebar').position()['top']; const wintop = window.scrollTop;
var wintop = win.scrollTop(); const winbot = wintop + window.offsetHeight
var winbot = wintop + win.innerHeight(); const curtop = sb.offsetTop;
var curtop = sb.position()['top']; const curbot = curtop + sbh;
var curbot = curtop + sbh;
// does sidebar fit in window? // does sidebar fit in window?
if (sbh < win.innerHeight()) { if (sbh < window.offsetHeight) {
// yes: easy case -- always keep at the top // yes: easy case -- always keep at the top
sb.css('top', $u.min([$u.max([0, wintop - offset - 10]), sb.style.top = Math.min(Math.max(0, wintop - offset - 10), window.innerHeight - sbh - 200)
$(document).height() - sbh - 200]));
} else { } else {
// no: only scroll if top/bottom edge of sidebar is at // no: only scroll if top/bottom edge of sidebar is at
// top/bottom edge of window // top/bottom edge of window
if (curtop > wintop && curbot > winbot) { if (curtop > wintop && curbot > winbot) {
sb.css('top', $u.max([wintop - offset - 10, 0])); sb.style.top = Math.max(wintop - offset - 10, 0)
} else if (curtop < wintop && curbot < winbot) { } else if (curtop < wintop && curbot < winbot) {
sb.css('top', $u.min([winbot - sbh - offset - 20, sb.style.top = Math.min(winbot - sbh - offset - 20, window.innerHeight - sbh - 200)
$(document).height() - sbh - 200]));
} }
} }
}); }
</script> </script>
{%- endif %} {%- endif %}
{% endblock %} {% endblock %}

View File

@ -1,4 +1,4 @@
[theme] [theme]
inherit = basic inherit = basic
stylesheet = sphinx13.css stylesheet = sphinx13.css
pygments_style = trac pygments_style = default

View File

@ -1,5 +1,6 @@
# Sphinx documentation build configuration file # Sphinx documentation build configuration file
import os
import re import re
import sphinx import sphinx
@ -14,7 +15,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
@ -81,11 +82,11 @@ autodoc_member_order = 'groupwise'
autosummary_generate = False autosummary_generate = False
todo_include_todos = True todo_include_todos = True
extlinks = {'duref': ('https://docutils.sourceforge.io/docs/ref/rst/' extlinks = {'duref': ('https://docutils.sourceforge.io/docs/ref/rst/'
'restructuredtext.html#%s', ''), 'restructuredtext.html#%s', '%s'),
'durole': ('https://docutils.sourceforge.io/docs/ref/rst/' 'durole': ('https://docutils.sourceforge.io/docs/ref/rst/'
'roles.html#%s', ''), 'roles.html#%s', '%s'),
'dudir': ('https://docutils.sourceforge.io/docs/ref/rst/' 'dudir': ('https://docutils.sourceforge.io/docs/ref/rst/'
'directives.html#%s', '')} 'directives.html#%s', '%s')}
man_pages = [ man_pages = [
('contents', 'sphinx-all', 'Sphinx documentation generator system manual', ('contents', 'sphinx-all', 'Sphinx documentation generator system manual',
@ -108,7 +109,8 @@ 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://docs.python-requests.org/en/latest/', 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:
@ -138,10 +140,33 @@ def parse_event(env, sig, signode):
return name return name
def linkify_issues_in_changelog(app, docname, source):
""" Linkify issue references like #123 in changelog to GitHub. """
if docname == 'changes':
changelog_path = os.path.join(os.path.dirname(__file__), "../CHANGES")
# this path trickery is needed because this script can
# be invoked with different working directories:
# * running make in docs/
# * running python setup.py build_sphinx in the repo root dir
with open(changelog_path) as f:
changelog = f.read()
def linkify(match):
url = 'https://github.com/sphinx-doc/sphinx/issues/' + match[1]
return '`{} <{}>`_'.format(match[0], url)
linkified_changelog = re.sub(r'(?:PR)?#([0-9]+)\b', linkify, changelog)
source[0] = source[0].replace('.. include:: ../CHANGES', linkified_changelog)
def setup(app): def setup(app):
from sphinx.ext.autodoc import cut_lines from sphinx.ext.autodoc import cut_lines
from sphinx.util.docfields import GroupedField from sphinx.util.docfields import GroupedField
app.connect('autodoc-process-docstring', cut_lines(4, what=['module'])) app.connect('autodoc-process-docstring', cut_lines(4, what=['module']))
app.connect('source-read', linkify_issues_in_changelog)
app.add_object_type('confval', 'confval', app.add_object_type('confval', 'confval',
objname='configuration value', objname='configuration value',
indextemplate='pair: %s; configuration value') indextemplate='pair: %s; configuration value')

View File

@ -88,8 +88,8 @@ Python :mod:`ConfigParser` module) and has the following structure:
Distribute your theme as a Python package Distribute your theme as a Python package
----------------------------------------- -----------------------------------------
As a way to distribute your theme, you can use Python package. Python package As a way to distribute your theme, you can use a Python package. This makes it
brings to users easy setting up ways. easier for users to set up your theme.
To distribute your theme as a Python package, please define an entry point To distribute your theme as a Python package, please define an entry point
called ``sphinx.html_themes`` in your ``setup.py`` file, and write a ``setup()`` called ``sphinx.html_themes`` in your ``setup.py`` file, and write a ``setup()``
@ -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

View File

@ -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)

View File

@ -87,7 +87,7 @@ class RecipeIndex(Index):
# first letter of the recipe as a key to group thing # first letter of the recipe as a key to group thing
# #
# name, subtype, docname, anchor, extra, qualifier, description # name, subtype, docname, anchor, extra, qualifier, description
for name, dispname, typ, docname, anchor, _ in recipes: for _name, dispname, typ, docname, anchor, _priority in recipes:
content[dispname[0].lower()].append( content[dispname[0].lower()].append(
(dispname, 0, docname, anchor, docname, '', typ)) (dispname, 0, docname, anchor, docname, '', typ))

View File

@ -298,7 +298,9 @@ Here is a more detailed list of these events.
Emitted when a cross-reference to an object cannot be resolved even after Emitted when a cross-reference to an object cannot be resolved even after
:event:`missing-reference`. If the event handler can emit warnings for :event:`missing-reference`. If the event handler can emit warnings for
the missing reference, it should return ``True``. the missing reference, it should return ``True``. The configuration variables
:confval:`nitpick_ignore` and :confval:`nitpick_ignore_regex` prevent the
event from being emitted for the corresponding nodes.
.. versionadded:: 3.4 .. versionadded:: 3.4

View File

@ -22,6 +22,31 @@ The following is a list of deprecated interfaces.
- (will be) Removed - (will be) Removed
- Alternatives - Alternatives
* - :doc:`Setuptools integration </usage/advanced/setuptools>`
- 5.0
- 7.0
- N/A
* - The ``locale`` argument of ``sphinx.util.i18n:babel_format_date()``
- 5.0
- 7.0
- N/A
* - The ``language`` argument of ``sphinx.util.i18n:format_date()``
- 5.0
- 7.0
- N/A
* - ``sphinx.writers.latex.LaTeXWriter.docclasses``
- 5.0
- 7.0
- N/A
* - ``sphinx.ext.napoleon.docstring.GoogleDocstring._qualify_name()``
- 4.5
- 6.0
- N/A
* - ``sphinx.ext.autodoc.AttributeDocumenter._datadescriptor`` * - ``sphinx.ext.autodoc.AttributeDocumenter._datadescriptor``
- 4.3 - 4.3
- 6.0 - 6.0

View File

@ -35,3 +35,4 @@ to configure their settings appropriately.
.. module:: sphinx.parsers .. module:: sphinx.parsers
.. autoclass:: Parser .. autoclass:: Parser
:members:

View File

@ -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
@ -346,19 +350,3 @@ The following notes may be helpful if you want to create Texinfo files:
scheme ``info``. For example:: scheme ``info``. For example::
info:Texinfo#makeinfo_options info:Texinfo#makeinfo_options
- Inline markup
The standard formatting for ``*strong*`` and ``_emphasis_`` can
result in ambiguous output when used to markup parameter names and
other values. Since this is a fairly common practice, the default
formatting has been changed so that ``emphasis`` and ``strong`` are
now displayed like ```literal'``\s.
The standard formatting can be re-enabled by adding the following to
your :file:`conf.py`::
texinfo_elements = {'preamble': """
@definfoenclose strong,*,*
@definfoenclose emph,_,_
"""}

View File

@ -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

View File

@ -100,6 +100,27 @@ But you can also explicitly enable the pending ones using e.g.
``PYTHONWARNINGS=default`` (see the :ref:`Python docs on configuring warnings ``PYTHONWARNINGS=default`` (see the :ref:`Python docs on configuring warnings
<python:describing-warning-filters>`) for more details. <python:describing-warning-filters>`) for more details.
Python version support policy
-----------------------------
The minimum Python version Sphinx supports is the default Python version
installed in the oldest `Long Term Support version of
Ubuntu <https://ubuntu.com/about/release-cycle>`_ that has standard support.
For example, as of July 2021, Ubuntu 16.04 has just entered extended
security maintenance (therefore, it doesn't count as standard support) and
the oldest LTS release to consider is Ubuntu 18.04 LTS, supported until
April 2023 and shipping Python 3.6.
This is a summary table with the current policy:
========== ========= ======
Date Ubuntu Python
========== ========= ======
April 2021 18.04 LTS 3.6+
---------- --------- ------
April 2023 20.04 LTS 3.8+
========== ========= ======
Release procedures Release procedures
------------------ ------------------

View File

@ -75,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

View File

@ -304,6 +304,22 @@ variables to customize behavior:
Additional options for :program:`sphinx-build`. These options can Additional options for :program:`sphinx-build`. These options can
also be set via the shortcut variable **O** (capital 'o'). also be set via the shortcut variable **O** (capital 'o').
.. describe:: NO_COLOR
When set (regardless of value), :program:`sphinx-build` will not use color
in terminal output. ``NO_COLOR`` takes precedence over ``FORCE_COLOR``. See
`no-color.org <https://no-color.org/>`__ for other libraries supporting this
community standard.
.. versionadded:: 4.5.0
.. describe:: FORCE_COLOR
When set (regardless of value), :program:`sphinx-build` will use color in
terminal output. ``NO_COLOR`` takes precedence over ``FORCE_COLOR``.
.. versionadded:: 4.5.0
.. _when-deprecation-warnings-are-displayed: .. _when-deprecation-warnings-are-displayed:
Deprecation Warnings Deprecation Warnings

View File

@ -4,10 +4,9 @@ Automatic documentation generation from code
In the :ref:`previous section <tutorial-describing-objects>` of the tutorial In the :ref:`previous section <tutorial-describing-objects>` of the tutorial
you manually documented a Python function in Sphinx. However, the description 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 was out of sync with the code itself, since the function signature was not
the same. Besides, it would be nice to reuse `Python the same. Besides, it would be nice to reuse :pep:`Python docstrings
docstrings <https://www.python.org/dev/peps/pep-0257/#what-is-a-docstring>`_ <257#what-is-a-docstring>` in the documentation, rather than having to write
in the documentation, rather than having to write the information in two the information in two places.
places.
Fortunately, :doc:`the autodoc extension </usage/extensions/autodoc>` provides this Fortunately, :doc:`the autodoc extension </usage/extensions/autodoc>` provides this
functionality. functionality.

279
doc/tutorial/deploying.rst Normal file
View 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.

View File

@ -57,7 +57,7 @@ Notice several things:
- Sphinx parsed the argument of the ``.. py:function`` directive and - Sphinx parsed the argument of the ``.. py:function`` directive and
highlighted the module, the function name, and the parameters appropriately. highlighted the module, the function name, and the parameters appropriately.
- The directive content includes a one-line description of the function, - 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 as well as an :ref:`info field list <info-field-lists>` containing the function
parameter, its expected type, the return value, and the return type. parameter, its expected type, the return value, and the return type.
.. note:: .. note::

View File

@ -35,4 +35,5 @@ project.
narrative-documentation narrative-documentation
describing-code describing-code
automatic-doc-generation automatic-doc-generation
deploying
end end

View File

@ -68,6 +68,24 @@ be translated you need to follow these instructions:
* Run your desired build. * Run your desired build.
In order to protect against mistakes, a warning is emitted if
cross-references in the translated paragraph do not match those from the
original. This can be turned off globally using the
:confval:`suppress_warnings` configuration variable. Alternatively, to
turn it off for one message only, end the message with ``#noqa`` like
this::
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse
risus tortor, luctus id ultrices at. #noqa
(Write ``\#noqa`` in case you want to have "#noqa" literally in the
text. This does not apply to code blocks, where ``#noqa`` is ignored
because code blocks do not contain references anyway.)
.. versionadded:: 4.5
The ``#noqa`` mechanism.
Translating with sphinx-intl Translating with sphinx-intl
---------------------------- ----------------------------

View File

@ -6,6 +6,10 @@ Setuptools integration
Sphinx supports integration with setuptools and distutils through a custom Sphinx supports integration with setuptools and distutils through a custom
command - :class:`~sphinx.setup_command.BuildDoc`. command - :class:`~sphinx.setup_command.BuildDoc`.
.. deprecated:: 5.0
This feature will be removed in v7.0.
Using setuptools integration Using setuptools integration
---------------------------- ----------------------------
@ -64,7 +68,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 +79,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 +92,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 +109,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 +121,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 +135,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 +173,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

View File

@ -316,7 +316,11 @@ General configuration
* ``app.add_role`` * ``app.add_role``
* ``app.add_generic_role`` * ``app.add_generic_role``
* ``app.add_source_parser`` * ``app.add_source_parser``
* ``autosectionlabel.*``
* ``download.not_readable`` * ``download.not_readable``
* ``epub.unknown_project_files``
* ``epub.duplicated_toc_entry``
* ``i18n.inconsistent_references``
* ``image.not_readable`` * ``image.not_readable``
* ``ref.term`` * ``ref.term``
* ``ref.ref`` * ``ref.ref``
@ -332,11 +336,9 @@ General configuration
* ``toc.excluded`` * ``toc.excluded``
* ``toc.not_readable`` * ``toc.not_readable``
* ``toc.secnum`` * ``toc.secnum``
* ``epub.unknown_project_files``
* ``epub.duplicated_toc_entry``
* ``autosectionlabel.*``
You can choose from these types. You can choose from these types. You can also give only the first
component to exclude all warnings attached to it.
Now, this option should be considered *experimental*. Now, this option should be considered *experimental*.
@ -366,6 +368,10 @@ General configuration
Added ``toc.excluded`` and ``toc.not_readable`` Added ``toc.excluded`` and ``toc.not_readable``
.. versionadded:: 4.5
Added ``i18n.inconsistent_references``
.. 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
@ -392,7 +398,7 @@ General configuration
.. confval:: manpages_url .. confval:: manpages_url
A URL to cross-reference :rst:role:`manpage` directives. If this is A URL to cross-reference :rst:role:`manpage` roles. If this is
defined to ``https://manpages.debian.org/{path}``, the defined to ``https://manpages.debian.org/{path}``, the
:literal:`:manpage:`man(1)`` role will link to :literal:`:manpage:`man(1)`` role will link to
<https://manpages.debian.org/man(1)>. The patterns available are: <https://manpages.debian.org/man(1)>. The patterns available are:
@ -1351,6 +1357,13 @@ that use Sphinx's HTMLWriter class.
.. versionadded:: 1.0 .. versionadded:: 1.0
.. confval:: html_show_search_summary
If true, the text around the keyword is shown as summary of each search result.
Default is ``True``.
.. versionadded:: 4.5
.. confval:: html_show_sphinx .. confval:: html_show_sphinx
If true, "Created using Sphinx" is shown in the HTML footer. Default is If true, "Created using Sphinx" is shown in the HTML footer. Default is
@ -2499,6 +2512,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:
@ -2683,6 +2703,19 @@ Options for the linkcheck builder
.. 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
--------------------------- ---------------------------

View File

@ -41,7 +41,7 @@ you can also enable the :mod:`napoleon <sphinx.ext.napoleon>` extension.
docstrings to correct reStructuredText before :mod:`autodoc` processes them. docstrings to correct reStructuredText before :mod:`autodoc` processes them.
.. _Google: https://github.com/google/styleguide/blob/gh-pages/pyguide.md#38-comments-and-docstrings .. _Google: https://github.com/google/styleguide/blob/gh-pages/pyguide.md#38-comments-and-docstrings
.. _NumPy: https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt .. _NumPy: https://numpydoc.readthedocs.io/en/latest/format.html#docstring-standard
Directives Directives
@ -468,7 +468,7 @@ There are also config values that you can set:
.. confval:: autodoc_class_signature .. confval:: autodoc_class_signature
This value selects how the signautre will be displayed for the class defined This value selects how the signature will be displayed for the class defined
by :rst:dir:`autoclass` directive. The possible values are: by :rst:dir:`autoclass` directive. The possible values are:
``"mixed"`` ``"mixed"``
@ -528,7 +528,8 @@ There are also config values that you can set:
The supported options are ``'members'``, ``'member-order'``, The supported options are ``'members'``, ``'member-order'``,
``'undoc-members'``, ``'private-members'``, ``'special-members'``, ``'undoc-members'``, ``'private-members'``, ``'special-members'``,
``'inherited-members'``, ``'show-inheritance'``, ``'ignore-module-all'``, ``'inherited-members'``, ``'show-inheritance'``, ``'ignore-module-all'``,
``'imported-members'``, ``'exclude-members'`` and ``'class-doc-from'``. ``'imported-members'``, ``'exclude-members'``, ``'class-doc-from'`` and
``'no-value'``.
.. versionadded:: 1.8 .. versionadded:: 1.8
@ -541,6 +542,9 @@ There are also config values that you can set:
.. versionchanged:: 4.1 .. versionchanged:: 4.1
Added ``'class-doc-from'``. Added ``'class-doc-from'``.
.. versionchanged:: 4.5
Added ``'no-value'``.
.. confval:: autodoc_docstring_signature .. confval:: autodoc_docstring_signature
Functions imported from C modules cannot be introspected, and therefore the Functions imported from C modules cannot be introspected, and therefore the
@ -638,8 +642,8 @@ There are also config values that you can set:
full-qualified object name. It is used to keep type aliases not evaluated in full-qualified object name. It is used to keep type aliases not evaluated in
the document. Defaults to empty (``{}``). the document. Defaults to empty (``{}``).
The type aliases are only available if your program enables `Postponed The type aliases are only available if your program enables :pep:`Postponed
Evaluation of Annotations (PEP 563)`__ feature via ``from __future__ import Evaluation of Annotations (PEP 563) <563>` feature via ``from __future__ import
annotations``. annotations``.
For example, there is code using a type alias:: For example, there is code using a type alias::
@ -666,10 +670,24 @@ There are also config values that you can set:
... ...
.. __: https://www.python.org/dev/peps/pep-0563/
.. __: 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
* ``'short'`` -- Suppress the leading module names of the typehints
(ex. ``io.StringIO`` -> ``StringIO``) (default)
.. versionadded:: 4.4
.. versionchanged:: 5.0
The default setting was changed to ``'short'``
.. 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

View File

@ -255,7 +255,7 @@ Autosummary uses the following Jinja template files:
- :file:`autosummary/attribute.rst` -- template for class attributes - :file:`autosummary/attribute.rst` -- template for class attributes
- :file:`autosummary/method.rst` -- template for class methods - :file:`autosummary/method.rst` -- template for class methods
The following variables available in the templates: The following variables are available in the templates:
.. currentmodule:: None .. currentmodule:: None
@ -300,7 +300,7 @@ The following variables available in the templates:
.. data:: functions .. data:: functions
List containing names of "public" functions in the module. Here, "public" List containing names of "public" functions in the module. Here, "public"
here means that the name does not start with an underscore. Only available means that the name does not start with an underscore. Only available
for modules. for modules.
.. data:: classes .. data:: classes

View File

@ -93,8 +93,8 @@ a comma-separated list of group names.
* ``<``, ``>``: Exclusive ordered comparison clause * ``<``, ``>``: Exclusive ordered comparison clause
* ``===``: Arbitrary equality clause. * ``===``: Arbitrary equality clause.
``pyversion`` option is followed `PEP-440: Version Specifiers ``pyversion`` option is followed :pep:`PEP-440: Version Specifiers
<https://www.python.org/dev/peps/pep-0440/#version-specifiers>`__. <440#version-specifiers>`.
.. versionadded:: 1.6 .. versionadded:: 1.6

View File

@ -45,7 +45,7 @@ on the first line, separated by a colon.
def function_with_types_in_docstring(param1, param2): def function_with_types_in_docstring(param1, param2):
"""Example function with types documented in the docstring. """Example function with types documented in the docstring.
`PEP 484`_ type annotations are supported. If attribute, parameter, and :pep:`484` type annotations are supported. If attribute, parameter, and
return types are annotated according to `PEP 484`_, they do not need to be return types are annotated according to `PEP 484`_, they do not need to be
included in the docstring: included in the docstring:
@ -55,10 +55,6 @@ def function_with_types_in_docstring(param1, param2):
Returns: Returns:
bool: The return value. True for success, False otherwise. bool: The return value. True for success, False otherwise.
.. _PEP 484:
https://www.python.org/dev/peps/pep-0484/
""" """
@ -311,4 +307,4 @@ class ExamplePEP526Class:
""" """
attr1: str attr1: str
attr2: int attr2: int

View File

@ -37,8 +37,8 @@ module_level_variable1 : int
with it. with it.
.. _NumPy Documentation HOWTO: .. _NumPy docstring standard:
https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt https://numpydoc.readthedocs.io/en/latest/format.html#docstring-standard
""" """
@ -55,7 +55,7 @@ on the first line, separated by a colon.
def function_with_types_in_docstring(param1, param2): def function_with_types_in_docstring(param1, param2):
"""Example function with types documented in the docstring. """Example function with types documented in the docstring.
`PEP 484`_ type annotations are supported. If attribute, parameter, and :pep:`484` type annotations are supported. If attribute, parameter, and
return types are annotated according to `PEP 484`_, they do not need to be return types are annotated according to `PEP 484`_, they do not need to be
included in the docstring: included in the docstring:
@ -70,10 +70,6 @@ def function_with_types_in_docstring(param1, param2):
------- -------
bool bool
True if successful, False otherwise. True if successful, False otherwise.
.. _PEP 484:
https://www.python.org/dev/peps/pep-0484/
""" """

View File

@ -59,3 +59,11 @@ The extension adds a config value:
Since links are generated from the role in the reading stage, they appear as Since links are generated from the role in the reading stage, they appear as
ordinary links to e.g. the ``linkcheck`` builder. ordinary links to e.g. the ``linkcheck`` builder.
.. confval:: extlinks_detect_hardcoded_links
If enabled, extlinks emits a warning if a hardcoded link is replaceable
by an extlink, and suggests a replacement via warning. It defaults to
``False``.
.. versionadded:: 4.5

View File

@ -8,20 +8,25 @@
.. versionadded:: 0.5 .. versionadded:: 0.5
This extension can generate automatic links to the documentation of objects in This extension can generate links to the documentation of objects in external
other projects. projects, either explicitly through the :rst:role:`external` role, or as a
fallback resolution for any other cross-reference.
Usage is simple: whenever Sphinx encounters a cross-reference that has no Usage for fallback resolution is simple: whenever Sphinx encounters a
matching target in the current documentation set, it looks for targets in the cross-reference that has no matching target in the current documentation set,
documentation sets configured in :confval:`intersphinx_mapping`. A reference it looks for targets in the external documentation sets configured in
like ``:py:class:`zipfile.ZipFile``` can then link to the Python documentation :confval:`intersphinx_mapping`. A reference like
``:py:class:`zipfile.ZipFile``` can then link to the Python documentation
for the ZipFile class, without you having to specify where it is located for the ZipFile class, without you having to specify where it is located
exactly. exactly.
When using the "new" format (see below), you can even force lookup in a foreign When using the :rst:role:`external` role, you can force lookup to any external
set by prefixing the link target appropriately. A link like ``:ref:`comparison projects, and optionally to a specific external project.
manual <python:comparisons>``` will then link to the label "comparisons" in the A link like ``:external:ref:`comparison manual <comparisons>``` will then link
doc set "python", if it exists. to the label "comparisons" in whichever configured external project, if it
exists,
and a link like ``:external+python:ref:`comparison manual <comparisons>``` will
link to the label "comparisons" only in the doc set "python", if it exists.
Behind the scenes, this works as follows: Behind the scenes, this works as follows:
@ -30,8 +35,8 @@ Behind the scenes, this works as follows:
* Projects using the Intersphinx extension can specify the location of such * Projects using the Intersphinx extension can specify the location of such
mapping files in the :confval:`intersphinx_mapping` config value. The mapping mapping files in the :confval:`intersphinx_mapping` config value. The mapping
will then be used to resolve otherwise missing references to objects into will then be used to resolve both :rst:role:`external` references, and also
links to the other documentation. otherwise missing references to objects into links to the other documentation.
* By default, the mapping file is assumed to be at the same location as the rest * By default, the mapping file is assumed to be at the same location as the rest
of the documentation; however, the location of the mapping file can also be of the documentation; however, the location of the mapping file can also be
@ -79,10 +84,10 @@ linking:
at the same location as the base URI) or another local file path or a full at the same location as the base URI) or another local file path or a full
HTTP URI to an inventory file. HTTP URI to an inventory file.
The unique identifier can be used to prefix cross-reference targets, so that The unique identifier can be used in the :rst:role:`external` role, so that
it is clear which intersphinx set the target belongs to. A link like it is clear which intersphinx set the target belongs to. A link like
``:ref:`comparison manual <python:comparisons>``` will link to the label ``external:python+ref:`comparison manual <comparisons>``` will link to the
"comparisons" in the doc set "python", if it exists. label "comparisons" in the doc set "python", if it exists.
**Example** **Example**
@ -152,6 +157,10 @@ linking:
.. versionadded:: 4.3 .. versionadded:: 4.3
.. versionchanged:: 5.0
Changed default value from an empty list to ``['std:doc']``.
A list of strings being either: A list of strings being either:
- the name of a specific reference type in a domain, - the name of a specific reference type in a domain,
@ -160,23 +169,52 @@ linking:
``std:*``, ``py:*``, or ``cpp:*``, or ``std:*``, ``py:*``, or ``cpp:*``, or
- simply a wildcard ``*``. - simply a wildcard ``*``.
The default value is an empty list. The default value is ``['std:doc']``.
When a cross-reference without an explicit inventory specification is being When a non-:rst:role:`external` cross-reference is being resolved by
resolved by intersphinx, skip resolution if it matches one of the intersphinx, skip resolution if it matches one of the specifications in this
specifications in this list. list.
For example, with ``intersphinx_disabled_reftypes = ['std:doc']`` For example, with ``intersphinx_disabled_reftypes = ['std:doc']``
a cross-reference ``:doc:`installation``` will not be attempted to be a cross-reference ``:doc:`installation``` will not be attempted to be
resolved by intersphinx, but ``:doc:`otherbook:installation``` will be resolved by intersphinx, but ``:external+otherbook:doc:`installation``` will
attempted to be resolved in the inventory named ``otherbook`` in be attempted to be resolved in the inventory named ``otherbook`` in
:confval:`intersphinx_mapping`. :confval:`intersphinx_mapping`.
At the same time, all cross-references generated in, e.g., Python, At the same time, all cross-references generated in, e.g., Python,
declarations will still be attempted to be resolved by intersphinx. declarations will still be attempted to be resolved by intersphinx.
If ``*`` is in the list of domains, then no references without an explicit If ``*`` is in the list of domains, then no non-:rst:role:`external`
inventory will be resolved by intersphinx. references will be resolved by intersphinx.
Explicitly Reference External Objects
-------------------------------------
The Intersphinx extension provides the following role.
.. rst:role:: external
.. versionadded:: 4.4
Use Intersphinx to perform lookup only in external projects, and not the
current project. Intersphinx still needs to know the type of object you
would like to find, so the general form of this role is to write the
cross-refererence as if the object is in the current project, but then prefix
it with ``:external``.
The two forms are then
- ``:external:domain:reftype:`target```,
e.g., ``:external:py:class:`zipfile.ZipFile```, or
- ``:external:reftype:`target```,
e.g., ``:external:doc:`installation```.
If you would like to constrain the lookup to a specific external project,
then the key of the project, as specified in :confval:`intersphinx_mapping`,
is added as well to get the two forms
- ``:external+invname:domain:reftype:`target```,
e.g., ``:external+python:py:class:`zipfile.ZipFile```, or
- ``:external+invname:reftype:`target```,
e.g., ``:external+python:doc:`installation```.
Showing all links of an Intersphinx mapping file Showing all links of an Intersphinx mapping file
------------------------------------------------ ------------------------------------------------

View File

@ -26,7 +26,7 @@ Are you tired of writing docstrings that look like this::
:rtype: BufferedFileStorage :rtype: BufferedFileStorage
`reStructuredText`_ is great, but it creates visually dense, hard to read `reStructuredText`_ is great, but it creates visually dense, hard to read
`docstrings`_. Compare the jumble above to the same thing rewritten :pep:`docstrings <287>`. Compare the jumble above to the same thing rewritten
according to the `Google Python Style Guide`_:: according to the `Google Python Style Guide`_::
Args: Args:
@ -50,7 +50,6 @@ the documentation, so it doesn't modify any of the docstrings in your actual
source code files. source code files.
.. _ReStructuredText: https://docutils.sourceforge.io/rst.html .. _ReStructuredText: https://docutils.sourceforge.io/rst.html
.. _docstrings: https://www.python.org/dev/peps/pep-0287/
.. _Google Python Style Guide: .. _Google Python Style Guide:
https://google.github.io/styleguide/pyguide.html https://google.github.io/styleguide/pyguide.html
.. _Google: .. _Google:
@ -199,11 +198,11 @@ not be mixed. Choose one style for your project and be consistent with it.
Type Annotations Type Annotations
~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~
`PEP 484`_ introduced a standard way to express types in Python code. :pep:`484` introduced a standard way to express types in Python code.
This is an alternative to expressing types directly in docstrings. This is an alternative to expressing types directly in docstrings.
One benefit of expressing types according to `PEP 484`_ is that One benefit of expressing types according to :pep:`484` is that
type checkers and IDEs can take advantage of them for static code type checkers and IDEs can take advantage of them for static code
analysis. `PEP 484`_ was then extended by `PEP 526`_ which introduced analysis. :pep:`484` was then extended by :pep:`526` which introduced
a similar way to annotate variables (and attributes). a similar way to annotate variables (and attributes).
Google style with Python 3 type annotations:: Google style with Python 3 type annotations::
@ -267,9 +266,7 @@ Google style with types in docstrings::
`Python 2/3 compatible annotations`_ aren't currently `Python 2/3 compatible annotations`_ aren't currently
supported by Sphinx and won't show up in the docs. supported by Sphinx and won't show up in the docs.
.. _PEP 484: https://www.python.org/dev/peps/pep-0484/ .. _Python 2/3 compatible annotations: https://peps.python.org/pep-0484/#suggested-syntax-for-python-2-7-and-straddling-code
.. _PEP 526: https://www.python.org/dev/peps/pep-0526/
.. _Python 2/3 compatible annotations: https://www.python.org/dev/peps/pep-0484/#suggested-syntax-for-python-2-7-and-straddling-code
Configuration Configuration
@ -548,7 +545,7 @@ sure that "sphinx.ext.napoleon" is enabled in `conf.py`::
.. confval:: napoleon_attr_annotations .. confval:: napoleon_attr_annotations
True to allow using `PEP 526`_ attributes annotations in classes. True to allow using :pep:`526` attributes annotations in classes.
If an attribute is documented in the docstring without a type and If an attribute is documented in the docstring without a type and
has an annotation in the class body, that type is used. has an annotation in the class body, that type is used.

View File

@ -222,8 +222,8 @@ of images:
- `sphinxdoc/sphinx-latexpdf`_ - `sphinxdoc/sphinx-latexpdf`_
.. _Docker Hub: https://hub.docker.com/ .. _Docker Hub: https://hub.docker.com/
.. _sphinxdoc/sphinx: https://hub.docker.com/repository/docker/sphinxdoc/sphinx .. _sphinxdoc/sphinx: https://hub.docker.com/r/sphinxdoc/sphinx
.. _sphinxdoc/sphinx-latexpdf: https://hub.docker.com/repository/docker/sphinxdoc/sphinx-latexpdf .. _sphinxdoc/sphinx-latexpdf: https://hub.docker.com/r/sphinxdoc/sphinx-latexpdf
Former one is used for standard usage of Sphinx, and latter one is mainly used for Former one is used for standard usage of Sphinx, and latter one is mainly used for
PDF builds using LaTeX. Please choose one for your purpose. PDF builds using LaTeX. Please choose one for your purpose.
@ -239,19 +239,19 @@ 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 use the following command 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
For more details, please read `README file`__ of docker images. For more details, please read `README file`__ of docker images.
.. __: https://hub.docker.com/repository/docker/sphinxdoc/sphinx .. __: https://hub.docker.com/r/sphinxdoc/sphinx
Installation from source Installation from source

View File

@ -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

View File

@ -245,10 +245,10 @@ follow:
* ``#`` with overline, for parts * ``#`` with overline, for parts
* ``*`` with overline, for chapters * ``*`` with overline, for chapters
* ``=``, for sections * ``=`` for sections
* ``-``, for subsections * ``-`` for subsections
* ``^``, for subsubsections * ``^`` for subsubsections
* ``"``, for paragraphs * ``"`` for paragraphs
Of course, you are free to use your own marker characters (see the reST Of course, you are free to use your own marker characters (see the reST
documentation), and use a deeper nesting level, but keep in mind that most documentation), and use a deeper nesting level, but keep in mind that most

View File

@ -598,6 +598,7 @@ __ https://pygments.org/docs/lexers
are removed via :func:`textwrap.dedent()`. For example:: are removed via :func:`textwrap.dedent()`. For example::
.. code-block:: ruby .. code-block:: ruby
:linenos:
:dedent: 4 :dedent: 4
some ruby code some ruby code
@ -831,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
----------------------- -----------------------

View File

@ -211,6 +211,12 @@ The following directives are provided for module and class contents:
.. versionadded:: 4.0 .. versionadded:: 4.0
.. rst::directive:option:: module
:type: text
Describe the location where the object is defined. The default value is
the module specified by :rst:dir:`py:currentmodule`.
.. rst:directive:: .. py:data:: name .. rst:directive:: .. py:data:: name
Describes global data in a module, including both variables and values used Describes global data in a module, including both variables and values used
@ -237,6 +243,12 @@ The following directives are provided for module and class contents:
.. versionadded:: 4.0 .. versionadded:: 4.0
.. rst::directive:option:: module
:type: text
Describe the location where the object is defined. The default value is
the module specified by :rst:dir:`py:currentmodule`.
.. rst:directive:: .. py:exception:: name .. rst:directive:: .. py:exception:: name
Describes an exception class. The signature can, but need not include Describes an exception class. The signature can, but need not include
@ -251,6 +263,12 @@ The following directives are provided for module and class contents:
.. versionadded:: 3.1 .. versionadded:: 3.1
.. rst::directive:option:: module
:type: text
Describe the location where the object is defined. The default value is
the module specified by :rst:dir:`py:currentmodule`.
.. rst:directive:: .. py:class:: name .. rst:directive:: .. py:class:: name
.. py:class:: name(parameters) .. py:class:: name(parameters)
@ -291,6 +309,12 @@ The following directives are provided for module and class contents:
.. versionadded:: 3.1 .. versionadded:: 3.1
.. rst::directive:option:: module
:type: text
Describe the location where the object is defined. The default value is
the module specified by :rst:dir:`py:currentmodule`.
.. rst:directive:: .. py:attribute:: name .. rst:directive:: .. py:attribute:: name
Describes an object data attribute. The description should include Describes an object data attribute. The description should include
@ -317,6 +341,12 @@ The following directives are provided for module and class contents:
.. versionadded:: 4.0 .. versionadded:: 4.0
.. rst::directive:option:: module
:type: text
Describe the location where the object is defined. The default value is
the module specified by :rst:dir:`py:currentmodule`.
.. rst:directive:: .. py:property:: name .. rst:directive:: .. py:property:: name
Describes an object property. Describes an object property.
@ -340,6 +370,12 @@ The following directives are provided for module and class contents:
.. rst:directive:option:: type: type of the property .. rst:directive:option:: type: type of the property
:type: text :type: text
.. rst::directive:option:: module
:type: text
Describe the location where the object is defined. The default value is
the module specified by :rst:dir:`py:currentmodule`.
.. rst:directive:: .. py:method:: name(parameters) .. rst:directive:: .. py:method:: name(parameters)
Describes an object method. The parameters should not include the ``self`` Describes an object method. The parameters should not include the ``self``
@ -385,6 +421,12 @@ The following directives are provided for module and class contents:
.. versionadded:: 3.1 .. versionadded:: 3.1
.. rst::directive:option:: module
:type: text
Describe the location where the object is defined. The default value is
the module specified by :rst:dir:`py:currentmodule`.
.. rst:directive:option:: property .. rst:directive:option:: property
:type: no value :type: no value

View File

@ -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
@ -158,9 +158,18 @@ These themes are:
dimension string such as '70em' or '50%'. Use 'none' if you don't dimension string such as '70em' or '50%'. Use 'none' if you don't
want a width limit. Defaults may depend on the theme (often 800px). want a width limit. Defaults may depend on the theme (often 800px).
- **navigation_with_keys** (true or false): Allow navigating to the - **navigation_with_keys** (true or false): Allow navigating
previous/next page using the keyboard's left and right arrows. Defaults to with the following keyboard shortcuts:
``False``.
- :kbd:`Left arrow`: previous page
- :kbd:`Right arrow`: next page
Defaults to ``False``.
- **enable_search_shortcuts** (true or false): Allow jumping to the search box
with :kbd:`/` and allow removal of search highlighting with :kbd:`Esc`.
Defaults to ``True``.
- **globaltoc_collapse** (true or false): Only expand subsections - **globaltoc_collapse** (true or false): Only expand subsections
of the current document in ``globaltoc.html`` of the current document in ``globaltoc.html``

View File

@ -15,8 +15,6 @@ module.exports = function(config) {
// list of files / patterns to load in the browser // list of files / patterns to load in the browser
files: [ files: [
'sphinx/themes/basic/static/underscore.js',
'sphinx/themes/basic/static/jquery.js',
'sphinx/themes/basic/static/doctools.js', 'sphinx/themes/basic/static/doctools.js',
'sphinx/themes/basic/static/searchtools.js', 'sphinx/themes/basic/static/searchtools.js',
'tests/js/*.js' 'tests/js/*.js'
@ -59,7 +57,7 @@ module.exports = function(config) {
// start these browsers // start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: ['Chrome', 'Firefox'], browsers: ["Firefox"],
// Continuous Integration mode // Continuous Integration mode

2573
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -12,9 +12,8 @@
}, },
"devDependencies": { "devDependencies": {
"jasmine-core": "^3.4.0", "jasmine-core": "^3.4.0",
"karma": "^4.0.0", "karma": "^6.3.16",
"karma-chrome-launcher": "^3.0.0", "karma-firefox-launcher": "^2.0.0",
"karma-firefox-launcher": "^1.1.0", "karma-jasmine": "^4.0.0"
"karma-jasmine": "^2.0.0"
} }
} }

View File

@ -1,5 +1,5 @@
[metadata] [metadata]
license_file = LICENSE license_files = LICENSE
[egg_info] [egg_info]
tag_build = .dev tag_build = .dev
@ -35,12 +35,6 @@ import-order-style = smarkets
per-file-ignores = per-file-ignores =
tests/*: E501 tests/*: E501
[flake8:local-plugins]
extension =
X101 = utils.checks:sphinx_has_header
paths =
.
[isort] [isort]
line_length = 95 line_length = 95

View File

@ -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:

View File

@ -1,12 +1,4 @@
""" """The Sphinx documentation toolchain."""
Sphinx
~~~~~~
The Sphinx documentation toolchain.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
# Keep this file executable as-is in Python 3! # Keep this file executable as-is in Python 3!
# (Otherwise getting the version out of it from setup.py is impossible.) # (Otherwise getting the version out of it from setup.py is impossible.)
@ -27,8 +19,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.4.0+' __version__ = '5.0.0+'
__released__ = '4.4.0' # used when Sphinx builds its own docs __released__ = '5.0.0' # used when Sphinx builds its own docs
#: Version info for better programmatic use. #: Version info for better programmatic use.
#: #:
@ -38,7 +30,7 @@ __released__ = '4.4.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, 4, 0, 'beta', 0) version_info = (5, 0, 0, 'final', 0)
package_dir = path.abspath(path.dirname(__file__)) package_dir = path.abspath(path.dirname(__file__))

View File

@ -1,12 +1,4 @@
""" """The Sphinx documentation toolchain."""
sphinx.__main__
~~~~~~~~~~~~~~~
The Sphinx documentation toolchain.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import sys import sys

View File

@ -1,12 +1,4 @@
""" """Additional docutils nodes."""
sphinx.addnodes
~~~~~~~~~~~~~~~
Additional docutils nodes.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from typing import TYPE_CHECKING, Any, Dict, List, Sequence from typing import TYPE_CHECKING, Any, Dict, List, Sequence
@ -16,6 +8,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.
@ -85,7 +84,7 @@ class toctree(nodes.General, nodes.Element, translatable):
def preserve_original_messages(self) -> None: def preserve_original_messages(self) -> None:
# toctree entries # toctree entries
rawentries = self.setdefault('rawentries', []) rawentries = self.setdefault('rawentries', [])
for title, docname in self['entries']: for title, _docname in self['entries']:
if title: if title:
rawentries.append(title) rawentries.append(title)

View File

@ -1,18 +1,10 @@
""" """Sphinx application class and extensibility interface.
sphinx.application
~~~~~~~~~~~~~~~~~~
Sphinx application class and extensibility interface. 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.
: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 +187,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
@ -273,7 +259,7 @@ class Sphinx:
"""Load translated strings from the configured localedirs if enabled in """Load translated strings from the configured localedirs if enabled in
the configuration. the configuration.
""" """
if self.config.language is None: if self.config.language == 'en':
self.translator, has_translation = locale.init([], None) self.translator, has_translation = locale.init([], None)
else: else:
logger.info(bold(__('loading translations [%s]... ') % self.config.language), logger.info(bold(__('loading translations [%s]... ') % self.config.language),
@ -292,8 +278,7 @@ class Sphinx:
locale_dirs += [path.join(package_dir, 'locale')] locale_dirs += [path.join(package_dir, 'locale')]
self.translator, has_translation = locale.init(locale_dirs, self.config.language) self.translator, has_translation = locale.init(locale_dirs, self.config.language)
if has_translation or self.config.language == 'en': if has_translation:
# "en" never needs to be translated
logger.info(__('done')) logger.info(__('done'))
else: else:
logger.info(__('not available for built-in messages')) logger.info(__('not available for built-in messages'))
@ -937,24 +922,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';")
@ -983,7 +975,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
@ -991,12 +991,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::

View File

@ -1,12 +1,4 @@
""" """Builder superclass for all builders."""
sphinx.builders
~~~~~~~~~~~~~~~
Builder superclass for all builders.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import pickle import pickle
import time import time
@ -176,7 +168,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
@ -562,7 +554,7 @@ class Builder:
for chunk in status_iterator(chunks, __('writing output... '), "darkgreen", for chunk in status_iterator(chunks, __('writing output... '), "darkgreen",
len(chunks), self.app.verbosity): len(chunks), self.app.verbosity):
arg = [] arg = []
for i, docname in enumerate(chunk): for docname in chunk:
doctree = self.env.get_and_resolve_doctree(docname, self) doctree = self.env.get_and_resolve_doctree(docname, self)
self.write_doc_serialized(docname, doctree) self.write_doc_serialized(docname, doctree)
arg.append((docname, doctree)) arg.append((docname, doctree))

View File

@ -1,12 +1,4 @@
""" """Base class of epub2/epub3 builders."""
sphinx.builders._epub_base
~~~~~~~~~~~~~~~~~~~~~~~~~~
Base class of epub2/epub3 builders.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import html import html
import os import os
@ -277,7 +269,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 +277,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 +315,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 = list(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 = list(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 +331,10 @@ class EpubBuilder(StandaloneHTMLBuilder):
if show_urls == 'no': if show_urls == 'no':
return return
if show_urls == 'footnote': if show_urls == 'footnote':
doc = list(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 list(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():
@ -377,14 +369,14 @@ class EpubBuilder(StandaloneHTMLBuilder):
"""Fix href attributes for genindex pages.""" """Fix href attributes for genindex pages."""
# XXX: modifies tree inline # XXX: modifies tree inline
# Logic modeled from themes/basic/genindex.html # Logic modeled from themes/basic/genindex.html
for key, columns in tree: for _key, columns in tree:
for entryname, (links, subitems, key_) in columns: for _entryname, (links, subitems, _key) in columns:
for (i, (ismain, link)) in enumerate(links): for (i, (ismain, link)) in enumerate(links):
m = self.refuri_re.match(link) m = self.refuri_re.match(link)
if m: if m:
links[i] = (ismain, links[i] = (ismain,
self.fix_fragment(m.group(1), m.group(2))) self.fix_fragment(m.group(1), m.group(2)))
for subentryname, subentrylinks in subitems: for _subentryname, subentrylinks in subitems:
for (i, (ismain, link)) in enumerate(subentrylinks): for (i, (ismain, link)) in enumerate(subentrylinks):
m = self.refuri_re.match(link) m = self.refuri_re.match(link)
if m: if m:
@ -703,7 +695,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)

View File

@ -1,12 +1,4 @@
""" """Changelog builder."""
sphinx.builders.changes
~~~~~~~~~~~~~~~~~~~~~~~
Changelog builder.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import html import html
from os import path from os import path
@ -148,7 +140,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

View File

@ -1,12 +1,4 @@
""" """Directory HTML builders."""
sphinx.builders.dirhtml
~~~~~~~~~~~~~~~~~~~~~~~
Directory HTML builders.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from os import path from os import path
from typing import Any, Dict from typing import Any, Dict

View File

@ -1,12 +1,4 @@
""" """Do syntax checks, but no writing."""
sphinx.builders.dummy
~~~~~~~~~~~~~~~~~~~~~
Do syntax checks, but no writing.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from typing import Any, Dict, Set from typing import Any, Dict, Set

View File

@ -1,12 +1,6 @@
""" """Build epub3 files.
sphinx.builders.epub3
~~~~~~~~~~~~~~~~~~~~~
Build epub3 files. Originally derived from epub.py.
Originally derived from epub.py.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
""" """
import html import html

View File

@ -1,12 +1,4 @@
""" """The MessageCatalogBuilder class."""
sphinx.builders.gettext
~~~~~~~~~~~~~~~~~~~~~~~
The MessageCatalogBuilder class.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from codecs import open from codecs import open
from collections import OrderedDict, defaultdict from collections import OrderedDict, defaultdict
@ -146,7 +138,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)
@ -157,7 +149,7 @@ class I18nBuilder(Builder):
if 'index' in self.env.config.gettext_additional_targets: if 'index' in self.env.config.gettext_additional_targets:
# Extract translatable messages from index entries. # Extract translatable messages from index entries.
for node, entries in traverse_translatable_index(doctree): for node, entries in traverse_translatable_index(doctree):
for typ, msg, tid, main, key_ in entries: for typ, msg, _tid, _main, _key in entries:
for m in split_index_msg(typ, msg): for m in split_index_msg(typ, msg):
if typ == 'pair' and m in pairindextypes.values(): if typ == 'pair' and m in pairindextypes.values():
# avoid built-in translated message was incorporated # avoid built-in translated message was incorporated
@ -180,7 +172,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:
@ -227,7 +219,7 @@ class MessageCatalogBuilder(I18nBuilder):
template_files = set() template_files = set()
for template_path in self.config.templates_path: for template_path in self.config.templates_path:
tmpl_abs_path = path.join(self.app.srcdir, template_path) tmpl_abs_path = path.join(self.app.srcdir, template_path)
for dirpath, dirs, files in walk(tmpl_abs_path): for dirpath, _dirs, files in walk(tmpl_abs_path):
for fn in files: for fn in files:
if fn.endswith('.html'): if fn.endswith('.html'):
filename = canon_path(path.join(dirpath, fn)) filename = canon_path(path.join(dirpath, fn))
@ -247,7 +239,7 @@ class MessageCatalogBuilder(I18nBuilder):
try: try:
with open(template, encoding='utf-8') as f: with open(template, encoding='utf-8') as f:
context = f.read() context = f.read()
for line, meth, msg in extract_translations(context): for line, _meth, msg in extract_translations(context):
origin = MsgOrigin(template, line) origin = MsgOrigin(template, line)
self.catalogs['sphinx'].add(msg, origin) self.catalogs['sphinx'].add(msg, origin)
except Exception as exc: except Exception as exc:

View File

@ -1,12 +1,4 @@
""" """Several HTML builders."""
sphinx.builders.html
~~~~~~~~~~~~~~~~~~~~
Several HTML builders.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import html import html
import os import os
@ -15,7 +7,7 @@ import re
import sys import sys
from datetime import datetime from datetime import datetime
from os import path from os import path
from typing import IO, Any, Dict, Iterable, Iterator, List, Set, Tuple, Type from typing import IO, Any, Dict, Iterable, Iterator, List, Optional, Set, Tuple, Type
from urllib.parse import quote from urllib.parse import quote
from docutils import nodes from docutils import nodes
@ -76,6 +68,17 @@ def get_stable_hash(obj: Any) -> str:
return md5(str(obj).encode()).hexdigest() return md5(str(obj).encode()).hexdigest()
def convert_locale_to_language_tag(locale: Optional[str]) -> Optional[str]:
"""Convert a locale string to a language tag (ex. en_US -> en-US).
refs: BCP 47 (:rfc:`5646`)
"""
if locale:
return locale.replace('_', '-')
else:
return None
class Stylesheet(str): class Stylesheet(str):
"""A metadata of stylesheet. """A metadata of stylesheet.
@ -315,8 +318,11 @@ class StandaloneHTMLBuilder(Builder):
self.script_files = [] self.script_files = []
self.add_js_file('documentation_options.js', id="documentation_options", self.add_js_file('documentation_options.js', id="documentation_options",
data_url_root='', priority=200) data_url_root='', priority=200)
# Remove frameworks and compatability module below in Sphinx 6.0
# xref RemovedInSphinx60Warning
self.add_js_file('jquery.js', priority=200) self.add_js_file('jquery.js', priority=200)
self.add_js_file('underscore.js', priority=200) self.add_js_file('underscore.js', priority=200)
self.add_js_file('_sphinx_javascript_frameworks_compat.js', priority=200)
self.add_js_file('doctools.js', priority=200) self.add_js_file('doctools.js', priority=200)
for filename, attrs in self.app.registry.js_files: for filename, attrs in self.app.registry.js_files:
@ -326,7 +332,7 @@ class StandaloneHTMLBuilder(Builder):
attrs.setdefault('priority', 800) # User's JSs are loaded after extensions' attrs.setdefault('priority', 800) # User's JSs are loaded after extensions'
self.add_js_file(filename, **attrs) self.add_js_file(filename, **attrs)
if self.config.language and self._get_translations_js(): if self._get_translations_js():
self.add_js_file('translations.js') self.add_js_file('translations.js')
def add_js_file(self, filename: str, **kwargs: Any) -> None: def add_js_file(self, filename: str, **kwargs: Any) -> None:
@ -431,8 +437,6 @@ class StandaloneHTMLBuilder(Builder):
if self.search: if self.search:
from sphinx.search import IndexBuilder from sphinx.search import IndexBuilder
lang = self.config.html_search_language or self.config.language lang = self.config.html_search_language or self.config.language
if not lang:
lang = 'en'
self.indexer = IndexBuilder(self.env, lang, self.indexer = IndexBuilder(self.env, lang,
self.config.html_search_options, self.config.html_search_options,
self.config.html_search_scorer) self.config.html_search_scorer)
@ -486,7 +490,7 @@ class StandaloneHTMLBuilder(Builder):
rellinks: List[Tuple[str, str, str, str]] = [] rellinks: List[Tuple[str, str, str, str]] = []
if self.use_index: if self.use_index:
rellinks.append(('genindex', _('General Index'), 'I', _('index'))) rellinks.append(('genindex', _('General Index'), 'I', _('index')))
for indexname, indexcls, content, collapse in self.domain_indices: for indexname, indexcls, _content, _collapse in self.domain_indices:
# if it has a short name # if it has a short name
if indexcls.shortname: if indexcls.shortname:
rellinks.append((indexname, indexcls.localname, rellinks.append((indexname, indexcls.localname,
@ -509,6 +513,7 @@ class StandaloneHTMLBuilder(Builder):
'docstitle': self.config.html_title, 'docstitle': self.config.html_title,
'shorttitle': self.config.html_short_title, 'shorttitle': self.config.html_short_title,
'show_copyright': self.config.html_show_copyright, 'show_copyright': self.config.html_show_copyright,
'show_search_summary': self.config.html_show_search_summary,
'show_sphinx': self.config.html_show_sphinx, 'show_sphinx': self.config.html_show_sphinx,
'has_source': self.config.html_copy_source, 'has_source': self.config.html_copy_source,
'show_source': self.config.html_show_sourcelink, 'show_source': self.config.html_show_sourcelink,
@ -516,7 +521,7 @@ class StandaloneHTMLBuilder(Builder):
'file_suffix': self.out_suffix, 'file_suffix': self.out_suffix,
'link_suffix': self.link_suffix, 'link_suffix': self.link_suffix,
'script_files': self.script_files, 'script_files': self.script_files,
'language': self.config.language, 'language': convert_locale_to_language_tag(self.config.language),
'css_files': self.css_files, 'css_files': self.css_files,
'sphinx_version': __display_version__, 'sphinx_version': __display_version__,
'sphinx_version_tuple': sphinx_version, 'sphinx_version_tuple': sphinx_version,
@ -767,10 +772,9 @@ class StandaloneHTMLBuilder(Builder):
def copy_translation_js(self) -> None: def copy_translation_js(self) -> None:
"""Copy a JavaScript file for translations.""" """Copy a JavaScript file for translations."""
if self.config.language is not None: jsfile = self._get_translations_js()
jsfile = self._get_translations_js() if jsfile:
if jsfile: copyfile(jsfile, path.join(self.outdir, '_static', 'translations.js'))
copyfile(jsfile, path.join(self.outdir, '_static', 'translations.js'))
def copy_stemmer_js(self) -> None: def copy_stemmer_js(self) -> None:
"""Copy a JavaScript file for stemmer.""" """Copy a JavaScript file for stemmer."""
@ -866,7 +870,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.
@ -1340,6 +1344,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
app.add_config_value('html_file_suffix', None, 'html', [str]) app.add_config_value('html_file_suffix', None, 'html', [str])
app.add_config_value('html_link_suffix', None, 'html', [str]) app.add_config_value('html_link_suffix', None, 'html', [str])
app.add_config_value('html_show_copyright', True, 'html') app.add_config_value('html_show_copyright', True, 'html')
app.add_config_value('html_show_search_summary', True, 'html')
app.add_config_value('html_show_sphinx', True, 'html') app.add_config_value('html_show_sphinx', True, 'html')
app.add_config_value('html_context', {}, 'html') app.add_config_value('html_context', {}, 'html')
app.add_config_value('html_output_encoding', 'utf-8', 'html') app.add_config_value('html_output_encoding', 'utf-8', 'html')

View File

@ -1,12 +1,4 @@
""" """Transforms for HTML builder."""
sphinx.builders.html.transforms
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Transforms for HTML builder.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import re import re
from typing import Any, Dict, List from typing import Any, Dict, List
@ -36,7 +28,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 +40,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

View File

@ -1,15 +1,6 @@
""" """LaTeX builder."""
sphinx.builders.latex
~~~~~~~~~~~~~~~~~~~~~
LaTeX builder.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import os import os
import warnings
from os import path from os import path
from typing import Any, Dict, Iterable, List, Tuple, Union from typing import Any, Dict, Iterable, List, Tuple, Union
@ -24,7 +15,6 @@ from sphinx.builders.latex.constants import ADDITIONAL_SETTINGS, DEFAULT_SETTING
from sphinx.builders.latex.theming import Theme, ThemeFactory from sphinx.builders.latex.theming import Theme, ThemeFactory
from sphinx.builders.latex.util import ExtBabel from sphinx.builders.latex.util import ExtBabel
from sphinx.config import ENUM, Config from sphinx.config import ENUM, Config
from sphinx.deprecation import RemovedInSphinx50Warning
from sphinx.environment.adapters.asset import ImageAdapter from sphinx.environment.adapters.asset import ImageAdapter
from sphinx.errors import NoUri, SphinxError from sphinx.errors import NoUri, SphinxError
from sphinx.locale import _, __ from sphinx.locale import _, __
@ -172,9 +162,8 @@ class LaTeXBuilder(Builder):
self.context.update(ADDITIONAL_SETTINGS.get(self.config.latex_engine, {})) self.context.update(ADDITIONAL_SETTINGS.get(self.config.latex_engine, {}))
# Add special settings for (latex_engine, language_code) # Add special settings for (latex_engine, language_code)
if self.config.language: key = (self.config.latex_engine, self.config.language[:2])
key = (self.config.latex_engine, self.config.language[:2]) self.context.update(ADDITIONAL_SETTINGS.get(key, {}))
self.context.update(ADDITIONAL_SETTINGS.get(key, {}))
# Apply user settings to context # Apply user settings to context
self.context.update(self.config.latex_elements) self.context.update(self.config.latex_elements)
@ -205,7 +194,7 @@ class LaTeXBuilder(Builder):
def init_babel(self) -> None: def init_babel(self) -> None:
self.babel = ExtBabel(self.config.language, not self.context['babel']) self.babel = ExtBabel(self.config.language, not self.context['babel'])
if self.config.language and not self.babel.is_supported_language(): if not self.babel.is_supported_language():
# emit warning if specified language is invalid # emit warning if specified language is invalid
# (only emitting, nothing changed to processing) # (only emitting, nothing changed to processing)
logger.warning(__('no Babel option known for language %r'), logger.warning(__('no Babel option known for language %r'),
@ -234,12 +223,11 @@ class LaTeXBuilder(Builder):
self.context['classoptions'] += ',' + self.babel.get_language() self.context['classoptions'] += ',' + self.babel.get_language()
# this branch is not taken for xelatex/lualatex if default settings # this branch is not taken for xelatex/lualatex if default settings
self.context['multilingual'] = self.context['babel'] self.context['multilingual'] = self.context['babel']
if self.config.language: self.context['shorthandoff'] = SHORTHANDOFF
self.context['shorthandoff'] = SHORTHANDOFF
# Times fonts don't work with Cyrillic languages # Times fonts don't work with Cyrillic languages
if self.babel.uses_cyrillic() and 'fontpkg' not in self.config.latex_elements: if self.babel.uses_cyrillic() and 'fontpkg' not in self.config.latex_elements:
self.context['fontpkg'] = '' self.context['fontpkg'] = ''
elif self.context['polyglossia']: elif self.context['polyglossia']:
self.context['classoptions'] += ',' + self.babel.get_language() self.context['classoptions'] += ',' + self.babel.get_language()
options = self.babel.get_mainlanguage_options() options = self.babel.get_mainlanguage_options()
@ -280,7 +268,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 +298,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 +326,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 +341,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)]
@ -382,14 +370,10 @@ class LaTeXBuilder(Builder):
# configure usage of xindy (impacts Makefile and latexmkrc) # configure usage of xindy (impacts Makefile and latexmkrc)
# FIXME: convert this rather to a confval with suitable default # FIXME: convert this rather to a confval with suitable default
# according to language ? but would require extra documentation # according to language ? but would require extra documentation
if self.config.language: xindy_lang_option = XINDY_LANG_OPTIONS.get(self.config.language[:2],
xindy_lang_option = \ '-L general -C utf8 ')
XINDY_LANG_OPTIONS.get(self.config.language[:2], xindy_cyrillic = self.config.language[:2] in XINDY_CYRILLIC_SCRIPTS
'-L general -C utf8 ')
xindy_cyrillic = self.config.language[:2] in XINDY_CYRILLIC_SCRIPTS
else:
xindy_lang_option = '-L english -C utf8 '
xindy_cyrillic = False
context = { context = {
'latex_engine': self.config.latex_engine, 'latex_engine': self.config.latex_engine,
'xindy_use': self.config.latex_use_xindy, 'xindy_use': self.config.latex_use_xindy,
@ -449,18 +433,6 @@ class LaTeXBuilder(Builder):
filename = path.join(package_dir, 'templates', 'latex', 'sphinxmessages.sty_t') filename = path.join(package_dir, 'templates', 'latex', 'sphinxmessages.sty_t')
copy_asset_file(filename, self.outdir, context=context, renderer=LaTeXRenderer()) copy_asset_file(filename, self.outdir, context=context, renderer=LaTeXRenderer())
@property
def usepackages(self) -> List[Tuple[str, str]]:
warnings.warn('LaTeXBuilder.usepackages is deprecated.',
RemovedInSphinx50Warning, stacklevel=2)
return self.app.registry.latex_packages
@property
def usepackages_after_hyperref(self) -> List[Tuple[str, str]]:
warnings.warn('LaTeXBuilder.usepackages_after_hyperref is deprecated.',
RemovedInSphinx50Warning, stacklevel=2)
return self.app.registry.latex_packages_after_hyperref
def validate_config_values(app: Sphinx, config: Config) -> None: def validate_config_values(app: Sphinx, config: Config) -> None:
for key in list(config.latex_elements): for key in list(config.latex_elements):
@ -488,7 +460,7 @@ def default_latex_engine(config: Config) -> str:
""" Better default latex_engine settings for specific languages. """ """ Better default latex_engine settings for specific languages. """
if config.language == 'ja': if config.language == 'ja':
return 'uplatex' return 'uplatex'
elif (config.language or '').startswith('zh'): elif config.language.startswith('zh'):
return 'xelatex' return 'xelatex'
elif config.language == 'el': elif config.language == 'el':
return 'xelatex' return 'xelatex'

View File

@ -1,12 +1,4 @@
""" """consntants for LaTeX builder."""
sphinx.builders.latex.constants
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
consntants for LaTeX builder.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from typing import Any, Dict from typing import Any, Dict

View File

@ -1,12 +1,4 @@
""" """Additional nodes for LaTeX writer."""
sphinx.builders.latex.nodes
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Additional nodes for LaTeX writer.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from docutils import nodes from docutils import nodes

View File

@ -1,12 +1,4 @@
""" """Theming support for LaTeX builder."""
sphinx.builders.latex.theming
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Theming support for LaTeX builder.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import configparser import configparser
from os import path from os import path

View File

@ -1,12 +1,4 @@
""" """Transforms for LaTeX builder."""
sphinx.builders.latex.transforms
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Transforms for LaTeX builder.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from typing import Any, Dict, List, Set, Tuple, cast from typing import Any, Dict, List, Set, Tuple, cast
@ -33,7 +25,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 +37,7 @@ class SubstitutionDefinitionsRemover(SphinxPostTransform):
formats = ('latex',) formats = ('latex',)
def run(self, **kwargs: Any) -> None: def run(self, **kwargs: Any) -> None:
for node in list(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 +73,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 list(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:'):
@ -237,7 +229,8 @@ class LaTeXFootnoteTransform(SphinxPostTransform):
blah blah blah ... blah blah blah ...
* Replace second and subsequent footnote references which refers same footnote definition * Replace second and subsequent footnote references which refers same footnote definition
by footnotemark node. by footnotemark node. Additionally, the footnote definition node is marked as
"referred".
Before:: Before::
@ -258,7 +251,7 @@ class LaTeXFootnoteTransform(SphinxPostTransform):
After:: After::
blah blah blah blah blah blah
<footnote ids="id1"> <footnote ids="id1" referred=True>
<label> <label>
1 1
<paragraph> <paragraph>
@ -348,7 +341,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)
@ -358,7 +351,7 @@ class LaTeXFootnoteTransform(SphinxPostTransform):
class LaTeXFootnoteVisitor(nodes.NodeVisitor): class LaTeXFootnoteVisitor(nodes.NodeVisitor):
def __init__(self, document: nodes.document, footnotes: List[nodes.footnote]) -> None: def __init__(self, document: nodes.document, footnotes: List[nodes.footnote]) -> None:
self.appeared: Set[Tuple[str, str]] = set() self.appeared: Dict[Tuple[str, str], nodes.footnote] = {}
self.footnotes: List[nodes.footnote] = footnotes self.footnotes: List[nodes.footnote] = footnotes
self.pendings: List[nodes.footnote] = [] self.pendings: List[nodes.footnote] = []
self.table_footnotes: List[nodes.footnote] = [] self.table_footnotes: List[nodes.footnote] = []
@ -423,7 +416,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)
@ -439,22 +432,24 @@ class LaTeXFootnoteVisitor(nodes.NodeVisitor):
def visit_footnote_reference(self, node: nodes.footnote_reference) -> None: def visit_footnote_reference(self, node: nodes.footnote_reference) -> None:
number = node.astext().strip() number = node.astext().strip()
docname = node['docname'] docname = node['docname']
if self.restricted: if (docname, number) in self.appeared:
mark = footnotemark('', number, refid=node['refid']) footnote = self.appeared.get((docname, number))
node.replace_self(mark) footnote["referred"] = True
if (docname, number) not in self.appeared:
footnote = self.get_footnote_by_reference(node)
self.pendings.append(footnote)
elif (docname, number) in self.appeared:
mark = footnotemark('', number, refid=node['refid']) mark = footnotemark('', number, refid=node['refid'])
node.replace_self(mark) node.replace_self(mark)
else: else:
footnote = self.get_footnote_by_reference(node) footnote = self.get_footnote_by_reference(node)
self.footnotes.remove(footnote) if self.restricted:
node.replace_self(footnote) mark = footnotemark('', number, refid=node['refid'])
footnote.walkabout(self) node.replace_self(mark)
self.pendings.append(footnote)
else:
self.footnotes.remove(footnote)
node.replace_self(footnote)
footnote.walkabout(self)
self.appeared.add((docname, number)) self.appeared[(docname, number)] = footnote
raise nodes.SkipNode raise nodes.SkipNode
def get_footnote_by_reference(self, node: nodes.footnote_reference) -> nodes.footnote: def get_footnote_by_reference(self, node: nodes.footnote_reference) -> nodes.footnote:
@ -501,7 +496,7 @@ class BibliographyTransform(SphinxPostTransform):
def run(self, **kwargs: Any) -> None: def run(self, **kwargs: Any) -> None:
citations = thebibliography() citations = thebibliography()
for node in list(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 +516,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 +535,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 +550,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 +561,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 +597,9 @@ class IndexInSectionTitleTransform(SphinxPostTransform):
formats = ('latex',) formats = ('latex',)
def run(self, **kwargs: Any) -> None: def run(self, **kwargs: Any) -> None:
for node in list(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(list(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)

View File

@ -1,12 +1,4 @@
""" """Utilities for LaTeX builder."""
sphinx.builders.latex.util
~~~~~~~~~~~~~~~~~~~~~~~~~~
Utilities for LaTeX builder.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from typing import Optional from typing import Optional
@ -20,7 +12,7 @@ class ExtBabel(Babel):
self.language_code = language_code self.language_code = language_code
self.use_polyglossia = use_polyglossia self.use_polyglossia = use_polyglossia
self.supported = True self.supported = True
super().__init__(language_code or '') super().__init__(language_code)
def uses_cyrillic(self) -> bool: def uses_cyrillic(self) -> bool:
return self.language in self.cyrillic_languages return self.language in self.cyrillic_languages

View File

@ -1,37 +1,25 @@
""" """The CheckExternalLinksBuilder class."""
sphinx.builders.linkcheck
~~~~~~~~~~~~~~~~~~~~~~~~~
The CheckExternalLinksBuilder class.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import json import json
import re import re
import socket import socket
import time import time
import warnings
from datetime import datetime, timezone from datetime import datetime, timezone
from email.utils import parsedate_to_datetime from email.utils import parsedate_to_datetime
from html.parser import HTMLParser from html.parser import HTMLParser
from os import path from os import path
from queue import PriorityQueue, Queue from queue import PriorityQueue, Queue
from threading import Thread from threading import Thread
from typing import (Any, Dict, Generator, List, NamedTuple, Optional, Pattern, Set, Tuple, from typing import Any, Dict, Generator, List, NamedTuple, Optional, Tuple, Union, cast
Union, cast)
from urllib.parse import unquote, urlparse, urlunparse from urllib.parse import unquote, urlparse, urlunparse
from docutils import nodes from docutils import nodes
from docutils.nodes import Element
from requests import Response from requests import Response
from requests.exceptions import ConnectionError, HTTPError, TooManyRedirects from requests.exceptions import ConnectionError, HTTPError, TooManyRedirects
from sphinx.application import Sphinx from sphinx.application import Sphinx
from sphinx.builders.dummy import DummyBuilder from sphinx.builders.dummy import DummyBuilder
from sphinx.config import Config from sphinx.config import Config
from sphinx.deprecation import RemovedInSphinx50Warning
from sphinx.environment import BuildEnvironment from sphinx.environment import BuildEnvironment
from sphinx.locale import __ from sphinx.locale import __
from sphinx.transforms.post_transforms import SphinxPostTransform from sphinx.transforms.post_transforms import SphinxPostTransform
@ -43,18 +31,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]]
@ -67,16 +68,6 @@ QUEUE_POLL_SECS = 1
DEFAULT_DELAY = 60.0 DEFAULT_DELAY = 60.0
def node_line_or_0(node: Element) -> int:
"""
PriorityQueue items must be comparable. The line number is part of the
tuple used by the PriorityQueue, keep an homogeneous type for comparison.
"""
warnings.warn('node_line_or_0() is deprecated.',
RemovedInSphinx50Warning, stacklevel=2)
return get_node_line(node) or 0
class AnchorCheckParser(HTMLParser): class AnchorCheckParser(HTMLParser):
"""Specialized HTML parser that looks for a specific anchor.""" """Specialized HTML parser that looks for a specific anchor."""
@ -120,114 +111,11 @@ class CheckExternalLinksBuilder(DummyBuilder):
'%(outdir)s/output.txt') '%(outdir)s/output.txt')
def init(self) -> None: def init(self) -> None:
self.broken_hyperlinks = 0
self.hyperlinks: Dict[str, Hyperlink] = {} self.hyperlinks: Dict[str, Hyperlink] = {}
self._good: Set[str] = set()
self._broken: Dict[str, str] = {}
self._redirected: Dict[str, Tuple[str, int]] = {}
# set a timeout for non-responding servers # set a timeout for non-responding servers
socket.setdefaulttimeout(5.0) socket.setdefaulttimeout(5.0)
# create queues and worker threads
self._wqueue: PriorityQueue[CheckRequestType] = PriorityQueue()
self._rqueue: Queue[CheckResult] = Queue()
@property
def anchors_ignore(self) -> List[Pattern]:
warnings.warn(
"%s.%s is deprecated." % (self.__class__.__name__, "anchors_ignore"),
RemovedInSphinx50Warning,
stacklevel=2,
)
return [re.compile(x) for x in self.config.linkcheck_anchors_ignore]
@property
def auth(self) -> List[Tuple[Pattern, Any]]:
warnings.warn(
"%s.%s is deprecated." % (self.__class__.__name__, "auth"),
RemovedInSphinx50Warning,
stacklevel=2,
)
return [(re.compile(pattern), auth_info) for pattern, auth_info
in self.config.linkcheck_auth]
@property
def to_ignore(self) -> List[Pattern]:
warnings.warn(
"%s.%s is deprecated." % (self.__class__.__name__, "to_ignore"),
RemovedInSphinx50Warning,
stacklevel=2,
)
return [re.compile(x) for x in self.config.linkcheck_ignore]
@property
def good(self) -> Set[str]:
warnings.warn(
"%s.%s is deprecated." % (self.__class__.__name__, "good"),
RemovedInSphinx50Warning,
stacklevel=2,
)
return self._good
@property
def broken(self) -> Dict[str, str]:
warnings.warn(
"%s.%s is deprecated." % (self.__class__.__name__, "broken"),
RemovedInSphinx50Warning,
stacklevel=2,
)
return self._broken
@property
def redirected(self) -> Dict[str, Tuple[str, int]]:
warnings.warn(
"%s.%s is deprecated." % (self.__class__.__name__, "redirected"),
RemovedInSphinx50Warning,
stacklevel=2,
)
return self._redirected
def check_thread(self) -> None:
warnings.warn(
"%s.%s is deprecated." % (self.__class__.__name__, "check_thread"),
RemovedInSphinx50Warning,
stacklevel=2,
)
# do nothing.
def limit_rate(self, response: Response) -> Optional[float]:
warnings.warn(
"%s.%s is deprecated." % (self.__class__.__name__, "limit_rate"),
RemovedInSphinx50Warning,
stacklevel=2,
)
worker = HyperlinkAvailabilityCheckWorker(self.env, self.config,
None, None, {})
return worker.limit_rate(response)
def rqueue(self, response: Response) -> Queue:
warnings.warn(
"%s.%s is deprecated." % (self.__class__.__name__, "rqueue"),
RemovedInSphinx50Warning,
stacklevel=2,
)
return self._rqueue
def workers(self, response: Response) -> List[Thread]:
warnings.warn(
"%s.%s is deprecated." % (self.__class__.__name__, "workers"),
RemovedInSphinx50Warning,
stacklevel=2,
)
return []
def wqueue(self, response: Response) -> Queue:
warnings.warn(
"%s.%s is deprecated." % (self.__class__.__name__, "wqueue"),
RemovedInSphinx50Warning,
stacklevel=2,
)
return self._wqueue
def process_result(self, result: CheckResult) -> None: def process_result(self, result: CheckResult) -> None:
filename = self.env.doc2path(result.docname, None) filename = self.env.doc2path(result.docname, None)
@ -260,6 +148,7 @@ class CheckExternalLinksBuilder(DummyBuilder):
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,
result.uri + ': ' + result.message) result.uri + ': ' + result.message)
self.broken_hyperlinks += 1
elif result.status == 'redirected': elif result.status == 'redirected':
try: try:
text, color = { text, color = {
@ -292,7 +181,7 @@ class CheckExternalLinksBuilder(DummyBuilder):
self.json_outfile.write('\n') self.json_outfile.write('\n')
def finish(self) -> None: def finish(self) -> None:
checker = HyperlinkAvailabilityChecker(self.env, self.config, self) checker = HyperlinkAvailabilityChecker(self.env, self.config)
logger.info('') logger.info('')
with open(path.join(self.outdir, 'output.txt'), 'w') as self.txt_outfile,\ with open(path.join(self.outdir, 'output.txt'), 'w') as self.txt_outfile,\
@ -300,42 +189,32 @@ class CheckExternalLinksBuilder(DummyBuilder):
for result in checker.check(self.hyperlinks): for result in checker.check(self.hyperlinks):
self.process_result(result) self.process_result(result)
if self._broken: if self.broken_hyperlinks:
self.app.statuscode = 1 self.app.statuscode = 1
class HyperlinkAvailabilityChecker: class HyperlinkAvailabilityChecker:
def __init__(self, env: BuildEnvironment, config: Config, def __init__(self, env: BuildEnvironment, config: Config) -> None:
builder: CheckExternalLinksBuilder = None) -> None:
# Warning: builder argument will be removed in the sphinx-5.0.
# Don't use it from extensions.
# tag: RemovedInSphinx50Warning
self.builder = builder
self.config = config self.config = config
self.env = env self.env = env
self.rate_limits: Dict[str, RateLimit] = {} self.rate_limits: Dict[str, RateLimit] = {}
self.rqueue: Queue = Queue()
self.workers: List[Thread] = [] self.workers: List[Thread] = []
self.wqueue: PriorityQueue = PriorityQueue()
self.to_ignore = [re.compile(x) for x in self.config.linkcheck_ignore] self.to_ignore = [re.compile(x) for x in self.config.linkcheck_ignore]
if builder:
self.rqueue = builder._rqueue
self.wqueue = builder._wqueue
else:
self.rqueue = Queue()
self.wqueue = PriorityQueue()
def invoke_threads(self) -> None: def invoke_threads(self) -> None:
for i in range(self.config.linkcheck_workers): for _i in range(self.config.linkcheck_workers):
thread = HyperlinkAvailabilityCheckWorker(self.env, self.config, thread = HyperlinkAvailabilityCheckWorker(self.env, self.config,
self.rqueue, self.wqueue, self.rqueue, self.wqueue,
self.rate_limits, self.builder) self.rate_limits)
thread.start() thread.start()
self.workers.append(thread) self.workers.append(thread)
def shutdown_threads(self) -> None: def shutdown_threads(self) -> None:
self.wqueue.join() self.wqueue.join()
for worker in self.workers: for _worker in self.workers:
self.wqueue.put(CheckRequest(CHECK_IMMEDIATELY, None), False) self.wqueue.put(CheckRequest(CHECK_IMMEDIATELY, None), False)
def check(self, hyperlinks: Dict[str, Hyperlink]) -> Generator[CheckResult, None, None]: def check(self, hyperlinks: Dict[str, Hyperlink]) -> Generator[CheckResult, None, None]:
@ -365,11 +244,7 @@ class HyperlinkAvailabilityCheckWorker(Thread):
"""A worker class for checking the availability of hyperlinks.""" """A worker class for checking the availability of hyperlinks."""
def __init__(self, env: BuildEnvironment, config: Config, rqueue: Queue, def __init__(self, env: BuildEnvironment, config: Config, rqueue: Queue,
wqueue: Queue, rate_limits: Dict[str, RateLimit], wqueue: Queue, rate_limits: Dict[str, RateLimit]) -> None:
builder: CheckExternalLinksBuilder = None) -> None:
# Warning: builder argument will be removed in the sphinx-5.0.
# Don't use it from extensions.
# tag: RemovedInSphinx50Warning
self.config = config self.config = config
self.env = env self.env = env
self.rate_limits = rate_limits self.rate_limits = rate_limits
@ -378,20 +253,11 @@ 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]
if builder:
# if given, fill the result of checks as cache
self._good = builder._good
self._broken = builder._broken
self._redirected = builder._redirected
else:
# only for compatibility. Will be removed in Sphinx-5.0
self._good = set()
self._broken = {}
self._redirected = {}
super().__init__(daemon=True) super().__init__(daemon=True)
def run(self) -> None: def run(self) -> None:
@ -519,6 +385,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:')):
@ -530,14 +405,7 @@ class HyperlinkAvailabilityCheckWorker(Thread):
if path.exists(path.join(srcdir, uri)): if path.exists(path.join(srcdir, uri)):
return 'working', '', 0 return 'working', '', 0
else: else:
self._broken[uri] = ''
return 'broken', '', 0 return 'broken', '', 0
elif uri in self._good:
return 'working', 'old', 0
elif uri in self._broken:
return 'broken', self._broken[uri], 0
elif uri in self._redirected:
return 'redirected', self._redirected[uri][0], self._redirected[uri][1]
# need to actually check the URI # need to actually check the URI
for _ in range(self.config.linkcheck_retries): for _ in range(self.config.linkcheck_retries):
@ -545,13 +413,6 @@ class HyperlinkAvailabilityCheckWorker(Thread):
if status != "broken": if status != "broken":
break break
if status == "working":
self._good.add(uri)
elif status == "broken":
self._broken[uri] = info
elif status == "redirected":
self._redirected[uri] = (info, code)
return (status, info, code) return (status, info, code)
while True: while True:
@ -639,7 +500,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 +514,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 +560,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)

View File

@ -1,12 +1,4 @@
""" """Manual pages builder."""
sphinx.builders.manpage
~~~~~~~~~~~~~~~~~~~~~~~
Manual pages builder.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from os import path from os import path
from typing import Any, Dict, List, Set, Tuple, Union from typing import Any, Dict, List, Set, Tuple, Union
@ -18,7 +10,6 @@ from sphinx import addnodes
from sphinx.application import Sphinx from sphinx.application import Sphinx
from sphinx.builders import Builder from sphinx.builders import Builder
from sphinx.config import Config from sphinx.config import Config
from sphinx.errors import NoUri
from sphinx.locale import __ from sphinx.locale import __
from sphinx.util import logging, progress_message from sphinx.util import logging, progress_message
from sphinx.util.console import darkgreen # type: ignore from sphinx.util.console import darkgreen # type: ignore
@ -49,9 +40,7 @@ class ManualPageBuilder(Builder):
return 'all manpages' # for now return 'all manpages' # for now
def get_target_uri(self, docname: str, typ: str = None) -> str: def get_target_uri(self, docname: str, typ: str = None) -> str:
if typ == 'token': return ''
return ''
raise NoUri(docname, typ)
@progress_message(__('writing')) @progress_message(__('writing'))
def write(self, *ignored: Any) -> None: def write(self, *ignored: Any) -> None:
@ -98,7 +87,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)

View File

@ -1,12 +1,4 @@
""" """Single HTML builders."""
sphinx.builders.singlehtml
~~~~~~~~~~~~~~~~~~~~~~~~~~
Single HTML builders.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from os import path from os import path
from typing import Any, Dict, List, Tuple, Union from typing import Any, Dict, List, Tuple, Union
@ -54,7 +46,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']

View File

@ -1,12 +1,4 @@
""" """Texinfo builder."""
sphinx.builders.texinfo
~~~~~~~~~~~~~~~~~~~~~~~
Texinfo builder.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import os import os
from os import path from os import path
@ -138,7 +130,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 +144,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 +203,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',

View File

@ -1,12 +1,4 @@
""" """Plain-text Sphinx builder."""
sphinx.builders.text
~~~~~~~~~~~~~~~~~~~~
Plain-text Sphinx builder.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from os import path from os import path
from typing import Any, Dict, Iterator, Set, Tuple from typing import Any, Dict, Iterator, Set, Tuple

View File

@ -1,12 +1,4 @@
""" """Docutils-native XML and pseudo-XML builders."""
sphinx.builders.xml
~~~~~~~~~~~~~~~~~~~
Docutils-native XML and pseudo-XML builders.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from os import path from os import path
from typing import Any, Dict, Iterator, Set, Type, Union from typing import Any, Dict, Iterator, Set, Type, Union
@ -71,7 +63,10 @@ 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 domain in self.env.domains.values():
xmlns = "xmlns:" + domain.name
doctree[xmlns] = "https://www.sphinx-doc.org/" # type: ignore
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)

View File

@ -1,9 +1 @@
""" """Modules for command line executables."""
sphinx.cmd
~~~~~~~~~~
Modules for command line executables.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -1,12 +1,4 @@
""" """Build documentation from a provided source."""
sphinx.cmd.build
~~~~~~~~~~~~~~~~
Build documentation from a provided source.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import argparse import argparse
import bdb import bdb
@ -16,6 +8,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 +21,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 +234,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') % (

View File

@ -1,17 +1,10 @@
""" """sphinx-build -M command-line handling.
sphinx.cmd.make_mode
~~~~~~~~~~~~~~~~~~~~
sphinx-build -M command-line handling. This replaces the old, platform-dependent and once-generated content
of Makefile / make.bat.
This replaces the old, platform-dependent and once-generated content This is in its own module so that importing it is fast. It should not
of Makefile / make.bat. import the main Sphinx modules (like sphinx.applications, sphinx.builders).
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).
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
""" """
import os import os

View File

@ -1,12 +1,4 @@
""" """Quickly setup documentation source to work with Sphinx."""
sphinx.cmd.quickstart
~~~~~~~~~~~~~~~~~~~~~
Quickly setup documentation source to work with Sphinx.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import argparse import argparse
import locale import locale

View File

@ -1,12 +1,4 @@
""" """Build configuration file handling."""
sphinx.config
~~~~~~~~~~~~~
Build configuration file handling.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import re import re
import traceback import traceback
@ -100,7 +92,7 @@ class Config:
# the real default is locale-dependent # the real default is locale-dependent
'today_fmt': (None, 'env', [str]), 'today_fmt': (None, 'env', [str]),
'language': (None, 'env', [str]), 'language': ('en', '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', []), 'gettext_allow_fuzzy_translations': (False, 'gettext', []),
@ -206,7 +198,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 '
@ -251,13 +243,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
@ -413,7 +416,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
@ -427,7 +430,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
@ -452,13 +455,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:

View File

@ -1,12 +1,4 @@
""" """Sphinx deprecation classes and utilities."""
sphinx.deprecation
~~~~~~~~~~~~~~~~~~
Sphinx deprecation classes and utilities.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import sys import sys
import warnings import warnings
@ -22,6 +14,10 @@ class RemovedInSphinx60Warning(PendingDeprecationWarning):
pass pass
class RemovedInSphinx70Warning(PendingDeprecationWarning):
pass
RemovedInNextVersionWarning = RemovedInSphinx50Warning RemovedInNextVersionWarning = RemovedInSphinx50Warning

View File

@ -1,12 +1,4 @@
""" """Handlers for additional ReST directives."""
sphinx.directives
~~~~~~~~~~~~~~~~~
Handlers for additional ReST directives.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import re import re
from typing import TYPE_CHECKING, Any, Dict, Generic, List, Tuple, TypeVar, cast from typing import TYPE_CHECKING, Any, Dict, Generic, List, Tuple, TypeVar, cast
@ -17,7 +9,6 @@ from docutils.parsers.rst import directives, roles
from sphinx import addnodes from sphinx import addnodes
from sphinx.addnodes import desc_signature from sphinx.addnodes import desc_signature
from sphinx.deprecation import RemovedInSphinx50Warning, deprecated_alias
from sphinx.util import docutils from sphinx.util import docutils
from sphinx.util.docfields import DocFieldTransformer, Field, TypedField from sphinx.util.docfields import DocFieldTransformer, Field, TypedField
from sphinx.util.docutils import SphinxDirective from sphinx.util.docutils import SphinxDirective
@ -176,7 +167,7 @@ class ObjectDescription(SphinxDirective, Generic[T]):
self.names: List[T] = [] self.names: List[T] = []
signatures = self.get_signatures() signatures = self.get_signatures()
for i, sig in enumerate(signatures): for sig in signatures:
# add a signature node for each signature in the current unit # add a signature node for each signature in the current unit
# and add a reference target for it # and add a reference target for it
signode = addnodes.desc_signature(sig, '') signode = addnodes.desc_signature(sig, '')
@ -266,16 +257,6 @@ class DefaultDomain(SphinxDirective):
return [] return []
deprecated_alias('sphinx.directives',
{
'DescDirective': ObjectDescription,
},
RemovedInSphinx50Warning,
{
'DescDirective': 'sphinx.directives.ObjectDescription',
})
def setup(app: "Sphinx") -> Dict[str, Any]: def setup(app: "Sphinx") -> Dict[str, Any]:
app.add_config_value("strip_signature_backslash", False, 'env') app.add_config_value("strip_signature_backslash", False, 'env')
directives.register_directive('default-role', DefaultRole) directives.register_directive('default-role', DefaultRole)

View File

@ -1,11 +1,3 @@
"""
sphinx.directives.code
~~~~~~~~~~~~~~~~~~~~~~
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import sys import sys
import textwrap import textwrap
from difflib import unified_diff from difflib import unified_diff
@ -57,7 +49,7 @@ class Highlight(SphinxDirective):
def dedent_lines(lines: List[str], dedent: int, location: Tuple[str, int] = None) -> List[str]: def dedent_lines(lines: List[str], dedent: int, location: Tuple[str, int] = None) -> List[str]:
if not dedent: if dedent is None:
return textwrap.dedent(''.join(lines)).splitlines(True) return textwrap.dedent(''.join(lines)).splitlines(True)
if any(s[:dedent].strip() for s in lines): if any(s[:dedent].strip() for s in lines):
@ -138,9 +130,9 @@ class CodeBlock(SphinxDirective):
if 'dedent' in self.options: if 'dedent' in self.options:
location = self.state_machine.get_source_and_line(self.lineno) location = self.state_machine.get_source_and_line(self.lineno)
lines = code.split('\n') lines = code.splitlines(True)
lines = dedent_lines(lines, self.options['dedent'], location=location) lines = dedent_lines(lines, self.options['dedent'], location=location)
code = '\n'.join(lines) code = ''.join(lines)
literal: Element = nodes.literal_block(code, code) literal: Element = nodes.literal_block(code, code)
if 'linenos' in self.options or 'lineno-start' in self.options: if 'linenos' in self.options or 'lineno-start' in self.options:
@ -232,9 +224,9 @@ class LiteralIncludeReader:
self.start_filter, self.start_filter,
self.end_filter, self.end_filter,
self.lines_filter, self.lines_filter,
self.dedent_filter,
self.prepend_filter, self.prepend_filter,
self.append_filter, self.append_filter]
self.dedent_filter]
lines = self.read_file(self.filename, location=location) lines = self.read_file(self.filename, location=location)
for func in filters: for func in filters:
lines = func(lines, location=location) lines = func(lines, location=location)

View File

@ -1,11 +1,3 @@
"""
sphinx.directives.other
~~~~~~~~~~~~~~~~~~~~~~~
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import re import re
from typing import TYPE_CHECKING, Any, Dict, List, cast from typing import TYPE_CHECKING, Any, Dict, List, cast
@ -342,7 +334,7 @@ class Only(SphinxDirective):
# be placed in the doctree. # be placed in the doctree.
n_sects_to_raise = current_depth - nested_depth + 1 n_sects_to_raise = current_depth - nested_depth + 1
parent = cast(nodes.Element, self.state.parent) parent = cast(nodes.Element, self.state.parent)
for i in range(n_sects_to_raise): for _i in range(n_sects_to_raise):
if parent.parent: if parent.parent:
parent = parent.parent parent = parent.parent
parent.append(node) parent.append(node)

View File

@ -1,15 +1,7 @@
"""
sphinx.directives.patches
~~~~~~~~~~~~~~~~~~~~~~~~~
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
: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
@ -29,13 +21,10 @@ from sphinx.util.osutil import SEP, os_path, relpath
from sphinx.util.typing import OptionSpec from sphinx.util.typing import OptionSpec
try: try:
from docutils.nodes import meta as meta_node # type: ignore
from docutils.parsers.rst.directives.misc import Meta as MetaBase # type: ignore from docutils.parsers.rst.directives.misc import Meta as MetaBase # type: ignore
except ImportError: except ImportError:
# docutils-0.17 or older # docutils-0.17 or older
from docutils.parsers.rst.directives.html import Meta as MetaBase from docutils.parsers.rst.directives.html import Meta as MetaBase
from docutils.parsers.rst.directives.html import MetaBody
meta_node = MetaBody.meta
if TYPE_CHECKING: if TYPE_CHECKING:
from sphinx.application import Sphinx from sphinx.application import Sphinx
@ -71,11 +60,13 @@ class Figure(images.Figure):
class Meta(MetaBase, 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], meta_node)): 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

View File

@ -1,12 +1,7 @@
""" """Support for domains.
sphinx.domains
~~~~~~~~~~~~~~
Support for domains, which are groupings of description directives Domains 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.
:license: BSD, see LICENSE for details.
""" """
import copy import copy

View File

@ -1,12 +1,4 @@
""" """The C language domain."""
sphinx.domains.c
~~~~~~~~~~~~~~~~
The C language domain.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import re import re
from typing import (Any, Callable, Dict, Generator, Iterator, List, Optional, Tuple, TypeVar, from typing import (Any, Callable, Dict, Generator, Iterator, List, Optional, Tuple, TypeVar,
@ -20,7 +12,7 @@ from sphinx import addnodes
from sphinx.addnodes import pending_xref from sphinx.addnodes import pending_xref
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 RemovedInSphinx60Warning
from sphinx.directives import ObjectDescription from sphinx.directives import ObjectDescription
from sphinx.domains import Domain, ObjType from sphinx.domains import Domain, ObjType
from sphinx.environment import BuildEnvironment from sphinx.environment import BuildEnvironment
@ -92,31 +84,22 @@ _id_prefix = [None, 'c.', 'Cv2.']
_string_re = re.compile(r"[LuU8]?('([^'\\]*(?:\\.[^'\\]*)*)'" _string_re = re.compile(r"[LuU8]?('([^'\\]*(?:\\.[^'\\]*)*)'"
r'|"([^"\\]*(?:\\.[^"\\]*)*)")', re.S) r'|"([^"\\]*(?:\\.[^"\\]*)*)")', re.S)
# bool, complex, and imaginary are macro "keywords", so they are handled seperately
_simple_type_specifiers_re = re.compile(r"""(?x) _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 +209,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 +619,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 +2564,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_specifiers_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
@ -3260,7 +3268,7 @@ class CObject(ObjectDescription[ASTDeclaration]):
msg = "{}: Pre-v3 C type directive '.. c:type:: {}' converted to " \ msg = "{}: Pre-v3 C type directive '.. c:type:: {}' converted to " \
"'.. c:{}:: {}'." \ "'.. c:{}:: {}'." \
"\nThe original parsing error was:\n{}" "\nThe original parsing error was:\n{}"
msg = msg.format(RemovedInSphinx50Warning.__name__, msg = msg.format(RemovedInSphinx60Warning.__name__,
sig, ast.objectType, ast, eOrig) sig, ast.objectType, ast, eOrig)
logger.warning(msg, location=signode) logger.warning(msg, location=signode)
except DefinitionError as e: except DefinitionError as e:
@ -3540,7 +3548,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
@ -3638,8 +3646,7 @@ class CAliasObject(ObjectDescription):
" When skipping the root declaration," " When skipping the root declaration,"
" need 'maxdepth' 0 for infinite or at least 2.", " need 'maxdepth' 0 for infinite or at least 2.",
location=self.get_location()) location=self.get_location())
signatures = self.get_signatures() for sig in self.get_signatures():
for i, sig in enumerate(signatures):
node.append(AliasNode(sig, aliasOptions, self.state.document, env=self.env)) node.append(AliasNode(sig, aliasOptions, self.state.document, env=self.env))
return [node] return [node]
@ -3694,7 +3701,7 @@ class CXRefRole(XRefRole):
if self.env.config['c_warn_on_allowed_pre_v3']: if self.env.config['c_warn_on_allowed_pre_v3']:
msg = "{}: Pre-v3 C type role ':c:type:`{}`' converted to ':c:expr:`{}`'." msg = "{}: Pre-v3 C type role ':c:type:`{}`' converted to ':c:expr:`{}`'."
msg += "\nThe original parsing error was:\n{}" msg += "\nThe original parsing error was:\n{}"
msg = msg.format(RemovedInSphinx50Warning.__name__, text, text, eOrig) msg = msg.format(RemovedInSphinx60Warning.__name__, text, text, eOrig)
logger.warning(msg, location=self.get_location()) logger.warning(msg, location=self.get_location())
return [signode], [] return [signode], []

View File

@ -1,12 +1,4 @@
""" """The changeset domain."""
sphinx.domains.changeset
~~~~~~~~~~~~~~~~~~~~~~~~
The changeset domain.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from typing import TYPE_CHECKING, Any, Dict, List, NamedTuple, cast from typing import TYPE_CHECKING, Any, Dict, List, NamedTuple, cast
@ -130,7 +122,7 @@ class ChangeSetDomain(Domain):
self.changesets.setdefault(version, []).append(changeset) self.changesets.setdefault(version, []).append(changeset)
def clear_doc(self, docname: str) -> None: def clear_doc(self, docname: str) -> None:
for version, changes in self.changesets.items(): for changes in self.changesets.values():
for changeset in changes[:]: for changeset in changes[:]:
if changeset.docname == docname: if changeset.docname == docname:
changes.remove(changeset) changes.remove(changeset)

View File

@ -1,12 +1,4 @@
""" """The citation domain."""
sphinx.domains.citation
~~~~~~~~~~~~~~~~~~~~~~~
The citation domain.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Set, Tuple, cast from typing import TYPE_CHECKING, Any, Dict, List, Optional, Set, Tuple, cast
@ -48,7 +40,7 @@ class CitationDomain(Domain):
return self.data.setdefault('citation_refs', {}) return self.data.setdefault('citation_refs', {})
def clear_doc(self, docname: str) -> None: def clear_doc(self, docname: str) -> None:
for key, (fn, _l, lineno) in list(self.citations.items()): for key, (fn, _l, _lineno) in list(self.citations.items()):
if fn == docname: if fn == docname:
del self.citations[key] del self.citations[key]
for key, docnames in list(self.citation_refs.items()): for key, docnames in list(self.citation_refs.items()):
@ -81,7 +73,7 @@ class CitationDomain(Domain):
docnames.add(self.env.docname) docnames.add(self.env.docname)
def check_consistency(self) -> None: def check_consistency(self) -> None:
for name, (docname, labelid, lineno) in self.citations.items(): for name, (docname, _labelid, lineno) in self.citations.items():
if name not in self.citation_refs: if name not in self.citation_refs:
logger.warning(__('Citation [%s] is not referenced.'), name, logger.warning(__('Citation [%s] is not referenced.'), name,
type='ref', subtype='citation', location=(docname, lineno)) type='ref', subtype='citation', location=(docname, lineno))
@ -112,7 +104,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 +123,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,

View File

@ -1,12 +1,4 @@
""" """The C++ language domain."""
sphinx.domains.cpp
~~~~~~~~~~~~~~~~~~
The C++ language domain.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import re import re
from typing import (Any, Callable, Dict, Generator, Iterator, List, Optional, Tuple, TypeVar, from typing import (Any, Callable, Dict, Generator, Iterator, List, Optional, Tuple, TypeVar,
@ -267,7 +259,8 @@ T = TypeVar('T')
class_object: class_object:
goal: a class declaration, but with specification of a base class goal: a class declaration, but with specification of a base class
grammar: grammar:
nested-name "final"[opt] (":" base-specifier-list)[opt] attribute-specifier-seq[opt]
nested-name "final"[opt] (":" base-specifier-list)[opt]
base-specifier-list -> base-specifier-list ->
base-specifier "..."[opt] base-specifier "..."[opt]
| base-specifier-list, base-specifier "..."[opt] | base-specifier-list, base-specifier "..."[opt]
@ -281,7 +274,8 @@ T = TypeVar('T')
goal: an unscoped enum or a scoped enum, optionally with the underlying goal: an unscoped enum or a scoped enum, optionally with the underlying
type specified type specified
grammar: grammar:
("class" | "struct")[opt] visibility[opt] nested-name (":" type)[opt] ("class" | "struct")[opt] visibility[opt]
attribute-specifier-seq[opt] nested-name (":" type)[opt]
enumerator_object: enumerator_object:
goal: an element in a scoped or unscoped enum. The name should be goal: an element in a scoped or unscoped enum. The name should be
injected according to the scopedness. injected according to the scopedness.
@ -338,24 +332,14 @@ _keywords = [
_simple_type_specifiers_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 +469,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 +770,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 +1837,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 +1850,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 '
@ -3324,16 +3312,20 @@ class ASTBaseClass(ASTBase):
class ASTClass(ASTBase): class ASTClass(ASTBase):
def __init__(self, name: ASTNestedName, final: bool, bases: List[ASTBaseClass]) -> None: def __init__(self, name: ASTNestedName, final: bool, bases: List[ASTBaseClass],
attrs: List[ASTAttribute]) -> None:
self.name = name self.name = name
self.final = final self.final = final
self.bases = bases self.bases = bases
self.attrs = attrs
def get_id(self, version: int, objectType: str, symbol: "Symbol") -> str: def get_id(self, version: int, objectType: str, symbol: "Symbol") -> str:
return symbol.get_full_nested_name().get_id(version) return symbol.get_full_nested_name().get_id(version)
def _stringify(self, transform: StringifyTransform) -> str: def _stringify(self, transform: StringifyTransform) -> str:
res = [] res = []
for attr in self.attrs:
res.append(transform(attr) + ' ')
res.append(transform(self.name)) res.append(transform(self.name))
if self.final: if self.final:
res.append(' final') res.append(' final')
@ -3350,6 +3342,9 @@ class ASTClass(ASTBase):
def describe_signature(self, signode: TextElement, mode: str, def describe_signature(self, signode: TextElement, mode: str,
env: "BuildEnvironment", symbol: "Symbol") -> None: env: "BuildEnvironment", symbol: "Symbol") -> None:
verify_description_mode(mode) verify_description_mode(mode)
for attr in self.attrs:
attr.describe_signature(signode)
signode += addnodes.desc_sig_space()
self.name.describe_signature(signode, mode, env, symbol=symbol) self.name.describe_signature(signode, mode, env, symbol=symbol)
if self.final: if self.final:
signode += addnodes.desc_sig_space() signode += addnodes.desc_sig_space()
@ -3367,8 +3362,9 @@ class ASTClass(ASTBase):
class ASTUnion(ASTBase): class ASTUnion(ASTBase):
def __init__(self, name: ASTNestedName) -> None: def __init__(self, name: ASTNestedName, attrs: List[ASTAttribute]) -> None:
self.name = name self.name = name
self.attrs = attrs
def get_id(self, version: int, objectType: str, symbol: "Symbol") -> str: def get_id(self, version: int, objectType: str, symbol: "Symbol") -> str:
if version == 1: if version == 1:
@ -3376,20 +3372,28 @@ class ASTUnion(ASTBase):
return symbol.get_full_nested_name().get_id(version) return symbol.get_full_nested_name().get_id(version)
def _stringify(self, transform: StringifyTransform) -> str: def _stringify(self, transform: StringifyTransform) -> str:
return transform(self.name) res = []
for attr in self.attrs:
res.append(transform(attr) + ' ')
res.append(transform(self.name))
return ''.join(res)
def describe_signature(self, signode: TextElement, mode: str, def describe_signature(self, signode: TextElement, mode: str,
env: "BuildEnvironment", symbol: "Symbol") -> None: env: "BuildEnvironment", symbol: "Symbol") -> None:
verify_description_mode(mode) verify_description_mode(mode)
for attr in self.attrs:
attr.describe_signature(signode)
signode += addnodes.desc_sig_space()
self.name.describe_signature(signode, mode, env, symbol=symbol) self.name.describe_signature(signode, mode, env, symbol=symbol)
class ASTEnum(ASTBase): class ASTEnum(ASTBase):
def __init__(self, name: ASTNestedName, scoped: str, def __init__(self, name: ASTNestedName, scoped: str, underlyingType: ASTType,
underlyingType: ASTType) -> None: attrs: List[ASTAttribute]) -> None:
self.name = name self.name = name
self.scoped = scoped self.scoped = scoped
self.underlyingType = underlyingType self.underlyingType = underlyingType
self.attrs = attrs
def get_id(self, version: int, objectType: str, symbol: "Symbol") -> str: def get_id(self, version: int, objectType: str, symbol: "Symbol") -> str:
if version == 1: if version == 1:
@ -3401,6 +3405,8 @@ class ASTEnum(ASTBase):
if self.scoped: if self.scoped:
res.append(self.scoped) res.append(self.scoped)
res.append(' ') res.append(' ')
for attr in self.attrs:
res.append(transform(attr) + ' ')
res.append(transform(self.name)) res.append(transform(self.name))
if self.underlyingType: if self.underlyingType:
res.append(' : ') res.append(' : ')
@ -3411,6 +3417,9 @@ class ASTEnum(ASTBase):
env: "BuildEnvironment", symbol: "Symbol") -> None: env: "BuildEnvironment", symbol: "Symbol") -> None:
verify_description_mode(mode) verify_description_mode(mode)
# self.scoped has been done by the CPPEnumObject # self.scoped has been done by the CPPEnumObject
for attr in self.attrs:
attr.describe_signature(signode)
signode += addnodes.desc_sig_space()
self.name.describe_signature(signode, mode, env, symbol=symbol) self.name.describe_signature(signode, mode, env, symbol=symbol)
if self.underlyingType: if self.underlyingType:
signode += addnodes.desc_sig_space() signode += addnodes.desc_sig_space()
@ -5391,7 +5400,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 +5864,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_specifiers_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()
@ -6483,6 +6582,12 @@ class DefinitionParser(BaseParser):
return ASTConcept(nestedName, initializer) return ASTConcept(nestedName, initializer)
def _parse_class(self) -> ASTClass: def _parse_class(self) -> ASTClass:
attrs = []
while 1:
attr = self._parse_attribute()
if attr is None:
break
attrs.append(attr)
name = self._parse_nested_name() name = self._parse_nested_name()
self.skip_ws() self.skip_ws()
final = self.skip_word_and_ws('final') final = self.skip_word_and_ws('final')
@ -6510,21 +6615,33 @@ class DefinitionParser(BaseParser):
continue continue
else: else:
break break
return ASTClass(name, final, bases) return ASTClass(name, final, bases, attrs)
def _parse_union(self) -> ASTUnion: def _parse_union(self) -> ASTUnion:
attrs = []
while 1:
attr = self._parse_attribute()
if attr is None:
break
attrs.append(attr)
name = self._parse_nested_name() name = self._parse_nested_name()
return ASTUnion(name) return ASTUnion(name, attrs)
def _parse_enum(self) -> ASTEnum: def _parse_enum(self) -> ASTEnum:
scoped = None # is set by CPPEnumObject scoped = None # is set by CPPEnumObject
attrs = []
while 1:
attr = self._parse_attribute()
if attr is None:
break
attrs.append(attr)
self.skip_ws() self.skip_ws()
name = self._parse_nested_name() name = self._parse_nested_name()
self.skip_ws() self.skip_ws()
underlyingType = None underlyingType = None
if self.skip_string(':'): if self.skip_string(':'):
underlyingType = self._parse_type(named=False) underlyingType = self._parse_type(named=False)
return ASTEnum(name, scoped, underlyingType) return ASTEnum(name, scoped, underlyingType, attrs)
def _parse_enumerator(self) -> ASTEnumerator: def _parse_enumerator(self) -> ASTEnumerator:
name = self._parse_nested_name() name = self._parse_nested_name()
@ -6541,7 +6658,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 +6730,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
@ -6789,7 +6906,7 @@ class DefinitionParser(BaseParser):
self.warn(msg) self.warn(msg)
newTemplates: List[Union[ASTTemplateParams, ASTTemplateIntroduction]] = [] newTemplates: List[Union[ASTTemplateParams, ASTTemplateIntroduction]] = []
for i in range(numExtra): for _i in range(numExtra):
newTemplates.append(ASTTemplateParams([])) newTemplates.append(ASTTemplateParams([]))
if templatePrefix and not isMemberInstantiation: if templatePrefix and not isMemberInstantiation:
newTemplates.extend(templatePrefix.templates) newTemplates.extend(templatePrefix.templates)
@ -7375,7 +7492,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
@ -7495,7 +7612,7 @@ class CPPAliasObject(ObjectDescription):
" need 'maxdepth' 0 for infinite or at least 2.", " need 'maxdepth' 0 for infinite or at least 2.",
location=self.get_location()) location=self.get_location())
signatures = self.get_signatures() signatures = self.get_signatures()
for i, sig in enumerate(signatures): for sig in signatures:
node.append(AliasNode(sig, aliasOptions, env=self.env)) node.append(AliasNode(sig, aliasOptions, env=self.env))
contentnode = addnodes.desc_content() contentnode = addnodes.desc_content()
@ -7697,7 +7814,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:
@ -7818,7 +7935,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
@ -7908,7 +8025,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
return { return {
'version': 'builtin', 'version': 'builtin',
'env_version': 4, 'env_version': 5,
'parallel_read_safe': True, 'parallel_read_safe': True,
'parallel_write_safe': True, 'parallel_write_safe': True,
} }

View File

@ -1,12 +1,4 @@
""" """The index domain."""
sphinx.domains.index
~~~~~~~~~~~~~~~~~~~~
The index domain.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Tuple from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Tuple
@ -48,7 +40,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 list(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])

View File

@ -1,12 +1,4 @@
""" """The JavaScript domain."""
sphinx.domains.javascript
~~~~~~~~~~~~~~~~~~~~~~~~~
The JavaScript domain.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from typing import Any, Dict, Iterator, List, Optional, Tuple, cast from typing import Any, Dict, Iterator, List, Optional, Tuple, cast
@ -385,10 +377,10 @@ class JavaScriptDomain(Domain):
self.modules[modname] = (self.env.docname, node_id) self.modules[modname] = (self.env.docname, node_id)
def clear_doc(self, docname: str) -> None: def clear_doc(self, docname: str) -> None:
for fullname, (pkg_docname, node_id, _l) in list(self.objects.items()): for fullname, (pkg_docname, _node_id, _l) in list(self.objects.items()):
if pkg_docname == docname: if pkg_docname == docname:
del self.objects[fullname] del self.objects[fullname]
for modname, (pkg_docname, node_id) in list(self.modules.items()): for modname, (pkg_docname, _node_id) in list(self.modules.items()):
if pkg_docname == docname: if pkg_docname == docname:
del self.modules[modname] del self.modules[modname]

View File

@ -1,12 +1,4 @@
""" """The math domain."""
sphinx.domains.math
~~~~~~~~~~~~~~~~~~~
The math domain.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Optional, Tuple from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Optional, Tuple
@ -78,10 +70,10 @@ 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()):
if doc == docname: if doc == docname:
del self.equations[equation_id] del self.equations[equation_id]

View File

@ -1,12 +1,4 @@
""" """The Python domain."""
sphinx.domains.python
~~~~~~~~~~~~~~~~~~~~~
The Python domain.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import builtins import builtins
import inspect import inspect
@ -26,7 +18,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, RemovedInSphinx60Warning from sphinx.deprecation import 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,46 +72,60 @@ class ModuleEntry(NamedTuple):
deprecated: bool deprecated: bool
def type_to_xref(target: 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 target == '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 = {}
refspecific = False reftype, target, title, refspecific = parse_reftarget(target, suppress_prefix)
if target.startswith('.'):
target = target[1:]
text = target
refspecific = True
elif target.startswith('~'):
target = target[1:]
text = target.split('.')[-1]
else:
text = target
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=target, refdomain='py', reftype=reftype, reftarget=target,
refspecific=refspecific, **kwargs) refspecific=refspecific, **kwargs)
def _parse_annotation(annotation: str, env: BuildEnvironment = None) -> List[Node]: def _parse_annotation(annotation: str, env: BuildEnvironment) -> List[Node]:
"""Parse type annotation.""" """Parse type annotation."""
def unparse(node: ast.AST) -> List[Node]: def unparse(node: ast.AST) -> List[Node]:
if isinstance(node, ast.Attribute): if isinstance(node, ast.Attribute):
@ -150,6 +156,8 @@ def _parse_annotation(annotation: str, env: BuildEnvironment = None) -> List[Nod
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:
@ -180,6 +188,8 @@ 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 = []
@ -196,25 +206,34 @@ 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
if env is None:
warnings.warn("The env parameter for _parse_annotation becomes required now.",
RemovedInSphinx50Warning, stacklevel=2)
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)]
@ -331,27 +350,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 list(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
@ -384,16 +403,7 @@ class PyXrefMixin:
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):
@ -401,16 +411,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]]):
@ -965,29 +966,6 @@ class PyProperty(PyObject):
return _('%s (%s property)') % (attrname, clsname) return _('%s (%s property)') % (attrname, clsname)
class PyDecoratorMixin:
"""
Mixin for decorator directives.
"""
def handle_signature(self, sig: str, signode: desc_signature) -> Tuple[str, str]:
for cls in self.__class__.__mro__:
if cls.__name__ != 'DirectiveAdapter':
warnings.warn('PyDecoratorMixin is deprecated. '
'Please check the implementation of %s' % cls,
RemovedInSphinx50Warning, stacklevel=2)
break
else:
warnings.warn('PyDecoratorMixin is deprecated',
RemovedInSphinx50Warning, stacklevel=2)
ret = super().handle_signature(sig, signode) # type: ignore
signode.insert(0, addnodes.desc_addname('@', '@'))
return ret
def needs_arglist(self) -> bool:
return False
class PyModule(SphinxDirective): class PyModule(SphinxDirective):
""" """
Directive to mark description of a new module. Directive to mark description of a new module.
@ -1467,7 +1445,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

View File

@ -1,12 +1,4 @@
""" """The reStructuredText domain."""
sphinx.domains.rst
~~~~~~~~~~~~~~~~~~
The reStructuredText domain.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import re import re
from typing import Any, Dict, Iterator, List, Optional, Tuple, cast from typing import Any, Dict, Iterator, List, Optional, Tuple, cast
@ -235,7 +227,7 @@ class ReSTDomain(Domain):
self.objects[objtype, name] = (self.env.docname, node_id) self.objects[objtype, name] = (self.env.docname, node_id)
def clear_doc(self, docname: str) -> None: def clear_doc(self, docname: str) -> None:
for (typ, name), (doc, node_id) in list(self.objects.items()): for (typ, name), (doc, _node_id) in list(self.objects.items()):
if doc == docname: if doc == docname:
del self.objects[typ, name] del self.objects[typ, name]

View File

@ -1,16 +1,6 @@
""" """The standard domain."""
sphinx.domains.std
~~~~~~~~~~~~~~~~~~
The standard domain.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import re import re
import unicodedata
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,
Tuple, Type, Union, cast) Tuple, Type, Union, cast)
@ -22,7 +12,6 @@ from docutils.statemachine import StringList
from sphinx import addnodes from sphinx import addnodes
from sphinx.addnodes import desc_signature, pending_xref from sphinx.addnodes import desc_signature, pending_xref
from sphinx.deprecation import RemovedInSphinx50Warning
from sphinx.directives import ObjectDescription from sphinx.directives import ObjectDescription
from sphinx.domains import Domain, ObjType from sphinx.domains import Domain, ObjType
from sphinx.locale import _, __ from sphinx.locale import _, __
@ -243,7 +232,7 @@ class Cmdoption(ObjectDescription[str]):
descr = _('%s command line option') % currprogram descr = _('%s command line option') % currprogram
else: else:
descr = _('command line option') descr = _('command line option')
for option in sig.split(', '): for option in signode.get('allnames', []):
entry = '; '.join([descr, option]) entry = '; '.join([descr, option])
self.indexnode['entries'].append(('pair', entry, signode['ids'][0], '', None)) self.indexnode['entries'].append(('pair', entry, signode['ids'][0], '', None))
@ -336,6 +325,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 +390,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 +405,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 +414,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]
@ -675,11 +657,6 @@ class StandardDomain(Domain):
objtype, name, docname, location=location) objtype, name, docname, location=location)
self.objects[objtype, name] = (self.env.docname, labelid) self.objects[objtype, name] = (self.env.docname, labelid)
def add_object(self, objtype: str, name: str, docname: str, labelid: str) -> None:
warnings.warn('StandardDomain.add_object() is deprecated.',
RemovedInSphinx50Warning, stacklevel=2)
self.objects[objtype, name] = (docname, labelid)
@property @property
def _terms(self) -> Dict[str, Tuple[str, str]]: def _terms(self) -> Dict[str, Tuple[str, str]]:
""".. note:: Will be removed soon. internal use only.""" """.. note:: Will be removed soon. internal use only."""
@ -775,7 +752,7 @@ class StandardDomain(Domain):
if not sectname: if not sectname:
continue 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:

View File

@ -1,12 +1,4 @@
""" """Global creation environment."""
sphinx.environment
~~~~~~~~~~~~~~~~~~
Global creation environment.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import os import os
import pickle import pickle
@ -49,7 +41,7 @@ default_settings: Dict[str, Any] = {
'embed_images': False, '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://peps.python.org/',
'pep_references': None, 'pep_references': None,
'rfc_base_url': 'https://datatracker.ietf.org/doc/html/', 'rfc_base_url': 'https://datatracker.ietf.org/doc/html/',
'rfc_references': None, 'rfc_references': None,
@ -261,7 +253,7 @@ class BuildEnvironment:
"""Update settings by new config.""" """Update settings by new config."""
self.settings['input_encoding'] = config.source_encoding self.settings['input_encoding'] = config.source_encoding
self.settings['trim_footnote_reference_space'] = config.trim_footnote_reference_space self.settings['trim_footnote_reference_space'] = config.trim_footnote_reference_space
self.settings['language_code'] = config.language or 'en' self.settings['language_code'] = config.language
# Allow to disable by 3rd party extension (workaround) # Allow to disable by 3rd party extension (workaround)
self.settings.setdefault('smart_quotes', True) self.settings.setdefault('smart_quotes', True)
@ -535,7 +527,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)
@ -621,7 +613,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:

View File

@ -1,9 +1 @@
""" """Sphinx environment adapters"""
sphinx.environment.adapters
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Sphinx environment adapters
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -1,12 +1,4 @@
""" """Assets adapter for sphinx.environment."""
sphinx.environment.adapters.asset
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Assets adapter for sphinx.environment.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from sphinx.environment import BuildEnvironment from sphinx.environment import BuildEnvironment

Some files were not shown because too many files have changed in this diff Show More