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:
test:
if: github.repository_owner == 'sphinx-doc'
runs-on: ubuntu-latest
steps:

View File

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

View File

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

View File

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

View File

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

View File

@ -18,6 +18,7 @@ Other co-maintainers:
Other contributors, listed alphabetically, are:
* Adam Turner -- JavaScript improvements
* Alastair Houghton -- Apple Help builder
* Alexander Todorov -- inheritance_diagram tests and improvements
* Andi Albrecht -- agogo theme
@ -73,6 +74,7 @@ Other contributors, listed alphabetically, are:
* Pauli Virtanen -- autodoc improvements, autosummary extension
* Eric N. Vander Weele -- autodoc improvements
* Stefan van der Walt -- autosummary extension
* Hugo van Kemenade -- support FORCE_COLOR and NO_COLOR
* Thomas Waldmann -- apidoc module fixes
* John Waltman -- Texinfo builder
* 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
@ -13,18 +103,118 @@ Deprecated
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
``__all__`` attribute if :confval:`autosummary_ignore_module_all` is set to
``False``. The default behaviour is unchanged. Autogen also now supports
this behavior with the ``--respect-module-all`` switch.
* #9555: autosummary: Improve error messages on failure to load target object
* #9800: extlinks: Emit warning if a hardcoded link is replaceable
by an extlink, suggesting a replacement.
* #9961: html: Support nested <kbd> HTML elements in other HTML builders
* #10013: html: Allow to change the loading method of JS via ``loading_method``
parameter for :meth:`Sphinx.add_js_file()`
* #9551: html search: "Hide Search Matches" link removes "highlight" parameter
from URL
* #9815: html theme: Wrap sidebar components in div to allow customizing their
layout via CSS
* #9827: i18n: Sort items in glossary by translated terms
* #9899: py domain: Allows to specify cross-reference specifier (``.`` and
``~``) as ``:type:`` option
* #9792: autodoc: Add new option for ``autodoc_typehints_description_target`` to
include undocumented return values but not undocumented parameters.
* #9894: linkcheck: add option ``linkcheck_exclude_documents`` to disable link
checking in matched documents.
* #9793: sphinx-build: Allow to use the parallel build feature in macOS on macOS
and Python3.8+
* #10055: sphinx-build: Create directories when ``-w`` option given
* #9993: std domain: Allow to refer an inline target (ex. ``_`target name```)
via :rst:role:`ref` role
* #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
----------
@ -33,32 +223,42 @@ Bugs fixed
* #9883: autodoc: doccomment for the alias to mocked object was ignored
* #9908: autodoc: debug message is shown on building document using NewTypes
with Python 3.10
* #9968: autodoc: instance variables are not shown if __init__ method has
position-only-arguments
* #9194: autodoc: types under the "typing" module are not hyperlinked
* #10009: autodoc: Crashes if target object raises an error on getting docstring
* #10058: autosummary: Imported members are not shown when
``autodoc_class_signature = 'separated'``
* #9947: i18n: topic directive having a bullet list can't be translatable
* #9878: mathjax: MathJax configuration is placed after loading MathJax itself
* #9932: napoleon: empty "returns" section is generated even if no description
* #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 (in development)
==============================
Dependencies
------------
Incompatible changes
--------------------
Deprecated
----------
Features added
--------------
Release 4.3.2 (released Dec 19, 2021)
=====================================
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)
=====================================
@ -75,7 +275,7 @@ Bugs fixed
* #9838: autodoc: AttributeError is raised on building document for functions
decorated by functools.lru_cache
* #9879: autodoc: AttributeError is raised on building document for an object
having invalid __doc__ atribute
having invalid __doc__ attribute
* #9844: autodoc: Failed to process a function wrapped with functools.partial if
:confval:`autodoc_preserve_defaults` enabled
* #9872: html: Class namespace collision between autodoc signatures and
@ -160,7 +360,7 @@ Bugs fixed
* #9752: autodoc: Failed to detect type annotation for slots attribute
* #9756: autodoc: Crashed if classmethod does not have __func__ attribute
* #9757: autodoc: :confval:`autodoc_inherit_docstrings` does not effect to
overriden classmethods
overridden classmethods
* #9781: autodoc: :confval:`autodoc_preserve_defaults` does not support
hexadecimal numeric
* #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/>`__
* `Blinker <https://pythonhosted.org/blinker/>`__
* `Calibre <https://manual.calibre-ebook.com/>`__
* `CherryPy <https://cherrypy.readthedocs.io/>`__
* `Click <https://click.palletsprojects.com/>`__ (customized)
* `coala <https://docs.coala.io/>`__ (customized)
* `CodePy <https://documen.tician.de/codepy/>`__
* `Django Q <https://django-q.readthedocs.io/>`__
* `Eve <https://docs.python-eve.org/>`__ (Python REST API framework)
* `Fabric <https://docs.fabfile.org/>`__
* `Fityk <https://fityk.nieto.pl/>`__
@ -30,19 +32,21 @@ Documentation using the alabaster theme
* `MDAnalysis <https://www.mdanalysis.org/docs/>`__ (customized)
* `MeshPy <https://documen.tician.de/meshpy/>`__
* `Molecule <https://molecule.readthedocs.io/>`__
* `Momotor LTI <https://momotor.org/doc/lti/canvas/>`__
* `Podman <https://docs.podman.io/>`__
* `PyCUDA <https://documen.tician.de/pycuda/>`__
* `PyOpenCL <https://documen.tician.de/pyopencl/>`__
* `PyLangAcq <https://pylangacq.org/>`__
* `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/>`__
* `Requests <http://www.python-requests.org/>`__
* `Requests <https://docs.python-requests.org/>`__
* `searx <https://asciimoo.github.io/searx/>`__
* `Spyder <https://docs.spyder-ide.org/>`__ (customized)
* `Tablib <http://docs.python-tablib.org/>`__
* `urllib3 <https://urllib3.readthedocs.io/>`__ (customized)
* `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
-------------------------------------
@ -58,8 +62,6 @@ Documentation using the classic theme
* `Buildbot <https://docs.buildbot.net/latest/>`__
* `CMake <https://cmake.org/documentation/>`__ (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/>`__
* `DEAP <https://deap.readthedocs.io/>`__ (customized)
* `Director <https://pythonhosted.org/director/>`__
@ -67,27 +69,26 @@ Documentation using the classic theme
* `F2py <http://f2py.sourceforge.net/docs/>`__
* `Generic Mapping Tools (GMT) <https://gmt.soest.hawaii.edu/doc/latest/>`__ (customized)
* `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)
* `Grok <http://grok.zope.org/doc/current/>`__ (customized)
* `GROMACS <https://manual.gromacs.org/documentation/>`__
* `GSL Shell <https://www.nongnu.org/gsl-shell/>`__
* `Hands-on Python Tutorial <https://anh.cs.luc.edu/python/hands-on/3.1/handsonHtml/>`__
* `Kaa <https://api.freevo.org/kaa-base/>`__ (customized)
* `Leo <https://leoeditor.com/>`__
* `LEPL <http://www.acooke.org/lepl/>`__ (customized)
* `Hands-on Python Tutorial <http://anh.cs.luc.edu/python/hands-on/3.1/handsonHtml/>`__
* `Kaa <https://freevo.github.io/kaa-base/>`__ (customized)
* `Leo <https://leoeditor.com/>`__ (customized)
* `Mayavi <https://docs.enthought.com/mayavi/mayavi/>`__ (customized)
* `MediaGoblin <https://mediagoblin.readthedocs.io/>`__ (customized)
* `mpmath <https://mpmath.org/doc/current/>`__
* `OpenCV <https://docs.opencv.org/>`__ (customized)
* `OpenEXR <https://excamera.com/articles/26/doc/index.html>`__
* `OpenGDA <http://www.opengda.org/gdadoc/html/>`__
* `Peach^3 <https://peach3.nl/doc/latest/userdoc/>`__ (customized)
* `OpenGDA <http://www.opengda.org/documentation/>`__
* `phpDocumentor <https://docs.phpdoc.org/>`__ (customized)
* `Plone <https://docs.plone.org/>`__ (customized)
* `PyEMD <https://pyemd.readthedocs.io/>`__
* `Pyevolve <http://pyevolve.sourceforge.net/>`__
* `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)
* `PyQt5 <http://pyqt.sourceforge.net/Docs/PyQt5/>`__ (customized)
* `Python 2 <https://docs.python.org/2/>`__
@ -120,7 +121,8 @@ Documentation using the sphinxdoc theme
* `MDAnalysis Tutorial <https://www.mdanalysis.org/MDAnalysisTutorial/>`__
* `NetworkX <https://networkx.github.io/>`__
* `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/>`__
* `Pysparse <http://pysparse.sourceforge.net/>`__
* `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/>`__
* `Cython <https://docs.cython.org/>`__
* `easybuild <https://easybuild.readthedocs.io/>`__
* `jsFiddle <http://doc.jsfiddle.net/>`__
* `libLAS <https://www.liblas.org/>`__ (customized)
* `libLAS <https://liblas.org/>`__ (customized)
* `Lmod <https://lmod.readthedocs.io/>`__
* `MapServer <https://mapserver.org/>`__ (customized)
* `Pandas <https://pandas.pydata.org/pandas-docs/stable/>`__
@ -154,11 +155,10 @@ Documentation using another builtin theme
* `Breathe <https://breathe.readthedocs.io/>`__ (haiku)
* `MPipe <https://vmlaker.github.io/mpipe/>`__ (sphinx13)
* `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)
* `Pylons <https://docs.pylonsproject.org/projects/pylons-webframework/>`__ (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) :-)
* `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/>`__
* `Blocks <https://blocks.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)
* `CherryPy <https://docs.cherrypy.org/>`__
* `citeproc-js <https://citeproc-js.readthedocs.io/>`__
* `cloud-init <https://cloudinit.readthedocs.io/>`__
* `CodeIgniter <https://www.codeigniter.com/user_guide/>`__
* `Conda <https://conda.io/docs/>`__
@ -187,7 +190,9 @@ Documentation using sphinx_rtd_theme
* `Databricks <https://docs.databricks.com/>`__ (customized)
* `Dataiku DSS <https://doc.dataiku.com/>`__
* `DNF <https://dnf.readthedocs.io/>`__
* `Distro Tracker <https://qa.pages.debian.net/distro-tracker/>`__
* `Django-cas-ng <https://djangocas.dev/docs/>`__
* `dj-stripe <https://dj-stripe.readthedocs.io/>`__
* `edX <https://docs.edx.org/>`__
* `Electrum <https://docs.electrum.org/>`__
* `Elemental <https://libelemental.org/documentation/dev/>`__
@ -207,13 +212,15 @@ Documentation using sphinx_rtd_theme
* `Graylog <https://docs.graylog.org/>`__
* `GPAW <https://wiki.fysik.dtu.dk/gpaw/>`__ (customized)
* `HDF5 for Python (h5py) <https://docs.h5py.org/>`__
* `HyperKitty <https://hyperkitty.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/>`__
* `Idris <https://docs.idris-lang.org/>`__
* `Inkscape <https://inkscape-manuals.readthedocs.io/>`__ (customized)
* `javasphinx <https://bronto-javasphinx.readthedocs.io/>`__
* `Julia <https://julia.readthedocs.io/>`__
* `Jupyter Notebook <https://jupyter-notebook.readthedocs.io/>`__
* `Kanboard <https://docs.kanboard.org/>`__
* `Lasagne <https://lasagne.readthedocs.io/>`__
* `latexindent.pl <https://latexindentpl.readthedocs.io/>`__
* `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/>`__
* `micca - MICrobial Community Analysis <https://micca.readthedocs.io/>`__
* `MicroPython <https://docs.micropython.org/>`__
* `Minds <https://www.minds.org/docs/>`__ (customized)
* `Mink <https://mink.behat.org/>`__
* `Mockery <https://docs.mockery.io/>`__
* `mod_wsgi <https://modwsgi.readthedocs.io/>`__
@ -234,9 +240,14 @@ Documentation using sphinx_rtd_theme
* `Mopidy <https://docs.mopidy.com/>`__
* `mpi4py <https://mpi4py.readthedocs.io/>`__
* `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>`__
* `nghttp2 <https://nghttp2.org/documentation/>`__
* `NICOS <https://forge.frm2.tum.de/nicos/doc/nicos-master/>`__ (customized)
* `OpenFAST <https://openfast.readthedocs.io/>`__
* `Panda3D <https://docs.panda3d.org/>`__ (customized)
* `Pelican <https://docs.getpelican.com/>`__
* `picamera <https://picamera.readthedocs.io/>`__
* `Pillow <https://pillow.readthedocs.io/>`__
@ -245,15 +256,21 @@ Documentation using sphinx_rtd_theme
* `peewee <https://docs.peewee-orm.com/>`__
* `Phinx <https://docs.phinx.org/>`__
* `phpMyAdmin <https://docs.phpmyadmin.net/>`__
* `PHPUnit <https://phpunit.readthedocs.io/>`__
* `PHPWord <https://phpword.readthedocs.io/>`__
* `PROS <https://pros.cs.purdue.edu/v5/>`__ (customized)
* `Pushkin <http://docs.pushkin.io/>`__
* `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/>`__
* `python-sqlparse <https://sqlparse.readthedocs.io/>`__
* `PyVISA <https://pyvisa.readthedocs.io/>`__
* `pyvista <https://docs.pyvista.org/>`__
* `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)
* `Releases Sphinx extension <https://releases.readthedocs.io/>`__
* `Qtile <https://docs.qtile.org/>`__
@ -269,6 +286,7 @@ Documentation using sphinx_rtd_theme
* `Sonos Controller (SoCo) <https://docs.python-soco.com/>`__
* `Sphinx AutoAPI <https://sphinx-autoapi.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 with Github Webpages <https://runawayhorse001.github.io/SphinxGithub>`__
* `SpotBugs <https://spotbugs.readthedocs.io/>`__
@ -281,15 +299,18 @@ Documentation using sphinx_rtd_theme
* `Topshelf <https://docs.topshelf-project.com/>`__
* `Theano <http://www.deeplearning.net/software/theano/>`__
* `ThreatConnect <https://docs.threatconnect.com/>`__
* `TrueNAS <https://www.ixsystems.com/documentation/truenas/>`__ (customized)
* `Tuleap <https://tuleap.net/doc/en/>`__
* `TYPO3 <https://docs.typo3.org/>`__ (customized)
* `Veyon <https://docs.veyon.io/>`__
* `Ubiquity <https://micro-framework.readthedocs.io/>`__
* `uWSGI <https://uwsgi-docs.readthedocs.io/>`__
* `virtualenv <https://virtualenv.readthedocs.io/>`__
* `Wagtail <https://docs.wagtail.io/>`__
* `Web Application Attack and Audit Framework (w3af) <https://docs.w3af.org/>`__
* `Weblate <https://docs.weblate.org/>`__
* `x265 <https://x265.readthedocs.io/>`__
* `Zeek <https://docs.zeek.org/>`__
* `Zulip <https://zulip.readthedocs.io/>`__
Documentation using sphinx_bootstrap_theme
@ -317,12 +338,12 @@ Documentation using a custom theme or integrated in a website
* `Bokeh <https://bokeh.pydata.org/>`__
* `Boto 3 <https://boto3.readthedocs.io/>`__
* `CakePHP <https://book.cakephp.org/>`__
* `CasperJS <http://docs.casperjs.org/>`__
* `Ceph <https://docs.ceph.com/docs/master/>`__
* `Chef <https://docs.chef.io/>`__
* `CKAN <https://docs.ckan.org/>`__
* `Confluent Platform <https://docs.confluent.io/>`__
* `Django <https://docs.djangoproject.com/>`__
* `django CMS <https://docs.django-cms.org/>`__
* `Doctrine <https://www.doctrine-project.org/>`__
* `Enterprise Toolkit for Acrobat products <https://www.adobe.com/devnet-docs/acrobatetk/>`__
* `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/>`__
* `H2O.ai <https://docs.h2o.ai/>`__
* `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/>`__
* `Kombu <http://docs.kombu.me/>`__
* `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/>`__
* `Open vSwitch <https://docs.openvswitch.org/>`__
* `PlatformIO <https://docs.platformio.org/>`__
* `Psycopg <https://www.psycopg.org/docs/>`__
* `PyEphem <https://rhodesmill.org/pyephem/>`__
* `Pygments <https://pygments.org/docs/>`__
* `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
-------------------------------------------
* `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)
* `Benoit Boissinot <https://bboissin.appspot.com/>`__ (classic, customized)
* `Computer Networks, Parallelization, and Simulation Laboratory (CNPSLab) <https://lab.miletic.net/>`__ (sphinx_rtd_theme)
* `Deep Learning Tutorials <http://www.deeplearning.net/tutorial/>`__ (sphinxdoc)
* `EBI Cloud Consultancy Team <https://tsi-ccdoc.readthedocs.io/>`__ (sphinx_rtd_theme)
* `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)
* `Loyola University Chicago COMP 339-439 Distributed Systems course <https://books.cs.luc.edu/distributedsystems/>`__ (sphinx_bootstrap_theme)
* `Pylearn2 <http://www.deeplearning.net/software/pylearn2/>`__ (sphinxdoc, customized)
* `Loyola University Chicago CS Academic Programs <https://academics.cs.luc.edu/index.html>`__ (sphinx_rtd_theme, customized)
* `PyXLL <https://www.pyxll.com/>`__ (sphinx_bootstrap_theme, customized)
* `SciPy Cookbook <https://scipy-cookbook.readthedocs.io/>`__ (sphinx_rtd_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
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
help pane to render rich documentation for functions, classes and methods
* `Spyder <https://docs.spyder-ide.org/current/panes/help.html>`__, the Scientific Python Development
Environment, uses Sphinx in its help pane to render rich documentation for functions, classes and methods
automatically or on-demand.

View File

@ -1,7 +1,10 @@
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.
Redistribution and use in source and binary forms, with or without

View File

@ -319,6 +319,10 @@ texinfo_documents = [
#
# texinfo_no_detailmenu = False
# If false, do not generate in manual @ref nodes.
#
# texinfo_cross_references = False
# -- A random example -----------------------------------------------------
import sys, os

View File

@ -14,10 +14,10 @@
<form action="https://groups.google.com/group/sphinx-users/boxsubscribe"
class="subscribeform">
<input type="text" name="email" value="your@email"
onfocus="$(this).val('');" />
onfocus="this.value = ''" />
<input type="submit" name="sub" value="Subscribe" />
</form>
</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
<a href="https://github.com/sphinx-doc/sphinx/issues">tracker</a>.{%endtrans%}</p>

View File

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

View File

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

View File

@ -1,5 +1,6 @@
# Sphinx documentation build configuration file
import os
import re
import sphinx
@ -14,7 +15,7 @@ templates_path = ['_templates']
exclude_patterns = ['_build']
project = 'Sphinx'
copyright = '2007-2021, Georg Brandl and the Sphinx team'
copyright = '2007-2022, Georg Brandl and the Sphinx team'
version = sphinx.__display_version__
release = version
show_authors = True
@ -81,11 +82,11 @@ autodoc_member_order = 'groupwise'
autosummary_generate = False
todo_include_todos = True
extlinks = {'duref': ('https://docutils.sourceforge.io/docs/ref/rst/'
'restructuredtext.html#%s', ''),
'restructuredtext.html#%s', '%s'),
'durole': ('https://docutils.sourceforge.io/docs/ref/rst/'
'roles.html#%s', ''),
'roles.html#%s', '%s'),
'dudir': ('https://docutils.sourceforge.io/docs/ref/rst/'
'directives.html#%s', '')}
'directives.html#%s', '%s')}
man_pages = [
('contents', 'sphinx-all', 'Sphinx documentation generator system manual',
@ -108,7 +109,8 @@ texinfo_documents = [
intersphinx_mapping = {
'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:
@ -138,10 +140,33 @@ def parse_event(env, sig, signode):
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):
from sphinx.ext.autodoc import cut_lines
from sphinx.util.docfields import GroupedField
app.connect('autodoc-process-docstring', cut_lines(4, what=['module']))
app.connect('source-read', linkify_issues_in_changelog)
app.add_object_type('confval', 'confval',
objname='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
-----------------------------------------
As a way to distribute your theme, you can use Python package. Python package
brings to users easy setting up ways.
As a way to distribute your theme, you can use a Python package. This makes it
easier for users to set up your theme.
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()``
@ -285,7 +285,7 @@ engine, allowing you to embed variables and control behavior.
For example, the following JavaScript structure:
.. code-block:: bash
.. code-block:: none
mymodule/
├── _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:
.. code-block:: bash
.. code-block:: none
_build/
└── html

View File

@ -9,7 +9,7 @@ from sphinx.ext.autodoc import ClassDocumenter, bool_option
class IntEnumDocumenter(ClassDocumenter):
objtype = 'intenum'
directivetype = 'class'
directivetype = ClassDocumenter.objtype
priority = 10 + ClassDocumenter.priority
option_spec = dict(ClassDocumenter.option_spec)
option_spec['hex'] = bool_option
@ -18,7 +18,10 @@ class IntEnumDocumenter(ClassDocumenter):
def can_document_member(cls,
member: Any, membername: str,
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:
super().add_directive_header(sig)
@ -36,14 +39,13 @@ class IntEnumDocumenter(ClassDocumenter):
use_hex = self.options.hex
self.add_line('', source_name)
for enum_value in enum_object:
the_value_name = enum_value.name
the_value_value = enum_value.value
for the_member_name, enum_member in enum_object.__members__.items():
the_member_value = enum_member.value
if use_hex:
the_value_value = hex(the_value_value)
the_member_value = hex(the_member_value)
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)

View File

@ -87,7 +87,7 @@ class RecipeIndex(Index):
# first letter of the recipe as a key to group thing
#
# 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(
(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
: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

View File

@ -22,6 +22,31 @@ The following is a list of deprecated interfaces.
- (will be) Removed
- 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``
- 4.3
- 6.0

View File

@ -35,3 +35,4 @@ to configure their settings appropriately.
.. module:: sphinx.parsers
.. 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`
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
``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
@ -346,19 +350,3 @@ The following notes may be helpful if you want to create Texinfo files:
scheme ``info``. For example::
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>
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.
.. _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
<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
------------------

View File

@ -75,7 +75,7 @@ and assuming ``docs/index.rst`` contained the following:
If you run the following:
.. code-block:: bash
.. code-block:: console
$ 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
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:
Deprecation Warnings

View File

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

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
highlighted the module, the function name, and the parameters appropriately.
- The directive content includes a one-line description of the function,
as well as a :ref:`info field list <info-field-lists>` containing the function
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.
.. note::

View File

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

View File

@ -68,6 +68,24 @@ be translated you need to follow these instructions:
* 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
----------------------------

View File

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

View File

@ -316,7 +316,11 @@ General configuration
* ``app.add_role``
* ``app.add_generic_role``
* ``app.add_source_parser``
* ``autosectionlabel.*``
* ``download.not_readable``
* ``epub.unknown_project_files``
* ``epub.duplicated_toc_entry``
* ``i18n.inconsistent_references``
* ``image.not_readable``
* ``ref.term``
* ``ref.ref``
@ -332,11 +336,9 @@ General configuration
* ``toc.excluded``
* ``toc.not_readable``
* ``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*.
@ -366,6 +368,10 @@ General configuration
Added ``toc.excluded`` and ``toc.not_readable``
.. versionadded:: 4.5
Added ``i18n.inconsistent_references``
.. confval:: needs_sphinx
If set to a ``major.minor`` version string like ``'1.1'``, Sphinx will
@ -392,7 +398,7 @@ General configuration
.. 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
:literal:`:manpage:`man(1)`` role will link to
<https://manpages.debian.org/man(1)>. The patterns available are:
@ -1351,6 +1357,13 @@ that use Sphinx's HTMLWriter class.
.. 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
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
.. 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:
@ -2683,6 +2703,19 @@ Options for the linkcheck builder
.. 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
---------------------------

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.
.. _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
@ -468,7 +468,7 @@ There are also config values that you can set:
.. 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:
``"mixed"``
@ -528,7 +528,8 @@ There are also config values that you can set:
The supported options are ``'members'``, ``'member-order'``,
``'undoc-members'``, ``'private-members'``, ``'special-members'``,
``'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
@ -541,6 +542,9 @@ There are also config values that you can set:
.. versionchanged:: 4.1
Added ``'class-doc-from'``.
.. versionchanged:: 4.5
Added ``'no-value'``.
.. confval:: autodoc_docstring_signature
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
the document. Defaults to empty (``{}``).
The type aliases are only available if your program enables `Postponed
Evaluation of Annotations (PEP 563)`__ feature via ``from __future__ import
The type aliases are only available if your program enables :pep:`Postponed
Evaluation of Annotations (PEP 563) <563>` feature via ``from __future__ import
annotations``.
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
.. 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
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/method.rst` -- template for class methods
The following variables available in the templates:
The following variables are available in the templates:
.. currentmodule:: None
@ -300,7 +300,7 @@ The following variables available in the templates:
.. data:: functions
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.
.. data:: classes

View File

@ -93,8 +93,8 @@ a comma-separated list of group names.
* ``<``, ``>``: Exclusive ordered comparison clause
* ``===``: Arbitrary equality clause.
``pyversion`` option is followed `PEP-440: Version Specifiers
<https://www.python.org/dev/peps/pep-0440/#version-specifiers>`__.
``pyversion`` option is followed :pep:`PEP-440: Version Specifiers
<440#version-specifiers>`.
.. 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):
"""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
included in the docstring:
@ -55,10 +55,6 @@ def function_with_types_in_docstring(param1, param2):
Returns:
bool: The return value. True for success, False otherwise.
.. _PEP 484:
https://www.python.org/dev/peps/pep-0484/
"""

View File

@ -37,8 +37,8 @@ module_level_variable1 : int
with it.
.. _NumPy Documentation HOWTO:
https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt
.. _NumPy docstring standard:
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):
"""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
included in the docstring:
@ -70,10 +70,6 @@ def function_with_types_in_docstring(param1, param2):
-------
bool
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
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
This extension can generate automatic links to the documentation of objects in
other projects.
This extension can generate links to the documentation of objects in external
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
matching target in the current documentation set, it looks for targets in the
documentation sets configured in :confval:`intersphinx_mapping`. A reference
like ``:py:class:`zipfile.ZipFile``` can then link to the Python documentation
Usage for fallback resolution is simple: whenever Sphinx encounters a
cross-reference that has no matching target in the current documentation set,
it looks for targets in the external documentation sets configured in
: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
exactly.
When using the "new" format (see below), you can even force lookup in a foreign
set by prefixing the link target appropriately. A link like ``:ref:`comparison
manual <python:comparisons>``` will then link to the label "comparisons" in the
doc set "python", if it exists.
When using the :rst:role:`external` role, you can force lookup to any external
projects, and optionally to a specific external project.
A link like ``:external:ref:`comparison manual <comparisons>``` will then link
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:
@ -30,8 +35,8 @@ Behind the scenes, this works as follows:
* Projects using the Intersphinx extension can specify the location of such
mapping files in the :confval:`intersphinx_mapping` config value. The mapping
will then be used to resolve otherwise missing references to objects into
links to the other documentation.
will then be used to resolve both :rst:role:`external` references, and also
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
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
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
``:ref:`comparison manual <python:comparisons>``` will link to the label
"comparisons" in the doc set "python", if it exists.
``external:python+ref:`comparison manual <comparisons>``` will link to the
label "comparisons" in the doc set "python", if it exists.
**Example**
@ -152,6 +157,10 @@ linking:
.. versionadded:: 4.3
.. versionchanged:: 5.0
Changed default value from an empty list to ``['std:doc']``.
A list of strings being either:
- the name of a specific reference type in a domain,
@ -160,23 +169,52 @@ linking:
``std:*``, ``py:*``, or ``cpp:*``, or
- 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
resolved by intersphinx, skip resolution if it matches one of the
specifications in this list.
When a non-:rst:role:`external` cross-reference is being resolved by
intersphinx, skip resolution if it matches one of the specifications in this
list.
For example, with ``intersphinx_disabled_reftypes = ['std:doc']``
a cross-reference ``:doc:`installation``` will not be attempted to be
resolved by intersphinx, but ``:doc:`otherbook:installation``` will be
attempted to be resolved in the inventory named ``otherbook`` in
resolved by intersphinx, but ``:external+otherbook:doc:`installation``` will
be attempted to be resolved in the inventory named ``otherbook`` in
:confval:`intersphinx_mapping`.
At the same time, all cross-references generated in, e.g., Python,
declarations will still be attempted to be resolved by intersphinx.
If ``*`` is in the list of domains, then no references without an explicit
inventory will be resolved by intersphinx.
If ``*`` is in the list of domains, then no non-:rst:role:`external`
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
------------------------------------------------

View File

@ -26,7 +26,7 @@ Are you tired of writing docstrings that look like this::
:rtype: BufferedFileStorage
`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`_::
Args:
@ -50,7 +50,6 @@ the documentation, so it doesn't modify any of the docstrings in your actual
source code files.
.. _ReStructuredText: https://docutils.sourceforge.io/rst.html
.. _docstrings: https://www.python.org/dev/peps/pep-0287/
.. _Google Python Style Guide:
https://google.github.io/styleguide/pyguide.html
.. _Google:
@ -199,11 +198,11 @@ not be mixed. Choose one style for your project and be consistent with it.
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.
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
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).
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
supported by Sphinx and won't show up in the docs.
.. _PEP 484: https://www.python.org/dev/peps/pep-0484/
.. _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
.. _Python 2/3 compatible annotations: https://peps.python.org/pep-0484/#suggested-syntax-for-python-2-7-and-straddling-code
Configuration
@ -548,7 +545,7 @@ sure that "sphinx.ext.napoleon" is enabled in `conf.py`::
.. 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
has an annotation in the class body, that type is used.

View File

@ -222,8 +222,8 @@ of images:
- `sphinxdoc/sphinx-latexpdf`_
.. _Docker Hub: https://hub.docker.com/
.. _sphinxdoc/sphinx: https://hub.docker.com/repository/docker/sphinxdoc/sphinx
.. _sphinxdoc/sphinx-latexpdf: https://hub.docker.com/repository/docker/sphinxdoc/sphinx-latexpdf
.. _sphinxdoc/sphinx: https://hub.docker.com/r/sphinxdoc/sphinx
.. _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
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
project:
.. code-block:: bash
.. code-block:: console
$ 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
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

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
configuration values from a few questions it asks you. To use this, run:
.. code-block:: shell
.. code-block:: console
$ 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
docs. A build is started with the :program:`sphinx-build` program:
.. code-block:: shell
.. code-block:: console
$ 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
running :command:`make` with the name of the builder. For example.
.. code-block:: shell
.. code-block:: console
$ make html

View File

@ -245,10 +245,10 @@ follow:
* ``#`` with overline, for parts
* ``*`` with overline, for chapters
* ``=``, for sections
* ``-``, for subsections
* ``^``, for subsubsections
* ``"``, for paragraphs
* ``=`` for sections
* ``-`` for subsections
* ``^`` for subsubsections
* ``"`` for paragraphs
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

View File

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

View File

@ -211,6 +211,12 @@ The following directives are provided for module and class contents:
.. 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
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
.. 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
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
.. 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
.. py:class:: name(parameters)
@ -291,6 +309,12 @@ The following directives are provided for module and class contents:
.. 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
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
.. 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
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
: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)
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
.. 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
: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
as a Python package, you can use it after installing
.. code-block:: bash
.. code-block:: console
# installing theme package
$ 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
want a width limit. Defaults may depend on the theme (often 800px).
- **navigation_with_keys** (true or false): Allow navigating to the
previous/next page using the keyboard's left and right arrows. Defaults to
``False``.
- **navigation_with_keys** (true or false): Allow navigating
with the following keyboard shortcuts:
- :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
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
files: [
'sphinx/themes/basic/static/underscore.js',
'sphinx/themes/basic/static/jquery.js',
'sphinx/themes/basic/static/doctools.js',
'sphinx/themes/basic/static/searchtools.js',
'tests/js/*.js'
@ -59,7 +57,7 @@ module.exports = function(config) {
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: ['Chrome', 'Firefox'],
browsers: ["Firefox"],
// Continuous Integration mode

2569
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@ -1,6 +1,5 @@
import os
import sys
from distutils import log
from io import StringIO
from setuptools import find_packages, setup
@ -29,8 +28,8 @@ install_requires = [
'alabaster>=0.7,<0.8',
'imagesize',
'requests>=2.5.0',
'setuptools',
'packaging',
"importlib-metadata>=4.4; python_version < '3.10'",
]
extras_require = {
@ -44,10 +43,9 @@ extras_require = {
'lint': [
'flake8>=3.5.0',
'isort',
'mypy>=0.900',
'mypy>=0.931',
'docutils-stubs',
"types-typed-ast",
"types-pkg_resources",
"types-requests",
],
'test': [
@ -149,7 +147,7 @@ else:
if catalog.fuzzy and not self.use_fuzzy:
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)
jscatalog = {}

View File

@ -1,12 +1,4 @@
"""
Sphinx
~~~~~~
The Sphinx documentation toolchain.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
"""The Sphinx documentation toolchain."""
# Keep this file executable as-is in Python 3!
# (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",
DeprecationWarning, module='docutils.io')
__version__ = '4.4.0+'
__released__ = '4.4.0' # used when Sphinx builds its own docs
__version__ = '5.0.0+'
__released__ = '5.0.0' # used when Sphinx builds its own docs
#: Version info for better programmatic use.
#:
@ -38,7 +30,7 @@ __released__ = '4.4.0' # used when Sphinx builds its own docs
#:
#: .. versionadded:: 1.2
#: 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__))

View File

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

View File

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

View File

@ -1,18 +1,10 @@
"""
sphinx.application
~~~~~~~~~~~~~~~~~~
Sphinx application class and extensibility interface.
"""Sphinx application class and extensibility interface.
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 pickle
import platform
import sys
import warnings
from collections import deque
@ -195,12 +187,6 @@ class Sphinx:
# say hello to the world
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
self.statuscode = 0
@ -273,7 +259,7 @@ class Sphinx:
"""Load translated strings from the configured localedirs if enabled in
the configuration.
"""
if self.config.language is None:
if self.config.language == 'en':
self.translator, has_translation = locale.init([], None)
else:
logger.info(bold(__('loading translations [%s]... ') % self.config.language),
@ -292,8 +278,7 @@ class Sphinx:
locale_dirs += [path.join(package_dir, 'locale')]
self.translator, has_translation = locale.init(locale_dirs, self.config.language)
if has_translation or self.config.language == 'en':
# "en" never needs to be translated
if has_translation:
logger.info(__('done'))
else:
logger.info(__('not available for built-in messages'))
@ -937,24 +922,31 @@ class Sphinx:
"""
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.
Add *filename* to the list of JavaScript files that the default HTML
template will include in order of *priority* (ascending). The filename
must be relative to the HTML static path , or a full URI with scheme.
If the priority of the JavaScript file is the same as others, the JavaScript
files will be included in order of registration. If the keyword
argument ``body`` is given, its value will be added between the
``<script>`` tags. Extra keyword arguments are included as attributes of
the ``<script>`` tag.
:param filename: The filename of the JavaScript file. It must be relative to the HTML
static path, a full URI with scheme, or ``None`` value. The ``None``
value is used to create inline ``<script>`` tag. See the description
of *kwargs* below.
:param priority: The priority to determine the order of ``<script>`` tag for
JavaScript files. See list of "prority range for JavaScript
files" below. If the priority of the JavaScript files it the same
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::
app.add_js_file('example.js')
# => <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>
app.add_js_file(None, body="var myVariable = 'foo';")
@ -983,7 +975,15 @@ class Sphinx:
.. versionchanged:: 3.5
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)
if hasattr(self.builder, 'add_js_file'):
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:
"""Register a stylesheet to include in the HTML output.
Add *filename* to the list of CSS files that the default HTML template
will include in order of *priority* (ascending). The filename must be
relative to the HTML static path, or a full URI with scheme. If the
priority of the CSS file is the same as others, the CSS files will be
included in order of registration. The keyword arguments are also
accepted for attributes of ``<link>`` tag.
:param filename: The filename of the CSS file. It must be relative to the HTML
static path, or a full URI with scheme.
:param priority: The priority to determine the order of ``<link>`` tag for the
CSS files. See list of "prority range for CSS files" below.
If the priority of the CSS files it the same as others, the
CSS files will be loaded in order of registration.
:param kwargs: Extra keyword arguments are included as attributes of the ``<link>``
tag.
Example::

View File

@ -1,12 +1,4 @@
"""
sphinx.builders
~~~~~~~~~~~~~~~
Builder superclass for all builders.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
"""Builder superclass for all builders."""
import pickle
import time
@ -176,7 +168,7 @@ class Builder:
def post_process_images(self, doctree: Node) -> None:
"""Pick the best candidate for all image URIs."""
images = ImageAdapter(self.env)
for node in doctree.traverse(nodes.image):
for node in doctree.findall(nodes.image):
if '?' in node['candidates']:
# don't rewrite nonlocal image URIs
continue
@ -562,7 +554,7 @@ class Builder:
for chunk in status_iterator(chunks, __('writing output... '), "darkgreen",
len(chunks), self.app.verbosity):
arg = []
for i, docname in enumerate(chunk):
for docname in chunk:
doctree = self.env.get_and_resolve_doctree(docname, self)
self.write_doc_serialized(docname, doctree)
arg.append((docname, doctree))

View File

@ -1,12 +1,4 @@
"""
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.
"""
"""Base class of epub2/epub3 builders."""
import html
import os
@ -277,7 +269,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
new_ids.append(new_id)
node['ids'] = new_ids
for reference in tree.traverse(nodes.reference):
for reference in tree.findall(nodes.reference):
if 'refuri' in reference:
m = self.refuri_re.match(reference['refuri'])
if m:
@ -285,14 +277,14 @@ class EpubBuilder(StandaloneHTMLBuilder):
if 'refid' in reference:
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)
next_node: Node = target.next_node(ascend=True)
if isinstance(next_node, nodes.Element):
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)
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
# b) place them after an (empty) Footnotes rubric
# 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:
fn = fns[-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:
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.append(nodes.Text(FOOTNOTES_RUBRIC_NAME))
doc.append(rub)
@ -339,10 +331,10 @@ class EpubBuilder(StandaloneHTMLBuilder):
if show_urls == 'no':
return
if show_urls == 'footnote':
doc = list(tree.traverse(nodes.document))[0]
doc = next(tree.findall(nodes.document))
fn_spot, fn_idx = footnote_spot(tree)
nr = 1
for node in list(tree.traverse(nodes.reference)):
for node in list(tree.findall(nodes.reference)):
uri = node.get('refuri', '')
if (uri.startswith('http:') or uri.startswith('https:') or
uri.startswith('ftp:')) and uri not in node.astext():
@ -377,14 +369,14 @@ class EpubBuilder(StandaloneHTMLBuilder):
"""Fix href attributes for genindex pages."""
# XXX: modifies tree inline
# Logic modeled from themes/basic/genindex.html
for key, columns in tree:
for entryname, (links, subitems, key_) in columns:
for _key, columns in tree:
for _entryname, (links, subitems, _key) in columns:
for (i, (ismain, link)) in enumerate(links):
m = self.refuri_re.match(link)
if m:
links[i] = (ismain,
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):
m = self.refuri_re.match(link)
if m:
@ -703,7 +695,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
epub_filename = path.join(self.outdir, outname)
with ZipFile(epub_filename, 'w', ZIP_DEFLATED) as epub:
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)
for filename in self.files:
epub.write(path.join(self.outdir, filename), filename, ZIP_DEFLATED)

View File

@ -1,12 +1,4 @@
"""
sphinx.builders.changes
~~~~~~~~~~~~~~~~~~~~~~~
Changelog builder.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
"""Changelog builder."""
import html
from os import path
@ -148,7 +140,7 @@ class ChangesBuilder(Builder):
def hl(self, text: str, version: str) -> str:
text = html.escape(text)
for directive in ['versionchanged', 'versionadded', 'deprecated']:
for directive in ('versionchanged', 'versionadded', 'deprecated'):
text = text.replace('.. %s:: %s' % (directive, version),
'<b>.. %s:: %s</b>' % (directive, version))
return text

View File

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

View File

@ -1,12 +1,4 @@
"""
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.
"""
"""Do syntax checks, but no writing."""
from typing import Any, Dict, Set

View File

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

View File

@ -1,12 +1,4 @@
"""
sphinx.builders.gettext
~~~~~~~~~~~~~~~~~~~~~~~
The MessageCatalogBuilder class.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
"""The MessageCatalogBuilder class."""
from codecs import open
from collections import OrderedDict, defaultdict
@ -146,7 +138,7 @@ class I18nBuilder(Builder):
def write_doc(self, docname: str, doctree: nodes.document) -> None:
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):
node.uid = '' # type: ignore # Hack UUID model
catalog.add(msg, node)
@ -157,7 +149,7 @@ class I18nBuilder(Builder):
if 'index' in self.env.config.gettext_additional_targets:
# Extract translatable messages from index entries.
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):
if typ == 'pair' and m in pairindextypes.values():
# avoid built-in translated message was incorporated
@ -180,7 +172,7 @@ if source_date_epoch is not None:
class LocalTimeZone(tzinfo):
def __init__(self, *args: Any, **kwargs: Any) -> None:
super().__init__(*args, **kwargs) # type: ignore
super().__init__(*args, **kwargs)
self.tzdelta = tzdelta
def utcoffset(self, dt: datetime) -> timedelta:
@ -227,7 +219,7 @@ class MessageCatalogBuilder(I18nBuilder):
template_files = set()
for template_path in self.config.templates_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:
if fn.endswith('.html'):
filename = canon_path(path.join(dirpath, fn))
@ -247,7 +239,7 @@ class MessageCatalogBuilder(I18nBuilder):
try:
with open(template, encoding='utf-8') as f:
context = f.read()
for line, meth, msg in extract_translations(context):
for line, _meth, msg in extract_translations(context):
origin = MsgOrigin(template, line)
self.catalogs['sphinx'].add(msg, origin)
except Exception as exc:

View File

@ -1,12 +1,4 @@
"""
sphinx.builders.html
~~~~~~~~~~~~~~~~~~~~
Several HTML builders.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
"""Several HTML builders."""
import html
import os
@ -15,7 +7,7 @@ import re
import sys
from datetime import datetime
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 docutils import nodes
@ -76,6 +68,17 @@ def get_stable_hash(obj: Any) -> str:
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):
"""A metadata of stylesheet.
@ -315,8 +318,11 @@ class StandaloneHTMLBuilder(Builder):
self.script_files = []
self.add_js_file('documentation_options.js', id="documentation_options",
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('underscore.js', priority=200)
self.add_js_file('_sphinx_javascript_frameworks_compat.js', priority=200)
self.add_js_file('doctools.js', priority=200)
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'
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')
def add_js_file(self, filename: str, **kwargs: Any) -> None:
@ -431,8 +437,6 @@ class StandaloneHTMLBuilder(Builder):
if self.search:
from sphinx.search import IndexBuilder
lang = self.config.html_search_language or self.config.language
if not lang:
lang = 'en'
self.indexer = IndexBuilder(self.env, lang,
self.config.html_search_options,
self.config.html_search_scorer)
@ -486,7 +490,7 @@ class StandaloneHTMLBuilder(Builder):
rellinks: List[Tuple[str, str, str, str]] = []
if self.use_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 indexcls.shortname:
rellinks.append((indexname, indexcls.localname,
@ -509,6 +513,7 @@ class StandaloneHTMLBuilder(Builder):
'docstitle': self.config.html_title,
'shorttitle': self.config.html_short_title,
'show_copyright': self.config.html_show_copyright,
'show_search_summary': self.config.html_show_search_summary,
'show_sphinx': self.config.html_show_sphinx,
'has_source': self.config.html_copy_source,
'show_source': self.config.html_show_sourcelink,
@ -516,7 +521,7 @@ class StandaloneHTMLBuilder(Builder):
'file_suffix': self.out_suffix,
'link_suffix': self.link_suffix,
'script_files': self.script_files,
'language': self.config.language,
'language': convert_locale_to_language_tag(self.config.language),
'css_files': self.css_files,
'sphinx_version': __display_version__,
'sphinx_version_tuple': sphinx_version,
@ -767,7 +772,6 @@ class StandaloneHTMLBuilder(Builder):
def copy_translation_js(self) -> None:
"""Copy a JavaScript file for translations."""
if self.config.language is not None:
jsfile = self._get_translations_js()
if jsfile:
copyfile(jsfile, path.join(self.outdir, '_static', 'translations.js'))
@ -866,7 +870,7 @@ class StandaloneHTMLBuilder(Builder):
Builder.post_process_images(self, doctree)
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']):
# resizing options are not given. scaled image link is available
# 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_link_suffix', None, 'html', [str])
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_context', {}, 'html')
app.add_config_value('html_output_encoding', 'utf-8', 'html')

View File

@ -1,12 +1,4 @@
"""
sphinx.builders.html.transforms
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Transforms for HTML builder.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
"""Transforms for HTML builder."""
import re
from typing import Any, Dict, List
@ -36,7 +28,7 @@ class KeyboardTransform(SphinxPostTransform):
x
"""
default_priority = 400
builders = ('html',)
formats = ('html',)
pattern = re.compile(r'(?<=.)(-|\+|\^|\s+)(?=.)')
multiwords_keys = (('caps', 'lock'),
('page' 'down'),
@ -48,7 +40,7 @@ class KeyboardTransform(SphinxPostTransform):
def run(self, **kwargs: Any) -> None:
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())
if len(parts) == 1 or self.is_multiwords_key(parts):
continue

View File

@ -1,15 +1,6 @@
"""
sphinx.builders.latex
~~~~~~~~~~~~~~~~~~~~~
LaTeX builder.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
"""LaTeX builder."""
import os
import warnings
from os import path
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.util import ExtBabel
from sphinx.config import ENUM, Config
from sphinx.deprecation import RemovedInSphinx50Warning
from sphinx.environment.adapters.asset import ImageAdapter
from sphinx.errors import NoUri, SphinxError
from sphinx.locale import _, __
@ -172,7 +162,6 @@ class LaTeXBuilder(Builder):
self.context.update(ADDITIONAL_SETTINGS.get(self.config.latex_engine, {}))
# Add special settings for (latex_engine, language_code)
if self.config.language:
key = (self.config.latex_engine, self.config.language[:2])
self.context.update(ADDITIONAL_SETTINGS.get(key, {}))
@ -205,7 +194,7 @@ class LaTeXBuilder(Builder):
def init_babel(self) -> None:
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
# (only emitting, nothing changed to processing)
logger.warning(__('no Babel option known for language %r'),
@ -234,7 +223,6 @@ class LaTeXBuilder(Builder):
self.context['classoptions'] += ',' + self.babel.get_language()
# this branch is not taken for xelatex/lualatex if default settings
self.context['multilingual'] = self.context['babel']
if self.config.language:
self.context['shorthandoff'] = SHORTHANDOFF
# Times fonts don't work with Cyrillic languages
@ -280,7 +268,7 @@ class LaTeXBuilder(Builder):
encoding='utf-8', overwrite_if_changed=True)
with progress_message(__("processing %s") % targetname):
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:
tocdepth = toctree.get('maxdepth')
else:
@ -310,7 +298,7 @@ class LaTeXBuilder(Builder):
def get_contentsname(self, indexfile: str) -> str:
tree = self.env.get_doctree(indexfile)
contentsname = None
for toctree in tree.traverse(addnodes.toctree):
for toctree in tree.findall(addnodes.toctree):
if 'caption' in toctree:
contentsname = toctree['caption']
break
@ -338,7 +326,7 @@ class LaTeXBuilder(Builder):
new_sect += nodes.title('<Set title in conf.py>',
'<Set title in conf.py>')
new_tree += new_sect
for node in tree.traverse(addnodes.toctree):
for node in tree.findall(addnodes.toctree):
new_sect += node
tree = new_tree
largetree = inline_all_toctrees(self, self.docnames, indexfile, tree,
@ -353,7 +341,7 @@ class LaTeXBuilder(Builder):
self.env.resolve_references(largetree, indexfile, self)
# resolve :ref:s to distant tex files -- we can't add a cross-reference,
# but append the document name
for pendingnode in largetree.traverse(addnodes.pending_xref):
for pendingnode in largetree.findall(addnodes.pending_xref):
docname = pendingnode['refdocname']
sectname = pendingnode['refsectname']
newnodes: List[Node] = [nodes.emphasis(sectname, sectname)]
@ -382,14 +370,10 @@ class LaTeXBuilder(Builder):
# configure usage of xindy (impacts Makefile and latexmkrc)
# FIXME: convert this rather to a confval with suitable default
# 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 = XINDY_LANG_OPTIONS.get(self.config.language[:2],
'-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 = {
'latex_engine': self.config.latex_engine,
'xindy_use': self.config.latex_use_xindy,
@ -449,18 +433,6 @@ class LaTeXBuilder(Builder):
filename = path.join(package_dir, 'templates', 'latex', 'sphinxmessages.sty_t')
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:
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. """
if config.language == 'ja':
return 'uplatex'
elif (config.language or '').startswith('zh'):
elif config.language.startswith('zh'):
return 'xelatex'
elif config.language == 'el':
return 'xelatex'

View File

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

View File

@ -1,12 +1,4 @@
"""
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.
"""
"""Additional nodes for LaTeX writer."""
from docutils import nodes

View File

@ -1,12 +1,4 @@
"""
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.
"""
"""Theming support for LaTeX builder."""
import configparser
from os import path

View File

@ -1,12 +1,4 @@
"""
sphinx.builders.latex.transforms
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Transforms for LaTeX builder.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
"""Transforms for LaTeX builder."""
from typing import Any, Dict, List, Set, Tuple, cast
@ -33,7 +25,7 @@ class FootnoteDocnameUpdater(SphinxTransform):
def apply(self, **kwargs: Any) -> None:
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
@ -45,7 +37,7 @@ class SubstitutionDefinitionsRemover(SphinxPostTransform):
formats = ('latex',)
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)
@ -81,7 +73,7 @@ class ShowUrlsTransform(SphinxPostTransform):
if show_urls is False or show_urls == 'no':
return
for node in list(self.document.traverse(nodes.reference)):
for node in list(self.document.findall(nodes.reference)):
uri = node.get('refuri', '')
if uri.startswith(URI_SCHEMES):
if uri.startswith('mailto:'):
@ -237,7 +229,8 @@ class LaTeXFootnoteTransform(SphinxPostTransform):
blah blah blah ...
* 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::
@ -258,7 +251,7 @@ class LaTeXFootnoteTransform(SphinxPostTransform):
After::
blah blah blah
<footnote ids="id1">
<footnote ids="id1" referred=True>
<label>
1
<paragraph>
@ -348,7 +341,7 @@ class LaTeXFootnoteTransform(SphinxPostTransform):
formats = ('latex',)
def run(self, **kwargs: Any) -> None:
footnotes = list(self.document.traverse(nodes.footnote))
footnotes = list(self.document.findall(nodes.footnote))
for node in footnotes:
node.parent.remove(node)
@ -358,7 +351,7 @@ class LaTeXFootnoteTransform(SphinxPostTransform):
class LaTeXFootnoteVisitor(nodes.NodeVisitor):
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.pendings: List[nodes.footnote] = []
self.table_footnotes: List[nodes.footnote] = []
@ -423,7 +416,7 @@ class LaTeXFootnoteVisitor(nodes.NodeVisitor):
self.unrestrict(node)
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):
fntext = footnotetext('', *footnote.children, ids=footnote['ids'])
tbody.insert(0, fntext)
@ -439,22 +432,24 @@ class LaTeXFootnoteVisitor(nodes.NodeVisitor):
def visit_footnote_reference(self, node: nodes.footnote_reference) -> None:
number = node.astext().strip()
docname = node['docname']
if self.restricted:
mark = footnotemark('', number, refid=node['refid'])
node.replace_self(mark)
if (docname, number) not in self.appeared:
footnote = self.get_footnote_by_reference(node)
self.pendings.append(footnote)
elif (docname, number) in self.appeared:
if (docname, number) in self.appeared:
footnote = self.appeared.get((docname, number))
footnote["referred"] = True
mark = footnotemark('', number, refid=node['refid'])
node.replace_self(mark)
else:
footnote = self.get_footnote_by_reference(node)
if self.restricted:
mark = footnotemark('', number, refid=node['refid'])
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
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:
citations = thebibliography()
for node in list(self.document.traverse(nodes.citation)):
for node in list(self.document.findall(nodes.citation)):
node.parent.remove(node)
citations += node
@ -521,7 +516,7 @@ class CitationReferenceTransform(SphinxPostTransform):
def run(self, **kwargs: Any) -> None:
domain = cast(CitationDomain, self.env.get_domain('citation'))
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))
if docname:
citation_ref = nodes.citation_reference('', '', *node.children,
@ -540,7 +535,7 @@ class MathReferenceTransform(SphinxPostTransform):
def run(self, **kwargs: Any) -> None:
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'):
docname, _ = equations.get(node['reftarget'], (None, None))
if docname:
@ -555,7 +550,7 @@ class LiteralBlockTransform(SphinxPostTransform):
def run(self, **kwargs: Any) -> None:
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)
node.replace_self(newnode)
@ -566,7 +561,7 @@ class DocumentTargetTransform(SphinxPostTransform):
formats = ('latex',)
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)
if section:
section['ids'].append(':doc') # special label for :doc:
@ -602,9 +597,9 @@ class IndexInSectionTitleTransform(SphinxPostTransform):
formats = ('latex',)
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):
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
node.remove(index)
node.parent.insert(i + 1, index)

View File

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

View File

@ -1,37 +1,25 @@
"""
sphinx.builders.linkcheck
~~~~~~~~~~~~~~~~~~~~~~~~~
The CheckExternalLinksBuilder class.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
"""The CheckExternalLinksBuilder class."""
import json
import re
import socket
import time
import warnings
from datetime import datetime, timezone
from email.utils import parsedate_to_datetime
from html.parser import HTMLParser
from os import path
from queue import PriorityQueue, Queue
from threading import Thread
from typing import (Any, Dict, Generator, List, NamedTuple, Optional, Pattern, Set, Tuple,
Union, cast)
from typing import Any, Dict, Generator, List, NamedTuple, Optional, Tuple, Union, cast
from urllib.parse import unquote, urlparse, urlunparse
from docutils import nodes
from docutils.nodes import Element
from requests import Response
from requests.exceptions import ConnectionError, HTTPError, TooManyRedirects
from sphinx.application import Sphinx
from sphinx.builders.dummy import DummyBuilder
from sphinx.config import Config
from sphinx.deprecation import RemovedInSphinx50Warning
from sphinx.environment import BuildEnvironment
from sphinx.locale import __
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)
Hyperlink = NamedTuple('Hyperlink', (('uri', str),
('docname', str),
('lineno', Optional[int])))
CheckRequest = NamedTuple('CheckRequest', (('next_check', float),
('hyperlink', Optional[Hyperlink])))
CheckResult = NamedTuple('CheckResult', (('uri', str),
('docname', str),
('lineno', int),
('status', str),
('message', str),
('code', int)))
RateLimit = NamedTuple('RateLimit', (('delay', float), ('next_check', float)))
class Hyperlink(NamedTuple):
uri: str
docname: str
lineno: Optional[int]
class CheckRequest(NamedTuple):
next_check: float
hyperlink: Optional[Hyperlink]
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
CheckRequestType = Union[CheckRequest, Tuple[float, str, str, int]]
@ -67,16 +68,6 @@ QUEUE_POLL_SECS = 1
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):
"""Specialized HTML parser that looks for a specific anchor."""
@ -120,114 +111,11 @@ class CheckExternalLinksBuilder(DummyBuilder):
'%(outdir)s/output.txt')
def init(self) -> None:
self.broken_hyperlinks = 0
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
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:
filename = self.env.doc2path(result.docname, None)
@ -260,6 +148,7 @@ class CheckExternalLinksBuilder(DummyBuilder):
logger.info(red('broken ') + result.uri + red(' - ' + result.message))
self.write_entry('broken', result.docname, filename, result.lineno,
result.uri + ': ' + result.message)
self.broken_hyperlinks += 1
elif result.status == 'redirected':
try:
text, color = {
@ -292,7 +181,7 @@ class CheckExternalLinksBuilder(DummyBuilder):
self.json_outfile.write('\n')
def finish(self) -> None:
checker = HyperlinkAvailabilityChecker(self.env, self.config, self)
checker = HyperlinkAvailabilityChecker(self.env, self.config)
logger.info('')
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):
self.process_result(result)
if self._broken:
if self.broken_hyperlinks:
self.app.statuscode = 1
class HyperlinkAvailabilityChecker:
def __init__(self, env: BuildEnvironment, config: Config,
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
def __init__(self, env: BuildEnvironment, config: Config) -> None:
self.config = config
self.env = env
self.rate_limits: Dict[str, RateLimit] = {}
self.rqueue: Queue = Queue()
self.workers: List[Thread] = []
self.wqueue: PriorityQueue = PriorityQueue()
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:
for i in range(self.config.linkcheck_workers):
for _i in range(self.config.linkcheck_workers):
thread = HyperlinkAvailabilityCheckWorker(self.env, self.config,
self.rqueue, self.wqueue,
self.rate_limits, self.builder)
self.rate_limits)
thread.start()
self.workers.append(thread)
def shutdown_threads(self) -> None:
self.wqueue.join()
for worker in self.workers:
for _worker in self.workers:
self.wqueue.put(CheckRequest(CHECK_IMMEDIATELY, None), False)
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."""
def __init__(self, env: BuildEnvironment, config: Config, rqueue: Queue,
wqueue: Queue, rate_limits: Dict[str, RateLimit],
builder: CheckExternalLinksBuilder = None) -> None:
# Warning: builder argument will be removed in the sphinx-5.0.
# Don't use it from extensions.
# tag: RemovedInSphinx50Warning
wqueue: Queue, rate_limits: Dict[str, RateLimit]) -> None:
self.config = config
self.env = env
self.rate_limits = rate_limits
@ -378,20 +253,11 @@ class HyperlinkAvailabilityCheckWorker(Thread):
self.anchors_ignore = [re.compile(x)
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
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)
def run(self) -> None:
@ -519,6 +385,15 @@ class HyperlinkAvailabilityCheckWorker(Thread):
def check(docname: str) -> Tuple[str, str, int]:
# 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:')):
return 'unchecked', '', 0
elif not uri.startswith(('http:', 'https:')):
@ -530,14 +405,7 @@ class HyperlinkAvailabilityCheckWorker(Thread):
if path.exists(path.join(srcdir, uri)):
return 'working', '', 0
else:
self._broken[uri] = ''
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
for _ in range(self.config.linkcheck_retries):
@ -545,13 +413,6 @@ class HyperlinkAvailabilityCheckWorker(Thread):
if status != "broken":
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)
while True:
@ -639,7 +500,7 @@ class HyperlinkCollector(SphinxPostTransform):
hyperlinks = builder.hyperlinks
# reference nodes
for refnode in self.document.traverse(nodes.reference):
for refnode in self.document.findall(nodes.reference):
if 'refuri' not in refnode:
continue
uri = refnode['refuri']
@ -653,7 +514,7 @@ class HyperlinkCollector(SphinxPostTransform):
hyperlinks[uri] = uri_info
# image nodes
for imgnode in self.document.traverse(nodes.image):
for imgnode in self.document.findall(nodes.image):
uri = imgnode['candidates'].get('?')
if uri and '://' in 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_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_auth', [], None)
app.add_config_value('linkcheck_request_headers', {}, None)

View File

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

View File

@ -1,12 +1,4 @@
"""
sphinx.builders.singlehtml
~~~~~~~~~~~~~~~~~~~~~~~~~~
Single HTML builders.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
"""Single HTML builders."""
from os import path
from typing import Any, Dict, List, Tuple, Union
@ -54,7 +46,7 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
def fix_refuris(self, tree: Node) -> None:
# fix refuris with double anchor
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:
continue
refuri = refnode['refuri']

View File

@ -1,12 +1,4 @@
"""
sphinx.builders.texinfo
~~~~~~~~~~~~~~~~~~~~~~~
Texinfo builder.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
"""Texinfo builder."""
import os
from os import path
@ -138,7 +130,7 @@ class TexinfoBuilder(Builder):
new_sect += nodes.title('<Set title in conf.py>',
'<Set title in conf.py>')
new_tree += new_sect
for node in tree.traverse(addnodes.toctree):
for node in tree.findall(addnodes.toctree):
new_sect += node
tree = new_tree
largetree = inline_all_toctrees(self, self.docnames, indexfile, tree,
@ -152,7 +144,7 @@ class TexinfoBuilder(Builder):
logger.info(__("resolving references..."))
self.env.resolve_references(largetree, indexfile, self)
# 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']
sectname = pendingnode['refsectname']
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_show_urls', 'footnote', None)
app.add_config_value('texinfo_no_detailmenu', False, None)
app.add_config_value('texinfo_cross_references', True, None)
return {
'version': 'builtin',

View File

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

View File

@ -1,12 +1,4 @@
"""
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.
"""
"""Docutils-native XML and pseudo-XML builders."""
from os import path
from typing import Any, Dict, Iterator, Set, Type, Union
@ -71,7 +63,10 @@ class XMLBuilder(Builder):
# work around multiple string % tuple issues in docutils;
# replace tuples in attribute values with lists
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():
if isinstance(value, tuple):
node.attributes[att] = list(value)

View File

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

View File

@ -1,12 +1,4 @@
"""
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.
"""
"""Build documentation from a provided source."""
import argparse
import bdb
@ -16,6 +8,7 @@ import os
import pdb
import sys
import traceback
from os import path
from typing import IO, Any, List
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.console import color_terminal, nocolor, red, terminal_safe # type: ignore
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
@ -240,6 +234,8 @@ def build_main(argv: List[str] = sys.argv[1:]) -> int:
if warning and args.warnfile:
try:
warnfile = abspath(args.warnfile)
ensuredir(path.dirname(warnfile))
warnfp = open(args.warnfile, 'w')
except Exception as exc:
parser.error(__('cannot open warning file %r: %s') % (

View File

@ -1,17 +1,10 @@
"""
sphinx.cmd.make_mode
~~~~~~~~~~~~~~~~~~~~
sphinx-build -M command-line handling.
"""sphinx-build -M command-line handling.
This replaces the old, platform-dependent and once-generated content
of Makefile / make.bat.
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

View File

@ -1,12 +1,4 @@
"""
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.
"""
"""Quickly setup documentation source to work with Sphinx."""
import argparse
import locale

View File

@ -1,12 +1,4 @@
"""
sphinx.config
~~~~~~~~~~~~~
Build configuration file handling.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
"""Build configuration file handling."""
import re
import traceback
@ -100,7 +92,7 @@ class Config:
# the real default is locale-dependent
'today_fmt': (None, 'env', [str]),
'language': (None, 'env', [str]),
'language': ('en', 'env', [str]),
'locale_dirs': (['locales'], 'env', []),
'figure_language_filename': ('{root}.{language}{ext}', 'env', [str]),
'gettext_allow_fuzzy_translations': (False, 'gettext', []),
@ -206,7 +198,7 @@ class Config:
except ValueError as exc:
raise ValueError(__('invalid number %r for config value %r, ignoring') %
(value, name)) from exc
elif hasattr(defvalue, '__call__'):
elif callable(defvalue):
return value
elif defvalue is not None and not isinstance(defvalue, str):
raise ValueError(__('cannot override config setting %r with unsupported '
@ -251,13 +243,24 @@ class Config:
if name in self.values:
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:
if name.startswith('_'):
raise AttributeError(name)
if name not in self.values:
raise AttributeError(__('No such config value: %s') % name)
default = self.values[name][0]
if hasattr(default, '__call__'):
if callable(default):
return default(self)
return default
@ -413,7 +416,7 @@ def check_confval_types(app: "Sphinx", config: Config) -> None:
for confval in config:
default, rebuild, annotations = config.values[confval.name]
if hasattr(default, '__call__'):
if callable(default):
default = default(config) # evaluate default value
if default is None and not annotations:
continue # neither inferable nor expliclitly annotated types
@ -427,7 +430,7 @@ def check_confval_types(app: "Sphinx", config: Config) -> None:
"but `{current}` is given.")
logger.warning(msg.format(name=confval.name,
current=confval.value,
candidates=annotations.candidates))
candidates=annotations.candidates), once=True)
else:
if type(confval.value) is type(default):
continue
@ -452,13 +455,13 @@ def check_confval_types(app: "Sphinx", config: Config) -> None:
permitted = " or ".join(wrapped_annotations)
logger.warning(msg.format(name=confval.name,
current=type(confval.value),
permitted=permitted))
permitted=permitted), once=True)
else:
msg = __("The config value `{name}' has type `{current.__name__}', "
"defaults to `{default.__name__}'.")
logger.warning(msg.format(name=confval.name,
current=type(confval.value),
default=type(default)))
default=type(default)), once=True)
def check_primary_domain(app: "Sphinx", config: Config) -> None:

View File

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

View File

@ -1,12 +1,4 @@
"""
sphinx.directives
~~~~~~~~~~~~~~~~~
Handlers for additional ReST directives.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
"""Handlers for additional ReST directives."""
import re
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.addnodes import desc_signature
from sphinx.deprecation import RemovedInSphinx50Warning, deprecated_alias
from sphinx.util import docutils
from sphinx.util.docfields import DocFieldTransformer, Field, TypedField
from sphinx.util.docutils import SphinxDirective
@ -176,7 +167,7 @@ class ObjectDescription(SphinxDirective, Generic[T]):
self.names: List[T] = []
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
# and add a reference target for it
signode = addnodes.desc_signature(sig, '')
@ -266,16 +257,6 @@ class DefaultDomain(SphinxDirective):
return []
deprecated_alias('sphinx.directives',
{
'DescDirective': ObjectDescription,
},
RemovedInSphinx50Warning,
{
'DescDirective': 'sphinx.directives.ObjectDescription',
})
def setup(app: "Sphinx") -> Dict[str, Any]:
app.add_config_value("strip_signature_backslash", False, 'env')
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 textwrap
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]:
if not dedent:
if dedent is None:
return textwrap.dedent(''.join(lines)).splitlines(True)
if any(s[:dedent].strip() for s in lines):
@ -138,9 +130,9 @@ class CodeBlock(SphinxDirective):
if 'dedent' in self.options:
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)
code = '\n'.join(lines)
code = ''.join(lines)
literal: Element = nodes.literal_block(code, code)
if 'linenos' in self.options or 'lineno-start' in self.options:
@ -232,9 +224,9 @@ class LiteralIncludeReader:
self.start_filter,
self.end_filter,
self.lines_filter,
self.dedent_filter,
self.prepend_filter,
self.append_filter,
self.dedent_filter]
self.append_filter]
lines = self.read_file(self.filename, location=location)
for func in filters:
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
from typing import TYPE_CHECKING, Any, Dict, List, cast
@ -342,7 +334,7 @@ class Only(SphinxDirective):
# be placed in the doctree.
n_sects_to_raise = current_depth - nested_depth + 1
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:
parent = parent.parent
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 warnings
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.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
try:
from docutils.nodes import meta as meta_node # type: ignore
from docutils.parsers.rst.directives.misc import Meta as MetaBase # type: ignore
except ImportError:
# docutils-0.17 or older
from docutils.parsers.rst.directives.html import Meta as MetaBase
from docutils.parsers.rst.directives.html import MetaBody
meta_node = MetaBody.meta
if TYPE_CHECKING:
from sphinx.application import Sphinx
@ -71,11 +60,13 @@ class Figure(images.Figure):
class Meta(MetaBase, SphinxDirective):
def run(self) -> List[Node]:
def run(self) -> Sequence[Node]:
result = super().run()
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
isinstance(node.details['nodes'][0], meta_node)):
isinstance(node.details['nodes'][0], addnodes.docutils_meta)):
meta = node.details['nodes'][0]
meta.source = self.env.doc2path(self.env.docname)
meta.line = self.lineno

View File

@ -1,12 +1,7 @@
"""
sphinx.domains
~~~~~~~~~~~~~~
"""Support for 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.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import copy

View File

@ -1,12 +1,4 @@
"""
sphinx.domains.c
~~~~~~~~~~~~~~~~
The C language domain.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
"""The C language domain."""
import re
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.application import Sphinx
from sphinx.builders import Builder
from sphinx.deprecation import RemovedInSphinx50Warning
from sphinx.deprecation import RemovedInSphinx60Warning
from sphinx.directives import ObjectDescription
from sphinx.domains import Domain, ObjType
from sphinx.environment import BuildEnvironment
@ -92,31 +84,22 @@ _id_prefix = [None, 'c.', 'Cv2.']
_string_re = re.compile(r"[LuU8]?('([^'\\]*(?:\\.[^'\\]*)*)'"
r'|"([^"\\]*(?:\\.[^"\\]*)*)")', re.S)
# bool, complex, and imaginary are macro "keywords", so they are handled seperately
_simple_type_specifiers_re = re.compile(r"""(?x)
\b(
void|_Bool|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)
void|_Bool
|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
""")
@ -226,7 +209,7 @@ class ASTNestedName(ASTBase):
assert not self.rooted, str(self)
assert len(self.names) == 1
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
# prefix.
prefix = ''
@ -636,8 +619,9 @@ class ASTTrailingTypeSpec(ASTBase):
class ASTTrailingTypeSpecFundamental(ASTTrailingTypeSpec):
def __init__(self, name: str) -> None:
self.names = name.split()
def __init__(self, names: List[str]) -> None:
assert len(names) != 0
self.names = names
def _stringify(self, transform: StringifyTransform) -> str:
return ' '.join(self.names)
@ -2580,12 +2564,36 @@ class DefinitionParser(BaseParser):
break
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:
# fundamental types, https://en.cppreference.com/w/c/language/type
# and extensions
self.skip_ws()
if self.match(_simple_type_specifiers_re):
return ASTTrailingTypeSpecFundamental(self.matched_text)
res = self._parse_simple_type_specifiers()
if res is not None:
return res
# prefixed
prefix = None
@ -3260,7 +3268,7 @@ class CObject(ObjectDescription[ASTDeclaration]):
msg = "{}: Pre-v3 C type directive '.. c:type:: {}' converted to " \
"'.. c:{}:: {}'." \
"\nThe original parsing error was:\n{}"
msg = msg.format(RemovedInSphinx50Warning.__name__,
msg = msg.format(RemovedInSphinx60Warning.__name__,
sig, ast.objectType, ast, eOrig)
logger.warning(msg, location=signode)
except DefinitionError as e:
@ -3540,7 +3548,7 @@ class AliasTransform(SphinxTransform):
return nodes
def apply(self, **kwargs: Any) -> None:
for node in self.document.traverse(AliasNode):
for node in self.document.findall(AliasNode):
node = cast(AliasNode, node)
sig = node.sig
parentKey = node.parentKey
@ -3638,8 +3646,7 @@ class CAliasObject(ObjectDescription):
" When skipping the root declaration,"
" need 'maxdepth' 0 for infinite or at least 2.",
location=self.get_location())
signatures = self.get_signatures()
for i, sig in enumerate(signatures):
for sig in self.get_signatures():
node.append(AliasNode(sig, aliasOptions, self.state.document, env=self.env))
return [node]
@ -3694,7 +3701,7 @@ class CXRefRole(XRefRole):
if self.env.config['c_warn_on_allowed_pre_v3']:
msg = "{}: Pre-v3 C type role ':c:type:`{}`' converted to ':c:expr:`{}`'."
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())
return [signode], []

View File

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

View File

@ -1,12 +1,4 @@
"""
sphinx.domains.citation
~~~~~~~~~~~~~~~~~~~~~~~
The citation domain.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
"""The citation domain."""
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', {})
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:
del self.citations[key]
for key, docnames in list(self.citation_refs.items()):
@ -81,7 +73,7 @@ class CitationDomain(Domain):
docnames.add(self.env.docname)
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:
logger.warning(__('Citation [%s] is not referenced.'), name,
type='ref', subtype='citation', location=(docname, lineno))
@ -112,7 +104,7 @@ class CitationDefinitionTransform(SphinxTransform):
def apply(self, **kwargs: Any) -> None:
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
node['docname'] = self.env.docname
domain.note_citation(node)
@ -131,7 +123,7 @@ class CitationReferenceTransform(SphinxTransform):
def apply(self, **kwargs: Any) -> None:
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()
ref = pending_xref(target, refdomain='citation', reftype='ref',
reftarget=target, refwarn=True,

View File

@ -1,12 +1,4 @@
"""
sphinx.domains.cpp
~~~~~~~~~~~~~~~~~~
The C++ language domain.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
"""The C++ language domain."""
import re
from typing import (Any, Callable, Dict, Generator, Iterator, List, Optional, Tuple, TypeVar,
@ -267,6 +259,7 @@ T = TypeVar('T')
class_object:
goal: a class declaration, but with specification of a base class
grammar:
attribute-specifier-seq[opt]
nested-name "final"[opt] (":" base-specifier-list)[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
type specified
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:
goal: an element in a scoped or unscoped enum. The name should be
injected according to the scopedness.
@ -338,24 +332,14 @@ _keywords = [
_simple_type_specifiers_re = re.compile(r"""(?x)
\b(
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
|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
""")
@ -485,12 +469,12 @@ _id_fundamental_v2 = {
'long double': 'e',
'__float80': 'e', '_Float64x': 'e',
'__float128': 'g', '_Float128': 'g',
'float _Complex': 'Cf', '_Complex float': 'Cf',
'double _Complex': 'Cd', '_Complex double': 'Cd',
'long double _Complex': 'Ce', '_Complex long double': 'Ce',
'float _Imaginary': 'f', '_Imaginary float': 'f',
'double _Imaginary': 'd', '_Imaginary double': 'd',
'long double _Imaginary': 'e', '_Imaginary long double': 'e',
'_Complex float': 'Cf',
'_Complex double': 'Cd',
'_Complex long double': 'Ce',
'_Imaginary float': 'f',
'_Imaginary double': 'd',
'_Imaginary long double': 'e',
'auto': 'Da',
'decltype(auto)': 'Dc',
'std::nullptr_t': 'Dn'
@ -786,7 +770,7 @@ class ASTNestedName(ASTBase):
assert len(self.names) == 1
assert not self.templates[0]
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
# prefix. however, only the identifier part should be a link, such
# that template args can be a link as well.
@ -1853,8 +1837,12 @@ class ASTTrailingTypeSpec(ASTBase):
class ASTTrailingTypeSpecFundamental(ASTTrailingTypeSpec):
def __init__(self, name: str) -> None:
self.names = name.split()
def __init__(self, names: List[str], canonNames: List[str]) -> None:
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:
return ' '.join(self.names)
@ -1862,14 +1850,14 @@ class ASTTrailingTypeSpecFundamental(ASTTrailingTypeSpec):
def get_id(self, version: int) -> str:
if version == 1:
res = []
for a in self.names:
for a in self.canonNames:
if a in _id_fundamental_v1:
res.append(_id_fundamental_v1[a])
else:
res.append(a)
return '-'.join(res)
txt = str(self)
txt = ' '.join(self.canonNames)
if txt not in _id_fundamental_v2:
raise Exception(
'Semi-internal error: Fundamental type "%s" can not be mapped '
@ -3324,16 +3312,20 @@ class ASTBaseClass(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.final = final
self.bases = bases
self.attrs = attrs
def get_id(self, version: int, objectType: str, symbol: "Symbol") -> str:
return symbol.get_full_nested_name().get_id(version)
def _stringify(self, transform: StringifyTransform) -> str:
res = []
for attr in self.attrs:
res.append(transform(attr) + ' ')
res.append(transform(self.name))
if self.final:
res.append(' final')
@ -3350,6 +3342,9 @@ class ASTClass(ASTBase):
def describe_signature(self, signode: TextElement, mode: str,
env: "BuildEnvironment", symbol: "Symbol") -> None:
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)
if self.final:
signode += addnodes.desc_sig_space()
@ -3367,8 +3362,9 @@ class ASTClass(ASTBase):
class ASTUnion(ASTBase):
def __init__(self, name: ASTNestedName) -> None:
def __init__(self, name: ASTNestedName, attrs: List[ASTAttribute]) -> None:
self.name = name
self.attrs = attrs
def get_id(self, version: int, objectType: str, symbol: "Symbol") -> str:
if version == 1:
@ -3376,20 +3372,28 @@ class ASTUnion(ASTBase):
return symbol.get_full_nested_name().get_id(version)
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,
env: "BuildEnvironment", symbol: "Symbol") -> None:
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)
class ASTEnum(ASTBase):
def __init__(self, name: ASTNestedName, scoped: str,
underlyingType: ASTType) -> None:
def __init__(self, name: ASTNestedName, scoped: str, underlyingType: ASTType,
attrs: List[ASTAttribute]) -> None:
self.name = name
self.scoped = scoped
self.underlyingType = underlyingType
self.attrs = attrs
def get_id(self, version: int, objectType: str, symbol: "Symbol") -> str:
if version == 1:
@ -3401,6 +3405,8 @@ class ASTEnum(ASTBase):
if self.scoped:
res.append(self.scoped)
res.append(' ')
for attr in self.attrs:
res.append(transform(attr) + ' ')
res.append(transform(self.name))
if self.underlyingType:
res.append(' : ')
@ -3411,6 +3417,9 @@ class ASTEnum(ASTBase):
env: "BuildEnvironment", symbol: "Symbol") -> None:
verify_description_mode(mode)
# 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)
if self.underlyingType:
signode += addnodes.desc_sig_space()
@ -5391,7 +5400,7 @@ class DefinitionParser(BaseParser):
postFixes: List[ASTPostfixOp] = []
while True:
self.skip_ws()
if prefixType in ['expr', 'cast', 'typeid']:
if prefixType in ('expr', 'cast', 'typeid'):
if self.skip_string_and_ws('['):
expr = self._parse_expression()
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:
# fundamental types, https://en.cppreference.com/w/cpp/language/type
# and extensions
self.skip_ws()
if self.match(_simple_type_specifiers_re):
return ASTTrailingTypeSpecFundamental(self.matched_text)
res = self._parse_simple_type_specifiers()
if res is not None:
return res
# decltype
self.skip_ws()
@ -6483,6 +6582,12 @@ class DefinitionParser(BaseParser):
return ASTConcept(nestedName, initializer)
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()
self.skip_ws()
final = self.skip_word_and_ws('final')
@ -6510,21 +6615,33 @@ class DefinitionParser(BaseParser):
continue
else:
break
return ASTClass(name, final, bases)
return ASTClass(name, final, bases, attrs)
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()
return ASTUnion(name)
return ASTUnion(name, attrs)
def _parse_enum(self) -> ASTEnum:
scoped = None # is set by CPPEnumObject
attrs = []
while 1:
attr = self._parse_attribute()
if attr is None:
break
attrs.append(attr)
self.skip_ws()
name = self._parse_nested_name()
self.skip_ws()
underlyingType = None
if self.skip_string(':'):
underlyingType = self._parse_type(named=False)
return ASTEnum(name, scoped, underlyingType)
return ASTEnum(name, scoped, underlyingType, attrs)
def _parse_enumerator(self) -> ASTEnumerator:
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()
if self.skip_word('template'):
# declare a tenplate template parameter
@ -6613,7 +6730,7 @@ class DefinitionParser(BaseParser):
pos = self.pos
err = None
try:
param = self._parse_template_paramter()
param = self._parse_template_parameter()
templateParams.append(param)
except DefinitionError as eParam:
self.pos = pos
@ -6789,7 +6906,7 @@ class DefinitionParser(BaseParser):
self.warn(msg)
newTemplates: List[Union[ASTTemplateParams, ASTTemplateIntroduction]] = []
for i in range(numExtra):
for _i in range(numExtra):
newTemplates.append(ASTTemplateParams([]))
if templatePrefix and not isMemberInstantiation:
newTemplates.extend(templatePrefix.templates)
@ -7375,7 +7492,7 @@ class AliasTransform(SphinxTransform):
return nodes
def apply(self, **kwargs: Any) -> None:
for node in self.document.traverse(AliasNode):
for node in self.document.findall(AliasNode):
node = cast(AliasNode, node)
sig = node.sig
parentKey = node.parentKey
@ -7495,7 +7612,7 @@ class CPPAliasObject(ObjectDescription):
" need 'maxdepth' 0 for infinite or at least 2.",
location=self.get_location())
signatures = self.get_signatures()
for i, sig in enumerate(signatures):
for sig in signatures:
node.append(AliasNode(sig, aliasOptions, env=self.env))
contentnode = addnodes.desc_content()
@ -7697,7 +7814,7 @@ class CPPDomain(Domain):
typ: str, target: str, node: pending_xref,
contnode: Element) -> Tuple[Optional[Element], Optional[str]]:
# add parens again for those that could be functions
if typ == 'any' or typ == 'func':
if typ in ('any', 'func'):
target += '()'
parser = DefinitionParser(target, location=node, config=env.config)
try:
@ -7818,7 +7935,7 @@ class CPPDomain(Domain):
if (env.config.add_function_parentheses and typ == 'func' and
title.endswith('operator()')):
addParen += 1
if ((typ == 'any' or typ == 'func') and
if (typ in ('any', 'func') and
title.endswith('operator') and
displayName.endswith('operator()')):
addParen += 1
@ -7908,7 +8025,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
return {
'version': 'builtin',
'env_version': 4,
'env_version': 5,
'parallel_read_safe': True,
'parallel_write_safe': True,
}

View File

@ -1,12 +1,4 @@
"""
sphinx.domains.index
~~~~~~~~~~~~~~~~~~~~
The index domain.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
"""The index domain."""
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:
"""Process a document after it is read by the environment."""
entries = self.entries.setdefault(env.docname, [])
for node in list(document.traverse(addnodes.index)):
for node in list(document.findall(addnodes.index)):
try:
for entry in node['entries']:
split_index_msg(entry[0], entry[1])

View File

@ -1,12 +1,4 @@
"""
sphinx.domains.javascript
~~~~~~~~~~~~~~~~~~~~~~~~~
The JavaScript domain.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
"""The JavaScript domain."""
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)
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:
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:
del self.modules[modname]

View File

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

View File

@ -1,12 +1,4 @@
"""
sphinx.domains.python
~~~~~~~~~~~~~~~~~~~~~
The Python domain.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
"""The Python domain."""
import builtins
import inspect
@ -26,7 +18,7 @@ from sphinx import addnodes
from sphinx.addnodes import desc_signature, pending_xref, pending_xref_condition
from sphinx.application import Sphinx
from sphinx.builders import Builder
from sphinx.deprecation import RemovedInSphinx50Warning, RemovedInSphinx60Warning
from sphinx.deprecation import RemovedInSphinx60Warning
from sphinx.directives import ObjectDescription
from sphinx.domains import Domain, Index, IndexEntry, ObjType
from sphinx.environment import BuildEnvironment
@ -80,46 +72,60 @@ class ModuleEntry(NamedTuple):
deprecated: bool
def type_to_xref(target: str, env: BuildEnvironment = None) -> addnodes.pending_xref:
"""Convert a type string to a cross reference node."""
if target == 'None':
def parse_reftarget(reftarget: str, suppress_prefix: bool = False
) -> Tuple[str, str, str, bool]:
"""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'
else:
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:
kwargs = {'py:module': env.ref_context.get('py:module'),
'py:class': env.ref_context.get('py:class')}
else:
kwargs = {}
refspecific = False
if target.startswith('.'):
target = target[1:]
text = target
refspecific = True
elif target.startswith('~'):
target = target[1:]
text = target.split('.')[-1]
else:
text = target
reftype, target, title, refspecific = parse_reftarget(target, suppress_prefix)
if env.config.python_use_unqualified_type_names:
# 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
# module should work not-dynamically.
shortname = text.split('.')[-1]
shortname = title.split('.')[-1]
contnodes: List[Node] = [pending_xref_condition('', shortname, condition='resolved'),
pending_xref_condition('', text, condition='*')]
pending_xref_condition('', title, condition='*')]
else:
contnodes = [nodes.Text(text)]
contnodes = [nodes.Text(title)]
return pending_xref('', *contnodes,
refdomain='py', reftype=reftype, reftarget=target,
refspecific=refspecific, **kwargs)
def _parse_annotation(annotation: str, env: BuildEnvironment = None) -> List[Node]:
def _parse_annotation(annotation: str, env: BuildEnvironment) -> List[Node]:
"""Parse type annotation."""
def unparse(node: ast.AST) -> List[Node]:
if isinstance(node, ast.Attribute):
@ -150,6 +156,8 @@ def _parse_annotation(annotation: str, env: BuildEnvironment = None) -> List[Nod
return unparse(node.value)
elif isinstance(node, ast.Index):
return unparse(node.value)
elif isinstance(node, ast.Invert):
return [addnodes.desc_sig_punctuation('', '~')]
elif isinstance(node, ast.List):
result = [addnodes.desc_sig_punctuation('', '[')]
if node.elts:
@ -180,6 +188,8 @@ def _parse_annotation(annotation: str, env: BuildEnvironment = None) -> List[Nod
if isinstance(subnode, nodes.Text):
result[i] = nodes.literal('', '', subnode)
return result
elif isinstance(node, ast.UnaryOp):
return unparse(node.op) + unparse(node.operand)
elif isinstance(node, ast.Tuple):
if node.elts:
result = []
@ -196,25 +206,34 @@ def _parse_annotation(annotation: str, env: BuildEnvironment = None) -> List[Nod
return result
else:
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('', "...")]
elif isinstance(node, ast.NameConstant):
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
if env is None:
warnings.warn("The env parameter for _parse_annotation becomes required now.",
RemovedInSphinx50Warning, stacklevel=2)
try:
tree = ast_parse(annotation)
result = unparse(tree)
for i, node in enumerate(result):
result: List[Node] = []
for node in unparse(tree):
if isinstance(node, nodes.literal):
result[i] = node[0]
result.append(node[0])
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
except SyntaxError:
return [type_to_xref(annotation, env)]
@ -331,19 +350,19 @@ class PyXrefMixin:
result = super().make_xref(rolename, domain, target, # type: ignore
innernode, contnode,
env, inliner=None, location=None)
if isinstance(result, pending_xref):
result['refspecific'] = True
result['py:module'] = env.ref_context.get('py:module')
result['py:class'] = env.ref_context.get('py:class')
if target.startswith(('.', '~')):
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:
reftype, reftarget, reftitle, _ = parse_reftarget(target)
if reftarget != reftitle:
result['reftype'] = reftype
result['reftarget'] = reftarget
result.clear()
result += innernode(reftitle, reftitle)
elif env.config.python_use_unqualified_type_names:
children = result.children
result.clear()
@ -384,16 +403,7 @@ class PyXrefMixin:
class PyField(PyXrefMixin, Field):
def make_xref(self, rolename: str, domain: str, target: str,
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)
pass
class PyGroupedField(PyXrefMixin, GroupedField):
@ -401,16 +411,7 @@ class PyGroupedField(PyXrefMixin, GroupedField):
class PyTypedField(PyXrefMixin, TypedField):
def make_xref(self, rolename: str, domain: str, target: str,
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)
pass
class PyObject(ObjectDescription[Tuple[str, str]]):
@ -965,29 +966,6 @@ class PyProperty(PyObject):
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):
"""
Directive to mark description of a new module.
@ -1467,7 +1445,7 @@ def builtin_resolver(app: Sphinx, env: BuildEnvironment,
return None
elif node.get('reftype') in ('class', 'obj') and node.get('reftarget') == 'None':
return contnode
elif node.get('reftype') in ('class', 'exc'):
elif node.get('reftype') in ('class', 'obj', 'exc'):
reftarget = node.get('reftarget')
if inspect.isclass(getattr(builtins, reftarget, None)):
# built-in class

View File

@ -1,12 +1,4 @@
"""
sphinx.domains.rst
~~~~~~~~~~~~~~~~~~
The reStructuredText domain.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
"""The reStructuredText domain."""
import re
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)
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:
del self.objects[typ, name]

View File

@ -1,16 +1,6 @@
"""
sphinx.domains.std
~~~~~~~~~~~~~~~~~~
The standard domain.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
"""The standard domain."""
import re
import unicodedata
import warnings
from copy import copy
from typing import (TYPE_CHECKING, Any, Callable, Dict, Iterable, Iterator, List, Optional,
Tuple, Type, Union, cast)
@ -22,7 +12,6 @@ from docutils.statemachine import StringList
from sphinx import addnodes
from sphinx.addnodes import desc_signature, pending_xref
from sphinx.deprecation import RemovedInSphinx50Warning
from sphinx.directives import ObjectDescription
from sphinx.domains import Domain, ObjType
from sphinx.locale import _, __
@ -243,7 +232,7 @@ class Cmdoption(ObjectDescription[str]):
descr = _('%s command line option') % currprogram
else:
descr = _('command line option')
for option in sig.split(', '):
for option in signode.get('allnames', []):
entry = '; '.join([descr, option])
self.indexnode['entries'].append(('pair', entry, signode['ids'][0], '', None))
@ -336,6 +325,7 @@ class Glossary(SphinxDirective):
def run(self) -> List[Node]:
node = addnodes.glossary()
node.document = self.state.document
node['sorted'] = ('sorted' in self.options)
# This directive implements a custom format of the reST definition list
# that allows multiple lines of terms before the definition. This is
@ -400,9 +390,8 @@ class Glossary(SphinxDirective):
was_empty = False
# now, parse all the entries into a big definition list
items = []
items: List[nodes.definition_list_item] = []
for terms, definition in entries:
termtexts: List[str] = []
termnodes: List[Node] = []
system_messages: List[Node] = []
for line, source, lineno in terms:
@ -416,7 +405,6 @@ class Glossary(SphinxDirective):
node_id=None, document=self.state.document)
term.rawsource = line
system_messages.extend(sysmsg)
termtexts.append(term.astext())
termnodes.append(term)
termnodes.extend(system_messages)
@ -426,16 +414,10 @@ class Glossary(SphinxDirective):
self.state.nested_parse(definition, definition.items[0][1],
defnode)
termnodes.append(defnode)
items.append((termtexts,
nodes.definition_list_item('', *termnodes)))
items.append(nodes.definition_list_item('', *termnodes))
if 'sorted' in self.options:
items.sort(key=lambda x:
unicodedata.normalize('NFD', x[0][0].lower()))
dlist = nodes.definition_list()
dlist = nodes.definition_list('', *items)
dlist['classes'].append('glossary')
dlist.extend(item[1] for item in items)
node += dlist
return messages + [node]
@ -675,11 +657,6 @@ class StandardDomain(Domain):
objtype, name, docname, location=location)
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
def _terms(self) -> Dict[str, Tuple[str, str]]:
""".. note:: Will be removed soon. internal use only."""
@ -775,7 +752,7 @@ class StandardDomain(Domain):
if not sectname:
continue
else:
toctree = next(iter(node.traverse(addnodes.toctree)), None)
toctree = next(node.findall(addnodes.toctree), None)
if toctree and toctree.get('caption'):
sectname = toctree.get('caption')
else:

View File

@ -1,12 +1,4 @@
"""
sphinx.environment
~~~~~~~~~~~~~~~~~~
Global creation environment.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
"""Global creation environment."""
import os
import pickle
@ -49,7 +41,7 @@ default_settings: Dict[str, Any] = {
'embed_images': False,
'embed_stylesheet': False,
'cloak_email_addresses': True,
'pep_base_url': 'https://www.python.org/dev/peps/',
'pep_base_url': 'https://peps.python.org/',
'pep_references': None,
'rfc_base_url': 'https://datatracker.ietf.org/doc/html/',
'rfc_references': None,
@ -261,7 +253,7 @@ class BuildEnvironment:
"""Update settings by new config."""
self.settings['input_encoding'] = config.source_encoding
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)
self.settings.setdefault('smart_quotes', True)
@ -535,7 +527,7 @@ class BuildEnvironment:
self.apply_post_transforms(doctree, docname)
# 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,
prune=prune_toctrees,
includehidden=includehidden)
@ -621,7 +613,7 @@ class BuildEnvironment:
def check_consistency(self) -> None:
"""Do consistency checks."""
included = set().union(*self.included.values()) # type: ignore
included = set().union(*self.included.values())
for docname in sorted(self.all_docs):
if docname not in self.files_to_rebuild:
if docname == self.config.root_doc:

View File

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

View File

@ -1,12 +1,4 @@
"""
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.
"""
"""Assets adapter for sphinx.environment."""
from sphinx.environment import BuildEnvironment

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