mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge branch '3.x' into fix-type-preprocessor
This commit is contained in:
commit
3ff956c27a
9
CHANGES
9
CHANGES
@ -10,6 +10,7 @@ Incompatible changes
|
||||
Deprecated
|
||||
----------
|
||||
|
||||
* ``sphinx.writers.texinfo.TexinfoWriter.desc``
|
||||
* C, parsing of pre-v3 style type directives and roles, along with the options
|
||||
:confval:`c_allow_pre_v3` and :confval:`c_warn_on_allowed_pre_v3`.
|
||||
|
||||
@ -69,14 +70,22 @@ Bugs fixed
|
||||
* #7691: linkcheck: HEAD requests are not used for checking
|
||||
* #4888: i18n: Failed to add an explicit title to ``:ref:`` role on translation
|
||||
* #7928: py domain: failed to resolve a type annotation for the attribute
|
||||
* #8008: py domain: failed to parse a type annotation containing ellipsis
|
||||
* #7994: std domain: option directive does not generate old node_id compatible
|
||||
with 2.x or older
|
||||
* #7968: i18n: The content of ``math`` directive is interpreted as reST on
|
||||
translation
|
||||
* #7768: i18n: The ``root`` element for :confval:`figure_language_filename` is
|
||||
not a path that user specifies in the document
|
||||
* #7993: texinfo: TypeError is raised for nested object descriptions
|
||||
* #7993: texinfo: a warning not supporting desc_signature_line node is shown
|
||||
* #7869: :rst:role:`abbr` role without an explanation will show the explanation
|
||||
from the previous abbr role
|
||||
* C and C++, removed ``noindex`` directive option as it did
|
||||
nothing.
|
||||
* #7619: Duplicated node IDs are generated if node has multiple IDs
|
||||
* #2050: Symbols sections are appeared twice in the index page
|
||||
* #8017: Fix circular import in sphinx.addnodes
|
||||
|
||||
Testing
|
||||
--------
|
||||
|
@ -10,7 +10,6 @@ Sphinx documentation contents
|
||||
development/index
|
||||
man/index
|
||||
|
||||
theming
|
||||
templating
|
||||
latex
|
||||
extdev/index
|
||||
|
34
doc/development/builders.rst
Normal file
34
doc/development/builders.rst
Normal file
@ -0,0 +1,34 @@
|
||||
Configuring builders
|
||||
====================
|
||||
|
||||
Discover builders by entry point
|
||||
--------------------------------
|
||||
|
||||
.. versionadded:: 1.6
|
||||
|
||||
:term:`builder` extensions can be discovered by means of `entry points`_ so
|
||||
that they do not have to be listed in the :confval:`extensions` configuration
|
||||
value.
|
||||
|
||||
Builder extensions should define an entry point in the ``sphinx.builders``
|
||||
group. The name of the entry point needs to match your builder's
|
||||
:attr:`~.Builder.name` attribute, which is the name passed to the
|
||||
:option:`sphinx-build -b` option. The entry point value should equal the
|
||||
dotted name of the extension module. Here is an example of how an entry point
|
||||
for 'mybuilder' can be defined in the extension's ``setup.py``
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
setup(
|
||||
# ...
|
||||
entry_points={
|
||||
'sphinx.builders': [
|
||||
'mybuilder = my.extension.module',
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
Note that it is still necessary to register the builder using
|
||||
:meth:`~.Sphinx.add_builder` in the extension's :func:`setup` function.
|
||||
|
||||
.. _entry points: https://setuptools.readthedocs.io/en/latest/setuptools.html#dynamic-discovery-of-services-and-plugins
|
@ -10,4 +10,12 @@ wish to use Sphinx with existing extensions, refer to :doc:`/usage/index`.
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
overview
|
||||
tutorials/index
|
||||
builders
|
||||
|
||||
.. toctree::
|
||||
:caption: Theming
|
||||
:maxdepth: 2
|
||||
|
||||
theming
|
||||
|
32
doc/development/overview.rst
Normal file
32
doc/development/overview.rst
Normal file
@ -0,0 +1,32 @@
|
||||
Developing extensions overview
|
||||
==============================
|
||||
|
||||
This page contains general information about developing Sphinx extensions.
|
||||
|
||||
Make an extension depend on another extension
|
||||
---------------------------------------------
|
||||
|
||||
Sometimes your extension depends on the functionality of another
|
||||
Sphinx extension. Most Sphinx extensions are activated in a
|
||||
project's :file:`conf.py` file, but this is not available to you as an
|
||||
extension developer.
|
||||
|
||||
.. module:: sphinx.application
|
||||
:noindex:
|
||||
|
||||
To ensure that another extension is activated as a part of your own extension,
|
||||
use the :meth:`Sphinx.setup_extension` method. This will
|
||||
activate another extension at run-time, ensuring that you have access to its
|
||||
functionality.
|
||||
|
||||
For example, the following code activates the ``recommonmark`` extension:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def setup(app):
|
||||
app.setup_extension("recommonmark")
|
||||
|
||||
.. note::
|
||||
|
||||
Since your extension will depend on another, make sure to include
|
||||
it as a part of your extension's installation requirements.
|
336
doc/development/theming.rst
Normal file
336
doc/development/theming.rst
Normal file
@ -0,0 +1,336 @@
|
||||
HTML theme development
|
||||
======================
|
||||
|
||||
.. versionadded:: 0.6
|
||||
|
||||
.. note::
|
||||
|
||||
This document provides information about creating your own theme. If you
|
||||
simply wish to use a pre-existing HTML themes, refer to
|
||||
:doc:`/usage/theming`.
|
||||
|
||||
Sphinx supports changing the appearance of its HTML output via *themes*. A
|
||||
theme is a collection of HTML templates, stylesheet(s) and other static files.
|
||||
Additionally, it has a configuration file which specifies from which theme to
|
||||
inherit, which highlighting style to use, and what options exist for customizing
|
||||
the theme's look and feel.
|
||||
|
||||
Themes are meant to be project-unaware, so they can be used for different
|
||||
projects without change.
|
||||
|
||||
.. note::
|
||||
|
||||
See :ref:`dev-extensions` for more information that may
|
||||
be helpful in developing themes.
|
||||
|
||||
|
||||
Creating themes
|
||||
---------------
|
||||
|
||||
Themes take the form of either a directory or a zipfile (whose name is the
|
||||
theme name), containing the following:
|
||||
|
||||
* A :file:`theme.conf` file.
|
||||
* HTML templates, if needed.
|
||||
* A ``static/`` directory containing any static files that will be copied to the
|
||||
output static directory on build. These can be images, styles, script files.
|
||||
|
||||
The :file:`theme.conf` file is in INI format [1]_ (readable by the standard
|
||||
Python :mod:`ConfigParser` module) and has the following structure:
|
||||
|
||||
.. sourcecode:: ini
|
||||
|
||||
[theme]
|
||||
inherit = base theme
|
||||
stylesheet = main CSS name
|
||||
pygments_style = stylename
|
||||
sidebars = localtoc.html, relations.html, sourcelink.html, searchbox.html
|
||||
|
||||
[options]
|
||||
variable = default value
|
||||
|
||||
* The **inherit** setting gives the name of a "base theme", or ``none``. The
|
||||
base theme will be used to locate missing templates (most themes will not have
|
||||
to supply most templates if they use ``basic`` as the base theme), its options
|
||||
will be inherited, and all of its static files will be used as well. If you
|
||||
want to also inherit the stylesheet, include it via CSS' ``@import`` in your
|
||||
own.
|
||||
|
||||
* The **stylesheet** setting gives the name of a CSS file which will be
|
||||
referenced in the HTML header. If you need more than one CSS file, either
|
||||
include one from the other via CSS' ``@import``, or use a custom HTML template
|
||||
that adds ``<link rel="stylesheet">`` tags as necessary. Setting the
|
||||
:confval:`html_style` config value will override this setting.
|
||||
|
||||
* The **pygments_style** setting gives the name of a Pygments style to use for
|
||||
highlighting. This can be overridden by the user in the
|
||||
:confval:`pygments_style` config value.
|
||||
|
||||
* The **pygments_dark_style** setting gives the name of a Pygments style to use
|
||||
for highlighting when the CSS media query ``(prefers-color-scheme: dark)``
|
||||
evaluates to true. It is injected into the page using
|
||||
:meth:`~Sphinx.add_css_file()`.
|
||||
|
||||
* The **sidebars** setting gives the comma separated list of sidebar templates
|
||||
for constructing sidebars. This can be overridden by the user in the
|
||||
:confval:`html_sidebars` config value.
|
||||
|
||||
* The **options** section contains pairs of variable names and default values.
|
||||
These options can be overridden by the user in :confval:`html_theme_options`
|
||||
and are accessible from all templates as ``theme_<name>``.
|
||||
|
||||
.. versionadded:: 1.7
|
||||
sidebar settings
|
||||
|
||||
|
||||
.. _distribute-your-theme:
|
||||
|
||||
Distribute your theme as a Python package
|
||||
-----------------------------------------
|
||||
|
||||
As a way to distribute your theme, you can use Python package. Python package
|
||||
brings to users easy setting up ways.
|
||||
|
||||
To distribute your theme as a Python package, please define an entry point
|
||||
called ``sphinx.html_themes`` in your ``setup.py`` file, and write a ``setup()``
|
||||
function to register your themes using ``add_html_theme()`` API in it::
|
||||
|
||||
# 'setup.py'
|
||||
setup(
|
||||
...
|
||||
entry_points = {
|
||||
'sphinx.html_themes': [
|
||||
'name_of_theme = your_package',
|
||||
]
|
||||
},
|
||||
...
|
||||
)
|
||||
|
||||
# 'your_package.py'
|
||||
from os import path
|
||||
|
||||
def setup(app):
|
||||
app.add_html_theme('name_of_theme', path.abspath(path.dirname(__file__)))
|
||||
|
||||
If your theme package contains two or more themes, please call
|
||||
``add_html_theme()`` twice or more.
|
||||
|
||||
.. versionadded:: 1.2
|
||||
'sphinx_themes' entry_points feature.
|
||||
|
||||
.. deprecated:: 1.6
|
||||
``sphinx_themes`` entry_points has been deprecated.
|
||||
|
||||
.. versionadded:: 1.6
|
||||
``sphinx.html_themes`` entry_points feature.
|
||||
|
||||
|
||||
Templating
|
||||
----------
|
||||
|
||||
The :doc:`guide to templating </templating>` is helpful if you want to write your
|
||||
own templates. What is important to keep in mind is the order in which Sphinx
|
||||
searches for templates:
|
||||
|
||||
* First, in the user's ``templates_path`` directories.
|
||||
* Then, in the selected theme.
|
||||
* Then, in its base theme, its base's base theme, etc.
|
||||
|
||||
When extending a template in the base theme with the same name, use the theme
|
||||
name as an explicit directory: ``{% extends "basic/layout.html" %}``. From a
|
||||
user ``templates_path`` template, you can still use the "exclamation mark"
|
||||
syntax as described in the templating document.
|
||||
|
||||
|
||||
.. _theming-static-templates:
|
||||
|
||||
Static templates
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Since theme options are meant for the user to configure a theme more easily,
|
||||
without having to write a custom stylesheet, it is necessary to be able to
|
||||
template static files as well as HTML files. Therefore, Sphinx supports
|
||||
so-called "static templates", like this:
|
||||
|
||||
If the name of a file in the ``static/`` directory of a theme (or in the user's
|
||||
static path, for that matter) ends with ``_t``, it will be processed by the
|
||||
template engine. The ``_t`` will be left from the final file name. For
|
||||
example, the *classic* theme has a file ``static/classic.css_t`` which uses
|
||||
templating to put the color options into the stylesheet. When a documentation
|
||||
is built with the classic theme, the output directory will contain a
|
||||
``_static/classic.css`` file where all template tags have been processed.
|
||||
|
||||
|
||||
Use custom page metadata in HTML templates
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Any key / value pairs in :doc:`field lists </usage/restructuredtext/field-lists>`
|
||||
that are placed *before* the page's title will be available to the Jinja
|
||||
template when building the page within the :data:`meta` attribute. For example,
|
||||
if a page had the following text before its first title:
|
||||
|
||||
.. code-block:: rst
|
||||
|
||||
:mykey: My value
|
||||
|
||||
My first title
|
||||
--------------
|
||||
|
||||
Then it could be accessed within a Jinja template like so:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{%- if meta is mapping %}
|
||||
{{ meta.get("mykey") }}
|
||||
{%- endif %}
|
||||
|
||||
Note the check that ``meta`` is a dictionary ("mapping" in Jinja
|
||||
terminology) to ensure that using it in this way is valid.
|
||||
|
||||
|
||||
Defining custom template functions
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Sometimes it is useful to define your own function in Python that you wish to
|
||||
then use in a template. For example, if you'd like to insert a template value
|
||||
with logic that depends on the user's configuration in the project, or if you'd
|
||||
like to include non-trivial checks and provide friendly error messages for
|
||||
incorrect configuration in the template.
|
||||
|
||||
To define your own template function, you'll need to define two functions
|
||||
inside your module:
|
||||
|
||||
* A **page context event handler** (or **registration**) function. This is
|
||||
connected to the :class:`.Sphinx` application via an event callback.
|
||||
* A **template function** that you will use in your Jinja template.
|
||||
|
||||
First, define the registration function, which accepts the arguments for
|
||||
:event:`html-page-context`.
|
||||
|
||||
Within the registration function, define the template function that you'd like to use
|
||||
within Jinja. The template function should return a string or Python objects (lists,
|
||||
dictionaries) with strings inside that Jinja uses in the templating process
|
||||
|
||||
.. note::
|
||||
|
||||
The template function will have access to all of the variables that
|
||||
are passed to the registration function.
|
||||
|
||||
At the end of the registration function, add the template function to the
|
||||
Sphinx application's context with ``context['template_func'] = template_func``.
|
||||
|
||||
Finally, in your extension's ``setup()`` function, add your registration
|
||||
function as a callback for :event:`html-page-context`.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# The registration function
|
||||
def setup_my_func(app, pagename, templatename, context, doctree):
|
||||
# The template function
|
||||
def my_func(mystring):
|
||||
return "Your string is %s" % mystring
|
||||
# Add it to the page's context
|
||||
context['my_func'] = my_func
|
||||
|
||||
# Your extension's setup function
|
||||
def setup(app):
|
||||
app.connect("html-page-context", setup_my_func)
|
||||
|
||||
Now, you will have access to this function in jinja like so:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
<div>
|
||||
{{ my_func("some string") }}
|
||||
</div>
|
||||
|
||||
|
||||
Add your own static files to the build assets
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If you are packaging your own build assets with an extension
|
||||
(e.g., a CSS or JavaScript file), you need to ensure that they are placed
|
||||
in the ``_static/`` folder of HTML outputs. To do so, you may copy them directly
|
||||
into a build's ``_static/`` folder at build time, generally via an event hook.
|
||||
Here is some sample code to accomplish this:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def copy_custom_files(app, exc):
|
||||
if app.builder.format == 'html' and not exc:
|
||||
staticdir = path.join(app.builder.outdir, '_static')
|
||||
copy_asset_file('path/to/myextension/_static/myjsfile.js', staticdir)
|
||||
|
||||
def setup(app):
|
||||
app.connect('builder-inited', copy_custom_files)
|
||||
|
||||
|
||||
Inject JavaScript based on user configuration
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If your extension makes use of JavaScript, it can be useful to allow users
|
||||
to control its behavior using their Sphinx configuration. However, this can
|
||||
be difficult to do if your JavaScript comes in the form of a static library
|
||||
(which will not be built with Jinja).
|
||||
|
||||
There are two ways to inject variables into the JavaScript space based on user
|
||||
configuration.
|
||||
|
||||
First, you may append ``_t`` to the end of any static files included with your
|
||||
extension. This will cause Sphinx to process these files with the templating
|
||||
engine, allowing you to embed variables and control behavior.
|
||||
|
||||
For example, the following JavaScript structure:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
mymodule/
|
||||
├── _static
|
||||
│ └── myjsfile.js_t
|
||||
└── mymodule.py
|
||||
|
||||
Will result in the following static file placed in your HTML's build output:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
_build/
|
||||
└── html
|
||||
└── _static
|
||||
└── myjsfile.js
|
||||
|
||||
See :ref:`theming-static-templates` for more information.
|
||||
|
||||
Second, you may use the :meth:`Sphinx.add_js_file` method without pointing it
|
||||
to a file. Normally, this method is used to insert a new JavaScript file
|
||||
into your site. However, if you do *not* pass a file path, but instead pass
|
||||
a string to the "body" argument, then this text will be inserted as JavaScript
|
||||
into your site's head. This allows you to insert variables into your project's
|
||||
JavaScript from Python.
|
||||
|
||||
For example, the following code will read in a user-configured value and then
|
||||
insert this value as a JavaScript variable, which your extension's JavaScript
|
||||
code may use:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# This function reads in a variable and inserts it into JavaScript
|
||||
def add_js_variable(app):
|
||||
# This is a configuration that you've specified for users in `conf.py`
|
||||
js_variable = app.config['my_javascript_variable']
|
||||
js_text = "var my_variable = '%s';" % js_variable
|
||||
app.add_js_file(None, body=js_text)
|
||||
# We connect this function to the step after the builder is initialized
|
||||
def setup(app):
|
||||
# Tell Sphinx about this configuration variable
|
||||
app.add_config_value('my_javascript_variable')
|
||||
# Run the function after the builder is initialized
|
||||
app.connect('builder-inited', add_js_variable)
|
||||
|
||||
As a result, in your theme you can use code that depends on the presence of
|
||||
this variable. Users can control the variable's value by defining it in their
|
||||
:file:`conf.py` file.
|
||||
|
||||
|
||||
.. [1] It is not an executable Python file, as opposed to :file:`conf.py`,
|
||||
because that would pose an unnecessary security risk if themes are
|
||||
shared.
|
@ -1,8 +1,11 @@
|
||||
.. _extension-tutorials-index:
|
||||
|
||||
Extension tutorials
|
||||
===================
|
||||
|
||||
Refer to the following tutorials to get started with extension development.
|
||||
|
||||
|
||||
.. toctree::
|
||||
:caption: Directive tutorials
|
||||
:maxdepth: 1
|
||||
|
@ -26,6 +26,11 @@ The following is a list of deprecated interfaces.
|
||||
- (will be) Removed
|
||||
- Alternatives
|
||||
|
||||
* - ``sphinx.writers.texinfo.TexinfoWriter.desc``
|
||||
- 3.2
|
||||
- 5.0
|
||||
- ``sphinx.writers.texinfo.TexinfoWriter.descs``
|
||||
|
||||
* - The first argument for
|
||||
``sphinx.ext.autosummary.generate.AutosummaryRenderer`` has been changed
|
||||
to Sphinx object
|
||||
|
@ -3,54 +3,41 @@
|
||||
Developing extensions for Sphinx
|
||||
================================
|
||||
|
||||
Since many projects will need special features in their documentation, Sphinx is
|
||||
designed to be extensible on several levels.
|
||||
Since many projects will need special features in their documentation, Sphinx
|
||||
is designed to be extensible on several levels.
|
||||
|
||||
This is what you can do in an extension: First, you can add new
|
||||
:term:`builder`\s to support new output formats or actions on the parsed
|
||||
documents. Then, it is possible to register custom reStructuredText roles and
|
||||
directives, extending the markup. And finally, there are so-called "hook
|
||||
points" at strategic places throughout the build process, where an extension can
|
||||
register a hook and run specialized code.
|
||||
Here are a few things you can do in an extension:
|
||||
|
||||
An extension is simply a Python module. When an extension is loaded, Sphinx
|
||||
imports this module and executes its ``setup()`` function, which in turn
|
||||
notifies Sphinx of everything the extension offers -- see the extension tutorial
|
||||
for examples.
|
||||
* Add new :term:`builder`\s to support new output formats or actions on the
|
||||
parsed documents.
|
||||
* Register custom reStructuredText roles and directives, extending the markup
|
||||
using the :doc:`markupapi`.
|
||||
* Add custom code to so-called "hook points" at strategic places throughout the
|
||||
build process, allowing you to register a hook and run specialized code.
|
||||
For example, see the :ref:`events`.
|
||||
|
||||
The configuration file itself can be treated as an extension if it contains a
|
||||
``setup()`` function. All other extensions to load must be listed in the
|
||||
:confval:`extensions` configuration value.
|
||||
An extension is simply a Python module with a ``setup()`` function. A user
|
||||
activates the extension by placing the extension's module name
|
||||
(or a sub-module) in their :confval:`extensions` configuration value.
|
||||
|
||||
Discovery of builders by entry point
|
||||
------------------------------------
|
||||
When :program:`sphinx-build` is executed, Sphinx will attempt to import each
|
||||
module that is listed, and execute ``yourmodule.setup(app)``. This
|
||||
function is used to prepare the extension (e.g., by executing Python code),
|
||||
linking resources that Sphinx uses in the build process (like CSS or HTML
|
||||
files), and notifying Sphinx of everything the extension offers (such
|
||||
as directive or role definitions). The ``app`` argument is an instance of
|
||||
:class:`.Sphinx` and gives you control over most aspects of the Sphinx build.
|
||||
|
||||
.. versionadded:: 1.6
|
||||
.. note::
|
||||
|
||||
:term:`builder` extensions can be discovered by means of `entry points`_ so
|
||||
that they do not have to be listed in the :confval:`extensions` configuration
|
||||
value.
|
||||
The configuration file itself can be treated as an extension if it
|
||||
contains a ``setup()`` function. All other extensions to load must be
|
||||
listed in the :confval:`extensions` configuration value.
|
||||
|
||||
Builder extensions should define an entry point in the ``sphinx.builders``
|
||||
group. The name of the entry point needs to match your builder's
|
||||
:attr:`~.Builder.name` attribute, which is the name passed to the
|
||||
:option:`sphinx-build -b` option. The entry point value should equal the
|
||||
dotted name of the extension module. Here is an example of how an entry point
|
||||
for 'mybuilder' can be defined in the extension's ``setup.py``::
|
||||
|
||||
setup(
|
||||
# ...
|
||||
entry_points={
|
||||
'sphinx.builders': [
|
||||
'mybuilder = my.extension.module',
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
Note that it is still necessary to register the builder using
|
||||
:meth:`~.Sphinx.add_builder` in the extension's :func:`setup` function.
|
||||
|
||||
.. _entry points: https://setuptools.readthedocs.io/en/latest/setuptools.html#dynamic-discovery-of-services-and-plugins
|
||||
The rest of this page describes some high-level aspects of developing
|
||||
extensions and various parts of Sphinx's behavior that you can control.
|
||||
For some examples of how extensions can be built and used to control different
|
||||
parts of Sphinx, see the :ref:`extension-tutorials-index`.
|
||||
|
||||
.. _important-objects:
|
||||
|
||||
@ -192,6 +179,11 @@ as metadata of the extension. Metadata keys currently recognized are:
|
||||
APIs used for writing extensions
|
||||
--------------------------------
|
||||
|
||||
These sections provide a more complete description of the tools at your
|
||||
disposal when developing Sphinx extensions. Some are core to Sphinx
|
||||
(such as the :doc:`appapi`) while others trigger specific behavior
|
||||
(such as the :doc:`i18n`)
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
|
159
doc/theming.rst
159
doc/theming.rst
@ -1,159 +0,0 @@
|
||||
.. highlight:: python
|
||||
|
||||
HTML theming support
|
||||
====================
|
||||
|
||||
.. versionadded:: 0.6
|
||||
|
||||
.. note::
|
||||
|
||||
This document provides information about creating your own theme. If you
|
||||
simply wish to use a pre-existing HTML themes, refer to
|
||||
:doc:`/usage/theming`.
|
||||
|
||||
Sphinx supports changing the appearance of its HTML output via *themes*. A
|
||||
theme is a collection of HTML templates, stylesheet(s) and other static files.
|
||||
Additionally, it has a configuration file which specifies from which theme to
|
||||
inherit, which highlighting style to use, and what options exist for customizing
|
||||
the theme's look and feel.
|
||||
|
||||
Themes are meant to be project-unaware, so they can be used for different
|
||||
projects without change.
|
||||
|
||||
|
||||
Creating themes
|
||||
---------------
|
||||
|
||||
Themes take the form of either a directory or a zipfile (whose name is the
|
||||
theme name), containing the following:
|
||||
|
||||
* A :file:`theme.conf` file.
|
||||
* HTML templates, if needed.
|
||||
* A ``static/`` directory containing any static files that will be copied to the
|
||||
output static directory on build. These can be images, styles, script files.
|
||||
|
||||
The :file:`theme.conf` file is in INI format [1]_ (readable by the standard
|
||||
Python :mod:`ConfigParser` module) and has the following structure:
|
||||
|
||||
.. sourcecode:: ini
|
||||
|
||||
[theme]
|
||||
inherit = base theme
|
||||
stylesheet = main CSS name
|
||||
pygments_style = stylename
|
||||
sidebars = localtoc.html, relations.html, sourcelink.html, searchbox.html
|
||||
|
||||
[options]
|
||||
variable = default value
|
||||
|
||||
* The **inherit** setting gives the name of a "base theme", or ``none``. The
|
||||
base theme will be used to locate missing templates (most themes will not have
|
||||
to supply most templates if they use ``basic`` as the base theme), its options
|
||||
will be inherited, and all of its static files will be used as well. If you
|
||||
want to also inherit the stylesheet, include it via CSS' ``@import`` in your
|
||||
own.
|
||||
|
||||
* The **stylesheet** setting gives the name of a CSS file which will be
|
||||
referenced in the HTML header. If you need more than one CSS file, either
|
||||
include one from the other via CSS' ``@import``, or use a custom HTML template
|
||||
that adds ``<link rel="stylesheet">`` tags as necessary. Setting the
|
||||
:confval:`html_style` config value will override this setting.
|
||||
|
||||
* The **pygments_style** setting gives the name of a Pygments style to use for
|
||||
highlighting. This can be overridden by the user in the
|
||||
:confval:`pygments_style` config value.
|
||||
|
||||
* The **pygments_dark_style** setting gives the name of a Pygments style to use
|
||||
for highlighting when the CSS media query ``(prefers-color-scheme: dark)``
|
||||
evaluates to true. It is injected into the page using
|
||||
:meth:`~Sphinx.add_css_file()`.
|
||||
|
||||
* The **sidebars** setting gives the comma separated list of sidebar templates
|
||||
for constructing sidebars. This can be overridden by the user in the
|
||||
:confval:`html_sidebars` config value.
|
||||
|
||||
* The **options** section contains pairs of variable names and default values.
|
||||
These options can be overridden by the user in :confval:`html_theme_options`
|
||||
and are accessible from all templates as ``theme_<name>``.
|
||||
|
||||
.. versionadded:: 1.7
|
||||
sidebar settings
|
||||
|
||||
|
||||
.. _distribute-your-theme:
|
||||
|
||||
Distribute your theme as a Python package
|
||||
-----------------------------------------
|
||||
|
||||
As a way to distribute your theme, you can use Python package. Python package
|
||||
brings to users easy setting up ways.
|
||||
|
||||
To distribute your theme as a Python package, please define an entry point
|
||||
called ``sphinx.html_themes`` in your ``setup.py`` file, and write a ``setup()``
|
||||
function to register your themes using ``add_html_theme()`` API in it::
|
||||
|
||||
# 'setup.py'
|
||||
setup(
|
||||
...
|
||||
entry_points = {
|
||||
'sphinx.html_themes': [
|
||||
'name_of_theme = your_package',
|
||||
]
|
||||
},
|
||||
...
|
||||
)
|
||||
|
||||
# 'your_package.py'
|
||||
from os import path
|
||||
|
||||
def setup(app):
|
||||
app.add_html_theme('name_of_theme', path.abspath(path.dirname(__file__)))
|
||||
|
||||
If your theme package contains two or more themes, please call
|
||||
``add_html_theme()`` twice or more.
|
||||
|
||||
.. versionadded:: 1.2
|
||||
'sphinx_themes' entry_points feature.
|
||||
|
||||
.. deprecated:: 1.6
|
||||
``sphinx_themes`` entry_points has been deprecated.
|
||||
|
||||
.. versionadded:: 1.6
|
||||
``sphinx.html_themes`` entry_points feature.
|
||||
|
||||
|
||||
Templating
|
||||
----------
|
||||
|
||||
The :doc:`guide to templating <templating>` is helpful if you want to write your
|
||||
own templates. What is important to keep in mind is the order in which Sphinx
|
||||
searches for templates:
|
||||
|
||||
* First, in the user's ``templates_path`` directories.
|
||||
* Then, in the selected theme.
|
||||
* Then, in its base theme, its base's base theme, etc.
|
||||
|
||||
When extending a template in the base theme with the same name, use the theme
|
||||
name as an explicit directory: ``{% extends "basic/layout.html" %}``. From a
|
||||
user ``templates_path`` template, you can still use the "exclamation mark"
|
||||
syntax as described in the templating document.
|
||||
|
||||
Static templates
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Since theme options are meant for the user to configure a theme more easily,
|
||||
without having to write a custom stylesheet, it is necessary to be able to
|
||||
template static files as well as HTML files. Therefore, Sphinx supports
|
||||
so-called "static templates", like this:
|
||||
|
||||
If the name of a file in the ``static/`` directory of a theme (or in the user's
|
||||
static path, for that matter) ends with ``_t``, it will be processed by the
|
||||
template engine. The ``_t`` will be left from the final file name. For
|
||||
example, the *classic* theme has a file ``static/classic.css_t`` which uses
|
||||
templating to put the color options into the stylesheet. When a documentation
|
||||
is built with the classic theme, the output directory will contain a
|
||||
``_static/classic.css`` file where all template tags have been processed.
|
||||
|
||||
.. [1] It is not an executable Python file, as opposed to :file:`conf.py`,
|
||||
because that would pose an unnecessary security risk if themes are
|
||||
shared.
|
@ -9,7 +9,14 @@ fields marked up like this::
|
||||
|
||||
:fieldname: Field content
|
||||
|
||||
Sphinx provides custom behavior for bibliographic fields compared to docutils.
|
||||
Sphinx extends standard docutils behavior for field lists and adds some extra
|
||||
functionality that is covered in this section.
|
||||
|
||||
.. note::
|
||||
|
||||
The values of field lists will be parsed as
|
||||
strings. You cannot use Python collections such as lists or dictionaries.
|
||||
|
||||
|
||||
.. _metadata:
|
||||
|
||||
@ -17,11 +24,20 @@ File-wide metadata
|
||||
------------------
|
||||
|
||||
A field list near the top of a file is normally parsed by docutils as the
|
||||
*docinfo* which is generally used to record the author, date of publication and
|
||||
other metadata. However, in Sphinx, a field list preceding any other markup is
|
||||
moved from the *docinfo* to the Sphinx environment as document metadata and is
|
||||
not displayed in the output; a field list appearing after the document title
|
||||
will be part of the *docinfo* as normal and will be displayed in the output.
|
||||
*docinfo* and shown on the page. However, in Sphinx, a field list preceding
|
||||
any other markup is moved from the *docinfo* to the Sphinx environment as
|
||||
document metadata, and is not displayed in the output.
|
||||
|
||||
.. note::
|
||||
|
||||
A field list appearing after the document title *will* be part of the
|
||||
*docinfo* as normal and will be displayed in the output.
|
||||
|
||||
|
||||
Special metadata fields
|
||||
-----------------------
|
||||
|
||||
Sphinx provides custom behavior for bibliographic fields compared to docutils.
|
||||
|
||||
At the moment, these metadata fields are recognized:
|
||||
|
||||
|
@ -2,8 +2,8 @@
|
||||
|
||||
.. _html-themes:
|
||||
|
||||
HTML
|
||||
====
|
||||
HTML Theming
|
||||
============
|
||||
|
||||
Sphinx provides a number of builders for HTML and HTML-based formats.
|
||||
|
||||
@ -21,7 +21,8 @@ Themes
|
||||
.. note::
|
||||
|
||||
This section provides information about using pre-existing HTML themes. If
|
||||
you wish to create your own theme, refer to :doc:`/theming`.
|
||||
you wish to create your own theme, refer to
|
||||
:doc:`/development/theming`.
|
||||
|
||||
Sphinx supports changing the appearance of its HTML output via *themes*. A
|
||||
theme is a collection of HTML templates, stylesheet(s) and other static files.
|
||||
@ -80,7 +81,7 @@ zipfile-based theme::
|
||||
html_theme = "dotted"
|
||||
|
||||
For more information on the design of themes, including information about
|
||||
writing your own themes, refer to :doc:`/theming`.
|
||||
writing your own themes, refer to :doc:`/development/theming`.
|
||||
|
||||
.. _builtin-themes:
|
||||
|
||||
@ -363,6 +364,7 @@ sphinx-themes.org__.
|
||||
.. versionchanged:: 1.4
|
||||
**sphinx_rtd_theme** has become optional.
|
||||
|
||||
|
||||
.. __: https://pypi.org/search/?q=&o=&c=Framework+%3A%3A+Sphinx+%3A%3A+Theme
|
||||
.. __: https://github.com/search?utf8=%E2%9C%93&q=sphinx+theme&type=
|
||||
.. __: https://sphinx-themes.org/
|
||||
|
@ -11,6 +11,7 @@
|
||||
import builtins
|
||||
import inspect
|
||||
import re
|
||||
import sys
|
||||
import typing
|
||||
import warnings
|
||||
from inspect import Parameter
|
||||
@ -134,6 +135,19 @@ def _parse_annotation(annotation: str, env: BuildEnvironment = None) -> List[Nod
|
||||
|
||||
return result
|
||||
else:
|
||||
if sys.version_info >= (3, 6):
|
||||
if isinstance(node, ast.Constant):
|
||||
if node.value is Ellipsis:
|
||||
return [addnodes.desc_sig_punctuation('', "...")]
|
||||
else:
|
||||
return [nodes.Text(node.value)]
|
||||
|
||||
if sys.version_info < (3, 8):
|
||||
if isinstance(node, ast.Ellipsis):
|
||||
return [addnodes.desc_sig_punctuation('', "...")]
|
||||
elif isinstance(node, ast.NameConstant):
|
||||
return [nodes.Text(node.value)]
|
||||
|
||||
raise SyntaxError # unsupported syntax
|
||||
|
||||
if env is None:
|
||||
|
@ -223,6 +223,11 @@ class Cmdoption(ObjectDescription):
|
||||
node_id = make_id(self.env, self.state.document, prefix, optname)
|
||||
signode['ids'].append(node_id)
|
||||
|
||||
old_node_id = self.make_old_id(prefix, optname)
|
||||
if old_node_id not in self.state.document.ids and \
|
||||
old_node_id not in signode['ids']:
|
||||
signode['ids'].append(old_node_id)
|
||||
|
||||
self.state.document.note_explicit_target(signode)
|
||||
|
||||
domain = cast(StandardDomain, self.env.get_domain('std'))
|
||||
@ -239,6 +244,14 @@ class Cmdoption(ObjectDescription):
|
||||
entry = '; '.join([descr, option])
|
||||
self.indexnode['entries'].append(('pair', entry, signode['ids'][0], '', None))
|
||||
|
||||
def make_old_id(self, prefix: str, optname: str) -> str:
|
||||
"""Generate old styled node_id for cmdoption.
|
||||
|
||||
.. note:: Old Styled node_id was used until Sphinx-3.0.
|
||||
This will be removed in Sphinx-5.0.
|
||||
"""
|
||||
return nodes.make_id(prefix + '-' + optname)
|
||||
|
||||
|
||||
class Program(SphinxDirective):
|
||||
"""
|
||||
|
@ -58,17 +58,13 @@ class ImageCollector(EnvironmentCollector):
|
||||
elif imguri.find('://') != -1:
|
||||
candidates['?'] = imguri
|
||||
continue
|
||||
rel_imgpath, full_imgpath = app.env.relfn2path(imguri, docname)
|
||||
if app.config.language:
|
||||
# substitute figures (ex. foo.png -> foo.en.png)
|
||||
i18n_full_imgpath = search_image_for_language(full_imgpath, app.env)
|
||||
if i18n_full_imgpath != full_imgpath:
|
||||
full_imgpath = i18n_full_imgpath
|
||||
rel_imgpath = relative_path(path.join(app.srcdir, 'dummy'),
|
||||
i18n_full_imgpath)
|
||||
# set imgpath as default URI
|
||||
node['uri'] = rel_imgpath
|
||||
if rel_imgpath.endswith(os.extsep + '*'):
|
||||
|
||||
if imguri.endswith(os.extsep + '*'):
|
||||
# Update `node['uri']` to a relative path from srcdir
|
||||
# from a relative path from current document.
|
||||
rel_imgpath, full_imgpath = app.env.relfn2path(imguri, docname)
|
||||
node['uri'] = rel_imgpath
|
||||
|
||||
if app.config.language:
|
||||
# Search language-specific figures at first
|
||||
i18n_imguri = get_image_filename_for_language(imguri, app.env)
|
||||
@ -77,7 +73,15 @@ class ImageCollector(EnvironmentCollector):
|
||||
|
||||
self.collect_candidates(app.env, full_imgpath, candidates, node)
|
||||
else:
|
||||
candidates['*'] = rel_imgpath
|
||||
if app.config.language:
|
||||
# substitute imguri by figure_language_filename
|
||||
# (ex. foo.png -> foo.en.png)
|
||||
imguri = search_image_for_language(imguri, app.env)
|
||||
|
||||
# Update `node['uri']` to a relative path from srcdir
|
||||
# from a relative path from current document.
|
||||
node['uri'], _ = app.env.relfn2path(imguri, docname)
|
||||
candidates['*'] = node['uri']
|
||||
|
||||
# map image paths to unique image names (so that they can be put
|
||||
# into a single directory)
|
||||
|
@ -320,8 +320,8 @@ def search_image_for_language(filename: str, env: "BuildEnvironment") -> str:
|
||||
return filename
|
||||
|
||||
translated = get_image_filename_for_language(filename, env)
|
||||
dirname = path.dirname(env.docname)
|
||||
if path.exists(path.join(env.srcdir, dirname, translated)):
|
||||
_, abspath = env.relfn2path(translated)
|
||||
if path.exists(abspath):
|
||||
return translated
|
||||
else:
|
||||
return filename
|
||||
|
@ -497,19 +497,26 @@ def signature(subject: Callable, bound_method: bool = False, follow_wrapped: boo
|
||||
def evaluate_signature(sig: inspect.Signature, globalns: Dict = None, localns: Dict = None
|
||||
) -> inspect.Signature:
|
||||
"""Evaluate unresolved type annotations in a signature object."""
|
||||
def evaluate_forwardref(ref: ForwardRef, globalns: Dict, localns: Dict) -> Any:
|
||||
"""Evaluate a forward reference."""
|
||||
if sys.version_info > (3, 9):
|
||||
return ref._evaluate(globalns, localns, frozenset())
|
||||
else:
|
||||
return ref._evaluate(globalns, localns)
|
||||
|
||||
def evaluate(annotation: Any, globalns: Dict, localns: Dict) -> Any:
|
||||
"""Evaluate unresolved type annotation."""
|
||||
try:
|
||||
if isinstance(annotation, str):
|
||||
ref = ForwardRef(annotation, True)
|
||||
annotation = ref._evaluate(globalns, localns)
|
||||
annotation = evaluate_forwardref(ref, globalns, localns)
|
||||
|
||||
if isinstance(annotation, ForwardRef):
|
||||
annotation = annotation._evaluate(globalns, localns)
|
||||
annotation = evaluate_forwardref(ref, globalns, localns)
|
||||
elif isinstance(annotation, str):
|
||||
# might be a ForwardRef'ed annotation in overloaded functions
|
||||
ref = ForwardRef(annotation, True)
|
||||
annotation = ref._evaluate(globalns, localns)
|
||||
annotation = evaluate_forwardref(ref, globalns, localns)
|
||||
except (NameError, TypeError):
|
||||
# failed to evaluate type. skipped.
|
||||
pass
|
||||
|
@ -10,14 +10,16 @@
|
||||
|
||||
import re
|
||||
import textwrap
|
||||
import warnings
|
||||
from os import path
|
||||
from typing import Any, Dict, Iterable, Iterator, List, Pattern, Set, Tuple, Union
|
||||
from typing import Any, Dict, Iterable, Iterator, List, Optional, Pattern, Set, Tuple, Union
|
||||
from typing import cast
|
||||
|
||||
from docutils import nodes, writers
|
||||
from docutils.nodes import Element, Node, Text
|
||||
|
||||
from sphinx import addnodes, __display_version__
|
||||
from sphinx.deprecation import RemovedInSphinx50Warning
|
||||
from sphinx.domains import IndexEntry
|
||||
from sphinx.domains.index import IndexDomain
|
||||
from sphinx.errors import ExtensionError
|
||||
@ -189,6 +191,7 @@ class TexinfoTranslator(SphinxTranslator):
|
||||
|
||||
self.body = [] # type: List[str]
|
||||
self.context = [] # type: List[str]
|
||||
self.descs = [] # type: List[addnodes.desc]
|
||||
self.previous_section = None # type: nodes.section
|
||||
self.section_level = 0
|
||||
self.seen_title = False
|
||||
@ -1365,12 +1368,12 @@ class TexinfoTranslator(SphinxTranslator):
|
||||
|
||||
# -- Desc
|
||||
|
||||
def visit_desc(self, node: Element) -> None:
|
||||
self.desc = node
|
||||
def visit_desc(self, node: addnodes.desc) -> None:
|
||||
self.descs.append(node)
|
||||
self.at_deffnx = '@deffn'
|
||||
|
||||
def depart_desc(self, node: Element) -> None:
|
||||
self.desc = None
|
||||
def depart_desc(self, node: addnodes.desc) -> None:
|
||||
self.descs.pop()
|
||||
self.ensure_eol()
|
||||
self.body.append('@end deffn\n')
|
||||
|
||||
@ -1399,6 +1402,12 @@ class TexinfoTranslator(SphinxTranslator):
|
||||
self.escape_hyphens -= 1
|
||||
self.desc_type_name = None
|
||||
|
||||
def visit_desc_signature_line(self, node: Element) -> None:
|
||||
pass
|
||||
|
||||
def depart_desc_signature_line(self, node: Element) -> None:
|
||||
pass
|
||||
|
||||
def visit_desc_name(self, node: Element) -> None:
|
||||
pass
|
||||
|
||||
@ -1454,9 +1463,8 @@ class TexinfoTranslator(SphinxTranslator):
|
||||
# -- instead of --
|
||||
# @deffn {Class} class Foo
|
||||
txt = node.astext().strip()
|
||||
if txt == self.desc['desctype'] or \
|
||||
txt == self.desc['objtype'] or \
|
||||
txt in self.desc_type_name.split():
|
||||
if ((self.descs and txt == self.descs[-1]['objtype']) or
|
||||
(self.desc_type_name and txt in self.desc_type_name.split())):
|
||||
raise nodes.SkipNode
|
||||
|
||||
def depart_desc_annotation(self, node: Element) -> None:
|
||||
@ -1526,3 +1534,11 @@ class TexinfoTranslator(SphinxTranslator):
|
||||
self.body.append('\n\n@example\n%s\n@end example\n\n' %
|
||||
self.escape_arg(node.astext()))
|
||||
raise nodes.SkipNode
|
||||
|
||||
@property
|
||||
def desc(self) -> Optional[addnodes.desc]:
|
||||
warnings.warn('TexinfoWriter.desc is deprecated.', RemovedInSphinx50Warning)
|
||||
if len(self.descs):
|
||||
return self.descs[-1]
|
||||
else:
|
||||
return None
|
||||
|
@ -262,6 +262,14 @@ def test_parse_annotation(app):
|
||||
[desc_sig_punctuation, ")"],
|
||||
[desc_sig_punctuation, "]"]))
|
||||
|
||||
doctree = _parse_annotation("Tuple[int, ...]", app.env)
|
||||
assert_node(doctree, ([pending_xref, "Tuple"],
|
||||
[desc_sig_punctuation, "["],
|
||||
[pending_xref, "int"],
|
||||
[desc_sig_punctuation, ", "],
|
||||
[desc_sig_punctuation, "..."],
|
||||
[desc_sig_punctuation, "]"]))
|
||||
|
||||
doctree = _parse_annotation("Callable[[int, int], int]", app.env)
|
||||
assert_node(doctree, ([pending_xref, "Callable"],
|
||||
[desc_sig_punctuation, "["],
|
||||
@ -274,6 +282,12 @@ def test_parse_annotation(app):
|
||||
[pending_xref, "int"],
|
||||
[desc_sig_punctuation, "]"]))
|
||||
|
||||
doctree = _parse_annotation("List[None]", app.env)
|
||||
assert_node(doctree, ([pending_xref, "List"],
|
||||
[desc_sig_punctuation, "["],
|
||||
[pending_xref, "None"],
|
||||
[desc_sig_punctuation, "]"]))
|
||||
|
||||
# None type makes an object-reference (not a class reference)
|
||||
doctree = _parse_annotation("None", app.env)
|
||||
assert_node(doctree, ([pending_xref, "None"],))
|
||||
|
Loading…
Reference in New Issue
Block a user