mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge branch '5.x'
# Conflicts: # setup.py # sphinx/__init__.py
This commit is contained in:
2
.github/workflows/docutils-latest.yml
vendored
2
.github/workflows/docutils-latest.yml
vendored
@@ -21,8 +21,6 @@ jobs:
|
||||
python-version: 3
|
||||
- name: Check Python version
|
||||
run: python --version
|
||||
- name: Unpin docutils
|
||||
run: sed -i -e "s/'docutils>=.*'/'docutils'/" setup.py
|
||||
- name: Install graphviz
|
||||
run: sudo apt-get install graphviz
|
||||
- name: Install dependencies
|
||||
|
||||
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
@@ -20,6 +20,6 @@ jobs:
|
||||
with:
|
||||
python-version: 3
|
||||
- name: Install dependencies
|
||||
run: pip install -U tox
|
||||
run: python -m pip install -U tox pip
|
||||
- name: Run Tox
|
||||
run: tox -e ${{ matrix.tool }}
|
||||
|
||||
11
CHANGES
11
CHANGES
@@ -27,6 +27,10 @@ Release 5.2.0 (in development)
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
* #10356: Sphinx now uses declarative metadata with ``pyproject.toml`` to
|
||||
create packages, using PyPA's ``build`` project as a build backend. Patch by
|
||||
Adam Turner.
|
||||
|
||||
Incompatible changes
|
||||
--------------------
|
||||
|
||||
@@ -41,9 +45,14 @@ Features added
|
||||
* #10755: linkcheck: Check the source URL of raw directives that use the ``url``
|
||||
option.
|
||||
* #10781: Allow :rst:role:`ref` role to be used with definitions and fields.
|
||||
* #10717: HTML Search: Increase priority for full title and
|
||||
* #10717: HTML Search: Increase priority for full title and
|
||||
subtitle matches in search results
|
||||
* #10718: HTML Search: Save search result score to the HTML element for debugging
|
||||
* #10673: Make toctree accept 'genindex', 'modindex' and 'search' docnames
|
||||
* #6316, #10804: Add domain objects to the table of contents. Patch by Adam Turner
|
||||
* #6692: HTML Search: Include explicit :rst:dir:`index` directive index entries
|
||||
in the search index and search results. Patch by Adam Turner
|
||||
* #10816: imgmath: Allow embedding images in HTML as base64
|
||||
|
||||
Bugs fixed
|
||||
----------
|
||||
|
||||
33
MANIFEST.in
33
MANIFEST.in
@@ -1,33 +0,0 @@
|
||||
include README.rst
|
||||
include LICENSE
|
||||
include AUTHORS
|
||||
include CHANGES
|
||||
include CHANGES.old
|
||||
include CODE_OF_CONDUCT
|
||||
include CONTRIBUTING.rst
|
||||
include EXAMPLES
|
||||
|
||||
include babel.cfg
|
||||
include Makefile
|
||||
include sphinx-autogen.py
|
||||
include sphinx-build.py
|
||||
include sphinx-quickstart.py
|
||||
include sphinx-apidoc.py
|
||||
include tox.ini
|
||||
include sphinx/locale/.tx/config
|
||||
include sphinx/py.typed
|
||||
|
||||
recursive-include sphinx/templates *
|
||||
recursive-include sphinx/texinputs *
|
||||
recursive-include sphinx/texinputs_win *
|
||||
recursive-include sphinx/themes *
|
||||
recursive-include sphinx/locale *.js *.pot *.po *.mo
|
||||
recursive-include sphinx/search/non-minified-js *.js
|
||||
recursive-include sphinx/search/minified-js *.js
|
||||
recursive-include sphinx/ext/autosummary/templates *
|
||||
recursive-include tests *
|
||||
recursive-include utils *
|
||||
|
||||
recursive-include doc *
|
||||
prune doc/_build
|
||||
prune sphinx/locale/.tx
|
||||
2
Makefile
2
Makefile
@@ -74,7 +74,7 @@ covertest:
|
||||
|
||||
.PHONY: build
|
||||
build:
|
||||
@$(PYTHON) setup.py build
|
||||
@$(PYTHON) -m build .
|
||||
|
||||
.PHONY: docs
|
||||
docs:
|
||||
|
||||
2
doc/_themes/sphinx13/static/sphinx13.css
vendored
2
doc/_themes/sphinx13/static/sphinx13.css
vendored
@@ -273,7 +273,7 @@ div.quotebar {
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
blockquote.epigraph {
|
||||
font-size: 1.5em;
|
||||
padding-left: 1rem;
|
||||
margin-left: 0;
|
||||
|
||||
@@ -120,8 +120,7 @@ class RecipeDomain(Domain):
|
||||
return '{}.{}'.format('recipe', node.arguments[0])
|
||||
|
||||
def get_objects(self):
|
||||
for obj in self.data['recipes']:
|
||||
yield obj
|
||||
yield from self.data['recipes']
|
||||
|
||||
def resolve_xref(self, env, fromdocname, builder, typ, target, node,
|
||||
contnode):
|
||||
|
||||
@@ -17,6 +17,7 @@ Domain API
|
||||
|
||||
.. autoclass:: ObjectDescription
|
||||
:members:
|
||||
:private-members: _toc_entry_name, _object_hierarchy_parts
|
||||
|
||||
Python Domain
|
||||
-------------
|
||||
|
||||
@@ -97,49 +97,49 @@ in which a Sphinx project is built: this works in several phases.
|
||||
|
||||
**Phase 0: Initialization**
|
||||
|
||||
In this phase, almost nothing of interest to us happens. The source
|
||||
directory is searched for source files, and extensions are initialized.
|
||||
Should a stored build environment exist, it is loaded, otherwise a new one is
|
||||
created.
|
||||
In this phase, almost nothing of interest to us happens. The source
|
||||
directory is searched for source files, and extensions are initialized.
|
||||
Should a stored build environment exist, it is loaded, otherwise a new one is
|
||||
created.
|
||||
|
||||
**Phase 1: Reading**
|
||||
|
||||
In Phase 1, all source files (and on subsequent builds, those that are new or
|
||||
changed) are read and parsed. This is the phase where directives and roles
|
||||
are encountered by docutils, and the corresponding code is executed. The
|
||||
output of this phase is a *doctree* for each source file; that is a tree of
|
||||
docutils nodes. For document elements that aren't fully known until all
|
||||
existing files are read, temporary nodes are created.
|
||||
In Phase 1, all source files (and on subsequent builds, those that are new or
|
||||
changed) are read and parsed. This is the phase where directives and roles
|
||||
are encountered by docutils, and the corresponding code is executed. The
|
||||
output of this phase is a *doctree* for each source file; that is a tree of
|
||||
docutils nodes. For document elements that aren't fully known until all
|
||||
existing files are read, temporary nodes are created.
|
||||
|
||||
There are nodes provided by docutils, which are documented `in the docutils
|
||||
documentation <https://docutils.sourceforge.io/docs/ref/doctree.html>`__.
|
||||
Additional nodes are provided by Sphinx and :ref:`documented here <nodes>`.
|
||||
There are nodes provided by docutils, which are documented `in the docutils
|
||||
documentation <https://docutils.sourceforge.io/docs/ref/doctree.html>`__.
|
||||
Additional nodes are provided by Sphinx and :ref:`documented here <nodes>`.
|
||||
|
||||
During reading, the build environment is updated with all meta- and cross
|
||||
reference data of the read documents, such as labels, the names of headings,
|
||||
described Python objects and index entries. This will later be used to
|
||||
replace the temporary nodes.
|
||||
During reading, the build environment is updated with all meta- and cross
|
||||
reference data of the read documents, such as labels, the names of headings,
|
||||
described Python objects and index entries. This will later be used to
|
||||
replace the temporary nodes.
|
||||
|
||||
The parsed doctrees are stored on the disk, because it is not possible to
|
||||
hold all of them in memory.
|
||||
The parsed doctrees are stored on the disk, because it is not possible to
|
||||
hold all of them in memory.
|
||||
|
||||
**Phase 2: Consistency checks**
|
||||
|
||||
Some checking is done to ensure no surprises in the built documents.
|
||||
Some checking is done to ensure no surprises in the built documents.
|
||||
|
||||
**Phase 3: Resolving**
|
||||
|
||||
Now that the metadata and cross-reference data of all existing documents is
|
||||
known, all temporary nodes are replaced by nodes that can be converted into
|
||||
output using components called transforms. For example, links are created
|
||||
for object references that exist, and simple literal nodes are created for
|
||||
those that don't.
|
||||
Now that the metadata and cross-reference data of all existing documents is
|
||||
known, all temporary nodes are replaced by nodes that can be converted into
|
||||
output using components called transforms. For example, links are created
|
||||
for object references that exist, and simple literal nodes are created for
|
||||
those that don't.
|
||||
|
||||
**Phase 4: Writing**
|
||||
|
||||
This phase converts the resolved doctrees to the desired output format, such
|
||||
as HTML or LaTeX. This happens via a so-called docutils writer that visits
|
||||
the individual nodes of each doctree and produces some output in the process.
|
||||
This phase converts the resolved doctrees to the desired output format, such
|
||||
as HTML or LaTeX. This happens via a so-called docutils writer that visits
|
||||
the individual nodes of each doctree and produces some output in the process.
|
||||
|
||||
.. note::
|
||||
|
||||
|
||||
@@ -407,12 +407,16 @@ Keys that don't need to be overridden unless in special cases are:
|
||||
``'geometry'``
|
||||
"geometry" package inclusion, the default definition is:
|
||||
|
||||
``'\\usepackage{geometry}'``
|
||||
.. code:: latex
|
||||
|
||||
'\\usepackage{geometry}'
|
||||
|
||||
with an additional ``[dvipdfm]`` for Japanese documents.
|
||||
The Sphinx LaTeX style file executes:
|
||||
|
||||
``\PassOptionsToPackage{hmargin=1in,vmargin=1in,marginpar=0.5in}{geometry}``
|
||||
.. code:: latex
|
||||
|
||||
\PassOptionsToPackage{hmargin=1in,vmargin=1in,marginpar=0.5in}{geometry}
|
||||
|
||||
which can be customized via corresponding :ref:`'sphinxsetup' options
|
||||
<latexsphinxsetup>`.
|
||||
|
||||
@@ -14,12 +14,12 @@ This file (containing Python code) is called the "build configuration file"
|
||||
and contains (almost) all configuration needed to customize Sphinx input
|
||||
and output behavior.
|
||||
|
||||
An optional file `docutils.conf`_ can be added to the configuration
|
||||
directory to adjust `Docutils`_ configuration if not otherwise overridden or
|
||||
set by Sphinx.
|
||||
An optional file `docutils.conf`_ can be added to the configuration
|
||||
directory to adjust `Docutils`_ configuration if not otherwise overridden or
|
||||
set by Sphinx.
|
||||
|
||||
.. _`docutils`: https://docutils.sourceforge.io/
|
||||
.. _`docutils.conf`: https://docutils.sourceforge.io/docs/user/config.html
|
||||
.. _`docutils`: https://docutils.sourceforge.io/
|
||||
.. _`docutils.conf`: https://docutils.sourceforge.io/docs/user/config.html
|
||||
|
||||
The configuration file is executed as Python code at build time (using
|
||||
:func:`importlib.import_module`, and with the current directory set to its
|
||||
@@ -419,9 +419,9 @@ General configuration
|
||||
:literal:`:manpage:`man(1)`` role will link to
|
||||
<https://manpages.debian.org/man(1)>. The patterns available are:
|
||||
|
||||
* ``page`` - the manual page (``man``)
|
||||
* ``section`` - the manual section (``1``)
|
||||
* ``path`` - the original manual page and section specified (``man(1)``)
|
||||
* ``page`` - the manual page (``man``)
|
||||
* ``section`` - the manual section (``1``)
|
||||
* ``path`` - the original manual page and section specified (``man(1)``)
|
||||
|
||||
This also supports manpages specified as ``man.1``.
|
||||
|
||||
@@ -678,6 +678,24 @@ General configuration
|
||||
:term:`object` names (for object types where a "module" of some kind is
|
||||
defined), e.g. for :rst:dir:`py:function` directives. Default is ``True``.
|
||||
|
||||
.. confval:: toc_object_entries_show_parents
|
||||
|
||||
A string that determines how domain objects (e.g. functions, classes,
|
||||
attributes, etc.) are displayed in their table of contents entry.
|
||||
|
||||
Use ``domain`` to allow the domain to determine the appropriate number of
|
||||
parents to show. For example, the Python domain would show ``Class.method()``
|
||||
and ``function()``, leaving out the ``module.`` level of parents.
|
||||
This is the default setting.
|
||||
|
||||
Use ``hide`` to only show the name of the element without any parents
|
||||
(i.e. ``method()``).
|
||||
|
||||
Use ``all`` to show the fully-qualified name for the object
|
||||
(i.e. ``module.Class.method()``), displaying all parents.
|
||||
|
||||
.. versionadded:: 5.2
|
||||
|
||||
.. confval:: show_authors
|
||||
|
||||
A boolean that decides whether :rst:dir:`codeauthor` and
|
||||
|
||||
@@ -48,14 +48,14 @@ The :mod:`sphinx.ext.autosummary` extension does this in two parts:
|
||||
|
||||
produces a table like this:
|
||||
|
||||
.. currentmodule:: sphinx
|
||||
.. currentmodule:: sphinx
|
||||
|
||||
.. autosummary::
|
||||
.. autosummary::
|
||||
|
||||
environment.BuildEnvironment
|
||||
util.relative_uri
|
||||
environment.BuildEnvironment
|
||||
util.relative_uri
|
||||
|
||||
.. currentmodule:: sphinx.ext.autosummary
|
||||
.. currentmodule:: sphinx.ext.autosummary
|
||||
|
||||
Autosummary preprocesses the docstrings and signatures with the same
|
||||
:event:`autodoc-process-docstring` and :event:`autodoc-process-signature`
|
||||
|
||||
@@ -133,6 +133,12 @@ are built:
|
||||
elements (cf the `dvisvgm FAQ`_). This option is used only when
|
||||
``imgmath_image_format`` is ``'svg'``.
|
||||
|
||||
.. confval:: imgmath_embed
|
||||
|
||||
Default: ``False``. If true, encode LaTeX output images within HTML files
|
||||
(base64 encoded) and do not save separate png/svg files to disk.
|
||||
|
||||
.. versionadded:: 5.2
|
||||
|
||||
:mod:`sphinx.ext.mathjax` -- Render math via JavaScript
|
||||
-------------------------------------------------------
|
||||
|
||||
@@ -94,38 +94,38 @@ Docstring Sections
|
||||
|
||||
All of the following section headers are supported:
|
||||
|
||||
* ``Args`` *(alias of Parameters)*
|
||||
* ``Arguments`` *(alias of Parameters)*
|
||||
* ``Attention``
|
||||
* ``Attributes``
|
||||
* ``Caution``
|
||||
* ``Danger``
|
||||
* ``Error``
|
||||
* ``Example``
|
||||
* ``Examples``
|
||||
* ``Hint``
|
||||
* ``Important``
|
||||
* ``Keyword Args`` *(alias of Keyword Arguments)*
|
||||
* ``Keyword Arguments``
|
||||
* ``Methods``
|
||||
* ``Note``
|
||||
* ``Notes``
|
||||
* ``Other Parameters``
|
||||
* ``Parameters``
|
||||
* ``Return`` *(alias of Returns)*
|
||||
* ``Returns``
|
||||
* ``Raise`` *(alias of Raises)*
|
||||
* ``Raises``
|
||||
* ``References``
|
||||
* ``See Also``
|
||||
* ``Tip``
|
||||
* ``Todo``
|
||||
* ``Warning``
|
||||
* ``Warnings`` *(alias of Warning)*
|
||||
* ``Warn`` *(alias of Warns)*
|
||||
* ``Warns``
|
||||
* ``Yield`` *(alias of Yields)*
|
||||
* ``Yields``
|
||||
* ``Args`` *(alias of Parameters)*
|
||||
* ``Arguments`` *(alias of Parameters)*
|
||||
* ``Attention``
|
||||
* ``Attributes``
|
||||
* ``Caution``
|
||||
* ``Danger``
|
||||
* ``Error``
|
||||
* ``Example``
|
||||
* ``Examples``
|
||||
* ``Hint``
|
||||
* ``Important``
|
||||
* ``Keyword Args`` *(alias of Keyword Arguments)*
|
||||
* ``Keyword Arguments``
|
||||
* ``Methods``
|
||||
* ``Note``
|
||||
* ``Notes``
|
||||
* ``Other Parameters``
|
||||
* ``Parameters``
|
||||
* ``Return`` *(alias of Returns)*
|
||||
* ``Returns``
|
||||
* ``Raise`` *(alias of Raises)*
|
||||
* ``Raises``
|
||||
* ``References``
|
||||
* ``See Also``
|
||||
* ``Tip``
|
||||
* ``Todo``
|
||||
* ``Warning``
|
||||
* ``Warnings`` *(alias of Warning)*
|
||||
* ``Warn`` *(alias of Warns)*
|
||||
* ``Warns``
|
||||
* ``Yield`` *(alias of Yields)*
|
||||
* ``Yields``
|
||||
|
||||
Google vs NumPy
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -208,9 +208,9 @@ You can read more about them in the `Python Packaging User Guide`_.
|
||||
Note that in some Linux distributions, such as Debian and Ubuntu,
|
||||
this might require an extra installation step as follows.
|
||||
|
||||
.. code-block:: console
|
||||
.. code-block:: console
|
||||
|
||||
$ apt-get install python3-venv
|
||||
$ apt-get install python3-venv
|
||||
|
||||
Docker
|
||||
------
|
||||
|
||||
@@ -137,11 +137,15 @@ declarations:
|
||||
|
||||
This directive marks the beginning of the description of a module (or package
|
||||
submodule, in which case the name should be fully qualified, including the
|
||||
package name). It does not create content (like e.g. :rst:dir:`py:class`
|
||||
does).
|
||||
package name). A description of the module such as the docstring can be
|
||||
placed in the body of the directive.
|
||||
|
||||
This directive will also cause an entry in the global module index.
|
||||
|
||||
.. versionchanged:: 5.2
|
||||
|
||||
Module directives support body content.
|
||||
|
||||
.. rubric:: options
|
||||
|
||||
.. rst:directive:option:: platform: platforms
|
||||
@@ -165,6 +169,8 @@ declarations:
|
||||
Mark a module as deprecated; it will be designated as such in various
|
||||
locations then.
|
||||
|
||||
|
||||
|
||||
.. rst:directive:: .. py:currentmodule:: name
|
||||
|
||||
This directive tells Sphinx that the classes, functions etc. documented from
|
||||
@@ -505,7 +511,8 @@ For functions with optional parameters that don't have default values
|
||||
(typically functions implemented in C extension modules without keyword
|
||||
argument support), you can use brackets to specify the optional parts:
|
||||
|
||||
.. py:function:: compile(source[, filename[, symbol]])
|
||||
.. py:function:: compile(source[, filename[, symbol]])
|
||||
:noindex:
|
||||
|
||||
It is customary to put the opening bracket before the comma.
|
||||
|
||||
@@ -561,20 +568,20 @@ explained by an example::
|
||||
|
||||
This will render like this:
|
||||
|
||||
.. py:function:: send_message(sender, recipient, message_body, [priority=1])
|
||||
:noindex:
|
||||
.. py:function:: send_message(sender, recipient, message_body, [priority=1])
|
||||
:noindex:
|
||||
|
||||
Send a message to a recipient
|
||||
Send a message to a recipient
|
||||
|
||||
:param str sender: The person sending the message
|
||||
:param str recipient: The recipient of the message
|
||||
:param str message_body: The body of the message
|
||||
:param priority: The priority of the message, can be a number 1-5
|
||||
:type priority: integer or None
|
||||
:return: the message id
|
||||
:rtype: int
|
||||
:raises ValueError: if the message_body exceeds 160 characters
|
||||
:raises TypeError: if the message_body is not a basestring
|
||||
:param str sender: The person sending the message
|
||||
:param str recipient: The recipient of the message
|
||||
:param str message_body: The body of the message
|
||||
:param priority: The priority of the message, can be a number 1-5
|
||||
:type priority: integer or None
|
||||
:return: the message id
|
||||
:rtype: int
|
||||
:raises ValueError: if the message_body exceeds 160 characters
|
||||
:raises TypeError: if the message_body is not a basestring
|
||||
|
||||
It is also possible to combine parameter type and description, if the type is a
|
||||
single word, like this::
|
||||
@@ -844,12 +851,16 @@ Example::
|
||||
This will be rendered as:
|
||||
|
||||
.. c:struct:: Data
|
||||
:noindexentry:
|
||||
|
||||
.. c:union:: @data
|
||||
:noindexentry:
|
||||
|
||||
.. c:var:: int a
|
||||
:noindexentry:
|
||||
|
||||
.. c:var:: double b
|
||||
:noindexentry:
|
||||
|
||||
Explicit ref: :c:var:`Data.@data.a`. Short-hand ref: :c:var:`Data.a`.
|
||||
|
||||
@@ -931,8 +942,10 @@ Inline Expressions and Types
|
||||
will be rendered as follows:
|
||||
|
||||
.. c:var:: int a = 42
|
||||
:noindexentry:
|
||||
|
||||
.. c:function:: int f(int i)
|
||||
:noindexentry:
|
||||
|
||||
An expression: :c:expr:`a * f(a)` (or as text: :c:texpr:`a * f(a)`).
|
||||
|
||||
@@ -1142,19 +1155,23 @@ visibility statement (``public``, ``private`` or ``protected``).
|
||||
The example are rendered as follows.
|
||||
|
||||
.. cpp:type:: std::vector<int> MyList
|
||||
:noindex:
|
||||
|
||||
A typedef-like declaration of a type.
|
||||
|
||||
.. cpp:type:: MyContainer::const_iterator
|
||||
:noindex:
|
||||
|
||||
Declaration of a type alias with unspecified type.
|
||||
|
||||
.. cpp:type:: MyType = std::unordered_map<int, std::string>
|
||||
:noindex:
|
||||
|
||||
Declaration of a type alias.
|
||||
|
||||
.. cpp:type:: template<typename T> \
|
||||
MyContainer = std::vector<T>
|
||||
:noindex:
|
||||
|
||||
.. rst:directive:: .. cpp:enum:: unscoped enum declaration
|
||||
.. cpp:enum-struct:: scoped enum declaration
|
||||
@@ -1281,12 +1298,16 @@ Example::
|
||||
This will be rendered as:
|
||||
|
||||
.. cpp:class:: Data
|
||||
:noindexentry:
|
||||
|
||||
.. cpp:union:: @data
|
||||
:noindexentry:
|
||||
|
||||
.. cpp:var:: int a
|
||||
:noindexentry:
|
||||
|
||||
.. cpp:var:: double b
|
||||
:noindexentry:
|
||||
|
||||
Explicit ref: :cpp:var:`Data::@data::a`. Short-hand ref: :cpp:var:`Data::a`.
|
||||
|
||||
@@ -1392,10 +1413,12 @@ introduction` instead of a template parameter list::
|
||||
They are rendered as follows.
|
||||
|
||||
.. cpp:function:: std::Iterator{It} void advance(It &it)
|
||||
:noindexentry:
|
||||
|
||||
A function template with a template parameter constrained to be an Iterator.
|
||||
|
||||
.. cpp:class:: std::LessThanComparable{T} MySortedContainer
|
||||
:noindexentry:
|
||||
|
||||
A class template with a template parameter constrained to be
|
||||
LessThanComparable.
|
||||
@@ -1425,8 +1448,10 @@ Inline Expressions and Types
|
||||
will be rendered as follows:
|
||||
|
||||
.. cpp:var:: int a = 42
|
||||
:noindexentry:
|
||||
|
||||
.. cpp:function:: int f(int i)
|
||||
:noindexentry:
|
||||
|
||||
An expression: :cpp:expr:`a * f(a)` (or as text: :cpp:texpr:`a * f(a)`).
|
||||
|
||||
@@ -1815,6 +1840,9 @@ The JavaScript domain (name **js**) provides the following directives:
|
||||
current module name.
|
||||
|
||||
.. versionadded:: 1.6
|
||||
.. versionchanged:: 5.2
|
||||
|
||||
Module directives support body content.
|
||||
|
||||
.. rst:directive:: .. js:function:: name(signature)
|
||||
|
||||
@@ -1838,15 +1866,16 @@ The JavaScript domain (name **js**) provides the following directives:
|
||||
|
||||
This is rendered as:
|
||||
|
||||
.. js:function:: $.getJSON(href, callback[, errback])
|
||||
.. js:function:: $.getJSON(href, callback[, errback])
|
||||
:noindex:
|
||||
|
||||
:param string href: An URI to the location of the resource.
|
||||
:param callback: Gets called with the object.
|
||||
:param errback:
|
||||
Gets called in case the request fails. And a lot of other
|
||||
text so we need multiple lines.
|
||||
:throws SomeError: For whatever reason in that case.
|
||||
:returns: Something.
|
||||
:param string href: An URI to the location of the resource.
|
||||
:param callback: Gets called with the object.
|
||||
:param errback:
|
||||
Gets called in case the request fails. And a lot of other
|
||||
text so we need multiple lines.
|
||||
:throws SomeError: For whatever reason in that case.
|
||||
:returns: Something.
|
||||
|
||||
.. rst:directive:: .. js:method:: name(signature)
|
||||
|
||||
@@ -1867,10 +1896,11 @@ The JavaScript domain (name **js**) provides the following directives:
|
||||
|
||||
This is rendered as:
|
||||
|
||||
.. js:class:: MyAnimal(name[, age])
|
||||
.. js:class:: MyAnimal(name[, age])
|
||||
:noindex:
|
||||
|
||||
:param string name: The name of the animal
|
||||
:param number age: an optional age for the animal
|
||||
:param string name: The name of the animal
|
||||
:param number age: an optional age for the animal
|
||||
|
||||
.. rst:directive:: .. js:data:: name
|
||||
|
||||
@@ -1913,13 +1943,15 @@ The reStructuredText domain (name **rst**) provides the following directives:
|
||||
|
||||
will be rendered as:
|
||||
|
||||
.. rst:directive:: foo
|
||||
.. rst:directive:: foo
|
||||
:noindex:
|
||||
|
||||
Foo description.
|
||||
Foo description.
|
||||
|
||||
.. rst:directive:: .. bar:: baz
|
||||
.. rst:directive:: .. bar:: baz
|
||||
:noindex:
|
||||
|
||||
Bar description.
|
||||
Bar description.
|
||||
|
||||
.. rst:directive:: .. rst:directive:option:: name
|
||||
|
||||
@@ -1935,12 +1967,14 @@ The reStructuredText domain (name **rst**) provides the following directives:
|
||||
|
||||
will be rendered as:
|
||||
|
||||
.. rst:directive:: toctree
|
||||
:noindex:
|
||||
.. rst:directive:: toctree
|
||||
:noindex:
|
||||
|
||||
.. rst:directive:option:: caption: caption of ToC
|
||||
.. rst:directive:option:: caption: caption of ToC
|
||||
:noindex:
|
||||
|
||||
.. rst:directive:option:: glob
|
||||
.. rst:directive:option:: glob
|
||||
:noindex:
|
||||
|
||||
.. rubric:: options
|
||||
|
||||
@@ -1968,9 +2002,10 @@ The reStructuredText domain (name **rst**) provides the following directives:
|
||||
|
||||
will be rendered as:
|
||||
|
||||
.. rst:role:: foo
|
||||
.. rst:role:: foo
|
||||
:noindex:
|
||||
|
||||
Foo description.
|
||||
Foo description.
|
||||
|
||||
.. _rst-roles:
|
||||
|
||||
|
||||
196
pyproject.toml
Normal file
196
pyproject.toml
Normal file
@@ -0,0 +1,196 @@
|
||||
[build-system]
|
||||
requires = ["flit_core>=3.7"]
|
||||
build-backend = "flit_core.buildapi"
|
||||
|
||||
# project metadata
|
||||
[project]
|
||||
name = "Sphinx"
|
||||
description = "Python documentation generator"
|
||||
readme = "README.rst"
|
||||
urls.Changelog = "https://www.sphinx-doc.org/en/master/changes.html"
|
||||
urls.Code = "https://github.com/sphinx-doc/sphinx"
|
||||
urls.Download = "https://pypi.org/project/Sphinx/"
|
||||
urls.Homepage = "https://www.sphinx-doc.org/"
|
||||
urls."Issue tracker" = "https://github.com/sphinx-doc/sphinx/issues"
|
||||
license.text = "BSD"
|
||||
requires-python = ">=3.6"
|
||||
|
||||
# Classifiers list: https://pypi.org/classifiers/
|
||||
classifiers = [
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
"Environment :: Console",
|
||||
"Environment :: Web Environment",
|
||||
"Intended Audience :: Developers",
|
||||
"Intended Audience :: Education",
|
||||
"Intended Audience :: End Users/Desktop",
|
||||
"Intended Audience :: Science/Research",
|
||||
"Intended Audience :: System Administrators",
|
||||
"License :: OSI Approved :: BSD License",
|
||||
"Operating System :: OS Independent",
|
||||
"Programming Language :: Python",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3 :: Only",
|
||||
"Programming Language :: Python :: 3.6",
|
||||
"Programming Language :: Python :: 3.7",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: Implementation :: CPython",
|
||||
"Programming Language :: Python :: Implementation :: PyPy",
|
||||
"Framework :: Setuptools Plugin",
|
||||
"Framework :: Sphinx",
|
||||
"Framework :: Sphinx :: Extension",
|
||||
"Framework :: Sphinx :: Theme",
|
||||
"Topic :: Documentation",
|
||||
"Topic :: Documentation :: Sphinx",
|
||||
"Topic :: Internet :: WWW/HTTP :: Site Management",
|
||||
"Topic :: Printing",
|
||||
"Topic :: Software Development",
|
||||
"Topic :: Software Development :: Documentation",
|
||||
"Topic :: Text Processing",
|
||||
"Topic :: Text Processing :: General",
|
||||
"Topic :: Text Processing :: Indexing",
|
||||
"Topic :: Text Processing :: Markup",
|
||||
"Topic :: Text Processing :: Markup :: HTML",
|
||||
"Topic :: Text Processing :: Markup :: LaTeX",
|
||||
"Topic :: Utilities",
|
||||
]
|
||||
dependencies = [
|
||||
"sphinxcontrib-applehelp",
|
||||
"sphinxcontrib-devhelp",
|
||||
"sphinxcontrib-jsmath",
|
||||
"sphinxcontrib-htmlhelp>=2.0.0",
|
||||
"sphinxcontrib-serializinghtml>=1.1.5",
|
||||
"sphinxcontrib-qthelp",
|
||||
"Jinja2>=3.0",
|
||||
"Pygments>=2.12",
|
||||
"docutils>=0.14,<0.20",
|
||||
"snowballstemmer>=2.0",
|
||||
"babel>=2.9",
|
||||
"alabaster>=0.7,<0.8",
|
||||
"imagesize>=1.3",
|
||||
"requests>=2.5.0",
|
||||
"packaging>=21.0",
|
||||
"importlib-metadata>=4.8; python_version < '3.10'",
|
||||
"colorama>=0.4.5; sys_platform == 'win32'",
|
||||
]
|
||||
dynamic = ["version"]
|
||||
|
||||
[project.optional-dependencies]
|
||||
docs = [
|
||||
"sphinxcontrib-websupport",
|
||||
]
|
||||
lint = [
|
||||
"flake8>=3.5.0",
|
||||
"flake8-comprehensions",
|
||||
"flake8-bugbear",
|
||||
"flake8-simplify",
|
||||
"isort",
|
||||
"mypy>=0.971",
|
||||
"sphinx-lint",
|
||||
"docutils-stubs",
|
||||
"types-typed-ast",
|
||||
"types-requests",
|
||||
]
|
||||
test = [
|
||||
"pytest>=4.6",
|
||||
"html5lib",
|
||||
"typed_ast; python_version < '3.8'",
|
||||
"cython",
|
||||
]
|
||||
|
||||
[[project.authors]]
|
||||
name = "Georg Brandl"
|
||||
email = "georg@python.org"
|
||||
|
||||
[project.scripts]
|
||||
sphinx-build = "sphinx.cmd.build:main"
|
||||
sphinx-quickstart = "sphinx.cmd.quickstart:main"
|
||||
sphinx-apidoc = "sphinx.ext.apidoc:main"
|
||||
sphinx-autogen = "sphinx.ext.autosummary.generate:main"
|
||||
|
||||
[project.entry-points."distutils.commands"]
|
||||
build_sphinx = 'sphinx.setup_command:BuildDoc'
|
||||
|
||||
[tool.flit.module]
|
||||
name = "sphinx"
|
||||
|
||||
[tool.flit.sdist]
|
||||
include = [
|
||||
"LICENSE",
|
||||
"AUTHORS",
|
||||
"CHANGES",
|
||||
# Documentation
|
||||
"doc/",
|
||||
"CODE_OF_CONDUCT", # used as an include in the Documentation
|
||||
"EXAMPLES", # used as an include in the Documentation
|
||||
# Tests
|
||||
"tests/",
|
||||
"tox.ini",
|
||||
# Utilities
|
||||
"utils/",
|
||||
"babel.cfg",
|
||||
]
|
||||
exclude = [
|
||||
"doc/_build",
|
||||
]
|
||||
|
||||
[tool.isort]
|
||||
line_length = 95
|
||||
|
||||
[tool.mypy]
|
||||
check_untyped_defs = true
|
||||
disallow_incomplete_defs = true
|
||||
follow_imports = "skip"
|
||||
ignore_missing_imports = true
|
||||
no_implicit_optional = true
|
||||
python_version = "3.6"
|
||||
show_column_numbers = true
|
||||
show_error_codes = true
|
||||
show_error_context = true
|
||||
strict_optional = true
|
||||
warn_redundant_casts = true
|
||||
warn_unused_ignores = true
|
||||
|
||||
[[tool.mypy.overrides]]
|
||||
module = [
|
||||
"sphinx.application",
|
||||
"sphinx.builders",
|
||||
"sphinx.builders.html",
|
||||
"sphinx.builders.latex",
|
||||
"sphinx.builders.linkcheck",
|
||||
"sphinx.directives.code",
|
||||
"sphinx.domains.*",
|
||||
"sphinx.environment",
|
||||
"sphinx.environment.adapters.toctree",
|
||||
"sphinx.environment.adapters.indexentries",
|
||||
"sphinx.ext.*",
|
||||
"sphinx.pycode.parser",
|
||||
"sphinx.registry",
|
||||
"sphinx.setup_command",
|
||||
"sphinx.testing.util",
|
||||
"sphinx.transforms.i18n",
|
||||
"sphinx.transforms.post_transforms.images",
|
||||
"sphinx.util.cfamily",
|
||||
"sphinx.util.docfields",
|
||||
"sphinx.util.docutils",
|
||||
"sphinx.util.nodes",
|
||||
"sphinx.util.typing",
|
||||
"sphinx.writers.latex",
|
||||
"sphinx.writers.text",
|
||||
"sphinx.writers.xml"
|
||||
]
|
||||
strict_optional = false
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
filterwarnings = [
|
||||
"all",
|
||||
"ignore::DeprecationWarning:docutils.io",
|
||||
"ignore::DeprecationWarning:pyximport.pyximport",
|
||||
"ignore::ImportWarning:importlib._bootstrap",
|
||||
]
|
||||
markers = [
|
||||
"apidoc",
|
||||
"setup_command",
|
||||
]
|
||||
testpaths = ["tests"]
|
||||
122
setup.cfg
122
setup.cfg
@@ -1,132 +1,12 @@
|
||||
[metadata]
|
||||
license_files = LICENSE
|
||||
|
||||
[egg_info]
|
||||
tag_build = .dev
|
||||
tag_date = true
|
||||
|
||||
[aliases]
|
||||
release = egg_info -Db ''
|
||||
upload = upload --sign --identity=36580288
|
||||
|
||||
[flake8]
|
||||
max-line-length = 95
|
||||
ignore = E116,E241,E251,E741,W504,I101,B006,B023
|
||||
ignore = E116,E241,E251,E741,W504,I101,B006,B023,SIM102,SIM103,SIM105,SIM110,SIM113,SIM114,SIM115,SIM117,SIM223,SIM300,SIM401,SIM904,SIM905,SIM907
|
||||
exclude = .git,.tox,.venv,tests/roots/*,build/*,doc/_build/*,sphinx/search/*,doc/usage/extensions/example*.py
|
||||
application-import-names = sphinx
|
||||
import-order-style = smarkets
|
||||
per-file-ignores =
|
||||
tests/*: E501
|
||||
|
||||
[isort]
|
||||
line_length = 95
|
||||
|
||||
[mypy]
|
||||
python_version = 3.6
|
||||
disallow_incomplete_defs = True
|
||||
show_column_numbers = True
|
||||
show_error_context = True
|
||||
show_error_codes = true
|
||||
ignore_missing_imports = True
|
||||
follow_imports = skip
|
||||
check_untyped_defs = True
|
||||
warn_unused_ignores = True
|
||||
strict_optional = True
|
||||
no_implicit_optional = True
|
||||
warn_redundant_casts = True
|
||||
|
||||
[mypy-sphinx.application]
|
||||
strict_optional = False
|
||||
|
||||
[mypy-sphinx.builders]
|
||||
strict_optional = False
|
||||
|
||||
[mypy-sphinx.builders.html]
|
||||
strict_optional = False
|
||||
|
||||
[mypy-sphinx.builders.latex]
|
||||
strict_optional = False
|
||||
|
||||
[mypy-sphinx.builders.linkcheck]
|
||||
strict_optional = False
|
||||
|
||||
[mypy-sphinx.directives.code]
|
||||
strict_optional = False
|
||||
|
||||
[mypy-sphinx.domains.*]
|
||||
strict_optional = False
|
||||
|
||||
[mypy-sphinx.environment]
|
||||
strict_optional = False
|
||||
|
||||
[mypy-sphinx.environment.adapters.toctree]
|
||||
strict_optional = False
|
||||
|
||||
[mypy-sphinx.environment.adapters.indexentries]
|
||||
strict_optional = False
|
||||
|
||||
[mypy-sphinx.ext.*]
|
||||
strict_optional = False
|
||||
|
||||
[mypy-sphinx.pycode.parser]
|
||||
strict_optional = False
|
||||
|
||||
[mypy-sphinx.registry]
|
||||
strict_optional = False
|
||||
|
||||
[mypy-sphinx.setup_command]
|
||||
strict_optional = False
|
||||
|
||||
[mypy-sphinx.testing.util]
|
||||
strict_optional = False
|
||||
|
||||
[mypy-sphinx.transforms.i18n]
|
||||
strict_optional = False
|
||||
|
||||
[mypy-sphinx.transforms.post_transforms.images]
|
||||
strict_optional = False
|
||||
|
||||
[mypy-sphinx.util.cfamily]
|
||||
strict_optional = False
|
||||
|
||||
[mypy-sphinx.util.docfields]
|
||||
strict_optional = False
|
||||
|
||||
[mypy-sphinx.util.docutils]
|
||||
strict_optional = False
|
||||
|
||||
[mypy-sphinx.util.nodes]
|
||||
strict_optional = False
|
||||
|
||||
[mypy-sphinx.util.typing]
|
||||
strict_optional = False
|
||||
|
||||
[mypy-sphinx.writers.html]
|
||||
strict_optional = False
|
||||
|
||||
[mypy-sphinx.writers.html5]
|
||||
strict_optional = False
|
||||
|
||||
[mypy-sphinx.writers.latex]
|
||||
strict_optional = False
|
||||
|
||||
[mypy-sphinx.writers.text]
|
||||
strict_optional = False
|
||||
|
||||
[mypy-sphinx.writers.xml]
|
||||
strict_optional = False
|
||||
|
||||
[tool:pytest]
|
||||
filterwarnings =
|
||||
all
|
||||
ignore::DeprecationWarning:docutils.io
|
||||
ignore::DeprecationWarning:pyximport.pyximport
|
||||
ignore::ImportWarning:importlib._bootstrap
|
||||
markers =
|
||||
apidoc
|
||||
setup_command
|
||||
testpaths = tests
|
||||
|
||||
[coverage:run]
|
||||
branch = True
|
||||
parallel = True
|
||||
|
||||
122
setup.py
122
setup.py
@@ -1,122 +0,0 @@
|
||||
from setuptools import find_packages, setup
|
||||
|
||||
import sphinx
|
||||
|
||||
with open('README.rst', encoding='utf-8') as f:
|
||||
long_desc = f.read()
|
||||
|
||||
setup(
|
||||
name='Sphinx',
|
||||
version=sphinx.__version__,
|
||||
url='https://www.sphinx-doc.org/',
|
||||
download_url='https://pypi.org/project/Sphinx/',
|
||||
license='BSD',
|
||||
author='Georg Brandl',
|
||||
author_email='georg@python.org',
|
||||
description='Python documentation generator',
|
||||
long_description=long_desc,
|
||||
long_description_content_type='text/x-rst',
|
||||
project_urls={
|
||||
"Code": "https://github.com/sphinx-doc/sphinx",
|
||||
"Changelog": "https://www.sphinx-doc.org/en/master/changes.html",
|
||||
"Issue tracker": "https://github.com/sphinx-doc/sphinx/issues",
|
||||
},
|
||||
zip_safe=False,
|
||||
classifiers=[
|
||||
'Development Status :: 5 - Production/Stable',
|
||||
'Environment :: Console',
|
||||
'Environment :: Web Environment',
|
||||
'Intended Audience :: Developers',
|
||||
'Intended Audience :: Education',
|
||||
'Intended Audience :: End Users/Desktop',
|
||||
'Intended Audience :: Science/Research',
|
||||
'Intended Audience :: System Administrators',
|
||||
'License :: OSI Approved :: BSD License',
|
||||
'Operating System :: OS Independent',
|
||||
'Programming Language :: Python',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3 :: Only',
|
||||
'Programming Language :: Python :: 3.7',
|
||||
'Programming Language :: Python :: 3.8',
|
||||
'Programming Language :: Python :: 3.9',
|
||||
'Programming Language :: Python :: 3.10',
|
||||
'Programming Language :: Python :: Implementation :: CPython',
|
||||
'Programming Language :: Python :: Implementation :: PyPy',
|
||||
'Framework :: Setuptools Plugin',
|
||||
'Framework :: Sphinx',
|
||||
'Framework :: Sphinx :: Extension',
|
||||
'Framework :: Sphinx :: Theme',
|
||||
'Topic :: Documentation',
|
||||
'Topic :: Documentation :: Sphinx',
|
||||
'Topic :: Internet :: WWW/HTTP :: Site Management',
|
||||
'Topic :: Printing',
|
||||
'Topic :: Software Development',
|
||||
'Topic :: Software Development :: Documentation',
|
||||
'Topic :: Text Processing',
|
||||
'Topic :: Text Processing :: General',
|
||||
'Topic :: Text Processing :: Indexing',
|
||||
'Topic :: Text Processing :: Markup',
|
||||
'Topic :: Text Processing :: Markup :: HTML',
|
||||
'Topic :: Text Processing :: Markup :: LaTeX',
|
||||
'Topic :: Utilities',
|
||||
],
|
||||
platforms='any',
|
||||
packages=find_packages(exclude=['tests', 'utils']),
|
||||
package_data = {
|
||||
'sphinx': ['py.typed'],
|
||||
},
|
||||
include_package_data=True,
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
'sphinx-build = sphinx.cmd.build:main',
|
||||
'sphinx-quickstart = sphinx.cmd.quickstart:main',
|
||||
'sphinx-apidoc = sphinx.ext.apidoc:main',
|
||||
'sphinx-autogen = sphinx.ext.autosummary.generate:main',
|
||||
],
|
||||
'distutils.commands': [
|
||||
'build_sphinx = sphinx.setup_command:BuildDoc',
|
||||
],
|
||||
},
|
||||
python_requires=">=3.7",
|
||||
install_requires=[
|
||||
'sphinxcontrib-applehelp',
|
||||
'sphinxcontrib-devhelp',
|
||||
'sphinxcontrib-jsmath',
|
||||
'sphinxcontrib-htmlhelp>=2.0.0',
|
||||
'sphinxcontrib-serializinghtml>=1.1.5',
|
||||
'sphinxcontrib-qthelp',
|
||||
'Jinja2>=3.0',
|
||||
'Pygments>=2.12',
|
||||
'docutils>=0.14,<0.20',
|
||||
'snowballstemmer>=2.0',
|
||||
'babel>=2.9',
|
||||
'alabaster>=0.7,<0.8',
|
||||
'imagesize>=1.3',
|
||||
'requests>=2.5.0',
|
||||
'packaging>=21.0',
|
||||
"importlib-metadata>=4.8; python_version < '3.10'",
|
||||
"colorama>=0.4.5; sys_platform == 'win32'",
|
||||
],
|
||||
extras_require={
|
||||
'docs': [
|
||||
'sphinxcontrib-websupport',
|
||||
],
|
||||
'lint': [
|
||||
'flake8>=3.5.0',
|
||||
'flake8-comprehensions',
|
||||
'flake8-bugbear',
|
||||
'isort',
|
||||
'mypy>=0.971',
|
||||
'sphinx-lint',
|
||||
'docutils-stubs',
|
||||
"types-typed-ast",
|
||||
"types-requests",
|
||||
],
|
||||
'test': [
|
||||
'pytest>=4.6',
|
||||
'html5lib',
|
||||
"typed_ast; python_version < '3.8'",
|
||||
'cython',
|
||||
],
|
||||
},
|
||||
)
|
||||
@@ -1,13 +1,11 @@
|
||||
"""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.)
|
||||
# (Otherwise getting the version out of it when packaging is impossible.)
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import warnings
|
||||
from os import path
|
||||
from subprocess import PIPE
|
||||
|
||||
from .deprecation import RemovedInNextVersionWarning
|
||||
|
||||
@@ -22,7 +20,7 @@ warnings.filterwarnings('ignore', 'The frontend.Option class .*',
|
||||
DeprecationWarning, module='docutils.frontend')
|
||||
|
||||
__version__ = '6.0.0'
|
||||
__released__ = '6.0.0' # used when Sphinx builds its own docs
|
||||
__display_version__ = __version__ # used for command line version
|
||||
|
||||
#: Version info for better programmatic use.
|
||||
#:
|
||||
@@ -36,18 +34,22 @@ version_info = (6, 0, 0, 'final', 0)
|
||||
|
||||
package_dir = path.abspath(path.dirname(__file__))
|
||||
|
||||
__display_version__ = __version__ # used for command line version
|
||||
if __version__.endswith('+'):
|
||||
# try to find out the commit hash if checked out from git, and append
|
||||
# it to __version__ (since we use this value from setup.py, it gets
|
||||
# automatically propagated to an installed copy as well)
|
||||
__display_version__ = __version__
|
||||
__version__ = __version__[:-1] # remove '+' for PEP-440 version spec.
|
||||
_in_development = True
|
||||
if _in_development:
|
||||
# Only import subprocess if needed
|
||||
import subprocess
|
||||
|
||||
try:
|
||||
ret = subprocess.run(['git', 'show', '-s', '--pretty=format:%h'],
|
||||
cwd=package_dir,
|
||||
stdout=PIPE, stderr=PIPE, encoding='ascii')
|
||||
if ret.stdout:
|
||||
__display_version__ += '/' + ret.stdout.strip()
|
||||
except Exception:
|
||||
pass
|
||||
ret = subprocess.run(
|
||||
['git', 'show', '-s', '--pretty=format:%h'],
|
||||
cwd=package_dir,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
encoding='ascii',
|
||||
).stdout
|
||||
if ret:
|
||||
__display_version__ += '+/' + ret.strip()
|
||||
del ret
|
||||
finally:
|
||||
del subprocess
|
||||
del _in_development
|
||||
|
||||
@@ -106,6 +106,8 @@ class Config:
|
||||
'default_role': (None, 'env', [str]),
|
||||
'add_function_parentheses': (True, 'env', []),
|
||||
'add_module_names': (True, 'env', []),
|
||||
'toc_object_entries_show_parents': ('domain', 'env',
|
||||
ENUM('domain', 'all', 'hide')),
|
||||
'trim_footnote_reference_space': (False, 'env', []),
|
||||
'show_authors': (False, 'env', []),
|
||||
'pygments_style': (None, 'html', [str]),
|
||||
|
||||
@@ -131,6 +131,44 @@ class ObjectDescription(SphinxDirective, Generic[T]):
|
||||
"""
|
||||
pass
|
||||
|
||||
def _object_hierarchy_parts(self, sig_node: desc_signature) -> Tuple[str, ...]:
|
||||
"""
|
||||
Returns a tuple of strings, one entry for each part of the object's
|
||||
hierarchy (e.g. ``('module', 'submodule', 'Class', 'method')``). The
|
||||
returned tuple is used to properly nest children within parents in the
|
||||
table of contents, and can also be used within the
|
||||
:py:meth:`_toc_entry_name` method.
|
||||
|
||||
This method must not be used outwith table of contents generation.
|
||||
"""
|
||||
return ()
|
||||
|
||||
def _toc_entry_name(self, sig_node: desc_signature) -> str:
|
||||
"""
|
||||
Returns the text of the table of contents entry for the object.
|
||||
|
||||
This function is called once, in :py:meth:`run`, to set the name for the
|
||||
table of contents entry (a special attribute ``_toc_name`` is set on the
|
||||
object node, later used in
|
||||
``environment.collectors.toctree.TocTreeCollector.process_doc().build_toc()``
|
||||
when the table of contents entries are collected).
|
||||
|
||||
To support table of contents entries for their objects, domains must
|
||||
override this method, also respecting the configuration setting
|
||||
``toc_object_entries_show_parents``. Domains must also override
|
||||
:py:meth:`_object_hierarchy_parts`, with one (string) entry for each part of the
|
||||
object's hierarchy. The result of this method is set on the signature
|
||||
node, and can be accessed as ``sig_node['_toc_parts']`` for use within
|
||||
this method. The resulting tuple is also used to properly nest children
|
||||
within parents in the table of contents.
|
||||
|
||||
An example implementations of this method is within the python domain
|
||||
(:meth:`PyObject._toc_entry_name`). The python domain sets the
|
||||
``_toc_parts`` attribute within the :py:meth:`handle_signature()`
|
||||
method.
|
||||
"""
|
||||
return ''
|
||||
|
||||
def run(self) -> List[Node]:
|
||||
"""
|
||||
Main directive entry function, called by docutils upon encountering the
|
||||
@@ -172,6 +210,7 @@ class ObjectDescription(SphinxDirective, Generic[T]):
|
||||
# 'desctype' is a backwards compatible attribute
|
||||
node['objtype'] = node['desctype'] = self.objtype
|
||||
node['noindex'] = noindex = ('noindex' in self.options)
|
||||
node['noindexentry'] = ('noindexentry' in self.options)
|
||||
if self.domain:
|
||||
node['classes'].append(self.domain)
|
||||
node['classes'].append(node['objtype'])
|
||||
@@ -194,6 +233,11 @@ class ObjectDescription(SphinxDirective, Generic[T]):
|
||||
signode.clear()
|
||||
signode += addnodes.desc_name(sig, sig)
|
||||
continue # we don't want an index entry here
|
||||
finally:
|
||||
# Private attributes for ToC generation. Will be modified or removed
|
||||
# without notice.
|
||||
signode['_toc_parts'] = self._object_hierarchy_parts(signode)
|
||||
signode['_toc_name'] = self._toc_entry_name(signode)
|
||||
if name not in self.names:
|
||||
self.names.append(name)
|
||||
if not noindex:
|
||||
@@ -203,6 +247,7 @@ class ObjectDescription(SphinxDirective, Generic[T]):
|
||||
|
||||
contentnode = addnodes.desc_content()
|
||||
node.append(contentnode)
|
||||
|
||||
if self.names:
|
||||
# needed for association of version{added,changed} directives
|
||||
self.env.temp_data['object'] = self.names[0]
|
||||
|
||||
@@ -77,10 +77,11 @@ class TocTree(SphinxDirective):
|
||||
return ret
|
||||
|
||||
def parse_content(self, toctree: addnodes.toctree) -> List[Node]:
|
||||
generated_docnames = frozenset(self.env.domains['std'].initial_data['labels'].keys())
|
||||
suffixes = self.config.source_suffix
|
||||
|
||||
# glob target documents
|
||||
all_docnames = self.env.found_docs.copy()
|
||||
all_docnames = self.env.found_docs.copy() | generated_docnames
|
||||
all_docnames.remove(self.env.docname) # remove current document
|
||||
|
||||
ret: List[Node] = []
|
||||
@@ -95,6 +96,9 @@ class TocTree(SphinxDirective):
|
||||
patname = docname_join(self.env.docname, entry)
|
||||
docnames = sorted(patfilter(all_docnames, patname))
|
||||
for docname in docnames:
|
||||
if docname in generated_docnames:
|
||||
# don't include generated documents in globs
|
||||
continue
|
||||
all_docnames.remove(docname) # don't include it again
|
||||
toctree['entries'].append((None, docname))
|
||||
toctree['includefiles'].append(docname)
|
||||
@@ -118,7 +122,7 @@ class TocTree(SphinxDirective):
|
||||
docname = docname_join(self.env.docname, docname)
|
||||
if url_re.match(ref) or ref == 'self':
|
||||
toctree['entries'].append((title, ref))
|
||||
elif docname not in self.env.found_docs:
|
||||
elif docname not in self.env.found_docs | generated_docnames:
|
||||
if excluded(self.env.doc2path(docname, False)):
|
||||
message = __('toctree contains reference to excluded document %r')
|
||||
subtype = 'excluded'
|
||||
|
||||
@@ -3142,6 +3142,7 @@ class CObject(ObjectDescription[ASTDeclaration]):
|
||||
"""
|
||||
|
||||
option_spec: OptionSpec = {
|
||||
'noindex': directives.flag,
|
||||
'noindexentry': directives.flag,
|
||||
}
|
||||
|
||||
|
||||
@@ -7186,6 +7186,7 @@ class CPPObject(ObjectDescription[ASTDeclaration]):
|
||||
]
|
||||
|
||||
option_spec: OptionSpec = {
|
||||
'noindex': directives.flag,
|
||||
'noindexentry': directives.flag,
|
||||
'tparam-line-spec': directives.flag,
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@ from sphinx.locale import _, __
|
||||
from sphinx.roles import XRefRole
|
||||
from sphinx.util import logging
|
||||
from sphinx.util.docfields import Field, GroupedField, TypedField
|
||||
from sphinx.util.docutils import SphinxDirective
|
||||
from sphinx.util.nodes import make_id, make_refnode
|
||||
from sphinx.util.docutils import SphinxDirective, switch_source_input
|
||||
from sphinx.util.nodes import make_id, make_refnode, nested_parse_with_titles
|
||||
from sphinx.util.typing import OptionSpec
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -108,6 +108,17 @@ class JSObject(ObjectDescription[Tuple[str, str]]):
|
||||
_pseudo_parse_arglist(signode, arglist)
|
||||
return fullname, prefix
|
||||
|
||||
def _object_hierarchy_parts(self, sig_node: desc_signature) -> Tuple[str, ...]:
|
||||
if 'fullname' not in sig_node:
|
||||
return ()
|
||||
modname = sig_node.get('module')
|
||||
fullname = sig_node['fullname']
|
||||
|
||||
if modname:
|
||||
return (modname, *fullname.split('.'))
|
||||
else:
|
||||
return tuple(fullname.split('.'))
|
||||
|
||||
def add_target_and_index(self, name_obj: Tuple[str, str], sig: str,
|
||||
signode: desc_signature) -> None:
|
||||
mod_name = self.env.ref_context.get('js:module')
|
||||
@@ -201,6 +212,25 @@ class JSObject(ObjectDescription[Tuple[str, str]]):
|
||||
"""
|
||||
return fullname.replace('$', '_S_')
|
||||
|
||||
def _toc_entry_name(self, sig_node: desc_signature) -> str:
|
||||
if not sig_node.get('_toc_parts'):
|
||||
return ''
|
||||
|
||||
config = self.env.app.config
|
||||
objtype = sig_node.parent.get('objtype')
|
||||
if config.add_function_parentheses and objtype in {'function', 'method'}:
|
||||
parens = '()'
|
||||
else:
|
||||
parens = ''
|
||||
*parents, name = sig_node['_toc_parts']
|
||||
if config.toc_object_entries_show_parents == 'domain':
|
||||
return sig_node.get('fullname', name) + parens
|
||||
if config.toc_object_entries_show_parents == 'hide':
|
||||
return name + parens
|
||||
if config.toc_object_entries_show_parents == 'all':
|
||||
return '.'.join(parents + [name + parens])
|
||||
return ''
|
||||
|
||||
|
||||
class JSCallable(JSObject):
|
||||
"""Description of a JavaScript function, method or constructor."""
|
||||
@@ -249,7 +279,7 @@ class JSModule(SphinxDirective):
|
||||
:param mod_name: Module name
|
||||
"""
|
||||
|
||||
has_content = False
|
||||
has_content = True
|
||||
required_arguments = 1
|
||||
optional_arguments = 0
|
||||
final_argument_whitespace = False
|
||||
@@ -261,7 +291,14 @@ class JSModule(SphinxDirective):
|
||||
mod_name = self.arguments[0].strip()
|
||||
self.env.ref_context['js:module'] = mod_name
|
||||
noindex = 'noindex' in self.options
|
||||
ret: List[Node] = []
|
||||
|
||||
content_node: Element = nodes.section()
|
||||
with switch_source_input(self.state, self.content):
|
||||
# necessary so that the child nodes get the right source/line set
|
||||
content_node.document = self.state.document
|
||||
nested_parse_with_titles(self.state, self.content, content_node)
|
||||
|
||||
ret: List[Node] = [*content_node.children]
|
||||
if not noindex:
|
||||
domain = cast(JavaScriptDomain, self.env.get_domain('js'))
|
||||
|
||||
|
||||
@@ -26,9 +26,10 @@ from sphinx.pycode.ast import parse as ast_parse
|
||||
from sphinx.roles import XRefRole
|
||||
from sphinx.util import logging
|
||||
from sphinx.util.docfields import Field, GroupedField, TypedField
|
||||
from sphinx.util.docutils import SphinxDirective
|
||||
from sphinx.util.docutils import SphinxDirective, switch_source_input
|
||||
from sphinx.util.inspect import signature_from_str
|
||||
from sphinx.util.nodes import find_pending_xref_condition, make_id, make_refnode
|
||||
from sphinx.util.nodes import (find_pending_xref_condition, make_id, make_refnode,
|
||||
nested_parse_with_titles)
|
||||
from sphinx.util.typing import OptionSpec, TextlikeNode
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -551,6 +552,17 @@ class PyObject(ObjectDescription[Tuple[str, str]]):
|
||||
|
||||
return fullname, prefix
|
||||
|
||||
def _object_hierarchy_parts(self, sig_node: desc_signature) -> Tuple[str, ...]:
|
||||
if 'fullname' not in sig_node:
|
||||
return ()
|
||||
modname = sig_node.get('module')
|
||||
fullname = sig_node['fullname']
|
||||
|
||||
if modname:
|
||||
return (modname, *fullname.split('.'))
|
||||
else:
|
||||
return tuple(fullname.split('.'))
|
||||
|
||||
def get_index_text(self, modname: str, name: Tuple[str, str]) -> str:
|
||||
"""Return the text for the index entry of the object."""
|
||||
raise NotImplementedError('must be implemented in subclasses')
|
||||
@@ -634,6 +646,25 @@ class PyObject(ObjectDescription[Tuple[str, str]]):
|
||||
else:
|
||||
self.env.ref_context.pop('py:module')
|
||||
|
||||
def _toc_entry_name(self, sig_node: desc_signature) -> str:
|
||||
if not sig_node.get('_toc_parts'):
|
||||
return ''
|
||||
|
||||
config = self.env.app.config
|
||||
objtype = sig_node.parent.get('objtype')
|
||||
if config.add_function_parentheses and objtype in {'function', 'method'}:
|
||||
parens = '()'
|
||||
else:
|
||||
parens = ''
|
||||
*parents, name = sig_node['_toc_parts']
|
||||
if config.toc_object_entries_show_parents == 'domain':
|
||||
return sig_node.get('fullname', name) + parens
|
||||
if config.toc_object_entries_show_parents == 'hide':
|
||||
return name + parens
|
||||
if config.toc_object_entries_show_parents == 'all':
|
||||
return '.'.join(parents + [name + parens])
|
||||
return ''
|
||||
|
||||
|
||||
class PyFunction(PyObject):
|
||||
"""Description of a function."""
|
||||
@@ -952,7 +983,7 @@ class PyModule(SphinxDirective):
|
||||
Directive to mark description of a new module.
|
||||
"""
|
||||
|
||||
has_content = False
|
||||
has_content = True
|
||||
required_arguments = 1
|
||||
optional_arguments = 0
|
||||
final_argument_whitespace = False
|
||||
@@ -969,7 +1000,14 @@ class PyModule(SphinxDirective):
|
||||
modname = self.arguments[0].strip()
|
||||
noindex = 'noindex' in self.options
|
||||
self.env.ref_context['py:module'] = modname
|
||||
ret: List[Node] = []
|
||||
|
||||
content_node: Element = nodes.section()
|
||||
with switch_source_input(self.state, self.content):
|
||||
# necessary so that the child nodes get the right source/line set
|
||||
content_node.document = self.state.document
|
||||
nested_parse_with_titles(self.state, self.content, content_node)
|
||||
|
||||
ret: List[Node] = [*content_node.children]
|
||||
if not noindex:
|
||||
# note module to the domain
|
||||
node_id = make_id(self.env, self.state.document, 'module', modname)
|
||||
|
||||
@@ -28,6 +28,10 @@ class ReSTMarkup(ObjectDescription[str]):
|
||||
"""
|
||||
Description of generic reST markup.
|
||||
"""
|
||||
option_spec: OptionSpec = {
|
||||
'noindex': directives.flag,
|
||||
'noindexentry': directives.flag,
|
||||
}
|
||||
|
||||
def add_target_and_index(self, name: str, sig: str, signode: desc_signature) -> None:
|
||||
node_id = make_id(self.env, self.state.document, self.objtype, name)
|
||||
@@ -37,9 +41,10 @@ class ReSTMarkup(ObjectDescription[str]):
|
||||
domain = cast(ReSTDomain, self.env.get_domain('rst'))
|
||||
domain.note_object(self.objtype, name, node_id, location=signode)
|
||||
|
||||
indextext = self.get_index_text(self.objtype, name)
|
||||
if indextext:
|
||||
self.indexnode['entries'].append(('single', indextext, node_id, '', None))
|
||||
if 'noindexentry' not in self.options:
|
||||
indextext = self.get_index_text(self.objtype, name)
|
||||
if indextext:
|
||||
self.indexnode['entries'].append(('single', indextext, node_id, '', None))
|
||||
|
||||
def get_index_text(self, objectname: str, name: str) -> str:
|
||||
return ''
|
||||
@@ -52,6 +57,32 @@ class ReSTMarkup(ObjectDescription[str]):
|
||||
"""
|
||||
return self.objtype + '-' + name
|
||||
|
||||
def _object_hierarchy_parts(self, sig_node: desc_signature) -> Tuple[str, ...]:
|
||||
if 'fullname' not in sig_node:
|
||||
return ()
|
||||
directive_names = []
|
||||
for parent in self.env.ref_context.get('rst:directives', ()):
|
||||
directive_names += parent.split(':')
|
||||
name = sig_node['fullname']
|
||||
return tuple(directive_names + name.split(':'))
|
||||
|
||||
def _toc_entry_name(self, sig_node: desc_signature) -> str:
|
||||
if not sig_node.get('_toc_parts'):
|
||||
return ''
|
||||
|
||||
config = self.env.app.config
|
||||
objtype = sig_node.parent.get('objtype')
|
||||
*parents, name = sig_node['_toc_parts']
|
||||
if objtype == 'directive:option':
|
||||
return f':{name}:'
|
||||
if config.toc_object_entries_show_parents in {'domain', 'all'}:
|
||||
name = ':'.join(sig_node['_toc_parts'])
|
||||
if objtype == 'role':
|
||||
return f':{name}:'
|
||||
if objtype == 'directive':
|
||||
return f'.. {name}::'
|
||||
return ''
|
||||
|
||||
|
||||
def parse_directive(d: str) -> Tuple[str, str]:
|
||||
"""Parse a directive signature.
|
||||
@@ -79,7 +110,8 @@ class ReSTDirective(ReSTMarkup):
|
||||
"""
|
||||
def handle_signature(self, sig: str, signode: desc_signature) -> str:
|
||||
name, args = parse_directive(sig)
|
||||
desc_name = '.. %s::' % name
|
||||
desc_name = f'.. {name}::'
|
||||
signode['fullname'] = name.strip()
|
||||
signode += addnodes.desc_name(desc_name, desc_name)
|
||||
if len(args) > 0:
|
||||
signode += addnodes.desc_addname(args, args)
|
||||
@@ -114,7 +146,9 @@ class ReSTDirectiveOption(ReSTMarkup):
|
||||
except ValueError:
|
||||
name, argument = sig, None
|
||||
|
||||
signode += addnodes.desc_name(':%s:' % name, ':%s:' % name)
|
||||
desc_name = f':{name}:'
|
||||
signode['fullname'] = name.strip()
|
||||
signode += addnodes.desc_name(desc_name, desc_name)
|
||||
if argument:
|
||||
signode += addnodes.desc_annotation(' ' + argument, ' ' + argument)
|
||||
if self.options.get('type'):
|
||||
@@ -170,7 +204,9 @@ class ReSTRole(ReSTMarkup):
|
||||
Description of a reST role.
|
||||
"""
|
||||
def handle_signature(self, sig: str, signode: desc_signature) -> str:
|
||||
signode += addnodes.desc_name(':%s:' % sig, ':%s:' % sig)
|
||||
desc_name = f':{sig}:'
|
||||
signode['fullname'] = sig.strip()
|
||||
signode += addnodes.desc_name(desc_name, desc_name)
|
||||
return sig
|
||||
|
||||
def get_index_text(self, objectname: str, name: str) -> str:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""The standard domain."""
|
||||
|
||||
import re
|
||||
import sys
|
||||
from copy import copy
|
||||
from typing import (TYPE_CHECKING, Any, Callable, Dict, Iterable, Iterator, List, Optional,
|
||||
Tuple, Type, Union, cast)
|
||||
@@ -28,6 +29,10 @@ if TYPE_CHECKING:
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
if sys.version_info[:2] >= (3, 8):
|
||||
from typing import Final
|
||||
else:
|
||||
Final = Any
|
||||
|
||||
# RE for option descriptions
|
||||
option_desc_re = re.compile(r'((?:/|--|-|\+)?[^\s=]+)(=?\s*.*)')
|
||||
@@ -584,7 +589,7 @@ class StandardDomain(Domain):
|
||||
'doc': XRefRole(warn_dangling=True, innernodeclass=nodes.inline),
|
||||
}
|
||||
|
||||
initial_data = {
|
||||
initial_data: Final = {
|
||||
'progoptions': {}, # (program, name) -> docname, labelid
|
||||
'objects': {}, # (type, name) -> docname, labelid
|
||||
'labels': { # labelname -> docname, labelid, sectionname
|
||||
|
||||
@@ -77,6 +77,60 @@ versioning_conditions: Dict[str, Union[bool, Callable]] = {
|
||||
'text': is_translatable,
|
||||
}
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import MutableMapping
|
||||
|
||||
from typing_extensions import Literal, overload
|
||||
|
||||
from sphinx.domains.c import CDomain
|
||||
from sphinx.domains.changeset import ChangeSetDomain
|
||||
from sphinx.domains.citation import CitationDomain
|
||||
from sphinx.domains.cpp import CPPDomain
|
||||
from sphinx.domains.index import IndexDomain
|
||||
from sphinx.domains.javascript import JavaScriptDomain
|
||||
from sphinx.domains.math import MathDomain
|
||||
from sphinx.domains.python import PythonDomain
|
||||
from sphinx.domains.rst import ReSTDomain
|
||||
from sphinx.domains.std import StandardDomain
|
||||
from sphinx.ext.duration import DurationDomain
|
||||
from sphinx.ext.todo import TodoDomain
|
||||
|
||||
class _DomainsType(MutableMapping[str, Domain]):
|
||||
@overload
|
||||
def __getitem__(self, key: Literal["c"]) -> CDomain: ... # NoQA: E704
|
||||
@overload
|
||||
def __getitem__(self, key: Literal["cpp"]) -> CPPDomain: ... # NoQA: E704
|
||||
@overload
|
||||
def __getitem__(self, key: Literal["changeset"]) -> ChangeSetDomain: ... # NoQA: E704
|
||||
@overload
|
||||
def __getitem__(self, key: Literal["citation"]) -> CitationDomain: ... # NoQA: E704
|
||||
@overload
|
||||
def __getitem__(self, key: Literal["index"]) -> IndexDomain: ... # NoQA: E704
|
||||
@overload
|
||||
def __getitem__(self, key: Literal["js"]) -> JavaScriptDomain: ... # NoQA: E704
|
||||
@overload
|
||||
def __getitem__(self, key: Literal["math"]) -> MathDomain: ... # NoQA: E704
|
||||
@overload
|
||||
def __getitem__(self, key: Literal["py"]) -> PythonDomain: ... # NoQA: E704
|
||||
@overload
|
||||
def __getitem__(self, key: Literal["rst"]) -> ReSTDomain: ... # NoQA: E704
|
||||
@overload
|
||||
def __getitem__(self, key: Literal["std"]) -> StandardDomain: ... # NoQA: E704
|
||||
@overload
|
||||
def __getitem__(self, key: Literal["duration"]) -> DurationDomain: ... # NoQA: E704
|
||||
@overload
|
||||
def __getitem__(self, key: Literal["todo"]) -> TodoDomain: ... # NoQA: E704
|
||||
@overload
|
||||
def __getitem__(self, key: str) -> Domain: ... # NoQA: E704
|
||||
def __getitem__(self, key): raise NotImplementedError # NoQA: E704
|
||||
def __setitem__(self, key, value): raise NotImplementedError # NoQA: E704
|
||||
def __delitem__(self, key): raise NotImplementedError # NoQA: E704
|
||||
def __iter__(self): raise NotImplementedError # NoQA: E704
|
||||
def __len__(self): raise NotImplementedError # NoQA: E704
|
||||
|
||||
else:
|
||||
_DomainsType = dict
|
||||
|
||||
|
||||
class BuildEnvironment:
|
||||
"""
|
||||
@@ -85,7 +139,7 @@ class BuildEnvironment:
|
||||
transformations to resolve links to them.
|
||||
"""
|
||||
|
||||
domains: Dict[str, Domain]
|
||||
domains: _DomainsType
|
||||
|
||||
# --------- ENVIRONMENT INITIALIZATION -------------------------------------
|
||||
|
||||
@@ -105,7 +159,7 @@ class BuildEnvironment:
|
||||
self.versioning_compare: bool = None
|
||||
|
||||
# all the registered domains, set by the application
|
||||
self.domains = {}
|
||||
self.domains = _DomainsType()
|
||||
|
||||
# the docutils settings for building
|
||||
self.settings = default_settings.copy()
|
||||
@@ -206,7 +260,7 @@ class BuildEnvironment:
|
||||
self.version = app.registry.get_envversion(app)
|
||||
|
||||
# initialize domains
|
||||
self.domains = {}
|
||||
self.domains = _DomainsType()
|
||||
for domain in app.registry.create_domains(self):
|
||||
self.domains[domain.name] = domain
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Toctree adapter for sphinx.environment."""
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Iterable, List, Optional, cast
|
||||
from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Optional, Tuple, cast
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.nodes import Element, Node
|
||||
@@ -54,6 +54,7 @@ class TocTree:
|
||||
"""
|
||||
if toctree.get('hidden', False) and not includehidden:
|
||||
return None
|
||||
generated_docnames: Dict[str, Tuple[str, str, str]] = self.env.domains['std'].initial_data['labels'].copy() # NoQA: E501
|
||||
|
||||
# For reading the following two helper function, it is useful to keep
|
||||
# in mind the node structure of a toctree (using HTML-like node names
|
||||
@@ -139,6 +140,16 @@ class TocTree:
|
||||
item = nodes.list_item('', para)
|
||||
# don't show subitems
|
||||
toc = nodes.bullet_list('', item)
|
||||
elif ref in generated_docnames:
|
||||
docname, _, sectionname = generated_docnames[ref]
|
||||
if not title:
|
||||
title = sectionname
|
||||
reference = nodes.reference('', title, internal=True,
|
||||
refuri=docname, anchorname='')
|
||||
para = addnodes.compact_paragraph('', '', reference)
|
||||
item = nodes.list_item('', para)
|
||||
# don't show subitems
|
||||
toc = nodes.bullet_list('', item)
|
||||
else:
|
||||
if ref in parents:
|
||||
logger.warning(__('circular toctree references '
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Toctree collector for sphinx.environment."""
|
||||
|
||||
from typing import Any, Dict, List, Optional, Set, Tuple, Type, TypeVar, cast
|
||||
from typing import Any, Dict, List, Optional, Sequence, Set, Tuple, TypeVar, Union, cast
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.nodes import Element, Node
|
||||
@@ -54,20 +54,14 @@ class TocTreeCollector(EnvironmentCollector):
|
||||
docname = app.env.docname
|
||||
numentries = [0] # nonlocal again...
|
||||
|
||||
def traverse_in_section(node: Element, cls: Type[N]) -> List[N]:
|
||||
"""Like traverse(), but stay within the same section."""
|
||||
result: List[N] = []
|
||||
if isinstance(node, cls):
|
||||
result.append(node)
|
||||
for child in node.children:
|
||||
if isinstance(child, nodes.section):
|
||||
continue
|
||||
elif isinstance(child, nodes.Element):
|
||||
result.extend(traverse_in_section(child, cls))
|
||||
return result
|
||||
|
||||
def build_toc(node: Element, depth: int = 1) -> Optional[nodes.bullet_list]:
|
||||
def build_toc(
|
||||
node: Union[Element, Sequence[Element]],
|
||||
depth: int = 1
|
||||
) -> Optional[nodes.bullet_list]:
|
||||
# list of table of contents entries
|
||||
entries: List[Element] = []
|
||||
# cache of parents -> list item
|
||||
memo_parents: Dict[Tuple[str, ...], nodes.list_item] = {}
|
||||
for sectionnode in node:
|
||||
# find all toctree nodes in this section and add them
|
||||
# to the toc (just copying the toctree node which is then
|
||||
@@ -79,13 +73,7 @@ class TocTreeCollector(EnvironmentCollector):
|
||||
visitor = SphinxContentsFilter(doctree)
|
||||
title.walkabout(visitor)
|
||||
nodetext = visitor.get_entry_text()
|
||||
if not numentries[0]:
|
||||
# for the very first toc entry, don't add an anchor
|
||||
# as it is the file's title anyway
|
||||
anchorname = ''
|
||||
else:
|
||||
anchorname = '#' + sectionnode['ids'][0]
|
||||
numentries[0] += 1
|
||||
anchorname = _make_anchor_name(sectionnode['ids'], numentries)
|
||||
# make these nodes:
|
||||
# list_item -> compact_paragraph -> reference
|
||||
reference = nodes.reference(
|
||||
@@ -97,22 +85,67 @@ class TocTreeCollector(EnvironmentCollector):
|
||||
if sub_item:
|
||||
item += sub_item
|
||||
entries.append(item)
|
||||
# Wrap items under an ``.. only::`` directive in a node for
|
||||
# post-processing
|
||||
elif isinstance(sectionnode, addnodes.only):
|
||||
onlynode = addnodes.only(expr=sectionnode['expr'])
|
||||
blist = build_toc(sectionnode, depth)
|
||||
if blist:
|
||||
onlynode += blist.children
|
||||
entries.append(onlynode)
|
||||
# check within the section for other node types
|
||||
elif isinstance(sectionnode, nodes.Element):
|
||||
for toctreenode in traverse_in_section(sectionnode,
|
||||
addnodes.toctree):
|
||||
item = toctreenode.copy()
|
||||
entries.append(item)
|
||||
# important: do the inventory stuff
|
||||
TocTree(app.env).note(docname, toctreenode)
|
||||
toctreenode: nodes.Node
|
||||
for toctreenode in sectionnode.findall():
|
||||
if isinstance(toctreenode, nodes.section):
|
||||
continue
|
||||
if isinstance(toctreenode, addnodes.toctree):
|
||||
item = toctreenode.copy()
|
||||
entries.append(item)
|
||||
# important: do the inventory stuff
|
||||
TocTree(app.env).note(docname, toctreenode)
|
||||
# add object signatures within a section to the ToC
|
||||
elif isinstance(toctreenode, addnodes.desc):
|
||||
for sig_node in toctreenode:
|
||||
if not isinstance(sig_node, addnodes.desc_signature):
|
||||
continue
|
||||
# Skip if no name set
|
||||
if not sig_node.get('_toc_name', ''):
|
||||
continue
|
||||
# Skip entries with no ID (e.g. with :noindex: set)
|
||||
ids = sig_node['ids']
|
||||
if not ids or sig_node.parent.get('noindexentry'):
|
||||
continue
|
||||
|
||||
anchorname = _make_anchor_name(ids, numentries)
|
||||
|
||||
reference = nodes.reference(
|
||||
'', '', nodes.literal('', sig_node['_toc_name']),
|
||||
internal=True, refuri=docname, anchorname=anchorname)
|
||||
para = addnodes.compact_paragraph('', '', reference,
|
||||
skip_section_number=True)
|
||||
entry = nodes.list_item('', para)
|
||||
*parents, _ = sig_node['_toc_parts']
|
||||
parents = tuple(parents)
|
||||
|
||||
# Cache parents tuple
|
||||
memo_parents[sig_node['_toc_parts']] = entry
|
||||
|
||||
# Nest children within parents
|
||||
if parents and parents in memo_parents:
|
||||
root_entry = memo_parents[parents]
|
||||
if isinstance(root_entry[-1], nodes.bullet_list):
|
||||
root_entry[-1].append(entry)
|
||||
else:
|
||||
root_entry.append(nodes.bullet_list('', entry))
|
||||
continue
|
||||
|
||||
entries.append(entry)
|
||||
|
||||
if entries:
|
||||
return nodes.bullet_list('', *entries)
|
||||
return None
|
||||
|
||||
toc = build_toc(doctree)
|
||||
if toc:
|
||||
app.env.tocs[docname] = toc
|
||||
@@ -153,6 +186,8 @@ class TocTreeCollector(EnvironmentCollector):
|
||||
_walk_toc(subnode, secnums, depth, titlenode)
|
||||
titlenode = None
|
||||
elif isinstance(subnode, addnodes.compact_paragraph):
|
||||
if 'skip_section_number' in subnode:
|
||||
continue
|
||||
numstack[-1] += 1
|
||||
reference = cast(nodes.reference, subnode[0])
|
||||
if depth > 0:
|
||||
@@ -201,6 +236,7 @@ class TocTreeCollector(EnvironmentCollector):
|
||||
|
||||
def assign_figure_numbers(self, env: BuildEnvironment) -> List[str]:
|
||||
"""Assign a figure number to each figure under a numbered toctree."""
|
||||
generated_docnames = frozenset(env.domains['std'].initial_data['labels'].keys())
|
||||
|
||||
rewrite_needed = []
|
||||
|
||||
@@ -212,7 +248,8 @@ class TocTreeCollector(EnvironmentCollector):
|
||||
def get_figtype(node: Node) -> Optional[str]:
|
||||
for domain in env.domains.values():
|
||||
figtype = domain.get_enumerable_node_type(node)
|
||||
if domain.name == 'std' and not domain.get_numfig_title(node): # type: ignore
|
||||
if (domain.name == 'std'
|
||||
and not domain.get_numfig_title(node)): # type: ignore[attr-defined] # NoQA: E501,W503
|
||||
# Skip if uncaptioned node
|
||||
continue
|
||||
|
||||
@@ -247,6 +284,7 @@ class TocTreeCollector(EnvironmentCollector):
|
||||
fignumbers[figure_id] = get_next_fignumber(figtype, secnum)
|
||||
|
||||
def _walk_doctree(docname: str, doctree: Element, secnum: Tuple[int, ...]) -> None:
|
||||
nonlocal generated_docnames
|
||||
for subnode in doctree.children:
|
||||
if isinstance(subnode, nodes.section):
|
||||
next_secnum = get_section_number(docname, subnode)
|
||||
@@ -259,6 +297,9 @@ class TocTreeCollector(EnvironmentCollector):
|
||||
if url_re.match(subdocname) or subdocname == 'self':
|
||||
# don't mess with those
|
||||
continue
|
||||
if subdocname in generated_docnames:
|
||||
# or these
|
||||
continue
|
||||
|
||||
_walk_doc(subdocname, secnum)
|
||||
elif isinstance(subnode, nodes.Element):
|
||||
@@ -283,6 +324,17 @@ class TocTreeCollector(EnvironmentCollector):
|
||||
return rewrite_needed
|
||||
|
||||
|
||||
def _make_anchor_name(ids: List[str], num_entries: List[int]) -> str:
|
||||
if not num_entries[0]:
|
||||
# for the very first toc entry, don't add an anchor
|
||||
# as it is the file's title anyway
|
||||
anchorname = ''
|
||||
else:
|
||||
anchorname = '#' + ids[0]
|
||||
num_entries[0] += 1
|
||||
return anchorname
|
||||
|
||||
|
||||
def setup(app: Sphinx) -> Dict[str, Any]:
|
||||
app.add_env_collector(TocTreeCollector)
|
||||
|
||||
|
||||
@@ -956,6 +956,15 @@ class ModuleDocumenter(Documenter):
|
||||
merge_members_option(self.options)
|
||||
self.__all__: Optional[Sequence[str]] = None
|
||||
|
||||
def add_content(self, more_content: Optional[StringList]) -> None:
|
||||
old_indent = self.indent
|
||||
self.indent += ' '
|
||||
super().add_content(None)
|
||||
self.indent = old_indent
|
||||
if more_content:
|
||||
for line, src in zip(more_content.data, more_content.items):
|
||||
self.add_line(line, src[0], src[1])
|
||||
|
||||
@classmethod
|
||||
def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any
|
||||
) -> bool:
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"""Render math in HTML via dvipng or dvisvgm."""
|
||||
|
||||
import base64
|
||||
import posixpath
|
||||
import re
|
||||
import shutil
|
||||
@@ -30,6 +31,8 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
templates_path = path.join(package_dir, 'templates', 'imgmath')
|
||||
|
||||
__all__ = ()
|
||||
|
||||
|
||||
class MathExtError(SphinxError):
|
||||
category = 'Math extension error'
|
||||
@@ -204,13 +207,17 @@ def convert_dvi_to_svg(dvipath: str, builder: Builder) -> Tuple[str, Optional[in
|
||||
return filename, depth
|
||||
|
||||
|
||||
def render_math(self: HTMLTranslator, math: str) -> Tuple[Optional[str], Optional[int]]:
|
||||
def render_math(
|
||||
self: HTMLTranslator,
|
||||
math: str,
|
||||
) -> Tuple[Optional[str], Optional[int], Optional[str], Optional[str]]:
|
||||
"""Render the LaTeX math expression *math* using latex and dvipng or
|
||||
dvisvgm.
|
||||
|
||||
Return the filename relative to the built document and the "depth",
|
||||
that is, the distance of image bottom and baseline in pixels, if the
|
||||
option to use preview_latex is switched on.
|
||||
Also return the temporary and destination files.
|
||||
|
||||
Error handling may seem strange, but follows a pattern: if LaTeX or dvipng
|
||||
(dvisvgm) aren't available, only a warning is generated (since that enables
|
||||
@@ -235,19 +242,19 @@ def render_math(self: HTMLTranslator, math: str) -> Tuple[Optional[str], Optiona
|
||||
depth = read_png_depth(outfn)
|
||||
elif image_format == 'svg':
|
||||
depth = read_svg_depth(outfn)
|
||||
return relfn, depth
|
||||
return relfn, depth, None, outfn
|
||||
|
||||
# if latex or dvipng (dvisvgm) has failed once, don't bother to try again
|
||||
if hasattr(self.builder, '_imgmath_warned_latex') or \
|
||||
hasattr(self.builder, '_imgmath_warned_image_translator'):
|
||||
return None, None
|
||||
return None, None, None, None
|
||||
|
||||
# .tex -> .dvi
|
||||
try:
|
||||
dvipath = compile_math(latex, self.builder)
|
||||
except InvokeError:
|
||||
self.builder._imgmath_warned_latex = True # type: ignore
|
||||
return None, None
|
||||
return None, None, None, None
|
||||
|
||||
# .dvi -> .png/.svg
|
||||
try:
|
||||
@@ -257,13 +264,19 @@ def render_math(self: HTMLTranslator, math: str) -> Tuple[Optional[str], Optiona
|
||||
imgpath, depth = convert_dvi_to_svg(dvipath, self.builder)
|
||||
except InvokeError:
|
||||
self.builder._imgmath_warned_image_translator = True # type: ignore
|
||||
return None, None
|
||||
return None, None, None, None
|
||||
|
||||
# Move generated image on tempdir to build dir
|
||||
ensuredir(path.dirname(outfn))
|
||||
shutil.move(imgpath, outfn)
|
||||
return relfn, depth, imgpath, outfn
|
||||
|
||||
return relfn, depth
|
||||
|
||||
def render_maths_to_base64(image_format: str, outfn: Optional[str]) -> str:
|
||||
with open(outfn, "rb") as f:
|
||||
encoded = base64.b64encode(f.read()).decode(encoding='utf-8')
|
||||
if image_format == 'png':
|
||||
return f'data:image/png;base64,{encoded}'
|
||||
if image_format == 'svg':
|
||||
return f'data:image/svg+xml;base64,{encoded}'
|
||||
raise MathExtError('imgmath_image_format must be either "png" or "svg"')
|
||||
|
||||
|
||||
def cleanup_tempdir(app: Sphinx, exc: Exception) -> None:
|
||||
@@ -285,7 +298,7 @@ def get_tooltip(self: HTMLTranslator, node: Element) -> str:
|
||||
|
||||
def html_visit_math(self: HTMLTranslator, node: nodes.math) -> None:
|
||||
try:
|
||||
fname, depth = render_math(self, '$' + node.astext() + '$')
|
||||
fname, depth, imgpath, outfn = render_math(self, '$' + node.astext() + '$')
|
||||
except MathExtError as exc:
|
||||
msg = str(exc)
|
||||
sm = nodes.system_message(msg, type='WARNING', level=2,
|
||||
@@ -293,14 +306,23 @@ def html_visit_math(self: HTMLTranslator, node: nodes.math) -> None:
|
||||
sm.walkabout(self)
|
||||
logger.warning(__('display latex %r: %s'), node.astext(), msg)
|
||||
raise nodes.SkipNode from exc
|
||||
if fname is None:
|
||||
if self.builder.config.imgmath_embed:
|
||||
image_format = self.builder.config.imgmath_image_format.lower()
|
||||
img_src = render_maths_to_base64(image_format, outfn)
|
||||
else:
|
||||
# Move generated image on tempdir to build dir
|
||||
if imgpath is not None:
|
||||
ensuredir(path.dirname(outfn))
|
||||
shutil.move(imgpath, outfn)
|
||||
img_src = fname
|
||||
if img_src is None:
|
||||
# something failed -- use text-only as a bad substitute
|
||||
self.body.append('<span class="math">%s</span>' %
|
||||
self.encode(node.astext()).strip())
|
||||
else:
|
||||
c = ('<img class="math" src="%s"' % fname) + get_tooltip(self, node)
|
||||
c = f'<img class="math" src="{img_src}"' + get_tooltip(self, node)
|
||||
if depth is not None:
|
||||
c += ' style="vertical-align: %dpx"' % (-depth)
|
||||
c += f' style="vertical-align: {-depth:d}px"'
|
||||
self.body.append(c + '/>')
|
||||
raise nodes.SkipNode
|
||||
|
||||
@@ -311,7 +333,7 @@ def html_visit_displaymath(self: HTMLTranslator, node: nodes.math_block) -> None
|
||||
else:
|
||||
latex = wrap_displaymath(node.astext(), None, False)
|
||||
try:
|
||||
fname, depth = render_math(self, latex)
|
||||
fname, depth, imgpath, outfn = render_math(self, latex)
|
||||
except MathExtError as exc:
|
||||
msg = str(exc)
|
||||
sm = nodes.system_message(msg, type='WARNING', level=2,
|
||||
@@ -326,12 +348,21 @@ def html_visit_displaymath(self: HTMLTranslator, node: nodes.math_block) -> None
|
||||
self.body.append('<span class="eqno">(%s)' % number)
|
||||
self.add_permalink_ref(node, _('Permalink to this equation'))
|
||||
self.body.append('</span>')
|
||||
if fname is None:
|
||||
if self.builder.config.imgmath_embed:
|
||||
image_format = self.builder.config.imgmath_image_format.lower()
|
||||
img_src = render_maths_to_base64(image_format, outfn)
|
||||
else:
|
||||
# Move generated image on tempdir to build dir
|
||||
if imgpath is not None:
|
||||
ensuredir(path.dirname(outfn))
|
||||
shutil.move(imgpath, outfn)
|
||||
img_src = fname
|
||||
if img_src is None:
|
||||
# something failed -- use text-only as a bad substitute
|
||||
self.body.append('<span class="math">%s</span></p>\n</div>' %
|
||||
self.encode(node.astext()).strip())
|
||||
else:
|
||||
self.body.append(('<img src="%s"' % fname) + get_tooltip(self, node) +
|
||||
self.body.append(f'<img src="{img_src}"' + get_tooltip(self, node) +
|
||||
'/></p>\n</div>')
|
||||
raise nodes.SkipNode
|
||||
|
||||
@@ -354,5 +385,6 @@ def setup(app: Sphinx) -> Dict[str, Any]:
|
||||
app.add_config_value('imgmath_latex_preamble', '', 'html')
|
||||
app.add_config_value('imgmath_add_tooltips', True, 'html')
|
||||
app.add_config_value('imgmath_font_size', 12, 'html')
|
||||
app.add_config_value('imgmath_embed', False, 'html', [bool])
|
||||
app.connect('build-finished', cleanup_tempdir)
|
||||
return {'version': sphinx.__display_version__, 'parallel_read_safe': True}
|
||||
|
||||
@@ -14,6 +14,7 @@ from docutils.nodes import Element, Node
|
||||
from sphinx import addnodes, package_dir
|
||||
from sphinx.deprecation import RemovedInSphinx70Warning
|
||||
from sphinx.environment import BuildEnvironment
|
||||
from sphinx.util import split_into
|
||||
|
||||
|
||||
class SearchLanguage:
|
||||
@@ -242,6 +243,7 @@ class IndexBuilder:
|
||||
# stemmed words in titles -> set(docname)
|
||||
self._title_mapping: Dict[str, Set[str]] = {}
|
||||
self._all_titles: Dict[str, List[Tuple[str, str]]] = {} # docname -> all titles
|
||||
self._index_entries: Dict[str, List[Tuple[str, str, str]]] = {} # docname -> index entry
|
||||
self._stem_cache: Dict[str, str] = {} # word -> stemmed word
|
||||
self._objtypes: Dict[Tuple[str, str], int] = {} # objtype -> index
|
||||
# objtype index -> (domain, type, objname (localized))
|
||||
@@ -378,12 +380,17 @@ class IndexBuilder:
|
||||
alltitles: Dict[str, List[Tuple[int, str]]] = {}
|
||||
for docname, titlelist in self._all_titles.items():
|
||||
for title, titleid in titlelist:
|
||||
alltitles.setdefault(title.lower(), []).append((fn2index[docname], titleid))
|
||||
alltitles.setdefault(title, []).append((fn2index[docname], titleid))
|
||||
|
||||
index_entries: Dict[str, List[Tuple[int, str]]] = {}
|
||||
for docname, entries in self._index_entries.items():
|
||||
for entry, entry_id, main_entry in entries:
|
||||
index_entries.setdefault(entry.lower(), []).append((fn2index[docname], entry_id))
|
||||
|
||||
return dict(docnames=docnames, filenames=filenames, titles=titles, terms=terms,
|
||||
objects=objects, objtypes=objtypes, objnames=objnames,
|
||||
titleterms=title_terms, envversion=self.env.version,
|
||||
alltitles=alltitles)
|
||||
alltitles=alltitles, indexentries=index_entries)
|
||||
|
||||
def label(self) -> str:
|
||||
return "%s (code: %s)" % (self.lang.language_name, self.lang.lang)
|
||||
@@ -441,6 +448,38 @@ class IndexBuilder:
|
||||
if _filter(stemmed_word) and not already_indexed:
|
||||
self._mapping.setdefault(stemmed_word, set()).add(docname)
|
||||
|
||||
# find explicit entries within index directives
|
||||
_index_entries: Set[Tuple[str, str, str]] = set()
|
||||
for node in doctree.findall(addnodes.index):
|
||||
for entry_type, value, tid, main, *index_key in node['entries']:
|
||||
tid = tid or ''
|
||||
try:
|
||||
if entry_type == 'single':
|
||||
try:
|
||||
entry, subentry = split_into(2, 'single', value)
|
||||
except ValueError:
|
||||
entry, = split_into(1, 'single', value)
|
||||
subentry = ''
|
||||
_index_entries.add((entry, tid, main))
|
||||
if subentry:
|
||||
_index_entries.add((subentry, tid, main))
|
||||
elif entry_type == 'pair':
|
||||
first, second = split_into(2, 'pair', value)
|
||||
_index_entries.add((first, tid, main))
|
||||
_index_entries.add((second, tid, main))
|
||||
elif entry_type == 'triple':
|
||||
first, second, third = split_into(3, 'triple', value)
|
||||
_index_entries.add((first, tid, main))
|
||||
_index_entries.add((second, tid, main))
|
||||
_index_entries.add((third, tid, main))
|
||||
elif entry_type in {'see', 'seealso'}:
|
||||
first, second = split_into(2, 'see', value)
|
||||
_index_entries.add((first, tid, main))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
self._index_entries[docname] = sorted(_index_entries)
|
||||
|
||||
def context_for_searchtool(self) -> Dict[str, Any]:
|
||||
if self.lang.js_splitter_code:
|
||||
js_splitter_code = self.lang.js_splitter_code
|
||||
|
||||
@@ -242,6 +242,7 @@ const Search = {
|
||||
const docNames = Search._index.docnames;
|
||||
const titles = Search._index.titles;
|
||||
const allTitles = Search._index.alltitles;
|
||||
const indexEntries = Search._index.indexentries;
|
||||
|
||||
// stem the search terms and add them to the correct list
|
||||
const stemmer = new Stemmer();
|
||||
@@ -280,12 +281,12 @@ const Search = {
|
||||
|
||||
const queryLower = query.toLowerCase();
|
||||
for (const [title, foundTitles] of Object.entries(allTitles)) {
|
||||
if (title.includes(queryLower) && (queryLower.length >= title.length/2)) {
|
||||
if (title.toLowerCase().includes(queryLower) && (queryLower.length >= title.length/2)) {
|
||||
for (const [file, id] of foundTitles) {
|
||||
let score = Math.round(100 * queryLower.length / title.length)
|
||||
results.push([
|
||||
docNames[file],
|
||||
titles[file],
|
||||
`${titles[file]} > ${title}`,
|
||||
id !== null ? "#" + id : "",
|
||||
null,
|
||||
score,
|
||||
@@ -295,6 +296,23 @@ const Search = {
|
||||
}
|
||||
}
|
||||
|
||||
// search for explicit entries in index directives
|
||||
for (const [entry, foundEntries] of Object.entries(indexEntries)) {
|
||||
if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) {
|
||||
for (const [file, id] of foundEntries) {
|
||||
let score = Math.round(100 * queryLower.length / entry.length)
|
||||
results.push([
|
||||
docNames[file],
|
||||
titles[file],
|
||||
id ? "#" + id : "",
|
||||
null,
|
||||
score,
|
||||
filenames[file],
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// lookup as object
|
||||
objectTerms.forEach((term) =>
|
||||
results.push(...Search.performObjectSearch(term, objectTerms))
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
from typing import Dict, Pattern
|
||||
|
||||
@@ -22,21 +23,8 @@ def terminal_safe(s: str) -> str:
|
||||
|
||||
|
||||
def get_terminal_width() -> int:
|
||||
"""Borrowed from the py lib."""
|
||||
if sys.platform == "win32":
|
||||
# For static typing, as fcntl & termios never exist on Windows.
|
||||
return int(os.environ.get('COLUMNS', 80)) - 1
|
||||
try:
|
||||
import fcntl
|
||||
import struct
|
||||
import termios
|
||||
call = fcntl.ioctl(0, termios.TIOCGWINSZ, struct.pack('hhhh', 0, 0, 0, 0))
|
||||
height, width = struct.unpack('hhhh', call)[:2]
|
||||
terminal_width = width
|
||||
except Exception:
|
||||
# FALLBACK
|
||||
terminal_width = int(os.environ.get('COLUMNS', 80)) - 1
|
||||
return terminal_width
|
||||
"""Return the width of the terminal in columns."""
|
||||
return shutil.get_terminal_size().columns - 1
|
||||
|
||||
|
||||
_tw: int = get_terminal_width()
|
||||
|
||||
@@ -70,7 +70,7 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
|
||||
Our custom HTML translator.
|
||||
"""
|
||||
|
||||
builder: "StandaloneHTMLBuilder" = None
|
||||
builder: "StandaloneHTMLBuilder"
|
||||
|
||||
def __init__(self, document: nodes.document, builder: Builder) -> None:
|
||||
super().__init__(document, builder)
|
||||
@@ -280,7 +280,7 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
|
||||
def depart_seealso(self, node: Element) -> None:
|
||||
self.depart_admonition(node)
|
||||
|
||||
def get_secnumber(self, node: Element) -> Tuple[int, ...]:
|
||||
def get_secnumber(self, node: Element) -> Optional[Tuple[int, ...]]:
|
||||
if node.get('secnumber'):
|
||||
return node['secnumber']
|
||||
elif isinstance(node.parent, nodes.section):
|
||||
|
||||
@@ -45,7 +45,7 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
|
||||
Our custom HTML translator.
|
||||
"""
|
||||
|
||||
builder: "StandaloneHTMLBuilder" = None
|
||||
builder: "StandaloneHTMLBuilder"
|
||||
# Override docutils.writers.html5_polyglot:HTMLTranslator
|
||||
# otherwise, nodes like <inline classes="s">...</inline> will be
|
||||
# converted to <s>...</s> by `visit_inline`.
|
||||
@@ -258,7 +258,7 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
|
||||
def depart_seealso(self, node: Element) -> None:
|
||||
self.depart_admonition(node)
|
||||
|
||||
def get_secnumber(self, node: Element) -> Tuple[int, ...]:
|
||||
def get_secnumber(self, node: Element) -> Optional[Tuple[int, ...]]:
|
||||
if node.get('secnumber'):
|
||||
return node['secnumber']
|
||||
|
||||
|
||||
@@ -259,7 +259,7 @@ def rstdim_to_latexdim(width_str: str, scale: int = 100) -> str:
|
||||
|
||||
|
||||
class LaTeXTranslator(SphinxTranslator):
|
||||
builder: "LaTeXBuilder" = None
|
||||
builder: "LaTeXBuilder"
|
||||
|
||||
secnumdepth = 2 # legacy sphinxhowto.cls uses this, whereas article.cls
|
||||
# default is originally 3. For book/report, 2 is already LaTeX default.
|
||||
|
||||
0
tests/roots/test-toctree-domain-objects/conf.py
Normal file
0
tests/roots/test-toctree-domain-objects/conf.py
Normal file
39
tests/roots/test-toctree-domain-objects/domains.rst
Normal file
39
tests/roots/test-toctree-domain-objects/domains.rst
Normal file
@@ -0,0 +1,39 @@
|
||||
test-domain-objects
|
||||
===================
|
||||
|
||||
.. py:module:: hello
|
||||
|
||||
.. py:function:: world() -> str
|
||||
|
||||
Prints "Hello, World!" to stdout
|
||||
|
||||
.. py:class:: HelloWorldPrinter
|
||||
|
||||
Controls printing of hello world
|
||||
|
||||
.. py:method:: set_language()
|
||||
|
||||
Sets the language of the HelloWorldPrinter instance
|
||||
|
||||
.. py:attribute:: output_count
|
||||
|
||||
Count of outputs of "Hello, World!"
|
||||
|
||||
.. py:method:: print_normal()
|
||||
:async:
|
||||
:classmethod:
|
||||
|
||||
Prints the normal form of "Hello, World!"
|
||||
|
||||
.. py:method:: print()
|
||||
|
||||
Prints "Hello, World!", including in the chosen language
|
||||
|
||||
.. py:function:: exit()
|
||||
:module: sys
|
||||
|
||||
Quits the interpreter
|
||||
|
||||
.. js:function:: fetch(resource)
|
||||
|
||||
Fetches the given resource, returns a Promise
|
||||
6
tests/roots/test-toctree-domain-objects/index.rst
Normal file
6
tests/roots/test-toctree-domain-objects/index.rst
Normal file
@@ -0,0 +1,6 @@
|
||||
.. toctree::
|
||||
:numbered:
|
||||
:caption: Table of Contents
|
||||
:name: mastertoc
|
||||
|
||||
domains
|
||||
0
tests/roots/test-toctree-index/conf.py
Normal file
0
tests/roots/test-toctree-index/conf.py
Normal file
8
tests/roots/test-toctree-index/foo.rst
Normal file
8
tests/roots/test-toctree-index/foo.rst
Normal file
@@ -0,0 +1,8 @@
|
||||
foo
|
||||
===
|
||||
|
||||
:index:`word`
|
||||
|
||||
.. py:module:: pymodule
|
||||
|
||||
.. py:function:: Timer.repeat(repeat=3, number=1000000)
|
||||
15
tests/roots/test-toctree-index/index.rst
Normal file
15
tests/roots/test-toctree-index/index.rst
Normal file
@@ -0,0 +1,15 @@
|
||||
test-toctree-index
|
||||
==================
|
||||
|
||||
.. toctree::
|
||||
|
||||
foo
|
||||
|
||||
|
||||
.. toctree::
|
||||
:caption: Indices
|
||||
|
||||
genindex
|
||||
modindex
|
||||
search
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import pytest
|
||||
from docutils import nodes
|
||||
from docutils.nodes import bullet_list, comment, list_item, reference, title
|
||||
from docutils.nodes import bullet_list, comment, list_item, literal, reference, title
|
||||
|
||||
from sphinx import addnodes
|
||||
from sphinx.addnodes import compact_paragraph, only
|
||||
@@ -126,6 +126,44 @@ def test_glob(app):
|
||||
assert app.env.numbered_toctrees == set()
|
||||
|
||||
|
||||
@pytest.mark.sphinx('dummy', testroot='toctree-domain-objects')
|
||||
def test_domain_objects(app):
|
||||
includefiles = ['domains']
|
||||
|
||||
app.build()
|
||||
|
||||
assert app.env.toc_num_entries['index'] == 0
|
||||
assert app.env.toc_num_entries['domains'] == 9
|
||||
assert app.env.toctree_includes['index'] == includefiles
|
||||
for file in includefiles:
|
||||
assert 'index' in app.env.files_to_rebuild[file]
|
||||
assert app.env.glob_toctrees == set()
|
||||
assert app.env.numbered_toctrees == {'index'}
|
||||
|
||||
# tocs
|
||||
toctree = app.env.tocs['domains']
|
||||
assert_node(toctree,
|
||||
[bullet_list, list_item, (compact_paragraph, # [0][0]
|
||||
[bullet_list, (list_item, # [0][1][0]
|
||||
[list_item, # [0][1][1]
|
||||
(compact_paragraph, # [0][1][1][0]
|
||||
[bullet_list, (list_item, # [0][1][1][1][0]
|
||||
list_item,
|
||||
list_item,
|
||||
list_item)])], # [0][1][1][1][3]
|
||||
list_item,
|
||||
list_item)])]) # [0][1][1]
|
||||
|
||||
assert_node(toctree[0][0],
|
||||
[compact_paragraph, reference, "test-domain-objects"])
|
||||
|
||||
assert_node(toctree[0][1][0],
|
||||
[list_item, ([compact_paragraph, reference, literal, "world()"])])
|
||||
|
||||
assert_node(toctree[0][1][1][1][3],
|
||||
[list_item, ([compact_paragraph, reference, literal, "HelloWorldPrinter.print()"])])
|
||||
|
||||
|
||||
@pytest.mark.sphinx('xml', testroot='toctree')
|
||||
@pytest.mark.test_params(shared_result='test_environment_toctree_basic')
|
||||
def test_get_toc_for(app):
|
||||
@@ -346,3 +384,17 @@ def test_get_toctree_for_includehidden(app):
|
||||
|
||||
assert_node(toctree[2],
|
||||
[bullet_list, list_item, compact_paragraph, reference, "baz"])
|
||||
|
||||
|
||||
@pytest.mark.sphinx('xml', testroot='toctree-index')
|
||||
def test_toctree_index(app):
|
||||
app.build()
|
||||
toctree = app.env.tocs['index']
|
||||
assert_node(toctree,
|
||||
[bullet_list, ([list_item, (compact_paragraph, # [0][0]
|
||||
[bullet_list, (addnodes.toctree, # [0][1][0]
|
||||
addnodes.toctree)])])]) # [0][1][1]
|
||||
assert_node(toctree[0][1][1], addnodes.toctree,
|
||||
caption="Indices", glob=False, hidden=False,
|
||||
titlesonly=False, maxdepth=-1, numbered=0,
|
||||
entries=[(None, 'genindex'), (None, 'modindex'), (None, 'search')])
|
||||
|
||||
@@ -19,7 +19,7 @@ def test_empty_all(app):
|
||||
'',
|
||||
'.. py:module:: target.empty_all',
|
||||
'',
|
||||
'docsting of empty_all module.',
|
||||
' docsting of empty_all module.',
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@@ -56,6 +56,24 @@ def test_imgmath_svg(app, status, warning):
|
||||
assert re.search(html, content, re.S)
|
||||
|
||||
|
||||
@pytest.mark.skipif(not has_binary('dvisvgm'),
|
||||
reason='Requires dvisvgm" binary')
|
||||
@pytest.mark.sphinx('html', testroot='ext-math-simple',
|
||||
confoverrides={'extensions': ['sphinx.ext.imgmath'],
|
||||
'imgmath_image_format': 'svg',
|
||||
'imgmath_embed': True})
|
||||
def test_imgmath_svg_embed(app, status, warning):
|
||||
app.builder.build_all()
|
||||
if "LaTeX command 'latex' cannot be run" in warning.getvalue():
|
||||
pytest.skip('LaTeX command "latex" is not available')
|
||||
if "dvisvgm command 'dvisvgm' cannot be run" in warning.getvalue():
|
||||
pytest.skip('dvisvgm command "dvisvgm" is not available')
|
||||
|
||||
content = (app.outdir / 'index.html').read_text(encoding='utf8')
|
||||
html = r'<img src="data:image/svg\+xml;base64,[\w\+/=]+"'
|
||||
assert re.search(html, content, re.DOTALL)
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-math',
|
||||
confoverrides={'extensions': ['sphinx.ext.mathjax'],
|
||||
'mathjax_options': {'integrity': 'sha384-0123456789'}})
|
||||
|
||||
@@ -178,7 +178,8 @@ def test_IndexBuilder():
|
||||
'test': [0, 1, 2, 3]},
|
||||
'titles': ('title1_1', 'title1_2', 'title2_1', 'title2_2'),
|
||||
'titleterms': {'section_titl': [0, 1, 2, 3]},
|
||||
'alltitles': {'section_title': [(0, 'section-title'), (1, 'section-title'), (2, 'section-title'), (3, 'section-title')]}
|
||||
'alltitles': {'section_title': [(0, 'section-title'), (1, 'section-title'), (2, 'section-title'), (3, 'section-title')]},
|
||||
'indexentries': {},
|
||||
}
|
||||
assert index._objtypes == {('dummy1', 'objtype1'): 0, ('dummy2', 'objtype1'): 1}
|
||||
assert index._objnames == {0: ('dummy1', 'objtype1', 'objtype1'),
|
||||
@@ -236,7 +237,8 @@ def test_IndexBuilder():
|
||||
'test': [0, 1]},
|
||||
'titles': ('title1_2', 'title2_2'),
|
||||
'titleterms': {'section_titl': [0, 1]},
|
||||
'alltitles': {'section_title': [(0, 'section-title'), (1, 'section-title')]}
|
||||
'alltitles': {'section_title': [(0, 'section-title'), (1, 'section-title')]},
|
||||
'indexentries': {},
|
||||
}
|
||||
assert index._objtypes == {('dummy1', 'objtype1'): 0, ('dummy2', 'objtype1'): 1}
|
||||
assert index._objnames == {0: ('dummy1', 'objtype1', 'objtype1'),
|
||||
|
||||
8
tox.ini
8
tox.ini
@@ -1,6 +1,7 @@
|
||||
[tox]
|
||||
minversion = 2.4.0
|
||||
envlist = docs,flake8,mypy,twine,py{36,37,38,39,310},du{14,15,16,17,18,19}
|
||||
isolated_build = True
|
||||
|
||||
[testenv]
|
||||
usedevelop = True
|
||||
@@ -34,7 +35,7 @@ commands=
|
||||
|
||||
[testenv:du-latest]
|
||||
commands =
|
||||
python -m pip install "git+https://repo.or.cz/docutils.git#subdirectory=docutils"
|
||||
python -m pip install "git+https://repo.or.cz/docutils.git#subdirectory=docutils" --no-warn-conflicts
|
||||
{[testenv]commands}
|
||||
|
||||
[testenv:flake8]
|
||||
@@ -92,7 +93,7 @@ description =
|
||||
Build documentation.
|
||||
extras =
|
||||
docs
|
||||
deps =
|
||||
deps =
|
||||
sphinx-autobuild
|
||||
commands =
|
||||
sphinx-autobuild ./doc ./build/sphinx/
|
||||
@@ -103,8 +104,9 @@ description =
|
||||
Lint package.
|
||||
deps =
|
||||
twine
|
||||
build
|
||||
commands =
|
||||
python setup.py release bdist_wheel sdist
|
||||
python -m build .
|
||||
twine check dist/*
|
||||
|
||||
[testenv:bindep]
|
||||
|
||||
@@ -23,19 +23,23 @@ def stringify_version(version_info, in_develop=True):
|
||||
|
||||
def bump_version(path, version_info, in_develop=True):
|
||||
version = stringify_version(version_info, in_develop)
|
||||
release = version
|
||||
if in_develop:
|
||||
version += '+'
|
||||
|
||||
with open(path, 'r+', encoding='utf-8') as f:
|
||||
body = f.read()
|
||||
body = re.sub(r"(?<=__version__ = ')[^']+", version, body)
|
||||
body = re.sub(r"(?<=__released__ = ')[^']+", release, body)
|
||||
body = re.sub(r"(?<=version_info = )\(.*\)", str(version_info), body)
|
||||
with open(path, 'r', encoding='utf-8') as f:
|
||||
lines = f.read().splitlines()
|
||||
|
||||
f.seek(0)
|
||||
f.truncate(0)
|
||||
f.write(body)
|
||||
for i, line in enumerate(lines):
|
||||
if line.startswith('__version__ = '):
|
||||
lines[i] = f"__version__ = '{version}'"
|
||||
continue
|
||||
if line.startswith('version_info = '):
|
||||
lines[i] = f'version_info = {version_info}'
|
||||
continue
|
||||
if line.startswith('_in_development = '):
|
||||
lines[i] = f'_in_development = {in_develop}'
|
||||
continue
|
||||
|
||||
with open(path, 'w', encoding='utf-8') as f:
|
||||
f.write('\n'.join(lines) + '\n')
|
||||
|
||||
|
||||
def parse_version(version):
|
||||
|
||||
@@ -10,7 +10,7 @@ for stable releases
|
||||
* Check diff by ``git diff``
|
||||
* ``git commit -am 'Bump to X.Y.Z final'``
|
||||
* ``make clean``
|
||||
* ``python setup.py release bdist_wheel sdist``
|
||||
* ``python -m build .``
|
||||
* ``twine upload dist/Sphinx-* --sign --identity [your GPG key]``
|
||||
* open https://pypi.org/project/Sphinx/ and check there are no obvious errors
|
||||
* ``sh utils/bump_docker.sh X.Y.Z``
|
||||
@@ -34,7 +34,7 @@ for first beta releases
|
||||
* Check diff by ``git diff``
|
||||
* ``git commit -am 'Bump to X.Y.0 beta1'``
|
||||
* ``make clean``
|
||||
* ``python setup.py release bdist_wheel sdist``
|
||||
* ``python -m build .``
|
||||
* ``twine upload dist/Sphinx-* --sign --identity [your GPG key]``
|
||||
* open https://pypi.org/project/Sphinx/ and check there are no obvious errors
|
||||
* ``git tag vX.Y.0b1``
|
||||
@@ -62,7 +62,7 @@ for other beta releases
|
||||
* Check diff by ``git diff``
|
||||
* ``git commit -am 'Bump to X.Y.0 betaN'``
|
||||
* ``make clean``
|
||||
* ``python setup.py release bdist_wheel sdist``
|
||||
* ``python -m build .``
|
||||
* ``twine upload dist/Sphinx-* --sign --identity [your GPG key]``
|
||||
* open https://pypi.org/project/Sphinx/ and check there are no obvious errors
|
||||
* ``git tag vX.Y.0bN``
|
||||
@@ -87,7 +87,7 @@ for major releases
|
||||
* Check diff by ``git diff``
|
||||
* ``git commit -am 'Bump to X.Y.0 final'``
|
||||
* ``make clean``
|
||||
* ``python setup.py release bdist_wheel sdist``
|
||||
* ``python -m build .``
|
||||
* ``twine upload dist/Sphinx-* --sign --identity [your GPG key]``
|
||||
* open https://pypi.org/project/Sphinx/ and check there are no obvious errors
|
||||
* ``sh utils/bump_docker.sh X.Y.Z``
|
||||
|
||||
Reference in New Issue
Block a user