diff --git a/doc/contents.rst b/doc/contents.rst index 17a3d4b5440..eb694629244 100644 --- a/doc/contents.rst +++ b/doc/contents.rst @@ -10,7 +10,6 @@ Sphinx documentation contents development/index man/index - theming templating latex extdev/index diff --git a/doc/development/tutorials/builders.rst b/doc/development/tutorials/builders.rst new file mode 100644 index 00000000000..c745113c562 --- /dev/null +++ b/doc/development/tutorials/builders.rst @@ -0,0 +1,32 @@ +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``:: + + 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 diff --git a/doc/development/tutorials/index.rst b/doc/development/tutorials/index.rst index a79e6a8b66f..9acfc6c76d7 100644 --- a/doc/development/tutorials/index.rst +++ b/doc/development/tutorials/index.rst @@ -1,8 +1,16 @@ +.. _extension-tutorials-index: + Extension tutorials =================== Refer to the following tutorials to get started with extension development. +.. toctree:: + :caption: General extension tutorials + + overview + builders + .. toctree:: :caption: Directive tutorials :maxdepth: 1 @@ -10,3 +18,9 @@ Refer to the following tutorials to get started with extension development. helloworld todo recipe + +.. toctree:: + :caption: Theming + :maxdepth: 1 + + theming-dev diff --git a/doc/development/tutorials/overview.rst b/doc/development/tutorials/overview.rst new file mode 100644 index 00000000000..8ffb8df42a8 --- /dev/null +++ b/doc/development/tutorials/overview.rst @@ -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. diff --git a/doc/theming.rst b/doc/development/tutorials/theming-dev.rst similarity index 52% rename from doc/theming.rst rename to doc/development/tutorials/theming-dev.rst index 6a154affdfe..4b396f0207a 100644 --- a/doc/theming.rst +++ b/doc/development/tutorials/theming-dev.rst @@ -1,7 +1,5 @@ -.. highlight:: python - -HTML theming support -==================== +HTML theme development +====================== .. versionadded:: 0.6 @@ -20,6 +18,11 @@ 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 --------------- @@ -125,7 +128,7 @@ If your theme package contains two or more themes, please call Templating ---------- -The :doc:`guide to templating ` is helpful if you want to write your +The :doc:`guide to 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: @@ -138,6 +141,9 @@ 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 ~~~~~~~~~~~~~~~~ @@ -154,6 +160,137 @@ 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 ` +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 + +
+ {{ my_func("some string") }} +
+ + +Inject javsacript 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. 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. diff --git a/doc/extdev/index.rst b/doc/extdev/index.rst index 266da52b73a..ad04951f385 100644 --- a/doc/extdev/index.rst +++ b/doc/extdev/index.rst @@ -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. - -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. - -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. - -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. - -Discovery of 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``:: - - 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 +Since many projects will need special features in their documentation, Sphinx +is designed to be extensible on several levels. + +Here are a few things you can do in an extension: + +* 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`. + +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. + +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. + +.. note:: + + 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. + +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 diff --git a/doc/usage/restructuredtext/field-lists.rst b/doc/usage/restructuredtext/field-lists.rst index 28b3cfe1bbb..d6d90caf78f 100644 --- a/doc/usage/restructuredtext/field-lists.rst +++ b/doc/usage/restructuredtext/field-lists.rst @@ -9,7 +9,17 @@ fields marked up like this:: :fieldname: Field content -Sphinx provides custom behavior for bibliographic fields compared to docutils. +Field lists are :duref:`originally defined in docutils ` +to show information about a page (such as the document author or date of +publication). + +.. note:: + + The values of field lists will be parsed as + strings. You cannot use Python collections such as lists or dictionaries. + +Sphinx treats field lists slightly differently, as explained +below. .. _metadata: @@ -17,11 +27,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: diff --git a/doc/usage/theming.rst b/doc/usage/theming.rst index 5474e9620d7..9a5d46fbfeb 100644 --- a/doc/usage/theming.rst +++ b/doc/usage/theming.rst @@ -2,8 +2,8 @@ .. _html-themes: -HTML -==== +HTML Theming +============ Sphinx provides a number of builders for HTML and HTML-based formats. @@ -21,7 +21,7 @@ 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/tutorials/theming-dev`. 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 +80,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/tutorials/theming-dev`. .. _builtin-themes: @@ -363,6 +363,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/