Merge branch 'master' into feature-autosummary-packages

This commit is contained in:
woutdenolf 2019-04-07 10:32:57 +02:00 committed by GitHub
commit 4004dd5635
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
366 changed files with 58277 additions and 51894 deletions

View File

@ -6,6 +6,6 @@ jobs:
working_directory: /sphinx
steps:
- checkout
- run: /python3.5/bin/pip install -U pip setuptools
- run: /python3.5/bin/pip install -U .[test,websupport]
- run: make test PYTHON=/python3.5/bin/python
- run: /python3.6/bin/pip install -U pip setuptools
- run: /python3.6/bin/pip install -U .[test,websupport]
- run: make test PYTHON=/python3.6/bin/python

View File

@ -15,7 +15,7 @@ Steps to reproduce the behavior:
```
<Paste your command-line here which cause the problem>
$ git clone htps://github.com/.../some_project
$ git clone https://github.com/.../some_project
$ cd some_project
$ pip install -r requirements.txt
$ cd docs

View File

@ -37,6 +37,7 @@ matrix:
services: xvfb
install:
- "sudo apt-get install graphviz"
- if [ $IS_PYTHON = true ]; then pip install -U tox codecov; fi
- if [ $IS_PYTHON = false ]; then npm install; fi

180
CHANGES
View File

@ -1,3 +1,26 @@
Release 3.0.0 (in development)
==============================
Dependencies
------------
Incompatible changes
--------------------
* Drop features and APIs deprecated in 1.8.x
Deprecated
----------
Features added
--------------
Bugs fixed
----------
Testing
--------
Release 2.1.0 (in development)
==============================
@ -7,6 +30,67 @@ Dependencies
Incompatible changes
--------------------
* Ignore filenames without file extension given to ``Builder.build_specific()``
API directly
Deprecated
----------
* ``sphinx.builders.latex.LaTeXBuilder.apply_transforms()``
* ``sphinx.directives.Acks``
* ``sphinx.directives.Author``
* ``sphinx.directives.Centered``
* ``sphinx.directives.Class``
* ``sphinx.directives.CodeBlock``
* ``sphinx.directives.Figure``
* ``sphinx.directives.HList``
* ``sphinx.directives.Highlight``
* ``sphinx.directives.Include``
* ``sphinx.directives.Index``
* ``sphinx.directives.LiteralInclude``
* ``sphinx.directives.Meta``
* ``sphinx.directives.Only``
* ``sphinx.directives.SeeAlso``
* ``sphinx.directives.TabularColumns``
* ``sphinx.directives.TocTree``
* ``sphinx.directives.VersionChange``
* ``sphinx.environment.NoUri``
* ``sphinx.ext.autodoc.importer.MockFinder``
* ``sphinx.ext.autodoc.importer.MockLoader``
* ``sphinx.ext.autodoc.importer.mock()``
* ``sphinx.ext.autosummary.autolink_role()``
* ``sphinx.util.docfields.DocFieldTransformer.preprocess_fieldtypes()``
* ``sphinx.util.node.find_source_node()``
* ``sphinx.util.i18n.find_catalog()``
* ``sphinx.util.i18n.find_catalog_files()``
* ``sphinx.util.i18n.find_catalog_source_files()``
For more details, see :ref:`deprecation APIs list <dev-deprecated-apis>`.
Features added
--------------
* Add a helper class ``sphinx.transforms.post_transforms.SphinxPostTransform``
* Add a helper method ``SphinxDirective.set_source_info()``
* #6180: Support ``--keep-going`` with BuildDoc setup command
* ``math`` directive now supports ``:class:`` option
* todo: ``todo`` directive now supports ``:name:`` option
Bugs fixed
----------
Testing
--------
Release 2.0.1 (in development)
==============================
Dependencies
------------
Incompatible changes
--------------------
Deprecated
----------
@ -16,36 +100,19 @@ Features added
Bugs fixed
----------
Testing
--------
Release 2.0.0 beta2 (in development)
====================================
Dependencies
------------
Incompatible changes
--------------------
Deprecated
----------
Features added
--------------
Bugs fixed
----------
* LaTeX: some system labels are not translated
Testing
--------
Release 2.0.0 beta1 (in development)
====================================
Release 2.0.0 (released Mar 29, 2019)
=====================================
Dependencies
------------
2.0.0b1
* LaTeX builder now depends on TeX Live 2015 or above.
* LaTeX builder (with ``'pdflatex'`` :confval:`latex_engine`) will process
Unicode Greek letters in text (not in math mark-up) via the text font and
@ -73,8 +140,11 @@ Dependencies
Incompatible changes
--------------------
2.0.0b1
* Drop python 2.7 and 3.4 support
* Drop docutils 0.11 support
* Drop features and APIs deprecated in 1.7.x
* The default setting for :confval:`master_doc` is changed to ``'index'`` which
has been longly used as default of sphinx-quickstart.
* LaTeX: Move message resources to ``sphinxmessage.sty``
@ -115,9 +185,15 @@ Incompatible changes
* #4550: All tables and figures without ``align`` option are displayed to center
* #4587: html: Output HTML5 by default
2.0.0b2
* texinfo: image files are copied into ``name-figure`` directory
Deprecated
----------
2.0.0b1
* Support for evaluating Python 2 syntax is deprecated. This includes
configuration files which should be converted to Python 3.
* The arguments of ``EpubBuilder.build_mimetype()``,
@ -205,6 +281,8 @@ For more details, see :ref:`deprecation APIs list <dev-deprecated-apis>`.
Features added
--------------
2.0.0b1
* #1618: The search results preview of generated HTML documentation is
reader-friendlier: instead of showing the snippets as raw reStructuredText
markup, Sphinx now renders the corresponding HTML. This means the Sphinx
@ -254,6 +332,8 @@ Features added
Bugs fixed
----------
2.0.0b1
* #1682: LaTeX: writer should not translate Greek unicode, but use textgreek
package
* #5247: LaTeX: PDF does not build with default font config for Russian
@ -275,25 +355,44 @@ Bugs fixed
* HTML search: search always returns nothing when multiple search terms are
used and one term is shorter than three characters
2.0.0b2
* #6096: html: Anchor links are not added to figures
* #3620: html: Defer searchindex.js rather than loading it via ajax
* #6113: html: Table cells and list items have large margins
* #5508: ``linenothreshold`` option for ``highlight`` directive was ignored
* texinfo: ``make install-info`` causes syntax error
* texinfo: ``make install-info`` fails on macOS
* #3079: texinfo: image files are not copied on ``make install-info``
* #5391: A cross reference in heading is rendered as literal
* #5946: C++, fix ``cpp:alias`` problems in LaTeX (and singlehtml)
* #6147: classes attribute of ``citation_reference`` node is lost
* AssertionError is raised when custom ``citation_reference`` node having
classes attribute refers missing citation (refs: #6147)
* #2155: Support ``code`` directive
* C++, fix parsing of braced initializers.
* #6172: AttributeError is raised for old styled index nodes
* #4872: inheritance_diagram: correctly describe behavior of ``parts`` option in
docs, allow negative values.
* #6178: i18n: Captions missing in translations for hidden TOCs
2.0.0 final
* #6196: py domain: unexpected prefix is generated
Testing
--------
2.0.0b1
* Stop to use ``SPHINX_TEST_TEMPDIR`` envvar
Release 1.8.5 (in development)
==============================
2.0.0b2
Dependencies
------------
* Add a helper function: ``sphinx.testing.restructuredtext.parse()``
Incompatible changes
--------------------
Deprecated
----------
Features added
--------------
Release 1.8.5 (released Mar 10, 2019)
=====================================
Bugs fixed
----------
@ -301,11 +400,18 @@ Bugs fixed
* LaTeX: Remove extraneous space after author names on PDF title page (refs: #6004)
* #6026: LaTeX: A cross reference to definition list does not work
* #6046: LaTeX: ``TypeError`` is raised when invalid latex_elements given
* #6067: LaTeX: images having a target are concatenated to next line
* #6067: LaTeX: images having a target are not aligned even if specified
* #6149: LaTeX: ``:index:`` role in titles causes ``Use of \@icentercr doesn't
match its definition`` error on latexpdf build
* #6019: imgconverter: Including multipage PDF fails
* #6047: autodoc: ``autofunction`` emits a warning for method objects
Testing
--------
* #6028: graphviz: Ensure the graphviz filenames are reproducible
* #6068: doctest: ``skipif`` option may remove the code block from documentation
* #6136: ``:name:`` option for ``math`` directive causes a crash
* #6139: intersphinx: ValueError on failure reporting
* #6135: changes: Fix UnboundLocalError when any module found
* #3859: manpage: code-block captions are not displayed correctly
Release 1.8.4 (released Feb 03, 2019)
=====================================

View File

@ -206,6 +206,7 @@ Documentation using sphinx_rtd_theme
* `Jupyter Notebook <https://jupyter-notebook.readthedocs.io/>`__
* `Lasagne <https://lasagne.readthedocs.io/>`__
* `latexindent.pl <https://latexindentpl.readthedocs.io/>`__
* `Learning Apache Spark with Python <https://runawayhorse001.github.io/LearningApacheSpark>`__
* `Linguistica <https://linguistica-uchicago.github.io/lxa5/>`__
* `Linux kernel <https://www.kernel.org/doc/html/latest/index.html>`__
* `MathJax <https://docs.mathjax.org/>`__
@ -240,6 +241,7 @@ Documentation using sphinx_rtd_theme
* `Releases Sphinx extension <https://releases.readthedocs.io/>`__
* `Qtile <http://docs.qtile.org/>`__
* `Quex <http://quex.sourceforge.net/doc/html/main.html>`__
* `QuTiP <http://qutip.org/docs/latest/>`__
* `Satchmo <http://docs.satchmoproject.com/>`__
* `Scapy <https://scapy.readthedocs.io/>`__
* `SimGrid <http://simgrid.gforge.inria.fr/simgrid/latest/doc/>`__
@ -251,6 +253,7 @@ Documentation using sphinx_rtd_theme
* `Sphinx AutoAPI <https://sphinx-autoapi.readthedocs.io/>`__
* `sphinx-argparse <https://sphinx-argparse.readthedocs.io/>`__
* `Sphinx-Gallery <https://sphinx-gallery.readthedocs.io/>`__ (customized)
* `Sphinx with Github Webpages <https://runawayhorse001.github.io/SphinxGithub>`__
* `SpotBugs <https://spotbugs.readthedocs.io/>`__
* `StarUML <https://docs.staruml.io/>`__
* `Sublime Text Unofficial Documentation <http://docs.sublimetext.info/>`__

View File

@ -14,6 +14,13 @@ variable_end_string = %>
block_start_string = <%
block_end_string = %>
# Extraction from Jinja2 template files
[jinja2: **/templates/latex/**.sty_t]
variable_start_string = <%=
variable_end_string = %>
block_start_string = <%
block_end_string = %>
# Extraction from Jinja2 HTML templates
[jinja2: **/themes/**.html]
encoding = utf-8

View File

@ -7,7 +7,7 @@ import sphinx
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.todo',
'sphinx.ext.autosummary', 'sphinx.ext.extlinks',
'sphinx.ext.viewcode']
'sphinx.ext.viewcode', 'sphinx.ext.inheritance_diagram']
master_doc = 'contents'
templates_path = ['_templates']
@ -48,7 +48,7 @@ epub_fix_images = False
epub_max_image_width = 0
epub_show_urls = 'inline'
epub_use_index = False
epub_guide = (('toc', 'contents.xhtml', u'Table of Contents'),)
epub_guide = (('toc', 'contents.xhtml', 'Table of Contents'),)
epub_description = 'Sphinx documentation generator system manual'
latex_documents = [('contents', 'sphinx.tex', 'Sphinx Documentation',

View File

@ -0,0 +1,11 @@
:orphan:
Tutorial examples
=================
This directory contains a number of examples used in the tutorials. These are
intended to be increasingly complex to demonstrate the various features of
Sphinx, but should aim to be as complicated as necessary but no more.
Individual sections are referenced by line numbers, meaning if you make changes
to the source files, you should update the references in the documentation
accordingly.

View File

@ -0,0 +1,19 @@
from docutils import nodes
from docutils.parsers.rst import Directive
class HelloWorld(Directive):
def run(self):
paragraph_node = nodes.paragraph(text='Hello World!')
return [paragraph_node]
def setup(app):
app.add_directive("helloworld", HelloWorld)
return {
'version': '0.1',
'parallel_read_safe': True,
'parallel_write_safe': True,
}

View File

@ -0,0 +1,161 @@
from collections import defaultdict
from docutils.parsers.rst import directives
from sphinx import addnodes
from sphinx.directives import ObjectDescription
from sphinx.domains import Domain
from sphinx.domains import Index
from sphinx.roles import XRefRole
from sphinx.util.nodes import make_refnode
class RecipeDirective(ObjectDescription):
"""A custom directive that describes a recipe."""
has_content = True
required_arguments = 1
option_spec = {
'contains': directives.unchanged_required,
}
def handle_signature(self, sig, signode):
signode += addnodes.desc_name(text=sig)
return sig
def add_target_and_index(self, name_cls, sig, signode):
signode['ids'].append('recipe' + '-' + sig)
if 'noindex' not in self.options:
ingredients = [
x.strip() for x in self.options.get('contains').split(',')]
recipes = self.env.get_domain('recipe')
recipes.add_recipe(sig, ingredients)
class IngredientIndex(Index):
"""A custom index that creates an ingredient matrix."""
name = 'ingredient'
localname = 'Ingredient Index'
shortname = 'Ingredient'
def generate(self, docnames=None):
content = defaultdict(list)
recipes = {name: (dispname, typ, docname, anchor)
for name, dispname, typ, docname, anchor, _
in self.domain.get_objects()}
recipe_ingredients = self.domain.data['recipe_ingredients']
ingredient_recipes = defaultdict(list)
# flip from recipe_ingredients to ingredient_recipes
for recipe_name, ingredients in recipe_ingredients.items():
for ingredient in ingredients:
ingredient_recipes[ingredient].append(recipe_name)
# convert the mapping of ingredient to recipes to produce the expected
# output, shown below, using the ingredient name as a key to group
#
# name, subtype, docname, anchor, extra, qualifier, description
for ingredient, recipe_names in ingredient_recipes.items():
for recipe_name in recipe_names:
dispname, typ, docname, anchor = recipes[recipe_name]
content[ingredient].append(
(dispname, 0, docname, anchor, docname, '', typ))
# convert the dict to the sorted list of tuples expected
content = sorted(content.items())
return content, True
class RecipeIndex(Index):
"""A custom index that creates an recipe matrix."""
name = 'recipe'
localname = 'Recipe Index'
shortname = 'Recipe'
def generate(self, docnames=None):
content = defaultdict(list)
# sort the list of recipes in alphabetical order
recipes = self.domain.get_objects()
recipes = sorted(recipes, key=lambda recipe: recipe[0])
# generate the expected output, shown below, from the above using the
# first letter of the recipe as a key to group thing
#
# name, subtype, docname, anchor, extra, qualifier, description
for name, dispname, typ, docname, anchor, _ in recipes:
content[dispname[0].lower()].append(
(dispname, 0, docname, anchor, docname, '', typ))
# convert the dict to the sorted list of tuples expected
content = sorted(content.items())
return content, True
class RecipeDomain(Domain):
name = 'recipe'
label = 'Recipe Sample'
roles = {
'ref': XRefRole()
}
directives = {
'recipe': RecipeDirective,
}
indices = {
RecipeIndex,
IngredientIndex
}
initial_data = {
'recipes': [], # object list
'recipe_ingredients': {}, # name -> object
}
def get_full_qualified_name(self, node):
return '{}.{}'.format('recipe', node.arguments[0])
def get_objects(self):
for obj in self.data['recipes']:
yield(obj)
def resolve_xref(self, env, fromdocname, builder, typ, target, node,
contnode):
match = [(docname, anchor)
for name, sig, typ, docname, anchor, prio
in self.get_objects() if sig == target]
if len(match) > 0:
todocname = match[0][0]
targ = match[0][1]
return make_refnode(builder, fromdocname, todocname, targ,
contnode, targ)
else:
print('Awww, found nothing')
return None
def add_recipe(self, signature, ingredients):
"""Add a new recipe to the domain."""
name = '{}.{}'.format('recipe', signature)
anchor = 'recipe-{}'.format(signature)
self.data['recipe_ingredients'][name] = ingredients
# name, dispname, type, docname, anchor, priority
self.data['recipes'].append(
(name, signature, 'Recipe', self.env.docname, anchor, 0))
def setup(app):
app.add_domain(RecipeDomain)
return {
'version': '0.1',
'parallel_read_safe': True,
'parallel_write_safe': True,
}

View File

@ -0,0 +1,124 @@
from docutils import nodes
from docutils.parsers.rst import Directive
from sphinx.locale import _
from sphinx.util.docutils import SphinxDirective
class todo(nodes.Admonition, nodes.Element):
pass
class todolist(nodes.General, nodes.Element):
pass
def visit_todo_node(self, node):
self.visit_admonition(node)
def depart_todo_node(self, node):
self.depart_admonition(node)
class TodolistDirective(Directive):
def run(self):
return [todolist('')]
class TodoDirective(SphinxDirective):
# this enables content in the directive
has_content = True
def run(self):
targetid = 'todo-%d' % self.env.new_serialno('todo')
targetnode = nodes.target('', '', ids=[targetid])
todo_node = todo('\n'.join(self.content))
todo_node += nodes.title(_('Todo'), _('Todo'))
self.state.nested_parse(self.content, self.content_offset, todo_node)
if not hasattr(self.env, 'todo_all_todos'):
self.env.todo_all_todos = []
self.env.todo_all_todos.append({
'docname': self.env.docname,
'lineno': self.lineno,
'todo': todo_node.deepcopy(),
'target': targetnode,
})
return [targetnode, todo_node]
def purge_todos(app, env, docname):
if not hasattr(env, 'todo_all_todos'):
return
env.todo_all_todos = [todo for todo in env.todo_all_todos
if todo['docname'] != docname]
def process_todo_nodes(app, doctree, fromdocname):
if not app.config.todo_include_todos:
for node in doctree.traverse(todo):
node.parent.remove(node)
# Replace all todolist nodes with a list of the collected todos.
# Augment each todo with a backlink to the original location.
env = app.builder.env
for node in doctree.traverse(todolist):
if not app.config.todo_include_todos:
node.replace_self([])
continue
content = []
for todo_info in env.todo_all_todos:
para = nodes.paragraph()
filename = env.doc2path(todo_info['docname'], base=None)
description = (
_('(The original entry is located in %s, line %d and can be found ') %
(filename, todo_info['lineno']))
para += nodes.Text(description, description)
# Create a reference
newnode = nodes.reference('', '')
innernode = nodes.emphasis(_('here'), _('here'))
newnode['refdocname'] = todo_info['docname']
newnode['refuri'] = app.builder.get_relative_uri(
fromdocname, todo_info['docname'])
newnode['refuri'] += '#' + todo_info['target']['refid']
newnode.append(innernode)
para += newnode
para += nodes.Text('.)', '.)')
# Insert into the todolist
content.append(todo_info['todo'])
content.append(para)
node.replace_self(content)
def setup(app):
app.add_config_value('todo_include_todos', False, 'html')
app.add_node(todolist)
app.add_node(todo,
html=(visit_todo_node, depart_todo_node),
latex=(visit_todo_node, depart_todo_node),
text=(visit_todo_node, depart_todo_node))
app.add_directive('todo', TodoDirective)
app.add_directive('todolist', TodolistDirective)
app.connect('doctree-resolved', process_todo_nodes)
app.connect('env-purge-doc', purge_todos)
return {
'version': '0.1',
'parallel_read_safe': True,
'parallel_write_safe': True,
}

View File

@ -1,85 +1,89 @@
Developing a "Hello world" directive
Developing a "Hello world" extension
====================================
The objective of this tutorial is to create a very basic extension that adds a new
directive that outputs a paragraph containing `hello world`.
The objective of this tutorial is to create a very basic extension that adds a
new directive. This directive will output a paragraph containing "hello world".
Only basic information is provided in this tutorial. For more information,
refer to the :doc:`other tutorials <index>` that go into more
details.
Only basic information is provided in this tutorial. For more information, refer
to the :doc:`other tutorials <index>` that go into more details.
.. warning:: For this extension, you will need some basic understanding of docutils_
.. warning::
For this extension, you will need some basic understanding of docutils_
and Python.
Creating a new extension file
-----------------------------
Your extension file could be in any folder of your project. In our case,
let's do the following:
Overview
--------
#. Create an :file:`_ext` folder in :file:`source`.
We want the extension to add the following to Sphinx:
* A ``helloworld`` directive, that will simply output the text "hello world".
Prerequisites
-------------
We will not be distributing this plugin via `PyPI`_ and will instead include it
as part of an existing project. This means you will need to use an existing
project or create a new one using :program:`sphinx-quickstart`.
We assume you are using separate source (:file:`source`) and build
(:file:`build`) folders. Your extension file could be in any folder of your
project. In our case, let's do the following:
#. Create an :file:`_ext` folder in :file:`source`
#. Create a new Python file in the :file:`_ext` folder called
:file:`helloworld.py`.
:file:`helloworld.py`
Here is an example of the folder structure you might obtain:
Here is an example of the folder structure you might obtain:
.. code-block:: text
.. code-block:: text
└── source
   ├── _ext
  └── helloworld.py
   ├── _static
   ├── conf.py
   ├── somefolder
   ├── index.rst
   ├── somefile.rst
   └── someotherfile.rst
└── source
   ├── _ext
  └── helloworld.py
   ├── _static
   ├── _themes
   ├── conf.py
   ├── somefolder
   ├── somefile.rst
   └── someotherfile.rst
Writing the extension
---------------------
Open :file:`helloworld.py` and paste the following code in it:
.. code-block:: python
.. literalinclude:: examples/helloworld.py
:language: python
:linenos:
from docutils import nodes
from docutils.parsers.rst import Directive
Some essential things are happening in this example, and you will see them for
all directives.
.. rubric:: The directive class
class HelloWorld(Directive):
def run(self):
paragraph_node = nodes.paragraph(text='Hello World!')
return [paragraph_node]
Our new directive is declared in the ``HelloWorld`` class.
.. literalinclude:: examples/helloworld.py
:language: python
:linenos:
:lines: 5-9
def setup(app):
app.add_directive("helloworld", HelloWorld)
Some essential things are happening in this example, and you will see them
in all directives:
.. rubric:: Directive declaration
Our new directive is declared in the ``HelloWorld`` class, it extends
docutils_' ``Directive`` class. All extensions that create directives
should extend this class.
.. rubric:: ``run`` method
This method is a requirement and it is part of every directive. It contains
the main logic of the directive and it returns a list of docutils nodes to
be processed by Sphinx.
This class extends the docutils_' ``Directive`` class. All extensions that
create directives should extend this class.
.. seealso::
:doc:`todo`
`The docutils documentation on creating directives <docutils directives>`_
.. rubric:: docutils nodes
The ``run`` method returns a list of nodes. Nodes are docutils' way of
representing the content of a document. There are many types of nodes
available: text, paragraph, reference, table, etc.
This class contains a ``run`` method. This method is a requirement and it is
part of every directive. It contains the main logic of the directive and it
returns a list of docutils nodes to be processed by Sphinx. These nodes are
docutils' way of representing the content of a document. There are many types of
nodes available: text, paragraph, reference, table, etc.
.. seealso::
@ -89,74 +93,97 @@ The ``nodes.paragraph`` class creates a new paragraph node. A paragraph
node typically contains some text that we can set during instantiation using
the ``text`` parameter.
.. rubric:: ``setup`` function
.. rubric:: The ``setup`` function
.. currentmodule:: sphinx.application
This function is a requirement. We use it to plug our new directive into
Sphinx.
The simplest thing you can do it call the ``app.add_directive`` method.
.. note::
.. literalinclude:: examples/helloworld.py
:language: python
:linenos:
:lines: 12-
The first argument is the name of the directive itself as used in an rST file.
The simplest thing you can do it call the :meth:`~Sphinx.add_directive` method,
which is what we've done here. For this particular call, the first argument is
the name of the directive itself as used in a reST file. In this case, we would
use ``helloworld``. For example:
In our case, we would use ``helloworld``:
.. code-block:: rst
.. code-block:: rst
Some intro text here...
Some intro text here...
.. helloworld::
.. helloworld::
Some more text here...
Some more text here...
We also return the :ref:`extension metadata <ext-metadata>` that indicates the
version of our extension, along with the fact that it is safe to use the
extension for both parallel reading and writing.
Updating the conf.py file
-------------------------
Using the extension
-------------------
The extension file has to be declared in your :file:`conf.py` file to make
Sphinx aware of it:
The extension has to be declared in your :file:`conf.py` file to make Sphinx
aware of it. There are two steps necessary here:
#. Open :file:`conf.py`. It is in the :file:`source` folder by default.
#. Add ``sys.path.append(os.path.abspath("./_ext"))`` before
the ``extensions`` variable declaration (if it exists).
#. Update or create the ``extensions`` list and add the
extension file name to the list:
#. Add the :file:`_ext` directory to the `Python path`_ using
``sys.path.append``. This should be placed at the top of the file.
.. code-block:: python
#. Update or create the :confval:`extensions` list and add the extension file
name to the list
extensions.append('helloworld')
For example:
You can now use the extension.
.. code-block:: python
.. admonition:: Example
import os
import sys
.. code-block:: rst
sys.path.append(os.path.abspath("./_ext"))
Some intro text here...
extensions = ['helloworld']
.. helloworld::
.. tip::
Some more text here...
We're not distributing this extension as a `Python package`_, we need to
modify the `Python path`_ so Sphinx can find our extension. This is why we
need the call to ``sys.path.append``.
The sample above would generate:
You can now use the extension in a file. For example:
.. code-block:: text
.. code-block:: rst
Some intro text here...
Some intro text here...
Hello World!
.. helloworld::
Some more text here...
Some more text here...
The sample above would generate:
.. code-block:: text
Some intro text here...
Hello World!
Some more text here...
Further reading
---------------
This is the very basic principle of an extension that creates a new directive.
For a more advanced example, refer to :doc:`todo`.
Further reading
---------------
You can create your own nodes if needed, refer to the
:doc:`todo` for more information.
.. _docutils: http://docutils.sourceforge.net/
.. _`docutils nodes`: http://docutils.sourceforge.net/docs/ref/doctree.html
.. _docutils directives: http://docutils.sourceforge.net/docs/howto/rst-directives.html
.. _docutils nodes: http://docutils.sourceforge.net/docs/ref/doctree.html
.. _PyPI: https://pypi.org/
.. _Python package: https://packaging.python.org/
.. _Python path: https://docs.python.org/3/using/cmdline.html#envvar-PYTHONPATH

View File

@ -9,3 +9,4 @@ Refer to the following tutorials to get started with extension development.
helloworld
todo
recipe

View File

@ -0,0 +1,217 @@
Developing a "recipe" extension
===============================
The objective of this tutorial is to illustrate roles, directives and domains.
Once complete, we will be able to use this extension to describe a recipe and
reference that recipe from elsewhere in our documentation.
.. note::
This tutorial is based on a guide first published on `opensource.com`_ and
is provided here with the original author's permission.
.. _opensource.com: https://opensource.com/article/18/11/building-custom-workflows-sphinx
Overview
--------
We want the extension to add the following to Sphinx:
* A ``recipe`` :term:`directive`, containing some content describing the recipe
steps, along with a ``:contains:`` option highlighting the main ingredients
of the recipe.
* A ``ref`` :term:`role`, which provides a cross-reference to the recipe
itself.
* A ``recipe`` :term:`domain`, which allows us to tie together the above role
and domain, along with things like indices.
For that, we will need to add the following elements to Sphinx:
* A new directive called ``recipe``
* New indexes to allow us to reference ingredient and recipes
* A new domain called ``recipe``, which will contain the ``recipe`` directive
and ``ref`` role
Prerequisites
-------------
We need the same setup as in :doc:`the previous extensions <todo>`. This time,
we will be putting out extension in a file called :file:`recipe.py`.
Here is an example of the folder structure you might obtain:
.. code-block:: text
└── source
   ├── _ext
  └── recipe.py
   ├── conf.py
   └── index.rst
Writing the extension
---------------------
Open :file:`recipe.py` and paste the following code in it, all of which we will
explain in detail shortly:
.. literalinclude:: examples/recipe.py
:language: python
:linenos:
Let's look at each piece of this extension step-by-step to explain what's going
on.
.. rubric:: The directive class
The first thing to examine is the ``RecipeDirective`` directive:
.. literalinclude:: examples/recipe.py
:language: python
:linenos:
:lines: 17-37
Unlike :doc:`helloworld` and :doc:`todo`, this directive doesn't derive from
:class:`docutils.parsers.rst.Directive` and doesn't define a ``run`` method.
Instead, it derives from :class:`sphinx.directives.ObjectDescription` and
defines ``handle_signature`` and ``add_target_and_index`` methods. This is
because ``ObjectDescription`` is a special-purpose directive that's intended
for describing things like classes, functions, or, in our case, recipes. More
specifically, ``handle_signature`` implements parsing the signature of the
directive and passes on the object's name and type to its superclass, while
``add_taget_and_index`` adds a target (to link to) and an entry to the index
for this node.
We also see that this directive defines ``has_content``, ``required_arguments``
and ``option_spec``. Unlike the ``TodoDirective`` directive added in the
:doc:`previous tutorial <todo>`, this directive takes a single argument, the
recipe name, and an option, ``contains``, in addition to the nested
reStructuredText in the body.
.. rubric:: The index classes
.. currentmodule:: sphinx.domains
.. todo:: Add brief overview of indices
.. literalinclude:: examples/recipe.py
:language: python
:linenos:
:lines: 40-102
Both ``IngredientIndex`` and ``RecipeIndex`` are derived from :class:`Index`.
They implement custom logic to generate a tuple of values that define the
index. Note that ``RecipeIndex`` is a simple index that has only one entry.
Extending it to cover more object types is not yet part of the code.
Both indices use the method :meth:`Index.generate` to do their work. This
method combines the information from our domain, sorts it, and returns it in a
list structure that will be accepted by Sphinx. This might look complicated but
all it really is is a list of tuples like ``('tomato', 'TomatoSoup', 'test',
'rec-TomatoSoup',...)``. Refer to the :doc:`domain API guide
</extdev/domainapi>` for more information on this API.
.. rubric:: The domain
A Sphinx domain is a specialized container that ties together roles,
directives, and indices, among other things. Let's look at the domain we're
creating here.
.. literalinclude:: examples/recipe.py
:language: python
:linenos:
:lines: 105-155
There are some interesting things to note about this ``recipe`` domain and domains
in general. Firstly, we actually register our directives, roles and indices
here, via the ``directives``, ``roles`` and ``indices`` attributes, rather than
via calls later on in ``setup``. We can also note that we aren't actually
defining a custom role and are instead reusing the
:class:`sphinx.roles.XRefRole` role and defining the
:class:`sphinx.domains.Domain.resolve_xref` method. This method takes two
arguments, ``typ`` and ``target``, which refer to the cross-reference type and
its target name. We'll use ``target`` to resolve our destination from our
domain's ``recipes`` because we currently have only one type of node.
Moving on, we can see that we've defined ``initial_data``. The values defined in
``initial_data`` will be copied to ``env.domaindata[domain_name]`` as the
initial data of the domain, and domain instances can access it via
``self.data``. We see that we have defined two items in ``initial_data``:
``recipes`` and ``recipe2ingredient``. These contain a list of all objects
defined (i.e. all recipes) and a hash that maps a canonical ingredient name to
the list of objects. The way we name objects is common across our extension and
is defined in the ``get_full_qualified_name`` method. For each object created,
the canonical name is ``recipe.<recipename>``, where ``<recipename>`` is the
name the documentation writer gives the object (a recipe). This enables the
extension to use different object types that share the same name. Having a
canonical name and central place for our objects is a huge advantage. Both our
indices and our cross-referencing code use this feature.
.. rubric:: The ``setup`` function
.. currentmodule:: sphinx.application
:doc:`As always <todo>`, the ``setup`` function is a requirement and is used to
hook the various parts of our extension into Sphinx. Let's look at the
``setup`` function for this extension.
.. literalinclude:: examples/recipe.py
:language: python
:linenos:
:lines: 158-
This looks a little different to what we're used to seeing. There are no calls
to :meth:`~Sphinx.add_directive` or even :meth:`~Sphinx.add_role`. Instead, we
have a single call to :meth:`~Sphinx.add_domain` followed by some
initialization of the :ref:`standard domain <domains-std>`. This is because we
had already registered our directives, roles and indexes as part of the
directive itself.
Using the extension
-------------------
You can now use the extension throughout your project. For example:
.. code-block:: rst
:caption: index.rst
Joe's Recipes
=============
Below are a collection of my favourite recipes. I highly recommend the
:recipe:ref:`TomatoSoup` recipe in particular!
.. toctree::
tomato-soup
.. code-block:: rst
:caption: tomato-soup.rst
The recipe contains `tomato` and `cilantro`.
.. recipe:recipe:: TomatoSoup
:contains: tomato cilantro salt pepper
This recipe is a tasty tomato soup, combine all ingredients
and cook.
The important things to note are the use of the ``:recipe:ref:`` role to
cross-reference the recipe actually defined elsewhere (using the
``:recipe:recipe:`` directive.
Further reading
---------------
For more information, refer to the `docutils`_ documentation and
:doc:`/extdev/index`.
.. _docutils: http://docutils.sourceforge.net/docs/

View File

@ -1,114 +1,99 @@
Developing a "TODO" extension
=============================
This section is intended as a walkthrough for the creation of custom extensions.
It covers the basics of writing and activating an extension, as well as
commonly used features of extensions.
As an example, we will cover a "todo" extension that adds capabilities to
include todo entries in the documentation, and to collect these in a central
place. (A similar "todo" extension is distributed with Sphinx.)
The objective of this tutorial is to create a more comprehensive extension than
that created in :doc:`helloworld`. Whereas that guide just covered writing a
custom :term:`directive`, this guide adds multiple directives, along with custom
nodes, additional config values and custom event handlers. To this end, we will
cover a ``todo`` extension that adds capabilities to include todo entries in the
documentation, and to collect these in a central place. This is similar the
``sphinxext.todo`` extension distributed with Sphinx.
Extension Design
----------------
Overview
--------
.. note:: To understand the design this extension, refer to
.. note::
To understand the design of this extension, refer to
:ref:`important-objects` and :ref:`build-phases`.
We want the extension to add the following to Sphinx:
* A "todo" directive, containing some content that is marked with "TODO", and
only shown in the output if a new config value is set. (Todo entries should
not be in the output by default.)
* A ``todo`` directive, containing some content that is marked with "TODO" and
only shown in the output if a new config value is set. Todo entries should not
be in the output by default.
* A "todolist" directive that creates a list of all todo entries throughout the
* A ``todolist`` directive that creates a list of all todo entries throughout the
documentation.
For that, we will need to add the following elements to Sphinx:
* New directives, called ``todo`` and ``todolist``.
* New document tree nodes to represent these directives, conventionally also
called ``todo`` and ``todolist``. We wouldn't need new nodes if the new
directives only produced some content representable by existing nodes.
* A new config value ``todo_include_todos`` (config value names should start
with the extension name, in order to stay unique) that controls whether todo
entries make it into the output.
* New event handlers: one for the :event:`doctree-resolved` event, to replace
the todo and todolist nodes, and one for :event:`env-purge-doc` (the reason
for that will be covered later).
The Setup Function
------------------
Prerequisites
-------------
.. currentmodule:: sphinx.application
As with :doc:`helloworld`, we will not be distributing this plugin via PyPI so
once again we need a Sphinx project to call this from. You can use an existing
project or create a new one using :program:`sphinx-quickstart`.
The new elements are added in the extension's setup function. Let us create a
new Python module called :file:`todo.py` and add the setup function::
We assume you are using separate source (:file:`source`) and build
(:file:`build`) folders. Your extension file could be in any folder of your
project. In our case, let's do the following:
def setup(app):
app.add_config_value('todo_include_todos', False, 'html')
#. Create an :file:`_ext` folder in :file:`source`
#. Create a new Python file in the :file:`_ext` folder called :file:`todo.py`
app.add_node(todolist)
app.add_node(todo,
html=(visit_todo_node, depart_todo_node),
latex=(visit_todo_node, depart_todo_node),
text=(visit_todo_node, depart_todo_node))
Here is an example of the folder structure you might obtain:
app.add_directive('todo', TodoDirective)
app.add_directive('todolist', TodolistDirective)
app.connect('doctree-resolved', process_todo_nodes)
app.connect('env-purge-doc', purge_todos)
.. code-block:: text
return {'version': '0.1'} # identifies the version of our extension
The calls in this function refer to classes and functions not yet written. What
the individual calls do is the following:
* :meth:`~Sphinx.add_config_value` lets Sphinx know that it should recognize the
new *config value* ``todo_include_todos``, whose default value should be
``False`` (this also tells Sphinx that it is a boolean value).
If the third argument was ``'html'``, HTML documents would be full rebuild if the
config value changed its value. This is needed for config values that
influence reading (build :ref:`phase 1 <build-phases>`).
* :meth:`~Sphinx.add_node` adds a new *node class* to the build system. It also
can specify visitor functions for each supported output format. These visitor
functions are needed when the new nodes stay until :ref:`phase 4 <build-phases>`
-- since the ``todolist`` node is always replaced in :ref:`phase 3 <build-phases>`,
it doesn't need any.
We need to create the two node classes ``todo`` and ``todolist`` later.
* :meth:`~Sphinx.add_directive` adds a new *directive*, given by name and class.
The handler functions are created later.
* Finally, :meth:`~Sphinx.connect` adds an *event handler* to the event whose
name is given by the first argument. The event handler function is called
with several arguments which are documented with the event.
└── source
   ├── _ext
  └── todo.py
   ├── _static
   ├── conf.py
   ├── somefolder
   ├── index.rst
   ├── somefile.rst
   └── someotherfile.rst
The Node Classes
----------------
Writing the extension
---------------------
Let's start with the node classes::
Open :file:`todo.py` and paste the following code in it, all of which we will
explain in detail shortly:
from docutils import nodes
.. literalinclude:: examples/todo.py
:language: python
:linenos:
class todo(nodes.Admonition, nodes.Element):
pass
This is far more extensive extension than the one detailed in :doc:`helloworld`,
however, we will will look at each piece step-by-step to explain what's
happening.
class todolist(nodes.General, nodes.Element):
pass
.. rubric:: The node classes
def visit_todo_node(self, node):
self.visit_admonition(node)
Let's start with the node classes:
def depart_todo_node(self, node):
self.depart_admonition(node)
.. literalinclude:: examples/todo.py
:language: python
:linenos:
:lines: 8-21
Node classes usually don't have to do anything except inherit from the standard
docutils classes defined in :mod:`docutils.nodes`. ``todo`` inherits from
@ -122,81 +107,54 @@ is just a "general" node.
<http://docutils.sourceforge.net/docs/ref/doctree.html>`__ and :ref:`Sphinx
<nodes>`.
The Directive Classes
---------------------
.. rubric:: The directive classes
A directive class is a class deriving usually from
:class:`docutils.parsers.rst.Directive`. The directive interface is also
:class:`docutils.parsers.rst.Directive`. The directive interface is also
covered in detail in the `docutils documentation`_; the important thing is that
the class should have attributes that configure the allowed markup,
and a ``run`` method that returns a list of nodes.
the class should have attributes that configure the allowed markup, and a
``run`` method that returns a list of nodes.
The ``todolist`` directive is quite simple::
Looking first at the ``TodolistDirective`` directive:
from docutils.parsers.rst import Directive
.. literalinclude:: examples/todo.py
:language: python
:linenos:
:lines: 24-27
class TodolistDirective(Directive):
It's very simple, creating and returning an instance of our ``todolist`` node
class. The ``TodolistDirective`` directive itself has neither content nor
arguments that need to be handled. That brings us to the ``TodoDirective``
directive:
def run(self):
return [todolist('')]
.. literalinclude:: examples/todo.py
:language: python
:linenos:
:lines: 30-53
An instance of our ``todolist`` node class is created and returned. The
todolist directive has neither content nor arguments that need to be handled.
The ``todo`` directive function looks like this::
from sphinx.locale import _
class TodoDirective(Directive):
# this enables content in the directive
has_content = True
def run(self):
env = self.state.document.settings.env
targetid = "todo-%d" % env.new_serialno('todo')
targetnode = nodes.target('', '', ids=[targetid])
todo_node = todo('\n'.join(self.content))
todo_node += nodes.title(_('Todo'), _('Todo'))
self.state.nested_parse(self.content, self.content_offset, todo_node)
if not hasattr(env, 'todo_all_todos'):
env.todo_all_todos = []
env.todo_all_todos.append({
'docname': env.docname,
'lineno': self.lineno,
'todo': todo_node.deepcopy(),
'target': targetnode,
})
return [targetnode, todo_node]
Several important things are covered here. First, as you can see, you can refer
to the :ref:`build environment instance <important-objects>` using ``self.state.document.settings.env``.
Then, to act as a link target (from the todolist), the todo directive needs to
return a target node in addition to the todo node. The target ID (in HTML, this
will be the anchor name) is generated by using ``env.new_serialno`` which
returns a new unique integer on each call and therefore leads to unique target
names. The target node is instantiated without any text (the first two
arguments).
Several important things are covered here. First, as you can see, we're now
subclassing the :class:`~sphinx.util.docutils.SphinxDirective` helper class
instead of the usual :class:`~docutils.parsers.rst.Directive` class. This
gives us access to the :ref:`build environment instance <important-objects>`
using the ``self.env`` property. Without this, we'd have to use the rather
convoluted ``self.state.document.settings.env``. Then, to act as a link target
(from ``TodolistDirective``), the ``TodoDirective`` directive needs to return a
target node in addition to the ``todo`` node. The target ID (in HTML, this will
be the anchor name) is generated by using ``env.new_serialno`` which returns a
new unique integer on each call and therefore leads to unique target names. The
target node is instantiated without any text (the first two arguments).
On creating admonition node, the content body of the directive are parsed using
``self.state.nested_parse``. The first argument gives the content body, and
the second one gives content offset. The third argument gives the parent node
of parsed result, in our case the ``todo`` node.
Then, the todo node is added to the environment. This is needed to be able to
create a list of all todo entries throughout the documentation, in the place
where the author puts a ``todolist`` directive. For this case, the environment
attribute ``todo_all_todos`` is used (again, the name should be unique, so it is
prefixed by the extension name). It does not exist when a new environment is
created, so the directive must check and create it if necessary. Various
information about the todo entry's location are stored along with a copy of the
node.
of parsed result, in our case the ``todo`` node. Following this, the ``todo``
node is added to the environment. This is needed to be able to create a list of
all todo entries throughout the documentation, in the place where the author
puts a ``todolist`` directive. For this case, the environment attribute
``todo_all_todos`` is used (again, the name should be unique, so it is prefixed
by the extension name). It does not exist when a new environment is created, so
the directive must check and create it if necessary. Various information about
the todo entry's location are stored along with a copy of the node.
In the last line, the nodes that should be put into the doctree are returned:
the target node and the admonition node.
@ -217,18 +175,20 @@ The node structure that the directive returns looks like this::
| ... |
+--------------------+
.. rubric:: The event handlers
The Event Handlers
------------------
Event handlers are one of Sphinx's most powerful features, providing a way to
do hook into any part of the documentation process. There are many events
provided by Sphinx itself, as detailed in :ref:`the API guide <events>`, and
we're going to use a subset of them here.
Finally, let's look at the event handlers. First, the one for the
:event:`env-purge-doc` event::
Let's look at the event handlers used in the above example. First, the one for
the :event:`env-purge-doc` event:
def purge_todos(app, env, docname):
if not hasattr(env, 'todo_all_todos'):
return
env.todo_all_todos = [todo for todo in env.todo_all_todos
if todo['docname'] != docname]
.. literalinclude:: examples/todo.py
:language: python
:linenos:
:lines: 56-61
Since we store information from source files in the environment, which is
persistent, it may become out of date when the source file changes. Therefore,
@ -238,62 +198,144 @@ Here we clear out all todos whose docname matches the given one from the
``todo_all_todos`` list. If there are todos left in the document, they will be
added again during parsing.
The other handler belongs to the :event:`doctree-resolved` event. This event is
emitted at the end of :ref:`phase 3 <build-phases>` and allows custom resolving
to be done::
The other handler belongs to the :event:`doctree-resolved` event:
def process_todo_nodes(app, doctree, fromdocname):
if not app.config.todo_include_todos:
for node in doctree.traverse(todo):
node.parent.remove(node)
.. literalinclude:: examples/todo.py
:language: python
:linenos:
:lines: 64-103
# Replace all todolist nodes with a list of the collected todos.
# Augment each todo with a backlink to the original location.
env = app.builder.env
for node in doctree.traverse(todolist):
if not app.config.todo_include_todos:
node.replace_self([])
continue
content = []
for todo_info in env.todo_all_todos:
para = nodes.paragraph()
filename = env.doc2path(todo_info['docname'], base=None)
description = (
_('(The original entry is located in %s, line %d and can be found ') %
(filename, todo_info['lineno']))
para += nodes.Text(description, description)
# Create a reference
newnode = nodes.reference('', '')
innernode = nodes.emphasis(_('here'), _('here'))
newnode['refdocname'] = todo_info['docname']
newnode['refuri'] = app.builder.get_relative_uri(
fromdocname, todo_info['docname'])
newnode['refuri'] += '#' + todo_info['target']['refid']
newnode.append(innernode)
para += newnode
para += nodes.Text('.)', '.)')
# Insert into the todolist
content.append(todo_info['todo'])
content.append(para)
node.replace_self(content)
It is a bit more involved. If our new "todo_include_todos" config value is
false, all todo and todolist nodes are removed from the documents.
If not, todo nodes just stay where and how they are. Todolist nodes are
The :event:`doctree-resolved` event is emitted at the end of :ref:`phase 3
(resolving) <build-phases>` and allows custom resolving to be done. The handler
we have written for this event is a bit more involved. If the
``todo_include_todos`` config value (which we'll describe shortly) is false,
all ``todo`` and ``todolist`` nodes are removed from the documents. If not,
``todo`` nodes just stay where and how they are. ``todolist`` nodes are
replaced by a list of todo entries, complete with backlinks to the location
where they come from. The list items are composed of the nodes from the todo
entry and docutils nodes created on the fly: a paragraph for each entry,
containing text that gives the location, and a link (reference node containing
an italic node) with the backreference. The reference URI is built by
``app.builder.get_relative_uri`` which creates a suitable URI depending on the
used builder, and appending the todo node's (the target's) ID as the anchor
name.
where they come from. The list items are composed of the nodes from the
``todo`` entry and docutils nodes created on the fly: a paragraph for each
entry, containing text that gives the location, and a link (reference node
containing an italic node) with the backreference. The reference URI is built
by :meth:`sphinx.builders.Builder.get_relative_uri`` which creates a suitable
URI depending on the used builder, and appending the todo node's (the target's)
ID as the anchor name.
.. rubric:: The ``setup`` function
.. currentmodule:: sphinx.application
As noted :doc:`previously <helloworld>`, the ``setup`` function is a requirement
and is used to plug directives into Sphinx. However, we also use it to hook up
the other parts of our extension. Let's look at our ``setup`` function:
.. literalinclude:: examples/todo.py
:language: python
:linenos:
:lines: 106-
The calls in this function refer to the classes and functions we added earlier.
What the individual calls do is the following:
* :meth:`~Sphinx.add_config_value` lets Sphinx know that it should recognize the
new *config value* ``todo_include_todos``, whose default value should be
``False`` (this also tells Sphinx that it is a boolean value).
If the third argument was ``'html'``, HTML documents would be full rebuild if the
config value changed its value. This is needed for config values that
influence reading (build :ref:`phase 1 (reading) <build-phases>`).
* :meth:`~Sphinx.add_node` adds a new *node class* to the build system. It also
can specify visitor functions for each supported output format. These visitor
functions are needed when the new nodes stay until :ref:`phase 4 (writing)
<build-phases>`. Since the ``todolist`` node is always replaced in
:ref:`phase 3 (resolving) <build-phases>`, it doesn't need any.
* :meth:`~Sphinx.add_directive` adds a new *directive*, given by name and class.
* Finally, :meth:`~Sphinx.connect` adds an *event handler* to the event whose
name is given by the first argument. The event handler function is called
with several arguments which are documented with the event.
With this, our extension is complete.
Using the extension
-------------------
As before, we need to enable the extension by declaring it in our
:file:`conf.py` file. There are two steps necessary here:
#. Add the :file:`_ext` directory to the `Python path`_ using
``sys.path.append``. This should be placed at the top of the file.
#. Update or create the :confval:`extensions` list and add the extension file
name to the list
In addition, we may wish to set the ``todo_include_todos`` config value. As
noted above, this defaults to ``False`` but we can set it explicitly.
For example:
.. code-block:: python
import os
import sys
sys.path.append(os.path.abspath("./_ext"))
extensions = ['todo']
todo_include_todos = False
You can now use the extension throughout your project. For example:
.. code-block:: rst
:caption: index.rst
Hello, world
============
.. toctree::
somefile.rst
someotherfile.rst
Hello world. Below is the list of TODOs.
.. todolist::
.. code-block:: rst
:caption: somefile.rst
foo
===
Some intro text here...
.. todo:: Fix this
.. code-block:: rst
:caption: someotherfile.rst
bar
===
Some more text here...
.. todo:: Fix that
Because we have configured ``todo_include_todos`` to ``False``, we won't
actually see anything rendered for the ``todo`` and ``todolist`` directives.
However, if we toggle this to true, we will see the output described
previously.
Further reading
---------------
For more information, refer to the `docutils`_ documentation and
:doc:`/extdev/index`.
.. _docutils: http://docutils.sourceforge.net/docs/
.. _Python path: https://docs.python.org/3/using/cmdline.html#envvar-PYTHONPATH
.. _docutils documentation: http://docutils.sourceforge.net/docs/ref/rst/directives.html

View File

@ -54,8 +54,6 @@ package.
.. automethod:: Sphinx.add_domain(domain)
.. automethod:: Sphinx.override_domain(domain)
.. method:: Sphinx.add_directive_to_domain(domain, name, func, content, arguments, \*\*options)
.. automethod:: Sphinx.add_directive_to_domain(domain, name, directiveclass)

1032
doc/extdev/deprecated.rst Normal file

File diff suppressed because it is too large Load Diff

View File

@ -15,3 +15,83 @@ i18n API
.. autofunction:: __
.. _ext-i18n:
Extension internationalization (`i18n`) and localization (`l10n`) using i18n API
---------------------------------------------------------------------------------
.. versionadded:: 1.8
An extension may naturally come with message translations. This is briefly
summarized in :func:`sphinx.locale.get_translation` help.
In practice, you have to:
#. Choose a name for your message catalog, which must be unique. Usually
the name of your extension is used for the name of message catalog.
#. Mark in your extension sources all messages as translatable, via
:func:`sphinx.locale.get_translation` function, usually renamed ``_()``,
e.g.:
.. code-block:: python
:caption: src/__init__.py
from sphinx.locale import get_translation
MESSAGE_CATALOG_NAME = 'myextension'
_ = get_translation(MESSAGE_CATALOG_NAME)
translated_text = _('Hello Sphinx!')
#. Set up your extension to be aware of its dedicated translations:
.. code-block:: python
:caption: src/__init__.py
def setup(app):
package_dir = path.abspath(path.dirname(__file__))
locale_dir = os.path.join(package_dir, 'locales')
app.add_message_catalog(MESSAGE_CATALOG_NAME, locale_dir)
#. Generate message catalog template ``*.pot`` file, usually in ``locale/``
source directory, for example via `Babel`_:
.. code-block:: console
$ pybabel extract --output=src/locale/myextension.pot src/
#. Create message catalogs (``*.po``) for each language which your extension
will provide localization, for example via `Babel`_:
.. code-block:: console
$ pybabel init --input-file=src/locale/myextension.pot --domain=myextension --output-dir=src/locale --locale=fr_FR
#. Translate message catalogs for each language manually
#. Compile message catalogs into ``*.mo`` files, for example via `Babel`_:
.. code-block:: console
$ pybabel compile --directory=src/locale --domain=myextension
#. Ensure that message catalog files are distributed when your package will
be installed, by adding equivalent line in your extension ``MANIFEST.in``:
.. code-block:: ini
:caption: MANIFEST.in
recursive-include src *.pot *.po *.mo
When the messages on your extension has been changed, you need to also update
message catalog template and message catalogs, for example via `Babel`_:
.. code-block:: console
$ pybabel extract --output=src/locale/myextension.pot src/
$ pybabel update --input-file=src/locale/myextension.pot --domain=myextension --output-dir=src/locale
.. _Babel: http://babel.pocoo.org/

View File

@ -187,6 +187,7 @@ as metadata of the extension. Metadata keys currently recognized are:
output files can be used when the extension is loaded. Since extensions
usually don't negatively influence the process, this defaults to ``True``.
APIs used for writing extensions
--------------------------------
@ -205,896 +206,4 @@ APIs used for writing extensions
logging
i18n
utils
.. _dev-deprecated-apis:
Deprecated APIs
---------------
On developing Sphinx, we are always careful to the compatibility of our APIs.
But, sometimes, the change of interface are needed for some reasons. In such
cases, we've marked them as deprecated. And they are kept during the two
major versions (for more details, please see :ref:`deprecation-policy`).
The following is a list of deprecated interfaces.
.. tabularcolumns:: |>{\raggedright}\Y{.4}|>{\centering}\Y{.1}|>{\centering}\Y{.12}|>{\raggedright\arraybackslash}\Y{.38}|
.. |LaTeXHyphenate| raw:: latex
\hspace{0pt}
.. list-table:: deprecated APIs
:header-rows: 1
:class: deprecated
:widths: 40, 10, 10, 40
* - Target
- |LaTeXHyphenate|\ Deprecated
- (will be) Removed
- Alternatives
* - ``encoding`` argument of ``autodoc.Documenter.get_doc()``,
``autodoc.DocstringSignatureMixin.get_doc()``,
``autodoc.DocstringSignatureMixin._find_signature()``, and
``autodoc.ClassDocumenter.get_doc()``
- 2.0
- 4.0
- N/A
* - arguments of ``EpubBuilder.build_mimetype()``,
``EpubBuilder.build_container()``, ``EpubBuilder.build_content()``,
``EpubBuilder.build_toc()`` and ``EpubBuilder.build_epub()``
- 2.0
- 4.0
- N/A
* - arguments of ``Epub3Builder.build_navigation_doc()``
- 2.0
- 4.0
- N/A
* - ``nodetype`` argument of
``sphinx.search.WordCollector.is_meta_keywords()``
- 2.0
- 4.0
- N/A
* - ``suffix`` argument of ``BuildEnvironment.doc2path()``
- 2.0
- 4.0
- N/A
* - string style ``base`` argument of ``BuildEnvironment.doc2path()``
- 2.0
- 4.0
- ``os.path.join()``
* - ``sphinx.addnodes.abbreviation``
- 2.0
- 4.0
- ``docutils.nodes.abbreviation``
* - ``sphinx.builders.applehelp``
- 2.0
- 4.0
- ``sphinxcontrib.applehelp``
* - ``sphinx.builders.devhelp``
- 2.0
- 4.0
- ``sphinxcontrib.devhelp``
* - ``sphinx.builders.epub3.Epub3Builder.validate_config_value()``
- 2.0
- 4.0
- ``sphinx.builders.epub3.validate_config_values()``
* - ``sphinx.builders.html.JSONHTMLBuilder``
- 2.0
- 4.0
- ``sphinx.builders.serializinghtml.JSONHTMLBuilder``
* - ``sphinx.builders.html.PickleHTMLBuilder``
- 2.0
- 4.0
- ``sphinx.builders.serializinghtml.PickleHTMLBuilder``
* - ``sphinx.builders.html.SerializingHTMLBuilder``
- 2.0
- 4.0
- ``sphinx.builders.serializinghtml.SerializingHTMLBuilder``
* - ``sphinx.builders.html.SingleFileHTMLBuilder``
- 2.0
- 4.0
- ``sphinx.builders.singlehtml.SingleFileHTMLBuilder``
* - ``sphinx.builders.html.WebHTMLBuilder``
- 2.0
- 4.0
- ``sphinx.builders.serializinghtml.PickleHTMLBuilder``
* - ``sphinx.builders.htmlhelp``
- 2.0
- 4.0
- ``sphinxcontrib.htmlhelp``
* - ``sphinx.builders.htmlhelp.HTMLHelpBuilder.open_file()``
- 2.0
- 4.0
- ``open()``
* - ``sphinx.builders.qthelp``
- 2.0
- 4.0
- ``sphinxcontrib.qthelp``
* - ``sphinx.cmd.quickstart.term_decode()``
- 2.0
- 4.0
- N/A
* - ``sphinx.cmd.quickstart.TERM_ENCODING``
- 2.0
- 4.0
- ``sys.stdin.encoding``
* - ``sphinx.config.check_unicode()``
- 2.0
- 4.0
- N/A
* - ``sphinx.config.string_classes``
- 2.0
- 4.0
- ``[str]``
* - ``sphinx.domains.cpp.DefinitionError.description``
- 2.0
- 4.0
- ``str(exc)``
* - ``sphinx.domains.cpp.NoOldIdError.description``
- 2.0
- 4.0
- ``str(exc)``
* - ``sphinx.domains.cpp.UnsupportedMultiCharacterCharLiteral.decoded``
- 2.0
- 4.0
- ``str(exc)``
* - ``sphinx.ext.autosummary.Autosummary.warn()``
- 2.0
- 4.0
- N/A
* - ``sphinx.ext.autosummary.Autosummary.genopt``
- 2.0
- 4.0
- N/A
* - ``sphinx.ext.autosummary.Autosummary.warnings``
- 2.0
- 4.0
- N/A
* - ``sphinx.ext.autosummary.Autosummary.result``
- 2.0
- 4.0
- N/A
* - ``sphinx.ext.doctest.doctest_encode()``
- 2.0
- 4.0
- N/A
* - ``sphinx.ext.jsmath``
- 2.0
- 4.0
- ``sphinxcontrib.jsmath``
* - ``sphinx.roles.abbr_role()``
- 2.0
- 4.0
- ``sphinx.roles.Abbreviation``
* - ``sphinx.roles.emph_literal_role()``
- 2.0
- 4.0
- ``sphinx.roles.EmphasizedLiteral``
* - ``sphinx.roles.menusel_role()``
- 2.0
- 4.0
- ``sphinx.roles.GUILabel`` or ``sphinx.roles.MenuSelection``
* - ``sphinx.roles.index_role()``
- 2.0
- 4.0
- ``sphinx.roles.Index``
* - ``sphinx.roles.indexmarkup_role()``
- 2.0
- 4.0
- ``sphinx.roles.PEP`` or ``sphinx.roles.RFC``
* - ``sphinx.testing.util.remove_unicode_literal()``
- 2.0
- 4.0
- N/A
* - ``sphinx.util.attrdict``
- 2.0
- 4.0
- N/A
* - ``sphinx.util.force_decode()``
- 2.0
- 4.0
- N/A
* - ``sphinx.util.get_matching_docs()``
- 2.0
- 4.0
- ``sphinx.util.get_matching_files()``
* - ``sphinx.util.inspect.Parameter``
- 2.0
- 3.0
- N/A
* - ``sphinx.util.jsonimpl``
- 2.0
- 4.0
- ``sphinxcontrib.serializinghtml.jsonimpl``
* - ``sphinx.util.osutil.EEXIST``
- 2.0
- 4.0
- ``errno.EEXIST`` or ``FileExistsError``
* - ``sphinx.util.osutil.EINVAL``
- 2.0
- 4.0
- ``errno.EINVAL``
* - ``sphinx.util.osutil.ENOENT``
- 2.0
- 4.0
- ``errno.ENOENT`` or ``FileNotFoundError``
* - ``sphinx.util.osutil.EPIPE``
- 2.0
- 4.0
- ``errno.ENOENT`` or ``BrokenPipeError``
* - ``sphinx.util.osutil.walk()``
- 2.0
- 4.0
- ``os.walk()``
* - ``sphinx.util.pycompat.NoneType``
- 2.0
- 4.0
- ``sphinx.util.typing.NoneType``
* - ``sphinx.util.pycompat.TextIOWrapper``
- 2.0
- 4.0
- ``io.TextIOWrapper``
* - ``sphinx.util.pycompat.UnicodeMixin``
- 2.0
- 4.0
- N/A
* - ``sphinx.util.pycompat.htmlescape()``
- 2.0
- 4.0
- ``html.escape()``
* - ``sphinx.util.pycompat.indent()``
- 2.0
- 4.0
- ``textwrap.indent()``
* - ``sphinx.util.pycompat.sys_encoding``
- 2.0
- 4.0
- ``sys.getdefaultencoding()``
* - ``sphinx.util.pycompat.terminal_safe()``
- 2.0
- 4.0
- ``sphinx.util.console.terminal_safe()``
* - ``sphinx.util.pycompat.u``
- 2.0
- 4.0
- N/A
* - ``sphinx.util.PeekableIterator``
- 2.0
- 4.0
- N/A
* - Omitting the ``filename`` argument in an overriddent
``IndexBuilder.feed()`` method.
- 2.0
- 4.0
- ``IndexBuilder.feed(docname, filename, title, doctree)``
* - ``sphinx.writers.latex.ExtBabel``
- 2.0
- 4.0
- ``sphinx.builders.latex.util.ExtBabel``
* - ``sphinx.writers.latex.LaTeXTranslator.babel_defmacro()``
- 2.0
- 4.0
- N/A
* - ``sphinx.application.Sphinx._setting_up_extension``
- 2.0
- 3.0
- N/A
* - The ``importer`` argument of ``sphinx.ext.autodoc.importer._MockModule``
- 2.0
- 3.0
- N/A
* - ``sphinx.ext.autodoc.importer._MockImporter``
- 2.0
- 3.0
- N/A
* - ``sphinx.io.SphinxBaseFileInput``
- 2.0
- 3.0
- N/A
* - ``sphinx.io.SphinxFileInput.supported``
- 2.0
- 3.0
- N/A
* - ``sphinx.io.SphinxRSTFileInput``
- 2.0
- 3.0
- N/A
* - ``sphinx.registry.SphinxComponentRegistry.add_source_input()``
- 2.0
- 3.0
- N/A
* - ``sphinx.writers.latex.LaTeXTranslator._make_visit_admonition()``
- 2.0
- 3.0
- N/A
* - ``sphinx.writers.latex.LaTeXTranslator.collect_footnotes()``
- 2.0
- 4.0
- N/A
* - ``sphinx.writers.texinfo.TexinfoTranslator._make_visit_admonition()``
- 2.0
- 3.0
- N/A
* - ``sphinx.writers.text.TextTranslator._make_depart_admonition()``
- 2.0
- 3.0
- N/A
* - ``sphinx.writers.latex.LaTeXTranslator.generate_numfig_format()``
- 2.0
- 4.0
- N/A
* - :rst:dir:`highlightlang`
- 1.8
- 4.0
- :rst:dir:`highlight`
* - :meth:`~sphinx.application.Sphinx.add_stylesheet()`
- 1.8
- 4.0
- :meth:`~sphinx.application.Sphinx.add_css_file()`
* - :meth:`~sphinx.application.Sphinx.add_javascript()`
- 1.8
- 4.0
- :meth:`~sphinx.application.Sphinx.add_js_file()`
* - :confval:`autodoc_default_flags`
- 1.8
- 4.0
- :confval:`autodoc_default_options`
* - ``content`` arguments of ``sphinx.util.image.guess_mimetype()``
- 1.8
- 3.0
- N/A
* - ``gettext_compact`` arguments of
``sphinx.util.i18n.find_catalog_source_files()``
- 1.8
- 3.0
- N/A
* - ``sphinx.io.SphinxI18nReader.set_lineno_for_reporter()``
- 1.8
- 3.0
- N/A
* - ``sphinx.io.SphinxI18nReader.line``
- 1.8
- 3.0
- N/A
* - ``sphinx.directives.other.VersionChanges``
- 1.8
- 3.0
- ``sphinx.domains.changeset.VersionChanges``
* - ``sphinx.highlighting.PygmentsBridge.unhighlight()``
- 1.8
- 3.0
- N/A
* - ``trim_doctest_flags`` arguments of
``sphinx.highlighting.PygmentsBridge``
- 1.8
- 3.0
- N/A
* - ``sphinx.ext.mathbase``
- 1.8
- 3.0
- N/A
* - ``sphinx.ext.mathbase.MathDomain``
- 1.8
- 3.0
- ``sphinx.domains.math.MathDomain``
* - ``sphinx.ext.mathbase.MathDirective``
- 1.8
- 3.0
- ``sphinx.directives.patches.MathDirective``
* - ``sphinx.ext.mathbase.math_role()``
- 1.8
- 3.0
- ``docutils.parsers.rst.roles.math_role()``
* - ``sphinx.ext.mathbase.setup_math()``
- 1.8
- 3.0
- :meth:`~sphinx.application.Sphinx.add_html_math_renderer()`
* - ``sphinx.ext.mathbase.is_in_section_title()``
- 1.8
- 3.0
- N/A
* - ``sphinx.ext.mathbase.get_node_equation_number()``
- 1.8
- 3.0
- ``sphinx.util.math.get_node_equation_number()``
* - ``sphinx.ext.mathbase.wrap_displaymath()``
- 1.8
- 3.0
- ``sphinx.util.math.wrap_displaymath()``
* - ``sphinx.ext.mathbase.math`` (node)
- 1.8
- 3.0
- ``docutils.nodes.math``
* - ``sphinx.ext.mathbase.displaymath`` (node)
- 1.8
- 3.0
- ``docutils.nodes.math_block``
* - ``sphinx.ext.mathbase.eqref`` (node)
- 1.8
- 3.0
- ``sphinx.builders.latex.nodes.math_reference``
* - ``viewcode_import`` (config value)
- 1.8
- 3.0
- :confval:`viewcode_follow_imported_members`
* - ``sphinx.writers.latex.Table.caption_footnotetexts``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.latex.Table.header_footnotetexts``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.latex.LaTeXTranslator.footnotestack``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.latex.LaTeXTranslator.in_container_literal_block``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.latex.LaTeXTranslator.next_section_ids``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.latex.LaTeXTranslator.next_hyperlink_ids``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.latex.LaTeXTranslator.restrict_footnote()``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.latex.LaTeXTranslator.unrestrict_footnote()``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.latex.LaTeXTranslator.push_hyperlink_ids()``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.latex.LaTeXTranslator.pop_hyperlink_ids()``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.latex.LaTeXTranslator.bibitems``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.latex.LaTeXTranslator.hlsettingstack``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.latex.ExtBabel.get_shorthandoff()``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.html.HTMLTranslator.highlightlang()``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.html.HTMLTranslator.highlightlang_base()``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.html.HTMLTranslator.highlightlangopts()``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.html.HTMLTranslator.highlightlinenothreshold()``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.html5.HTMLTranslator.highlightlang()``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.html5.HTMLTranslator.highlightlang_base()``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.html5.HTMLTranslator.highlightlangopts()``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.html5.HTMLTranslator.highlightlinenothreshold()``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.latex.LaTeXTranslator.check_latex_elements()``
- 1.8
- 3.0
- Nothing
* - ``sphinx.application.CONFIG_FILENAME``
- 1.8
- 3.0
- ``sphinx.config.CONFIG_FILENAME``
* - ``Config.check_unicode()``
- 1.8
- 3.0
- ``sphinx.config.check_unicode()``
* - ``Config.check_types()``
- 1.8
- 3.0
- ``sphinx.config.check_confval_types()``
* - ``dirname``, ``filename`` and ``tags`` arguments of
``Config.__init__()``
- 1.8
- 3.0
- ``Config.read()``
* - The value of :confval:`html_search_options`
- 1.8
- 3.0
- see :confval:`html_search_options`
* - ``sphinx.versioning.prepare()``
- 1.8
- 3.0
- ``sphinx.versioning.UIDTransform``
* - ``Sphinx.override_domain()``
- 1.8
- 3.0
- :meth:`~sphinx.application.Sphinx.add_domain()`
* - ``Sphinx.import_object()``
- 1.8
- 3.0
- ``sphinx.util.import_object()``
* - ``suffix`` argument of
:meth:`~sphinx.application.Sphinx.add_source_parser()`
- 1.8
- 3.0
- :meth:`~sphinx.application.Sphinx.add_source_suffix()`
* - ``BuildEnvironment.load()``
- 1.8
- 3.0
- ``pickle.load()``
* - ``BuildEnvironment.loads()``
- 1.8
- 3.0
- ``pickle.loads()``
* - ``BuildEnvironment.frompickle()``
- 1.8
- 3.0
- ``pickle.load()``
* - ``BuildEnvironment.dump()``
- 1.8
- 3.0
- ``pickle.dump()``
* - ``BuildEnvironment.dumps()``
- 1.8
- 3.0
- ``pickle.dumps()``
* - ``BuildEnvironment.topickle()``
- 1.8
- 3.0
- ``pickle.dump()``
* - ``BuildEnvironment._nitpick_ignore``
- 1.8
- 3.0
- :confval:`nitpick_ignore`
* - ``BuildEnvironment.versionchanges``
- 1.8
- 3.0
- N/A
* - ``BuildEnvironment.update()``
- 1.8
- 3.0
- ``Builder.read()``
* - ``BuildEnvironment.read_doc()``
- 1.8
- 3.0
- ``Builder.read_doc()``
* - ``BuildEnvironment._read_serial()``
- 1.8
- 3.0
- ``Builder.read()``
* - ``BuildEnvironment._read_parallel()``
- 1.8
- 3.0
- ``Builder.read()``
* - ``BuildEnvironment.write_doctree()``
- 1.8
- 3.0
- ``Builder.write_doctree()``
* - ``BuildEnvironment.note_versionchange()``
- 1.8
- 3.0
- ``ChangesDomain.note_changeset()``
* - ``warn()`` (template helper function)
- 1.8
- 3.0
- ``warning()``
* - :confval:`source_parsers`
- 1.8
- 3.0
- :meth:`~sphinx.application.Sphinx.add_source_parser()`
* - ``sphinx.util.docutils.directive_helper()``
- 1.8
- 3.0
- ``Directive`` class of docutils
* - ``sphinx.cmdline``
- 1.8
- 3.0
- ``sphinx.cmd.build``
* - ``sphinx.make_mode``
- 1.8
- 3.0
- ``sphinx.cmd.make_mode``
* - ``sphinx.locale.l_()``
- 1.8
- 3.0
- :func:`sphinx.locale._()`
* - ``sphinx.locale.lazy_gettext()``
- 1.8
- 3.0
- :func:`sphinx.locale._()`
* - ``sphinx.locale.mygettext()``
- 1.8
- 3.0
- :func:`sphinx.locale._()`
* - ``sphinx.util.copy_static_entry()``
- 1.5
- 3.0
- ``sphinx.util.fileutil.copy_asset()``
* - ``sphinx.build_main()``
- 1.7
- 2.0
- ``sphinx.cmd.build.build_main()``
* - ``sphinx.ext.intersphinx.debug()``
- 1.7
- 2.0
- ``sphinx.ext.intersphinx.inspect_main()``
* - ``sphinx.ext.autodoc.format_annotation()``
- 1.7
- 2.0
- ``sphinx.util.inspect.Signature``
* - ``sphinx.ext.autodoc.formatargspec()``
- 1.7
- 2.0
- ``sphinx.util.inspect.Signature``
* - ``sphinx.ext.autodoc.AutodocReporter``
- 1.7
- 2.0
- ``sphinx.util.docutils.switch_source_input()``
* - ``sphinx.ext.autodoc.add_documenter()``
- 1.7
- 2.0
- :meth:`~sphinx.application.Sphinx.add_autodocumenter()`
* - ``sphinx.ext.autodoc.AutoDirective._register``
- 1.7
- 2.0
- :meth:`~sphinx.application.Sphinx.add_autodocumenter()`
* - ``AutoDirective._special_attrgetters``
- 1.7
- 2.0
- :meth:`~sphinx.application.Sphinx.add_autodoc_attrgetter()`
* - ``Sphinx.warn()``, ``Sphinx.info()``
- 1.6
- 2.0
- :ref:`logging-api`
* - ``BuildEnvironment.set_warnfunc()``
- 1.6
- 2.0
- :ref:`logging-api`
* - ``BuildEnvironment.note_toctree()``
- 1.6
- 2.0
- ``Toctree.note()`` (in ``sphinx.environment.adapters.toctree``)
* - ``BuildEnvironment.get_toc_for()``
- 1.6
- 2.0
- ``Toctree.get_toc_for()`` (in ``sphinx.environment.adapters.toctree``)
* - ``BuildEnvironment.get_toctree_for()``
- 1.6
- 2.0
- ``Toctree.get_toctree_for()`` (in ``sphinx.environment.adapters.toctree``)
* - ``BuildEnvironment.create_index()``
- 1.6
- 2.0
- ``IndexEntries.create_index()`` (in ``sphinx.environment.adapters.indexentries``)
* - ``sphinx.websupport``
- 1.6
- 2.0
- `sphinxcontrib-websupport <https://pypi.org/project/sphinxcontrib-websupport/>`_
* - ``StandaloneHTMLBuilder.css_files``
- 1.6
- 2.0
- :meth:`~sphinx.application.Sphinx.add_stylesheet()`
* - ``document.settings.gettext_compact``
- 1.8
- 1.8
- :confval:`gettext_compact`
* - ``Sphinx.status_iterator()``
- 1.6
- 1.7
- ``sphinx.util.status_iterator()``
* - ``Sphinx.old_status_iterator()``
- 1.6
- 1.7
- ``sphinx.util.old_status_iterator()``
* - ``Sphinx._directive_helper()``
- 1.6
- 1.7
- ``sphinx.util.docutils.directive_helper()``
* - ``sphinx.util.compat.Directive``
- 1.6
- 1.7
- ``docutils.parsers.rst.Directive``
* - ``sphinx.util.compat.docutils_version``
- 1.6
- 1.7
- ``sphinx.util.docutils.__version_info__``
.. note:: On deprecating on public APIs (internal functions and classes),
we also follow the policy as much as possible.
deprecated

View File

@ -15,6 +15,9 @@ components (e.g. :class:`.Config`, :class:`.BuildEnvironment` and so on) easily.
.. autoclass:: sphinx.transforms.SphinxTransform
:members:
.. autoclass:: sphinx.transforms.post_transforms.SphinxPostTransform
:members:
.. autoclass:: sphinx.util.docutils.SphinxDirective
:members:

View File

@ -88,7 +88,7 @@ MediaWiki
Google Analytics
You can use a custom ``layout.html`` template, like this:
.. code-block:: html+django
.. code-block:: html+jinja
{% extends "!layout.html" %}
@ -119,6 +119,36 @@ Google Analytics
{% endblock %}
Google Search
To replace Sphinx's built-in search function with Google Search, proceed as
follows:
1. Go to https://cse.google.com/cse/all to create the Google Search code
snippet.
2. Copy the code snippet and paste it into ``_templates/searchbox.html`` in
your Sphinx project:
.. code-block:: html+jinja
<div>
<h3>{{ _('Quick search') }}</h3>
<script>
(function() {
var cx = '......';
var gcse = document.createElement('script');
gcse.type = 'text/javascript';
gcse.async = true;
gcse.src = 'https://cse.google.com/cse.js?cx=' + cx;
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(gcse, s);
})();
</script>
<gcse:search></gcse:search>
</div>
3. Add ``searchbox.html`` to the :confval:`html_sidebars` configuration value.
.. _api role: https://git.savannah.gnu.org/cgit/kenozooid.git/tree/doc/extapi.py
.. _xhtml to reST: http://docutils.sourceforge.net/sandbox/xhtml2rest/xhtml2rest.py

View File

@ -509,6 +509,13 @@ General configuration
.. versionadded:: 1.5
.. tip:: Sphinx uses requests_ as a HTTP library internally.
Therefore, Sphinx refers a certification file on the
directory pointed ``REQUESTS_CA_BUNDLE`` environment
variable if ``tls_cacerts`` not set.
.. _requests: http://docs.python-requests.org/en/master/
.. confval:: today
today_fmt

View File

@ -315,7 +315,7 @@ inserting them into the page source under a suitable :rst:dir:`py:module`,
Configuration
-------------
There are also new config values that you can set:
There are also config values that you can set:
.. confval:: autoclass_content

View File

@ -157,7 +157,7 @@ Generating stub pages automatically
-----------------------------------
If you do not want to create stub pages with :program:`sphinx-autogen`, you can
also use this new config value:
also use these config values:
.. confval:: autosummary_generate

View File

@ -13,7 +13,7 @@ This extension features one additional builder, the :class:`CoverageBuilder`.
.. todo:: Write this section.
Several new configuration values can be used to specify what the builder
Several configuration values can be used to specify what the builder
should check:
.. confval:: coverage_ignore_modules

View File

@ -18,7 +18,7 @@ tracker, at :samp:`https://github.com/sphinx-doc/sphinx/issues/{num}`. Typing
this URL again and again is tedious, so you can use :mod:`~sphinx.ext.extlinks`
to avoid repeating yourself.
The extension adds one new config value:
The extension adds a config value:
.. confval:: extlinks

View File

@ -90,7 +90,7 @@ It adds these directives:
.. versionadded:: 1.6
All three directives support a ``name`` option to set the label to graph.
There are also these new config values:
There are also these config values:
.. confval:: graphviz_dot

View File

@ -25,12 +25,18 @@ It adds this directive:
graph.
This directive supports an option called ``parts`` that, if given, must be an
integer, advising the directive to remove that many parts of module names
from the displayed names. (For example, if all your class names start with
``lib.``, you can give ``:parts: 1`` to remove that prefix from the displayed
node names.)
integer, advising the directive to keep that many dot-separated parts
in the displayed names (from right to left). For example, ``parts=1`` will
only display class names, without the names of the modules that contain
them.
It also supports a ``private-bases`` flag option; if given, private base
.. versionchanged:: 2.0
The value of for ``parts`` can also be negative, indicating how many
parts to drop from the left. For example, if all your class names start
with ``lib.``, you can give ``:parts: -1`` to remove that prefix from the
displayed node names.
The directive also supports a ``private-bases`` flag option; if given, private base
classes (those whose name starts with ``_``) will be included.
You can use ``caption`` option to give a caption to the diagram.
@ -92,6 +98,41 @@ It adds this directive:
Added ``top-classes`` option to limit the scope of inheritance graphs.
Examples
--------
The following are different inheritance diagrams for the internal
``InheritanceDiagram`` class that implements the directive.
With full names::
.. inheritance-diagram:: sphinx.ext.inheritance_diagram.InheritanceDiagram
.. inheritance-diagram:: sphinx.ext.inheritance_diagram.InheritanceDiagram
Showing class names only::
.. inheritance-diagram:: sphinx.ext.inheritance_diagram.InheritanceDiagram
:parts: 1
.. inheritance-diagram:: sphinx.ext.inheritance_diagram.InheritanceDiagram
:parts: 1
Stopping the diagram at :class:`sphinx.util.docutils.SphinxDirective` (the
highest superclass still part of Sphinx), and dropping the common left-most
part (``sphinx``) from all names::
.. inheritance-diagram:: sphinx.ext.inheritance_diagram.InheritanceDiagram
:top-classes: sphinx.util.docutils.SphinxDirective
:parts: -1
.. inheritance-diagram:: sphinx.ext.inheritance_diagram.InheritanceDiagram
:top-classes: sphinx.util.docutils.SphinxDirective
:parts: -1
Configuration
-------------

View File

@ -43,7 +43,7 @@ Configuration
-------------
To use Intersphinx linking, add ``'sphinx.ext.intersphinx'`` to your
:confval:`extensions` config value, and use these new config values to activate
:confval:`extensions` config value, and use these config values to activate
linking:
.. confval:: intersphinx_mapping

View File

@ -1084,7 +1084,7 @@ Overloaded (member) functions
When a (member) function is referenced using just its name, the reference
will point to an arbitrary matching overload.
The :rst:role:`cpp:any` and :rst:role:`cpp:func` roles will an alternative
The :rst:role:`cpp:any` and :rst:role:`cpp:func` roles use an alternative
format, which simply is a complete function declaration.
This will resolve to the exact matching overload.
As example, consider the following class declaration:
@ -1195,6 +1195,7 @@ Configuration Variables
See :ref:`cpp-config`.
.. _domains-std:
The Standard Domain
-------------------
@ -1294,8 +1295,6 @@ The JavaScript domain (name **js**) provides the following directives:
specified. If this option is specified, the directive will only update the
current module name.
To clear the current module, set the module name to ``null`` or ``None``
.. versionadded:: 1.6
.. rst:directive:: .. js:function:: name(signature)

View File

@ -39,7 +39,6 @@ extras_require = {
'colorama>=0.3.5',
],
'test': [
'mock',
'pytest',
'pytest-cov',
'html5lib',
@ -173,6 +172,7 @@ setup(
author_email='georg@python.org',
description='Python documentation generator',
long_description=long_desc,
long_description_content_type='text/x-rst',
zip_safe=False,
classifiers=[
'Development Status :: 5 - Production/Stable',

View File

@ -32,8 +32,8 @@ if 'PYTHONWARNINGS' not in os.environ:
warnings.filterwarnings('ignore', "'U' mode is deprecated",
DeprecationWarning, module='docutils.io')
__version__ = '2.1.0+'
__released__ = '2.1.0' # used when Sphinx builds its own docs
__version__ = '3.0.0+'
__released__ = '3.0.0' # used when Sphinx builds its own docs
#: Version info for better programmatic use.
#:
@ -43,7 +43,7 @@ __released__ = '2.1.0' # used when Sphinx builds its own docs
#:
#: .. versionadded:: 1.2
#: Before version 1.2, check the string ``sphinx.__version__``.
version_info = (2, 1, 0, 'beta', 0)
version_info = (3, 0, 0, 'beta', 0)
package_dir = path.abspath(path.dirname(__file__))

View File

@ -12,7 +12,7 @@ import warnings
from docutils import nodes
from sphinx.deprecation import RemovedInSphinx30Warning, RemovedInSphinx40Warning
from sphinx.deprecation import RemovedInSphinx40Warning
if False:
# For type annotation
@ -188,59 +188,6 @@ class production(nodes.Part, nodes.Inline, nodes.FixedTextElement):
"""Node for a single grammar production rule."""
# math nodes
class math(nodes.math):
"""Node for inline equations.
.. warning:: This node is provided to keep compatibility only.
It will be removed in nearly future. Don't use this from your extension.
.. deprecated:: 1.8
Use ``docutils.nodes.math`` instead.
"""
def __getitem__(self, key):
"""Special accessor for supporting ``node['latex']``."""
if key == 'latex' and 'latex' not in self.attributes:
warnings.warn("math node for Sphinx was replaced by docutils'. "
"Therefore please use ``node.astext()`` to get an equation instead.",
RemovedInSphinx30Warning, stacklevel=2)
return self.astext()
else:
return super().__getitem__(key)
class math_block(nodes.math_block):
"""Node for block level equations.
.. warning:: This node is provided to keep compatibility only.
It will be removed in nearly future. Don't use this from your extension.
.. deprecated:: 1.8
"""
def __getitem__(self, key):
if key == 'latex' and 'latex' not in self.attributes:
warnings.warn("displaymath node for Sphinx was replaced by docutils'. "
"Therefore please use ``node.astext()`` to get an equation instead.",
RemovedInSphinx30Warning, stacklevel=2)
return self.astext()
else:
return super().__getitem__(key)
class displaymath(math_block):
"""Node for block level equations.
.. warning:: This node is provided to keep compatibility only.
It will be removed in nearly future. Don't use this from your extension.
.. deprecated:: 1.8
"""
# other directive-level nodes
class index(nodes.Invisible, nodes.Inline, nodes.TextElement):
@ -379,7 +326,6 @@ def setup(app):
app.add_node(seealso)
app.add_node(productionlist)
app.add_node(production)
app.add_node(displaymath)
app.add_node(index)
app.add_node(centered)
app.add_node(acks)

View File

@ -15,7 +15,6 @@ import pickle
import sys
import warnings
from collections import deque
from inspect import isclass
from io import StringIO
from os import path
@ -25,9 +24,7 @@ import sphinx
from sphinx import package_dir, locale
from sphinx.config import Config
from sphinx.config import CONFIG_FILENAME # NOQA # for compatibility (RemovedInSphinx30)
from sphinx.deprecation import (
RemovedInSphinx30Warning, RemovedInSphinx40Warning
)
from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.environment import BuildEnvironment
from sphinx.errors import ApplicationError, ConfigError, VersionRequirementError
from sphinx.events import EventManager
@ -35,12 +32,11 @@ from sphinx.locale import __
from sphinx.project import Project
from sphinx.registry import SphinxComponentRegistry
from sphinx.util import docutils
from sphinx.util import import_object, progress_message
from sphinx.util import logging
from sphinx.util import progress_message
from sphinx.util.build_phase import BuildPhase
from sphinx.util.console import bold # type: ignore
from sphinx.util.docutils import directive_helper
from sphinx.util.i18n import find_catalog_source_files
from sphinx.util.i18n import CatalogRepository
from sphinx.util.logging import prefixed_warnings
from sphinx.util.osutil import abspath, ensuredir, relpath
from sphinx.util.tags import Tags
@ -91,11 +87,15 @@ builtin_extensions = (
'sphinx.parsers',
'sphinx.registry',
'sphinx.roles',
'sphinx.transforms',
'sphinx.transforms.compact_bullet_list',
'sphinx.transforms.i18n',
'sphinx.transforms.references',
'sphinx.transforms.post_transforms',
'sphinx.transforms.post_transforms.code',
'sphinx.transforms.post_transforms.images',
'sphinx.transforms.post_transforms.compat',
'sphinx.util.compat',
'sphinx.versioning',
# collectors should be loaded by specific order
'sphinx.environment.collectors.dependencies',
'sphinx.environment.collectors.asset',
@ -265,21 +265,21 @@ class Sphinx:
"""Load translated strings from the configured localedirs if enabled in
the configuration.
"""
if self.config.language is not None:
if self.config.language is None:
self.translator, has_translation = locale.init([], None)
else:
logger.info(bold(__('loading translations [%s]... ') % self.config.language),
nonl=True)
user_locale_dirs = [
path.join(self.srcdir, x) for x in self.config.locale_dirs]
# compile mo files if sphinx.po file in user locale directories are updated
for catinfo in find_catalog_source_files(
user_locale_dirs, self.config.language, domains=['sphinx'],
charset=self.config.source_encoding):
catinfo.write_mo(self.config.language)
locale_dirs = [None, path.join(package_dir, 'locale')] + user_locale_dirs
else:
locale_dirs = []
self.translator, has_translation = locale.init(locale_dirs, self.config.language)
if self.config.language is not None:
repo = CatalogRepository(self.srcdir, self.config.locale_dirs,
self.config.language, self.config.source_encoding)
for catalog in repo.catalogs:
if catalog.domain == 'sphinx' and catalog.is_outdated():
catalog.write_mo(self.config.language)
locale_dirs = [None, path.join(package_dir, 'locale')] + list(repo.locale_dirs)
self.translator, has_translation = locale.init(locale_dirs, self.config.language)
if has_translation or self.config.language == 'en':
# "en" never needs to be translated
logger.info(__('done'))
@ -392,18 +392,6 @@ class Sphinx:
if version > sphinx.__display_version__[:3]:
raise VersionRequirementError(version)
def import_object(self, objname, source=None):
# type: (str, str) -> Any
"""Import an object from a ``module.name`` string.
.. deprecated:: 1.8
Use ``sphinx.util.import_object()`` instead.
"""
warnings.warn('app.import_object() is deprecated. '
'Use sphinx.util.add_object_type() instead.',
RemovedInSphinx30Warning, stacklevel=2)
return import_object(objname, source=None)
# event interface
def connect(self, event, callback):
# type: (str, Callable) -> int
@ -588,36 +576,14 @@ class Sphinx:
self.registry.add_enumerable_node(node, figtype, title_getter, override=override)
self.add_node(node, override=override, **kwds)
@property
def enumerable_nodes(self):
# type: () -> Dict[Type[nodes.Node], Tuple[str, TitleGetter]]
warnings.warn('app.enumerable_nodes() is deprecated. '
'Use app.get_domain("std").enumerable_nodes instead.',
RemovedInSphinx30Warning, stacklevel=2)
return self.registry.enumerable_nodes
def add_directive(self, name, obj, content=None, arguments=None, override=False, **options): # NOQA
# type: (str, Any, bool, Tuple[int, int, bool], bool, Any) -> None
def add_directive(self, name, cls, override=False):
# type: (str, Type[Directive], bool) -> None
"""Register a Docutils directive.
*name* must be the prospective directive name. There are two possible
ways to write a directive:
- In the docutils 0.4 style, *obj* is the directive function.
*content*, *arguments* and *options* are set as attributes on the
function and determine whether the directive has content, arguments
and options, respectively. **This style is deprecated.**
- In the docutils 0.5 style, *obj* is the directive class.
It must already have attributes named *has_content*,
*required_arguments*, *optional_arguments*,
*final_argument_whitespace* and *option_spec* that correspond to the
options for the function way. See `the Docutils docs
<http://docutils.sourceforge.net/docs/howto/rst-directives.html>`_
for details.
The directive class must inherit from the class
``docutils.parsers.rst.Directive``.
*name* must be the prospective directive name. *cls* is a directive
class which inherits ``docutils.parsers.rst.Directive``. For more
details, see `the Docutils docs
<http://docutils.sourceforge.net/docs/howto/rst-directives.html>`_ .
For example, the (already existing) :rst:dir:`literalinclude` directive
would be added like this:
@ -648,17 +614,12 @@ class Sphinx:
.. versionchanged:: 1.8
Add *override* keyword.
"""
logger.debug('[app] adding directive: %r',
(name, obj, content, arguments, options))
logger.debug('[app] adding directive: %r', (name, cls))
if not override and docutils.is_directive_registered(name):
logger.warning(__('directive %r is already registered, it will be overridden'),
name, type='app', subtype='add_directive')
if not isclass(obj) or not issubclass(obj, Directive):
directive = directive_helper(obj, content, arguments, **options)
docutils.register_directive(name, directive)
else:
docutils.register_directive(name, obj)
docutils.register_directive(name, cls)
def add_role(self, name, role, override=False):
# type: (str, Any, bool) -> None
@ -711,26 +672,8 @@ class Sphinx:
"""
self.registry.add_domain(domain, override=override)
def override_domain(self, domain):
# type: (Type[Domain]) -> None
"""Override a registered domain.
Make the given *domain* class known to Sphinx, assuming that there is
already a domain with its ``.name``. The new domain must be a subclass
of the existing one.
.. versionadded:: 1.0
.. deprecated:: 1.8
Integrated to :meth:`add_domain`.
"""
warnings.warn('app.override_domain() is deprecated. '
'Use app.add_domain() with override option instead.',
RemovedInSphinx30Warning, stacklevel=2)
self.registry.add_domain(domain, override=True)
def add_directive_to_domain(self, domain, name, obj, has_content=None, argument_spec=None,
override=False, **option_spec):
# type: (str, str, Any, bool, Any, bool, Any) -> None
def add_directive_to_domain(self, domain, name, cls, override=False):
# type: (str, str, Type[Directive], bool) -> None
"""Register a Docutils directive in a domain.
Like :meth:`add_directive`, but the directive is added to the domain
@ -740,9 +683,7 @@ class Sphinx:
.. versionchanged:: 1.8
Add *override* keyword.
"""
self.registry.add_directive_to_domain(domain, name, obj,
has_content, argument_spec, override=override,
**option_spec)
self.registry.add_directive_to_domain(domain, name, cls, override=override)
def add_role_to_domain(self, domain, name, role, override=False):
# type: (str, str, Union[RoleFunction, XRefRole], bool) -> None
@ -827,9 +768,6 @@ class Sphinx:
For the role content, you have the same syntactical possibilities as
for standard Sphinx roles (see :ref:`xref-syntax`).
This method is also available under the deprecated alias
:meth:`add_description_unit`.
.. versionchanged:: 1.8
Add *override* keyword.
"""
@ -1203,13 +1141,6 @@ class Sphinx:
return True
@property
def _setting_up_extension(self):
# type: () -> List[str]
warnings.warn('app._setting_up_extension is deprecated.',
RemovedInSphinx30Warning)
return ['?']
class TemplateBridge:
"""

View File

@ -19,12 +19,11 @@ from sphinx.environment.adapters.asset import ImageAdapter
from sphinx.errors import SphinxError
from sphinx.io import read_doc
from sphinx.locale import __
from sphinx.util import i18n, import_object, logging, rst, progress_message, status_iterator
from sphinx.util import import_object, logging, rst, progress_message, status_iterator
from sphinx.util.build_phase import BuildPhase
from sphinx.util.console import bold # type: ignore
from sphinx.util.docutils import sphinx_domains
from sphinx.util.i18n import find_catalog
from sphinx.util.matching import Matcher
from sphinx.util.i18n import CatalogRepository, docname_to_domain
from sphinx.util.osutil import SEP, ensuredir, relative_uri, relpath
from sphinx.util.parallel import ParallelTasks, SerialTasks, make_chunks, \
parallel_available
@ -40,7 +39,7 @@ except ImportError:
if False:
# For type annotation
from typing import Any, Callable, Dict, Iterable, List, Sequence, Set, Tuple, Type, Union # NOQA
from typing import Any, Dict, Iterable, List, Sequence, Set, Tuple, Type, Union # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.config import Config # NOQA
from sphinx.environment import BuildEnvironment # NOQA
@ -236,14 +235,10 @@ class Builder:
def compile_all_catalogs(self):
# type: () -> None
catalogs = i18n.find_catalog_source_files(
[path.join(self.srcdir, x) for x in self.config.locale_dirs],
self.config.language,
charset=self.config.source_encoding,
force_all=True,
excluded=Matcher(['**/.?**']))
message = __('all of %d po files') % len(catalogs)
self.compile_catalogs(catalogs, message)
repo = CatalogRepository(self.srcdir, self.config.locale_dirs,
self.config.language, self.config.source_encoding)
message = __('all of %d po files') % len(list(repo.catalogs))
self.compile_catalogs(set(repo.catalogs), message)
def compile_specific_catalogs(self, specified_files):
# type: (List[str]) -> None
@ -251,28 +246,25 @@ class Builder:
# type: (str) -> str
docname = self.env.path2doc(path.abspath(fpath))
if docname:
return find_catalog(docname, self.config.gettext_compact)
return docname_to_domain(docname, self.config.gettext_compact)
else:
return None
specified_domains = set(map(to_domain, specified_files))
specified_domains.discard(None)
catalogs = i18n.find_catalog_source_files(
[path.join(self.srcdir, x) for x in self.config.locale_dirs],
self.config.language,
domains=list(specified_domains),
charset=self.config.source_encoding,
excluded=Matcher(['**/.?**']))
catalogs = set()
domains = set(map(to_domain, specified_files))
repo = CatalogRepository(self.srcdir, self.config.locale_dirs,
self.config.language, self.config.source_encoding)
for catalog in repo.catalogs:
if catalog.domain in domains and catalog.is_outdated():
catalogs.add(catalog)
message = __('targets for %d po files that are specified') % len(catalogs)
self.compile_catalogs(catalogs, message)
def compile_update_catalogs(self):
# type: () -> None
catalogs = i18n.find_catalog_source_files(
[path.join(self.srcdir, x) for x in self.config.locale_dirs],
self.config.language,
charset=self.config.source_encoding,
excluded=Matcher(['**/.?**']))
repo = CatalogRepository(self.srcdir, self.config.locale_dirs,
self.config.language, self.config.source_encoding)
catalogs = {c for c in repo.catalogs if c.is_outdated()}
message = __('targets for %d po files that are out of date') % len(catalogs)
self.compile_catalogs(catalogs, message)
@ -298,8 +290,7 @@ class Builder:
logger.warning(__('file %r given on command line is not under the '
'source directory, ignoring'), filename)
continue
if not (path.isfile(filename) or
any(path.isfile(filename + suffix) for suffix in suffixes)):
if not path.isfile(filename):
logger.warning(__('file %r given on command line does not exist, '
'ignoring'), filename)
continue

View File

@ -36,7 +36,6 @@ except ImportError:
if False:
# For type annotation
from typing import Any, Dict, List, Set, Tuple # NOQA
from sphinx.application import Sphinx # NOQA
logger = logging.getLogger(__name__)

View File

@ -83,8 +83,7 @@ class ChangesBuilder(Builder):
entry = '<b>%s</b>: <i>%s</i>.' % (descname, ttext)
apichanges.append((entry, changeset.docname, changeset.lineno))
elif descname or changeset.module:
if not changeset.module:
module = _('Builtins')
module = changeset.module or _('Builtins')
if not descname:
descname = _('Module level')
if context:
@ -149,8 +148,8 @@ class ChangesBuilder(Builder):
'text': text
}
f.write(self.templates.render('changes/rstsource.html', ctx))
themectx = dict(('theme_' + key, val) for (key, val) in
self.theme.get_options({}).items())
themectx = {'theme_' + key: val for (key, val) in
self.theme.get_options({}).items()}
copy_asset_file(path.join(package_dir, 'themes', 'default', 'static', 'default.css_t'),
self.outdir, context=themectx, renderer=self.templates)
copy_asset_file(path.join(package_dir, 'themes', 'basic', 'static', 'basic.css'),

View File

@ -25,8 +25,7 @@ from sphinx.util.osutil import make_filename
if False:
# For type annotation
from typing import Any, Dict, Iterable, List, Set, Tuple # NOQA
from docutils import nodes # NOQA
from typing import Any, Dict, List, Set, Tuple # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.config import Config # NOQA

View File

@ -16,13 +16,14 @@ from os import path, walk, getenv
from time import time
from uuid import uuid4
from sphinx import addnodes
from sphinx.builders import Builder
from sphinx.domains.python import pairindextypes
from sphinx.errors import ThemeError
from sphinx.locale import __
from sphinx.util import split_index_msg, logging, status_iterator
from sphinx.util.console import bold # type: ignore
from sphinx.util.i18n import find_catalog
from sphinx.util.i18n import docname_to_domain
from sphinx.util.nodes import extract_messages, traverse_translatable_index
from sphinx.util.osutil import relpath, ensuredir, canon_path
from sphinx.util.tags import Tags
@ -140,7 +141,12 @@ class I18nBuilder(Builder):
def write_doc(self, docname, doctree):
# type: (str, nodes.document) -> None
catalog = self.catalogs[find_catalog(docname, self.config.gettext_compact)]
catalog = self.catalogs[docname_to_domain(docname, self.config.gettext_compact)]
for toctree in self.env.tocs[docname].traverse(addnodes.toctree):
for node, msg in extract_messages(toctree):
node.uid = '' # type: ignore # Hack UUID model
catalog.add(msg, node)
for node, msg in extract_messages(doctree):
catalog.add(msg, node)

View File

@ -24,9 +24,7 @@ from docutils.utils import relative_path
from sphinx import package_dir, __display_version__
from sphinx.builders import Builder
from sphinx.deprecation import (
RemovedInSphinx30Warning, RemovedInSphinx40Warning, deprecated_alias
)
from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
from sphinx.environment.adapters.asset import ImageAdapter
from sphinx.environment.adapters.indexentries import IndexEntries
from sphinx.environment.adapters.toctree import TocTree
@ -105,39 +103,6 @@ class Stylesheet(str):
return self
class JSContainer(list):
"""The container for JavaScript scripts."""
def insert(self, index, obj):
# type: (int, str) -> None
warnings.warn('To modify script_files in the theme is deprecated. '
'Please insert a <script> tag directly in your theme instead.',
RemovedInSphinx30Warning, stacklevel=3)
super().insert(index, obj)
def extend(self, other): # type: ignore
# type: (List[str]) -> None
warnings.warn('To modify script_files in the theme is deprecated. '
'Please insert a <script> tag directly in your theme instead.',
RemovedInSphinx30Warning, stacklevel=3)
for item in other:
self.append(item)
def __iadd__(self, other): # type: ignore
# type: (List[str]) -> JSContainer
warnings.warn('To modify script_files in the theme is deprecated. '
'Please insert a <script> tag directly in your theme instead.',
RemovedInSphinx30Warning, stacklevel=3)
for item in other:
self.append(item)
return self
def __add__(self, other):
# type: (List[str]) -> JSContainer
ret = JSContainer(self)
ret += other
return ret
class JavaScript(str):
"""A metadata of javascript file.
@ -187,7 +152,7 @@ class BuildInfo:
self.tags_hash = ''
if config:
values = dict((c.name, c.value) for c in config.filter(config_categories))
values = {c.name: c.value for c in config.filter(config_categories)}
self.config_hash = get_stable_hash(values)
if tags:
@ -247,7 +212,7 @@ class StandaloneHTMLBuilder(Builder):
self.css_files = [] # type: List[Dict[str, str]]
# JS files
self.script_files = JSContainer() # type: List[JavaScript]
self.script_files = [] # type: List[JavaScript]
def init(self):
# type: () -> None
@ -1068,16 +1033,6 @@ class StandaloneHTMLBuilder(Builder):
return False
ctx['hasdoc'] = hasdoc
def warn(*args, **kwargs):
# type: (Any, Any) -> str
"""Simple warn() wrapper for themes."""
warnings.warn('The template function warn() was deprecated. '
'Use warning() instead.',
RemovedInSphinx30Warning, stacklevel=2)
logger.warning(*args, **kwargs)
return '' # return empty string
ctx['warn'] = warn
ctx['toctree'] = lambda **kw: self._get_local_toctree(pagename, **kw)
self.add_sidebars(pagename, ctx)
ctx.update(addctx)

View File

@ -9,6 +9,7 @@
"""
import os
import warnings
from os import path
from docutils.frontend import OptionParser
@ -16,18 +17,12 @@ from docutils.frontend import OptionParser
import sphinx.builders.latex.nodes # NOQA # Workaround: import this before writer to avoid ImportError
from sphinx import package_dir, addnodes, highlighting
from sphinx.builders import Builder
from sphinx.builders.latex.transforms import (
BibliographyTransform, CitationReferenceTransform, MathReferenceTransform,
FootnoteDocnameUpdater, LaTeXFootnoteTransform, LiteralBlockTransform,
ShowUrlsTransform, DocumentTargetTransform,
)
from sphinx.builders.latex.util import ExtBabel
from sphinx.config import ENUM
from sphinx.environment import NoUri
from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.environment.adapters.asset import ImageAdapter
from sphinx.errors import SphinxError
from sphinx.errors import NoUri, SphinxError
from sphinx.locale import _, __
from sphinx.transforms import SphinxTransformer
from sphinx.util import texescape, logging, progress_message, status_iterator
from sphinx.util.console import bold, darkgreen # type: ignore
from sphinx.util.docutils import SphinxFileOutput, new_document
@ -40,9 +35,11 @@ from sphinx.writers.latex import (
ADDITIONAL_SETTINGS, DEFAULT_SETTINGS, LaTeXWriter, LaTeXTranslator
)
# load docutils.nodes after loading sphinx.builders.latex.nodes
from docutils import nodes # NOQA
if False:
# For type annotation
from docutils import nodes # NOQA
from typing import Any, Dict, Iterable, List, Tuple, Union # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.config import Config # NOQA
@ -263,7 +260,6 @@ class LaTeXBuilder(Builder):
docname, toctree_only,
appendices=((docclass != 'howto') and self.config.latex_appendices or []))
doctree['tocdepth'] = tocdepth
self.apply_transforms(doctree)
self.post_process_images(doctree)
self.update_doc_context(title, author)
@ -295,7 +291,6 @@ class LaTeXBuilder(Builder):
def assemble_doctree(self, indexfile, toctree_only, appendices):
# type: (str, bool, List[str]) -> nodes.document
from docutils import nodes # NOQA
self.docnames = set([indexfile] + appendices)
logger.info(darkgreen(indexfile) + " ", nonl=True)
tree = self.env.get_doctree(indexfile)
@ -340,14 +335,8 @@ class LaTeXBuilder(Builder):
def apply_transforms(self, doctree):
# type: (nodes.document) -> None
transformer = SphinxTransformer(doctree)
transformer.set_environment(self.env)
transformer.add_transforms([BibliographyTransform,
ShowUrlsTransform,
LaTeXFootnoteTransform,
LiteralBlockTransform,
DocumentTargetTransform])
transformer.apply_transforms()
warnings.warn('LaTeXBuilder.apply_transforms() is deprecated.',
RemovedInSphinx40Warning)
def finish(self):
# type: () -> None
@ -484,11 +473,10 @@ def default_latex_documents(config):
def setup(app):
# type: (Sphinx) -> Dict[str, Any]
app.setup_extension('sphinx.builders.latex.transforms')
app.add_builder(LaTeXBuilder)
app.add_post_transform(CitationReferenceTransform)
app.add_post_transform(MathReferenceTransform)
app.connect('config-inited', validate_config_values)
app.add_transform(FootnoteDocnameUpdater)
app.add_config_value('latex_engine', default_latex_engine, None,
ENUM('pdflatex', 'xelatex', 'lualatex', 'platex'))

View File

@ -17,11 +17,13 @@ from sphinx.builders.latex.nodes import (
captioned_literal_block, footnotemark, footnotetext, math_reference, thebibliography
)
from sphinx.transforms import SphinxTransform
from sphinx.transforms.post_transforms import SphinxPostTransform
from sphinx.util.nodes import NodeMatcher
if False:
# For type annotation
from typing import Any, Dict, List, Set, Tuple, Union # NOQA
from typing import Any, Dict, List, Set, Tuple # NOQA
from sphinx.application import Sphinx # NOQA
URI_SCHEMES = ('mailto:', 'http:', 'https:', 'ftp:')
@ -38,7 +40,7 @@ class FootnoteDocnameUpdater(SphinxTransform):
node['docname'] = self.env.docname
class ShowUrlsTransform(SphinxTransform):
class ShowUrlsTransform(SphinxPostTransform):
"""Expand references to inline text or footnotes.
For more information, see :confval:`latex_show_urls`.
@ -46,11 +48,12 @@ class ShowUrlsTransform(SphinxTransform):
.. note:: This transform is used for integrated doctree
"""
default_priority = 400
builders = ('latex',)
# references are expanded to footnotes (or not)
expanded = False
def apply(self, **kwargs):
def run(self, **kwargs):
# type: (Any) -> None
try:
# replace id_prefix temporarily
@ -177,7 +180,7 @@ class FootnoteCollector(nodes.NodeVisitor):
self.footnote_refs.append(node)
class LaTeXFootnoteTransform(SphinxTransform):
class LaTeXFootnoteTransform(SphinxPostTransform):
"""Convert footnote definitions and references to appropriate form to LaTeX.
* Replace footnotes on restricted zone (e.g. headings) by footnotemark node.
@ -345,8 +348,9 @@ class LaTeXFootnoteTransform(SphinxTransform):
"""
default_priority = 600
builders = ('latex',)
def apply(self, **kwargs):
def run(self, **kwargs):
# type: (Any) -> None
footnotes = list(self.document.traverse(nodes.footnote))
for node in footnotes:
@ -486,7 +490,7 @@ class LaTeXFootnoteVisitor(nodes.NodeVisitor):
return None
class BibliographyTransform(SphinxTransform):
class BibliographyTransform(SphinxPostTransform):
"""Gather bibliography entries to tail of document.
Before::
@ -517,8 +521,9 @@ class BibliographyTransform(SphinxTransform):
...
"""
default_priority = 750
builders = ('latex',)
def apply(self, **kwargs):
def run(self, **kwargs):
# type: (Any) -> None
citations = thebibliography()
for node in self.document.traverse(nodes.citation):
@ -529,19 +534,17 @@ class BibliographyTransform(SphinxTransform):
self.document += citations
class CitationReferenceTransform(SphinxTransform):
class CitationReferenceTransform(SphinxPostTransform):
"""Replace pending_xref nodes for citation by citation_reference.
To handle citation reference easily on LaTeX writer, this converts
pending_xref nodes to citation_reference.
"""
default_priority = 5 # before ReferencesResolver
builders = ('latex',)
def apply(self, **kwargs):
def run(self, **kwargs):
# type: (Any) -> None
if self.app.builder.name != 'latex':
return
matcher = NodeMatcher(addnodes.pending_xref, refdomain='std', reftype='citation')
citations = self.env.get_domain('std').data['citations']
for node in self.document.traverse(matcher): # type: addnodes.pending_xref
@ -552,19 +555,17 @@ class CitationReferenceTransform(SphinxTransform):
node.replace_self(citation_ref)
class MathReferenceTransform(SphinxTransform):
class MathReferenceTransform(SphinxPostTransform):
"""Replace pending_xref nodes for math by math_reference.
To handle math reference easily on LaTeX writer, this converts pending_xref
nodes to math_reference.
"""
default_priority = 5 # before ReferencesResolver
builders = ('latex',)
def apply(self, **kwargs):
def run(self, **kwargs):
# type: (Any) -> None
if self.app.builder.name != 'latex':
return
equations = self.env.get_domain('math').data['objects']
for node in self.document.traverse(addnodes.pending_xref):
if node['refdomain'] == 'math' and node['reftype'] in ('eq', 'numref'):
@ -574,31 +575,83 @@ class MathReferenceTransform(SphinxTransform):
node.replace_self(refnode)
class LiteralBlockTransform(SphinxTransform):
class LiteralBlockTransform(SphinxPostTransform):
"""Replace container nodes for literal_block by captioned_literal_block."""
default_priority = 400
builders = ('latex',)
def apply(self, **kwargs):
def run(self, **kwargs):
# type: (Any) -> None
if self.app.builder.name != 'latex':
return
matcher = NodeMatcher(nodes.container, literal_block=True)
for node in self.document.traverse(matcher): # type: nodes.container
newnode = captioned_literal_block('', *node.children, **node.attributes)
node.replace_self(newnode)
class DocumentTargetTransform(SphinxTransform):
class DocumentTargetTransform(SphinxPostTransform):
"""Add :doc label to the first section of each document."""
default_priority = 400
builders = ('latex',)
def apply(self, **kwargs):
def run(self, **kwargs):
# type: (Any) -> None
if self.app.builder.name != 'latex':
return
for node in self.document.traverse(addnodes.start_of_file):
section = node.next_node(nodes.section)
if section:
section['ids'].append(':doc') # special label for :doc:
class IndexInSectionTitleTransform(SphinxTransform):
"""Move index nodes in section title to outside of the title.
LaTeX index macro is not compatible with some handling of section titles
such as uppercasing done on LaTeX side (cf. fncychap handling of ``\\chapter``).
Moving the index node to after the title node fixes that.
Before::
<section>
<title>
blah blah <index entries=[...]/>blah
<paragraph>
blah blah blah
...
After::
<section>
<title>
blah blah blah
<index entries=[...]/>
<paragraph>
blah blah blah
...
"""
default_priority = 400
def apply(self):
for node in self.document.traverse(nodes.title):
if isinstance(node.parent, nodes.section):
for i, index in enumerate(node.traverse(addnodes.index)):
# move the index node next to the section title
node.remove(index)
node.parent.insert(i + 1, index)
def setup(app):
# type: (Sphinx) -> Dict[str, Any]
app.add_transform(FootnoteDocnameUpdater)
app.add_post_transform(BibliographyTransform)
app.add_post_transform(CitationReferenceTransform)
app.add_post_transform(DocumentTargetTransform)
app.add_post_transform(IndexInSectionTitleTransform)
app.add_post_transform(LaTeXFootnoteTransform)
app.add_post_transform(LiteralBlockTransform)
app.add_post_transform(MathReferenceTransform)
app.add_post_transform(ShowUrlsTransform)
return {
'version': 'builtin',
'parallel_read_safe': True,
'parallel_write_safe': True,
}

View File

@ -8,12 +8,8 @@
:license: BSD, see LICENSE for details.
"""
import warnings
from docutils.writers.latex2e import Babel
from sphinx.deprecation import RemovedInSphinx30Warning
class ExtBabel(Babel):
cyrillic_languages = ('bulgarian', 'kazakh', 'mongolian', 'russian', 'ukrainian')
@ -25,13 +21,6 @@ class ExtBabel(Babel):
self.supported = True
super().__init__(language_code or '')
def get_shorthandoff(self):
# type: () -> str
warnings.warn('ExtBabel.get_shorthandoff() is deprecated.',
RemovedInSphinx30Warning, stacklevel=2)
from sphinx.writers.latex import SHORTHANDOFF
return SHORTHANDOFF
def uses_cyrillic(self):
# type: () -> bool
return self.language in self.cyrillic_languages

View File

@ -30,7 +30,7 @@ from sphinx.util.requests import is_ssl_error
if False:
# For type annotation
from typing import Any, Dict, List, Set, Tuple, Union # NOQA
from typing import Any, Dict, List, Set, Tuple # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.util.requests.requests import Response # NOQA

View File

@ -15,7 +15,7 @@ from docutils.io import FileOutput
from sphinx import addnodes
from sphinx.builders import Builder
from sphinx.environment import NoUri
from sphinx.errors import NoUri
from sphinx.locale import __
from sphinx.util import logging
from sphinx.util import progress_message

View File

@ -18,8 +18,8 @@ from docutils.io import FileOutput
from sphinx import addnodes
from sphinx import package_dir
from sphinx.builders import Builder
from sphinx.environment import NoUri
from sphinx.environment.adapters.asset import ImageAdapter
from sphinx.errors import NoUri
from sphinx.locale import _, __
from sphinx.util import logging
from sphinx.util import progress_message, status_iterator
@ -27,7 +27,7 @@ from sphinx.util.console import darkgreen # type: ignore
from sphinx.util.docutils import new_document
from sphinx.util.fileutil import copy_asset_file
from sphinx.util.nodes import inline_all_toctrees
from sphinx.util.osutil import SEP, make_filename_from_project
from sphinx.util.osutil import SEP, ensuredir, make_filename_from_project
from sphinx.writers.texinfo import TexinfoWriter, TexinfoTranslator
if False:
@ -134,6 +134,7 @@ class TexinfoBuilder(Builder):
settings.docname = docname
doctree.settings = settings
docwriter.write(doctree, destination)
self.copy_image_files(targetname[:-5])
def assemble_doctree(self, indexfile, toctree_only, appendices):
# type: (str, bool, List[str]) -> nodes.document
@ -180,11 +181,10 @@ class TexinfoBuilder(Builder):
def finish(self):
# type: () -> None
self.copy_image_files()
self.copy_support_files()
def copy_image_files(self):
# type: () -> None
def copy_image_files(self, targetname):
# type: (str) -> None
if self.images:
stringify_func = ImageAdapter(self.app.env).get_original_image_uri
for src in status_iterator(self.images, __('copying images... '), "brown",
@ -192,8 +192,9 @@ class TexinfoBuilder(Builder):
stringify_func=stringify_func):
dest = self.images[src]
try:
copy_asset_file(path.join(self.srcdir, src),
path.join(self.outdir, dest))
imagedir = path.join(self.outdir, targetname + '-figures')
ensuredir(imagedir)
copy_asset_file(path.join(self.srcdir, dest), imagedir)
except Exception as err:
logger.warning(__('cannot copy image file %r: %s'),
path.join(self.srcdir, src), err)

View File

@ -66,7 +66,7 @@ def handle_exception(app, args, exception, stderr=sys.stderr):
print(__('This can happen with very large or deeply nested source '
'files. You can carefully increase the default Python '
'recursion limit of 1000 in conf.py with e.g.:'), file=stderr)
print(__(' import sys; sys.setrecursionlimit(1500)'), file=stderr)
print(' import sys; sys.setrecursionlimit(1500)', file=stderr)
else:
print(red(__('Exception occurred:')), file=stderr)
print(format_exception_cut_frames().rstrip(), file=stderr)
@ -186,7 +186,7 @@ files can be built by specifying individual filenames.
group.add_argument('-W', action='store_true', dest='warningiserror',
help=__('turn warnings into errors'))
group.add_argument('--keep-going', action='store_true', dest='keep_going',
help=__("With -W, Keep going when getting warnings"))
help=__("With -W, keep going when getting warnings"))
group.add_argument('-T', action='store_true', dest='traceback',
help=__('show full traceback on exception'))
group.add_argument('-P', action='store_true', dest='pdb',

View File

@ -57,10 +57,8 @@ EXTENSIONS = OrderedDict([
('imgmath', __('include math, rendered as PNG or SVG images')),
('mathjax', __('include math, rendered in the browser by MathJax')),
('ifconfig', __('conditional inclusion of content based on config values')),
('viewcode',
__('include links to the source code of documented Python objects')),
('githubpages',
__('create .nojekyll file to publish the document on GitHub pages')),
('viewcode', __('include links to the source code of documented Python objects')),
('githubpages', __('create .nojekyll file to publish the document on GitHub pages')),
])
DEFAULTS = {
@ -140,8 +138,7 @@ def boolean(x):
def suffix(x):
# type: (str) -> str
if not (x[0:1] == '.' and len(x) > 1):
raise ValidationError(__("Please enter a file suffix, "
"e.g. '.rst' or '.txt'."))
raise ValidationError(__("Please enter a file suffix, e.g. '.rst' or '.txt'."))
return x
@ -246,16 +243,16 @@ def ask_user(d):
"""
print(bold(__('Welcome to the Sphinx %s quickstart utility.')) % __display_version__)
print(__('''
Please enter values for the following settings (just press Enter to
accept a default value, if one is given in brackets).'''))
print()
print(__('Please enter values for the following settings (just press Enter to\n'
'accept a default value, if one is given in brackets).'))
if 'path' in d:
print(bold(__('''
Selected root path: %s''') % d['path']))
print()
print(bold(__('Selected root path: %s')) % d['path'])
else:
print(__('''
Enter the root path for documentation.'''))
print()
print(__('Enter the root path for documentation.'))
d['path'] = do_prompt(__('Root path for the documentation'), '.', is_path)
while path.isfile(path.join(d['path'], 'conf.py')) or \
@ -265,70 +262,68 @@ Enter the root path for documentation.'''))
'selected root path.')))
print(__('sphinx-quickstart will not overwrite existing Sphinx projects.'))
print()
d['path'] = do_prompt(__('Please enter a new root path (or just Enter '
'to exit)'), '', is_path)
d['path'] = do_prompt(__('Please enter a new root path (or just Enter to exit)'),
'', is_path)
if not d['path']:
sys.exit(1)
if 'sep' not in d:
print(__('''
You have two options for placing the build directory for Sphinx output.
Either, you use a directory "_build" within the root path, or you separate
"source" and "build" directories within the root path.'''))
d['sep'] = do_prompt(__('Separate source and build directories (y/n)'),
'n', boolean)
print()
print(__('You have two options for placing the build directory for Sphinx output.\n'
'Either, you use a directory "_build" within the root path, or you separate\n'
'"source" and "build" directories within the root path.'))
d['sep'] = do_prompt(__('Separate source and build directories (y/n)'), 'n', boolean)
if 'dot' not in d:
print(__('''
Inside the root directory, two more directories will be created; "_templates"
for custom HTML templates and "_static" for custom stylesheets and other static
files. You can enter another prefix (such as ".") to replace the underscore.'''))
print()
print(__('Inside the root directory, two more directories will be created; "_templates"\n' # NOQA
'for custom HTML templates and "_static" for custom stylesheets and other static\n' # NOQA
'files. You can enter another prefix (such as ".") to replace the underscore.')) # NOQA
d['dot'] = do_prompt(__('Name prefix for templates and static dir'), '_', ok)
if 'project' not in d:
print(__('''
The project name will occur in several places in the built documentation.'''))
print()
print(__('The project name will occur in several places in the built documentation.'))
d['project'] = do_prompt(__('Project name'))
if 'author' not in d:
d['author'] = do_prompt(__('Author name(s)'))
if 'version' not in d:
print(__('''
Sphinx has the notion of a "version" and a "release" for the
software. Each version can have multiple releases. For example, for
Python the version is something like 2.5 or 3.0, while the release is
something like 2.5.1 or 3.0a1. If you don't need this dual structure,
just set both to the same value.'''))
print()
print(__('Sphinx has the notion of a "version" and a "release" for the\n'
'software. Each version can have multiple releases. For example, for\n'
'Python the version is something like 2.5 or 3.0, while the release is\n'
'something like 2.5.1 or 3.0a1. If you don\'t need this dual structure,\n'
'just set both to the same value.'))
d['version'] = do_prompt(__('Project version'), '', allow_empty)
if 'release' not in d:
d['release'] = do_prompt(__('Project release'), d['version'], allow_empty)
if 'language' not in d:
print(__('''
If the documents are to be written in a language other than English,
you can select a language here by its language code. Sphinx will then
translate text that it generates into that language.
For a list of supported codes, see
http://sphinx-doc.org/config.html#confval-language.'''))
print()
print(__('If the documents are to be written in a language other than English,\n'
'you can select a language here by its language code. Sphinx will then\n'
'translate text that it generates into that language.\n'
'\n'
'For a list of supported codes, see\n'
'http://sphinx-doc.org/config.html#confval-language.'))
d['language'] = do_prompt(__('Project language'), 'en')
if d['language'] == 'en':
d['language'] = None
if 'suffix' not in d:
print(__('''
The file name suffix for source files. Commonly, this is either ".txt"
or ".rst". Only files with this suffix are considered documents.'''))
print()
print(__('The file name suffix for source files. Commonly, this is either ".txt"\n'
'or ".rst". Only files with this suffix are considered documents.'))
d['suffix'] = do_prompt(__('Source file suffix'), '.rst', suffix)
if 'master' not in d:
print(__('''
One document is special in that it is considered the top node of the
"contents tree", that is, it is the root of the hierarchical structure
of the documents. Normally, this is "index", but if your "index"
document is a custom template, you can also set this to another filename.'''))
d['master'] = do_prompt(__('Name of your master document (without suffix)'),
'index')
print()
print(__('One document is special in that it is considered the top node of the\n'
'"contents tree", that is, it is the root of the hierarchical structure\n'
'of the documents. Normally, this is "index", but if your "index"\n'
'document is a custom template, you can also set this to another filename.'))
d['master'] = do_prompt(__('Name of your master document (without suffix)'), 'index')
while path.isfile(path.join(d['path'], d['master'] + d['suffix'])) or \
path.isfile(path.join(d['path'], 'source', d['master'] + d['suffix'])):
@ -341,30 +336,27 @@ document is a custom template, you can also set this to another filename.'''))
'existing file and press Enter'), d['master'])
if 'extensions' not in d:
print(__('Indicate which of the following Sphinx extensions should be '
'enabled:'))
print(__('Indicate which of the following Sphinx extensions should be enabled:'))
d['extensions'] = []
for name, description in EXTENSIONS.items():
if do_prompt('%s: %s (y/n)' % (name, description), 'n', boolean):
d['extensions'].append('sphinx.ext.%s' % name)
# Handle conflicting options
if set(['sphinx.ext.imgmath', 'sphinx.ext.mathjax']).issubset(
d['extensions']):
print(__('Note: imgmath and mathjax cannot be enabled at the same '
'time. imgmath has been deselected.'))
if {'sphinx.ext.imgmath', 'sphinx.ext.mathjax'}.issubset(d['extensions']):
print(__('Note: imgmath and mathjax cannot be enabled at the same time. '
'imgmath has been deselected.'))
d['extensions'].remove('sphinx.ext.imgmath')
if 'makefile' not in d:
print(__('''
A Makefile and a Windows command file can be generated for you so that you
only have to run e.g. `make html' instead of invoking sphinx-build
directly.'''))
print()
print(__('A Makefile and a Windows command file can be generated for you so that you\n'
'only have to run e.g. `make html\' instead of invoking sphinx-build\n'
'directly.'))
d['makefile'] = do_prompt(__('Create Makefile? (y/n)'), 'y', boolean)
if 'batchfile' not in d:
d['batchfile'] = do_prompt(__('Create Windows command file? (y/n)'),
'y', boolean)
d['batchfile'] = do_prompt(__('Create Windows command file? (y/n)'), 'y', boolean)
print()
@ -448,17 +440,18 @@ def generate(d, overwrite=True, silent=False, templatedir=None):
return
print()
print(bold(__('Finished: An initial directory structure has been created.')))
print(__('''
You should now populate your master file %s and create other documentation
source files. ''') % masterfile + ((d['makefile'] or d['batchfile']) and __('''\
Use the Makefile to build the docs, like so:
make builder
''') or __('''\
Use the sphinx-build command to build the docs, like so:
sphinx-build -b builder %s %s
''') % (srcdir, builddir)) + __('''\
where "builder" is one of the supported builders, e.g. html, latex or linkcheck.
'''))
print()
print(__('You should now populate your master file %s and create other documentation\n'
'source files. ') % masterfile, end='')
if d['makefile'] or d['batchfile']:
print(__('Use the Makefile to build the docs, like so:\n'
' make builder'))
else:
print(__('Use the sphinx-build command to build the docs, like so:\n'
' sphinx-build -b builder %s %s') % (srcdir, builddir))
print(__('where "builder" is one of the supported builders, '
'e.g. html, latex or linkcheck.'))
print()
def valid_dir(d):
@ -469,7 +462,7 @@ def valid_dir(d):
if not path.isdir(dir):
return False
if set(['Makefile', 'make.bat']) & set(os.listdir(dir)):
if {'Makefile', 'make.bat'} & set(os.listdir(dir)):
return False
if d['sep']:
@ -493,16 +486,18 @@ def valid_dir(d):
def get_parser():
# type: () -> argparse.ArgumentParser
description = __(
"\n"
"Generate required files for a Sphinx project.\n"
"\n"
"sphinx-quickstart is an interactive tool that asks some questions about your\n"
"project and then generates a complete documentation directory and sample\n"
"Makefile to be used with sphinx-build.\n"
)
parser = argparse.ArgumentParser(
usage='%(prog)s [OPTIONS] <PROJECT_DIR>',
epilog=__("For more information, visit <http://sphinx-doc.org/>."),
description=__("""
Generate required files for a Sphinx project.
sphinx-quickstart is an interactive tool that asks some questions about your
project and then generates a complete documentation directory and sample
Makefile to be used with sphinx-build.
"""))
description=description)
parser.add_argument('-q', '--quiet', action='store_true', dest='quiet',
default=None,
@ -590,7 +585,7 @@ def main(argv=sys.argv[1:]):
d = vars(args)
# delete None or False value
d = dict((k, v) for k, v in d.items() if v is not None)
d = {k: v for k, v in d.items() if v is not None}
# handle use of CSV-style extension values
d.setdefault('extensions', [])
@ -601,12 +596,12 @@ def main(argv=sys.argv[1:]):
try:
if 'quiet' in d:
if not set(['project', 'author']).issubset(d):
print(__('''"quiet" is specified, but any of "project" or \
"author" is not specified.'''))
if not {'project', 'author'}.issubset(d):
print(__('"quiet" is specified, but any of "project" or '
'"author" is not specified.'))
return 1
if set(['quiet', 'project', 'author']).issubset(d):
if {'quiet', 'project', 'author'}.issubset(d):
# quiet mode with all required params satisfied, use default
d.setdefault('version', '')
d.setdefault('release', d['version'])

View File

@ -1,49 +0,0 @@
"""
sphinx.cmdline
~~~~~~~~~~~~~~
sphinx-build command-line handling.
:copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import sys
import warnings
from sphinx.cmd import build
from sphinx.deprecation import RemovedInSphinx30Warning
if False:
# For type annotation
import argparse # NOQA
from typing import Any, IO, List, Union # NOQA
from sphinx.application import Sphinx # NOQA
def handle_exception(app, args, exception, stderr=sys.stderr):
# type: (Sphinx, Any, Union[Exception, KeyboardInterrupt], IO) -> None
warnings.warn('sphinx.cmdline module is deprecated. Use sphinx.cmd.build instead.',
RemovedInSphinx30Warning, stacklevel=2)
build.handle_exception(app, args, exception, stderr)
def jobs_argument(value):
# type: (str) -> int
warnings.warn('sphinx.cmdline module is deprecated. Use sphinx.cmd.build instead.',
RemovedInSphinx30Warning, stacklevel=2)
return build.jobs_argument(value)
def get_parser():
# type: () -> argparse.ArgumentParser
warnings.warn('sphinx.cmdline module is deprecated. Use sphinx.cmd.build instead.',
RemovedInSphinx30Warning, stacklevel=2)
return build.get_parser()
def main(argv=sys.argv[1:]):
# type: (List[str]) -> int
warnings.warn('sphinx.cmdline module is deprecated. Use sphinx.cmd.build instead.',
RemovedInSphinx30Warning, stacklevel=2)
return build.main(argv)

View File

@ -16,7 +16,7 @@ from collections import OrderedDict
from os import path, getenv
from typing import Any, NamedTuple, Union
from sphinx.deprecation import RemovedInSphinx30Warning, RemovedInSphinx40Warning
from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.errors import ConfigError, ExtensionError
from sphinx.locale import _, __
from sphinx.util import logging
@ -27,7 +27,7 @@ from sphinx.util.typing import NoneType
if False:
# For type annotation
from typing import Any, Callable, Dict, Generator, Iterator, List, Set, Tuple, Union # NOQA
from typing import Callable, Dict, Generator, Iterator, List, Set, Tuple # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.environment import BuildEnvironment # NOQA
from sphinx.util.tags import Tags # NOQA
@ -155,27 +155,8 @@ class Config:
'env', []),
} # type: Dict[str, Tuple]
def __init__(self, *args):
# type: (Any) -> None
if len(args) == 4:
# old style arguments: (dirname, filename, overrides, tags)
warnings.warn('The argument of Config() class has been changed. '
'Use Config.read() to read configuration from conf.py.',
RemovedInSphinx30Warning, stacklevel=2)
dirname, filename, overrides, tags = args
if dirname is None:
config = {} # type: Dict[str, Any]
else:
config = eval_config_file(path.join(dirname, filename), tags)
else:
# new style arguments: (config={}, overrides={})
if len(args) == 0:
config, overrides = {}, {}
elif len(args) == 1:
config, overrides = args[0], {}
else:
config, overrides = args[:2]
def __init__(self, config={}, overrides={}):
# type: (Dict[str, Any], Dict[str, Any]) -> None
self.overrides = overrides
self.values = Config.config_values.copy()
self._raw_config = config
@ -196,18 +177,6 @@ class Config:
namespace = eval_config_file(filename, tags)
return cls(namespace, overrides or {})
def check_types(self):
# type: () -> None
warnings.warn('Config.check_types() is deprecated. Use check_confval_types() instead.',
RemovedInSphinx30Warning, stacklevel=2)
check_confval_types(None, self)
def check_unicode(self):
# type: () -> None
warnings.warn('Config.check_unicode() is deprecated. Use check_unicode() instead.',
RemovedInSphinx30Warning, stacklevel=2)
check_unicode(self)
def convert_overrides(self, name, value):
# type: (str, Any) -> Any
if not isinstance(value, str):

View File

@ -17,15 +17,15 @@ if False:
from typing import Any, Dict, Type # NOQA
class RemovedInSphinx30Warning(PendingDeprecationWarning):
class RemovedInSphinx40Warning(DeprecationWarning):
pass
class RemovedInSphinx40Warning(PendingDeprecationWarning):
class RemovedInSphinx50Warning(PendingDeprecationWarning):
pass
RemovedInNextVersionWarning = RemovedInSphinx30Warning
RemovedInNextVersionWarning = RemovedInSphinx40Warning
def deprecated_alias(modname, objects, warning):
@ -37,7 +37,7 @@ def deprecated_alias(modname, objects, warning):
sys.modules[modname] = _ModuleWrapper(module, modname, objects, warning) # type: ignore
class _ModuleWrapper(object):
class _ModuleWrapper:
def __init__(self, module, modname, objects, warning):
# type: (Any, str, Dict, Type[Warning]) -> None
self._module = module

View File

@ -15,28 +15,15 @@ from docutils import nodes
from docutils.parsers.rst import directives, roles
from sphinx import addnodes
from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
from sphinx.util import docutils
from sphinx.util.docfields import DocFieldTransformer
from sphinx.util.docfields import DocFieldTransformer, TypedField
from sphinx.util.docutils import SphinxDirective
# import all directives sphinx provides
from sphinx.directives.code import ( # noqa
Highlight, CodeBlock, LiteralInclude
)
from sphinx.directives.other import ( # noqa
TocTree, Author, Index, VersionChange, SeeAlso,
TabularColumns, Centered, Acks, HList, Only, Include, Class
)
from sphinx.directives.patches import ( # noqa
Figure, Meta
)
if False:
# For type annotation
from typing import Any, Dict # NOQA
from typing import Any, Dict, Tuple # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.config import Config # NOQA
from sphinx.environment import BuildEnvironment # NOQA
from sphinx.util.docfields import Field # NOQA
from sphinx.util.typing import DirectiveOption # NOQA
@ -46,6 +33,19 @@ nl_escape_re = re.compile(r'\\\n')
strip_backslash_re = re.compile(r'\\(.)')
def optional_int(argument):
"""
Check for an integer argument or None value; raise ``ValueError`` if not.
"""
if argument is None:
return None
else:
value = int(argument)
if value < 0:
raise ValueError('negative value; must be positive or zero')
return value
class ObjectDescription(SphinxDirective):
"""
Directive to describe a class, function or similar object. Not used
@ -67,6 +67,23 @@ class ObjectDescription(SphinxDirective):
objtype = None # type: str
indexnode = None # type: addnodes.index
# Warning: this might be removed in future version. Don't touch this from extensions.
_doc_field_type_map = {} # type: Dict[str, Tuple[Field, bool]]
def get_field_type_map(self):
# type: () -> Dict[str, Tuple[Field, bool]]
if self._doc_field_type_map == {}:
for field in self.doc_field_types:
for name in field.names:
self._doc_field_type_map[name] = (field, False)
if field.is_typed:
typed_field = cast(TypedField, field)
for name in typed_field.typenames:
self._doc_field_type_map[name] = (field, True)
return self._doc_field_type_map
def get_signatures(self):
# type: () -> List[str]
"""
@ -187,10 +204,6 @@ class ObjectDescription(SphinxDirective):
return [self.indexnode, node]
# backwards compatible old name
DescDirective = ObjectDescription
class DefaultRole(SphinxDirective):
"""
Set the default interpreted text role. Overridden from docutils.
@ -243,6 +256,43 @@ class DefaultDomain(SphinxDirective):
self.env.temp_data['default_domain'] = self.env.domains.get(domain_name)
return []
from sphinx.directives.code import ( # noqa
Highlight, CodeBlock, LiteralInclude
)
from sphinx.directives.other import ( # noqa
TocTree, Author, Index, VersionChange, SeeAlso,
TabularColumns, Centered, Acks, HList, Only, Include, Class
)
from sphinx.directives.patches import ( # noqa
Figure, Meta
)
deprecated_alias('sphinx.directives',
{
'Highlight': Highlight,
'CodeBlock': CodeBlock,
'LiteralInclude': LiteralInclude,
'TocTree': TocTree,
'Author': Author,
'Index': Index,
'VersionChange': VersionChange,
'SeeAlso': SeeAlso,
'TabularColumns': TabularColumns,
'Centered': Centered,
'Acks': Acks,
'HList': HList,
'Only': Only,
'Include': Include,
'Class': Class,
'Figure': Figure,
'Meta': Meta,
},
RemovedInSphinx40Warning)
# backwards compatible old name (will be marked deprecated in 3.0)
DescDirective = ObjectDescription
def setup(app):
# type: (Sphinx) -> Dict[str, Any]

View File

@ -20,7 +20,6 @@ from sphinx.locale import __
from sphinx.util import logging
from sphinx.util import parselinenos
from sphinx.util.docutils import SphinxDirective
from sphinx.util.nodes import set_source_info
if False:
# For type annotation
@ -154,8 +153,8 @@ class CodeBlock(SphinxDirective):
code = '\n'.join(lines)
literal = nodes.literal_block(code, code) # type: nodes.Element
literal['linenos'] = 'linenos' in self.options or \
'lineno-start' in self.options
if 'linenos' in self.options or 'lineno-start' in self.options:
literal['linenos'] = True
literal['classes'] += self.options.get('class', [])
if self.arguments:
# highlight language specified
@ -173,7 +172,7 @@ class CodeBlock(SphinxDirective):
extra_args['hl_lines'] = hl_lines
if 'lineno-start' in self.options:
extra_args['linenostart'] = self.options['lineno-start']
set_source_info(self, literal)
self.set_source_info(literal)
caption = self.options.get('caption')
if caption:
@ -446,14 +445,14 @@ class LiteralInclude(SphinxDirective):
text, lines = reader.read(location=location)
retnode = nodes.literal_block(text, text, source=filename) # type: nodes.Element
set_source_info(self, retnode)
self.set_source_info(retnode)
if self.options.get('diff'): # if diff is set, set udiff
retnode['language'] = 'udiff'
elif 'language' in self.options:
retnode['language'] = self.options['language']
retnode['linenos'] = ('linenos' in self.options or
'lineno-start' in self.options or
'lineno-match' in self.options)
if ('linenos' in self.options or 'lineno-start' in self.options or
'lineno-match' in self.options):
retnode['linenos'] = True
retnode['classes'] += self.options.get('class', [])
extra_args = retnode['highlight_args'] = {}
if 'emphasize-lines' in self.options:

View File

@ -21,12 +21,11 @@ from sphinx.locale import _
from sphinx.util import url_re, docname_join
from sphinx.util.docutils import SphinxDirective
from sphinx.util.matching import Matcher, patfilter
from sphinx.util.nodes import explicit_title_re, set_source_info, \
process_index_entry
from sphinx.util.nodes import explicit_title_re, process_index_entry
if False:
# For type annotation
from typing import Any, Dict, Generator, List, Tuple # NOQA
from typing import Any, Dict, List # NOQA
from sphinx.application import Sphinx # NOQA
@ -77,7 +76,7 @@ class TocTree(SphinxDirective):
subnode['includehidden'] = 'includehidden' in self.options
subnode['numbered'] = self.options.get('numbered', 0)
subnode['titlesonly'] = 'titlesonly' in self.options
set_source_info(self, subnode)
self.set_source_info(subnode)
wrappernode = nodes.compound(classes=['toctree-wrapper'])
wrappernode.append(subnode)
self.add_name(wrappernode)
@ -204,7 +203,7 @@ class Index(SphinxDirective):
indexnode = addnodes.index()
indexnode['entries'] = []
indexnode['inline'] = False
set_source_info(self, indexnode)
self.set_source_info(indexnode)
for entry in arguments:
indexnode['entries'].extend(process_index_entry(entry, targetid))
return [indexnode, targetnode]
@ -231,7 +230,7 @@ class TabularColumns(SphinxDirective):
# type: () -> List[nodes.Node]
node = addnodes.tabular_col_spec()
node['spec'] = self.arguments[0]
set_source_info(self, node)
self.set_source_info(node)
return [node]
@ -330,7 +329,7 @@ class Only(SphinxDirective):
# type: () -> List[nodes.Node]
node = addnodes.only()
node.document = self.state.document
set_source_info(self, node)
self.set_source_info(node)
node['expr'] = self.arguments[0]
# Same as util.nested_parse_with_titles but try to handle nested

View File

@ -14,6 +14,7 @@ from docutils.parsers.rst import directives
from docutils.parsers.rst.directives import images, html, tables
from sphinx import addnodes
from sphinx.directives import optional_int
from sphinx.util.docutils import SphinxDirective
from sphinx.util.nodes import set_source_info
@ -110,6 +111,52 @@ class ListTable(tables.ListTable):
return title, message
class Code(SphinxDirective):
"""Parse and mark up content of a code block.
This is compatible with docutils' :rst:dir:`code` directive.
"""
optional_arguments = 1
option_spec = {
'class': directives.class_option,
'name': directives.unchanged,
'number-lines': optional_int,
}
has_content = True
def run(self):
# type: () -> List[nodes.Node]
self.assert_has_content()
code = '\n'.join(self.content)
node = nodes.literal_block(code, code,
classes=self.options.get('classes', []),
highlight_args={})
self.add_name(node)
set_source_info(self, node)
if self.arguments:
# highlight language specified
node['language'] = self.arguments[0]
node['force_highlighting'] = True
else:
# no highlight language specified. Then this directive refers the current
# highlight setting via ``highlight`` directive or ``highlight_language``
# configuration.
node['language'] = self.env.temp_data.get('highlight_language',
self.config.highlight_language)
node['force_highlighting'] = False
if 'number-lines' in self.options:
node['linenos'] = True
# if number given, treat as lineno-start.
if self.options['number-lines']:
node['highlight_args']['linenostart'] = self.options['number-lines']
return [node]
class MathDirective(SphinxDirective):
has_content = True
required_arguments = 0
@ -118,6 +165,7 @@ class MathDirective(SphinxDirective):
option_spec = {
'label': directives.unchanged,
'name': directives.unchanged,
'class': directives.class_option,
'nowrap': directives.flag,
}
@ -126,13 +174,17 @@ class MathDirective(SphinxDirective):
latex = '\n'.join(self.content)
if self.arguments and self.arguments[0]:
latex = self.arguments[0] + '\n\n' + latex
label = self.options.get('label', self.options.get('name'))
node = nodes.math_block(latex, latex,
classes=self.options.get('class', []),
docname=self.env.docname,
number=self.options.get('name'),
label=self.options.get('label'),
number=None,
label=label,
nowrap='nowrap' in self.options)
self.add_name(node)
self.set_source_info(node)
ret = [node] # type: List[nodes.Node]
set_source_info(self, node)
self.add_target(ret)
return ret
@ -171,6 +223,7 @@ def setup(app):
directives.register_directive('table', RSTTable)
directives.register_directive('csv-table', CSVTable)
directives.register_directive('list-table', ListTable)
directives.register_directive('code', Code)
directives.register_directive('math', MathDirective)
return {

View File

@ -91,32 +91,54 @@ class Index:
def generate(self, docnames=None):
# type: (Iterable[str]) -> Tuple[List[Tuple[str, List[IndexEntry]]], bool]
"""Return entries for the index given by *name*. If *docnames* is
given, restrict to entries referring to these docnames.
"""Get entries for the index.
The return value is a tuple of ``(content, collapse)``, where *collapse*
is a boolean that determines if sub-entries should start collapsed (for
output formats that support collapsing sub-entries).
If ``docnames`` is given, restrict to entries referring to these
docnames.
*content* is a sequence of ``(letter, entries)`` tuples, where *letter*
is the "heading" for the given *entries*, usually the starting letter.
The return value is a tuple of ``(content, collapse)``:
*entries* is a sequence of single entries, where a single entry is a
sequence ``[name, subtype, docname, anchor, extra, qualifier, descr]``.
The items in this sequence have the following meaning:
``collapse``
A boolean that determines if sub-entries should start collapsed (for
output formats that support collapsing sub-entries).
- `name` -- the name of the index entry to be displayed
- `subtype` -- sub-entry related type:
0 -- normal entry
1 -- entry with sub-entries
2 -- sub-entry
- `docname` -- docname where the entry is located
- `anchor` -- anchor for the entry within `docname`
- `extra` -- extra info for the entry
- `qualifier` -- qualifier for the description
- `descr` -- description for the entry
``content``:
A sequence of ``(letter, entries)`` tuples, where ``letter`` is the
"heading" for the given ``entries``, usually the starting letter, and
``entries`` is a sequence of single entries. Each entry is a sequence
``[name, subtype, docname, anchor, extra, qualifier, descr]``. The
items in this sequence have the following meaning:
Qualifier and description are not rendered e.g. in LaTeX output.
``name``
The name of the index entry to be displayed.
``subtype``
The sub-entry related type. One of:
``0``
A normal entry.
``1``
An entry with sub-entries.
``2``
A sub-entry.
``docname``
*docname* where the entry is located.
``anchor``
Anchor for the entry within ``docname``
``extra``
Extra info for the entry.
``qualifier``
Qualifier for the description.
``descr``
Description for the entry.
Qualifier and description are not rendered for some output formats such
as LaTeX.
"""
raise NotImplementedError
@ -318,21 +340,37 @@ class Domain:
def get_objects(self):
# type: () -> Iterable[Tuple[str, str, str, str, str, int]]
"""Return an iterable of "object descriptions", which are tuples with
five items:
"""Return an iterable of "object descriptions".
* `name` -- fully qualified name
* `dispname` -- name to display when searching/linking
* `type` -- object type, a key in ``self.object_types``
* `docname` -- the document where it is to be found
* `anchor` -- the anchor name for the object
* `priority` -- how "important" the object is (determines placement
in search results)
Object descriptions are tuples with six items:
- 1: default priority (placed before full-text matches)
- 0: object is important (placed before default-priority objects)
- 2: object is unimportant (placed after full-text matches)
- -1: object should not show up in search at all
``name``
Fully qualified name.
``dispname``
Name to display when searching/linking.
``type``
Object type, a key in ``self.object_types``.
``docname``
The document where it is to be found.
``anchor``
The anchor name for the object.
``priority``
How "important" the object is (determines placement in search
results). One of:
``1``
Default priority (placed before full-text matches).
``0``
Object is important (placed before default-priority objects).
``2``
Object is unimportant (placed after full-text matches).
``-1``
Object should not show up in search at all.
"""
return []

View File

@ -72,12 +72,12 @@ class CObject(ObjectDescription):
# These C types aren't described anywhere, so don't try to create
# a cross-reference to them
stopwords = set((
stopwords = {
'const', 'void', 'char', 'wchar_t', 'int', 'short',
'long', 'float', 'double', 'unsigned', 'signed', 'FILE',
'clock_t', 'time_t', 'ptrdiff_t', 'size_t', 'ssize_t',
'struct', '_Bool',
))
}
def _parse_type(self, node, ctype):
# type: (nodes.Element, str) -> None

View File

@ -14,12 +14,9 @@ from typing import cast
from docutils import nodes
from sphinx import addnodes
from sphinx import locale
from sphinx.deprecation import DeprecatedDict, RemovedInSphinx30Warning
from sphinx.domains import Domain
from sphinx.locale import _
from sphinx.util.docutils import SphinxDirective
from sphinx.util.nodes import set_source_info
if False:
@ -41,13 +38,6 @@ versionlabel_classes = {
'deprecated': 'deprecated',
}
locale.versionlabels = DeprecatedDict(
versionlabels,
'sphinx.locale.versionlabels is deprecated. '
'Please use sphinx.domains.changeset.versionlabels instead.',
RemovedInSphinx30Warning
)
# TODO: move to typing.NamedTuple after dropping py35 support (see #5958)
ChangeSet = namedtuple('ChangeSet',
@ -68,7 +58,7 @@ class VersionChange(SphinxDirective):
# type: () -> List[nodes.Node]
node = addnodes.versionmodified()
node.document = self.state.document
set_source_info(self, node)
self.set_source_info(node)
node['type'] = self.name
node['version'] = self.arguments[0]
text = versionlabels[self.name] % self.arguments[0]
@ -76,7 +66,7 @@ class VersionChange(SphinxDirective):
inodes, messages = self.state.inline_text(self.arguments[1],
self.lineno + 1)
para = nodes.paragraph(self.arguments[1], '', *inodes, translatable=False)
set_source_info(self, para)
self.set_source_info(para)
node.append(para)
else:
messages = []

View File

@ -19,7 +19,7 @@ from sphinx import addnodes
from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.directives import ObjectDescription
from sphinx.domains import Domain, ObjType
from sphinx.environment import NoUri
from sphinx.errors import NoUri
from sphinx.locale import _, __
from sphinx.roles import XRefRole
from sphinx.transforms import SphinxTransform
@ -1159,15 +1159,12 @@ class ASTNoexceptExpr(ASTBase):
class ASTNewExpr(ASTBase):
def __init__(self, rooted, isNewTypeId, typ, initList, initType):
# type: (bool, bool, ASTType, List[Any], str) -> None
def __init__(self, rooted, isNewTypeId, typ, initList):
# type: (bool, bool, ASTType, Any) -> None
self.rooted = rooted
self.isNewTypeId = isNewTypeId
self.typ = typ
self.initList = initList
self.initType = initType
if self.initList is not None:
assert self.initType in ')}'
def _stringify(self, transform):
# type: (Callable[[Any], str]) -> str
@ -1181,15 +1178,7 @@ class ASTNewExpr(ASTBase):
else:
assert False
if self.initList is not None:
if self.initType == ')':
res.append('(')
first = True
for e in self.initList:
if not first:
res.append(', ')
first = False
res.append(transform(e))
res.append(self.initType)
res.append(transform(self.initList))
return ''.join(res)
def get_id(self, version):
@ -1200,13 +1189,7 @@ class ASTNewExpr(ASTBase):
res.append('_')
res.append(self.typ.get_id(version))
if self.initList is not None:
if self.initType == ')':
res.append('pi')
for e in self.initList:
res.append(e.get_id(version))
res.append('E')
else:
assert False
res.append(self.initList.get_id(version))
else:
res.append('E')
return ''.join(res)
@ -1221,17 +1204,7 @@ class ASTNewExpr(ASTBase):
else:
assert False
if self.initList is not None:
if self.initType == ')':
signode.append(nodes.Text('('))
first = True
for e in self.initList:
if not first:
signode.append(nodes.Text(', '))
first = False
e.describe_signature(signode, mode, env, symbol)
signode.append(nodes.Text(')'))
else:
assert False
self.initList.describe_signature(signode, mode, env, symbol)
class ASTDeleteExpr(ASTBase):
@ -1323,38 +1296,24 @@ class ASTTypeId(ASTBase):
class ASTPostfixCallExpr(ASTBase):
def __init__(self, exprs):
self.exprs = exprs
def __init__(self, lst):
# type: (Union[ASTParenExprList, ASTBracedInitList]) -> None
self.lst = lst
def _stringify(self, transform):
# type: (Callable[[Any], str]) -> str
res = ['(']
first = True
for e in self.exprs:
if not first:
res.append(', ')
first = False
res.append(transform(e))
res.append(')')
return ''.join(res)
return transform(self.lst)
def get_id(self, idPrefix, version):
# type: (str, int) -> str
res = ['cl', idPrefix]
for e in self.exprs:
for e in self.lst.exprs:
res.append(e.get_id(version))
res.append('E')
return ''.join(res)
def describe_signature(self, signode, mode, env, symbol):
signode.append(nodes.Text('('))
first = True
for e in self.exprs:
if not first:
signode.append(nodes.Text(', '))
first = False
e.describe_signature(signode, mode, env, symbol)
signode.append(nodes.Text(')'))
self.lst.describe_signature(signode, mode, env, symbol)
class ASTPostfixArray(ASTBase):
@ -3232,18 +3191,85 @@ class ASTDeclaratorNameParamQual(ASTBase):
self.paramQual.describe_signature(signode, mode, env, symbol)
class ASTInitializer(ASTBase):
def __init__(self, value):
self.value = value
class ASTParenExprList(ASTBase):
def __init__(self, exprs):
# type: (List[Any]) -> None
self.exprs = exprs
def get_id(self, version):
# type: (int) -> str
return "pi%sE" % ''.join(e.get_id(version) for e in self.exprs)
def _stringify(self, transform):
# type: (Callable[[Any], str]) -> str
return ' = ' + transform(self.value)
exprs = [transform(e) for e in self.exprs]
return '(%s)' % ', '.join(exprs)
def describe_signature(self, signode, mode, env, symbol):
# type: (addnodes.desc_signature, str, BuildEnvironment, Symbol) -> None
_verify_description_mode(mode)
signode.append(nodes.Text(' = '))
signode.append(nodes.Text('('))
first = True
for e in self.exprs:
if not first:
signode.append(nodes.Text(', '))
else:
first = False
e.describe_signature(signode, mode, env, symbol)
signode.append(nodes.Text(')'))
class ASTBracedInitList(ASTBase):
def __init__(self, exprs, trailingComma):
# type: (List[Any], bool) -> None
self.exprs = exprs
self.trailingComma = trailingComma
def get_id(self, version):
# type: (int) -> str
return "il%sE" % ''.join(e.get_id(version) for e in self.exprs)
def _stringify(self, transform):
# type: (Callable[[Any], str]) -> str
exprs = [transform(e) for e in self.exprs]
trailingComma = ',' if self.trailingComma else ''
return '{%s%s}' % (', '.join(exprs), trailingComma)
def describe_signature(self, signode, mode, env, symbol):
# type: (addnodes.desc_signature, str, BuildEnvironment, Symbol) -> None
_verify_description_mode(mode)
signode.append(nodes.Text('{'))
first = True
for e in self.exprs:
if not first:
signode.append(nodes.Text(', '))
else:
first = False
e.describe_signature(signode, mode, env, symbol)
if self.trailingComma:
signode.append(nodes.Text(','))
signode.append(nodes.Text('}'))
class ASTInitializer(ASTBase):
def __init__(self, value, hasAssign=True):
# type: (Any, bool) -> None
self.value = value
self.hasAssign = hasAssign
def _stringify(self, transform):
# type: (Callable[[Any], str]) -> str
val = transform(self.value)
if self.hasAssign:
return ' = ' + val
else:
return val
def describe_signature(self, signode, mode, env, symbol):
# type: (addnodes.desc_signature, str, BuildEnvironment, Symbol) -> None
_verify_description_mode(mode)
if self.hasAssign:
signode.append(nodes.Text(' = '))
self.value.describe_signature(signode, 'markType', env, symbol)
@ -4844,35 +4870,67 @@ class DefinitionParser:
return res
return self._parse_nested_name()
def _parse_expression_list_or_braced_init_list(self):
# type: () -> Tuple[List[Any], str]
def _parse_initializer_list(self, name, open, close):
# type: (str, str, str) -> Tuple[List[Any], bool]
# Parse open and close with the actual initializer-list inbetween
# -> initializer-clause '...'[opt]
# | initializer-list ',' initializer-clause '...'[opt]
self.skip_ws()
if self.skip_string_and_ws('('):
close = ')'
name = 'parenthesized expression-list'
elif self.skip_string_and_ws('{'):
close = '}'
name = 'braced-init-list'
self.fail('Sorry, braced-init-list not yet supported.')
else:
if not self.skip_string_and_ws(open):
return None, None
if self.skip_string(close):
return [], False
exprs = []
self.skip_ws()
if not self.skip_string(close):
while True:
self.skip_ws()
expr = self._parse_expression(inTemplate=False)
self.skip_ws()
if self.skip_string('...'):
exprs.append(ASTPackExpansionExpr(expr))
else:
exprs.append(expr)
self.skip_ws()
if self.skip_string(close):
break
if not self.skip_string(','):
self.fail("Error in %s, expected ',' or '%s'." % (name, close))
return exprs, close
trailingComma = False
while True:
self.skip_ws()
expr = self._parse_expression(inTemplate=False)
self.skip_ws()
if self.skip_string('...'):
exprs.append(ASTPackExpansionExpr(expr))
else:
exprs.append(expr)
self.skip_ws()
if self.skip_string(close):
break
if not self.skip_string_and_ws(','):
self.fail("Error in %s, expected ',' or '%s'." % (name, close))
if self.current_char == close and close == '}':
self.pos += 1
trailingComma = True
break
return exprs, trailingComma
def _parse_paren_expression_list(self):
# type: () -> ASTParenExprList
# -> '(' expression-list ')'
# though, we relax it to also allow empty parens
# as it's needed in some cases
#
# expression-list
# -> initializer-list
exprs, trailingComma = self._parse_initializer_list("parenthesized expression-list",
'(', ')')
if exprs is None:
return None
return ASTParenExprList(exprs)
def _parse_braced_init_list(self):
# type: () -> ASTBracedInitList
# -> '{' initializer-list ','[opt] '}'
# | '{' '}'
exprs, trailingComma = self._parse_initializer_list("braced-init-list", '{', '}')
if exprs is None:
return None
return ASTBracedInitList(exprs, trailingComma)
def _parse_expression_list_or_braced_init_list(self):
# type: () -> Union[ASTParenExprList, ASTBracedInitList]
paren = self._parse_paren_expression_list()
if paren is not None:
return paren
return self._parse_braced_init_list()
def _parse_postfix_expression(self):
# -> primary
@ -4980,7 +5038,7 @@ class DefinitionParser:
raise self._make_multi_error(errors, header)
# and now parse postfixes
postFixes = []
postFixes = [] # type: List[Any]
while True:
self.skip_ws()
if prefixType in ['expr', 'cast', 'typeid']:
@ -5000,7 +5058,7 @@ class DefinitionParser:
self.pos -= 3
else:
name = self._parse_nested_name()
postFixes.append(ASTPostfixMember(name)) # type: ignore
postFixes.append(ASTPostfixMember(name))
continue
if self.skip_string('->'):
if self.skip_string('*'):
@ -5008,21 +5066,17 @@ class DefinitionParser:
self.pos -= 3
else:
name = self._parse_nested_name()
postFixes.append(ASTPostfixMemberOfPointer(name)) # type: ignore
postFixes.append(ASTPostfixMemberOfPointer(name))
continue
if self.skip_string('++'):
postFixes.append(ASTPostfixInc()) # type: ignore
postFixes.append(ASTPostfixInc())
continue
if self.skip_string('--'):
postFixes.append(ASTPostfixDec()) # type: ignore
postFixes.append(ASTPostfixDec())
continue
lst, typ = self._parse_expression_list_or_braced_init_list()
lst = self._parse_expression_list_or_braced_init_list()
if lst is not None:
if typ == ')':
postFixes.append(ASTPostfixCallExpr(lst)) # type: ignore
else:
assert typ == '}'
assert False
postFixes.append(ASTPostfixCallExpr(lst))
continue
break
if len(postFixes) == 0:
@ -5105,10 +5159,8 @@ class DefinitionParser:
decl = self._parse_declarator(named=False, paramMode="new")
else:
self.fail("Sorry, parenthesised type-id in new expression not yet supported.")
lst, typ = self._parse_expression_list_or_braced_init_list()
if lst:
assert typ in ")}"
return ASTNewExpr(rooted, isNewTypeId, ASTType(declSpecs, decl), lst, typ)
lst = self._parse_expression_list_or_braced_init_list()
return ASTNewExpr(rooted, isNewTypeId, ASTType(declSpecs, decl), lst)
# delete-expression
pos = self.pos
rooted = self.skip_string('::')
@ -5267,7 +5319,7 @@ class DefinitionParser:
value = self.matched_text
else:
# TODO: add handling of more bracket-like things, and quote handling
brackets = {'(': ')', '[': ']', '<': '>'}
brackets = {'(': ')', '{': '}', '[': ']', '<': '>'}
symbols = [] # type: List[str]
while not self.eof:
if (len(symbols) == 0 and self.current_char in end):
@ -5825,30 +5877,52 @@ class DefinitionParser:
def _parse_initializer(self, outer=None, allowFallback=True):
# type: (str, bool) -> ASTInitializer
# initializer # global vars
# -> brace-or-equal-initializer
# | '(' expression-list ')'
#
# brace-or-equal-initializer # member vars
# -> '=' initializer-clause
# | braced-init-list
#
# initializer-clause # function params, non-type template params (with '=' in front)
# -> assignment-expression
# | braced-init-list
#
# we don't distinguish between global and member vars, so disallow paren:
#
# -> braced-init-list # var only
# | '=' assignment-expression
# | '=' braced-init-list
self.skip_ws()
# TODO: support paren and brace initialization for memberObject
if outer == 'member':
bracedInit = self._parse_braced_init_list()
if bracedInit is not None:
return ASTInitializer(bracedInit, hasAssign=False)
if not self.skip_string('='):
return None
bracedInit = self._parse_braced_init_list()
if bracedInit is not None:
return ASTInitializer(bracedInit)
if outer == 'member':
fallbackEnd = [] # type: List[str]
elif outer == 'templateParam':
fallbackEnd = [',', '>']
elif outer is None: # function parameter
fallbackEnd = [',', ')']
else:
if outer == 'member':
def parser():
return self._parse_assignment_expression(inTemplate=False)
value = self._parse_expression_fallback([], parser,
allow=allowFallback)
elif outer == 'templateParam':
def parser():
return self._parse_assignment_expression(inTemplate=True)
value = self._parse_expression_fallback([',', '>'], parser,
allow=allowFallback)
elif outer is None: # function parameter
def parser():
return self._parse_assignment_expression(inTemplate=False)
value = self._parse_expression_fallback([',', ')'], parser,
allow=allowFallback)
else:
self.fail("Internal error, initializer for outer '%s' not "
"implemented." % outer)
return ASTInitializer(value)
self.fail("Internal error, initializer for outer '%s' not "
"implemented." % outer)
inTemplate = outer == 'templateParam'
def parser():
return self._parse_assignment_expression(inTemplate=inTemplate)
value = self._parse_expression_fallback(fallbackEnd, parser, allow=allowFallback)
return ASTInitializer(value)
def _parse_type(self, named, outer=None):
# type: (Union[bool, str], str) -> ASTType
@ -6734,27 +6808,20 @@ class CPPNamespacePopObject(SphinxDirective):
class AliasNode(nodes.Element):
def __init__(self, sig, warnEnv):
"""
:param sig: The name or function signature to alias.
:param warnEnv: An object which must have the following attributes:
env: a Sphinx environment
whatever DefinitionParser requires of warnEnv
"""
def __init__(self, sig, env=None, parentKey=None):
super().__init__()
self.sig = sig
env = warnEnv.env
if 'cpp:parent_symbol' not in env.temp_data:
root = env.domaindata['cpp']['root_symbol']
env.temp_data['cpp:parent_symbol'] = root
self.parentKey = env.temp_data['cpp:parent_symbol'].get_lookup_key()
try:
parser = DefinitionParser(sig, warnEnv, warnEnv.env.config)
self.ast, self.isShorthand = parser.parse_xref_object()
parser.assert_end()
except DefinitionError as e:
warnEnv.warn(e)
self.ast = None
if env is not None:
if 'cpp:parent_symbol' not in env.temp_data:
root = env.domaindata['cpp']['root_symbol']
env.temp_data['cpp:parent_symbol'] = root
self.parentKey = env.temp_data['cpp:parent_symbol'].get_lookup_key()
else:
assert parentKey is not None
self.parentKey = parentKey
def copy(self):
return self.__class__(self.sig, env=None, parentKey=self.parentKey)
class AliasTransform(SphinxTransform):
@ -6763,8 +6830,20 @@ class AliasTransform(SphinxTransform):
def apply(self, **kwargs):
# type: (Any) -> None
for node in self.document.traverse(AliasNode):
class Warner:
def warn(self, msg):
logger.warning(msg, location=node)
warner = Warner()
sig = node.sig
ast = node.ast
parentKey = node.parentKey
try:
parser = DefinitionParser(sig, warner, self.env.config)
ast, isShorthand = parser.parse_xref_object()
parser.assert_end()
except DefinitionError as e:
warner.warn(e)
ast, isShorthand = None, None
if ast is None:
# could not be parsed, so stop here
signode = addnodes.desc_signature(sig, '')
@ -6774,8 +6853,6 @@ class AliasTransform(SphinxTransform):
node.replace_self(signode)
continue
isShorthand = node.isShorthand
parentKey = node.parentKey
rootSymbol = self.env.domains['cpp'].data['root_symbol']
parentSymbol = rootSymbol.direct_lookup(parentKey)
if not parentSymbol:
@ -6833,10 +6910,6 @@ class AliasTransform(SphinxTransform):
class CPPAliasObject(ObjectDescription):
option_spec = {} # type: Dict
def warn(self, msg):
# type: (Union[str, Exception]) -> None
self.state_machine.reporter.warning(msg, line=self.lineno)
def run(self):
# type: () -> List[nodes.Node]
"""
@ -6859,7 +6932,7 @@ class CPPAliasObject(ObjectDescription):
self.names = [] # type: List[str]
signatures = self.get_signatures()
for i, sig in enumerate(signatures):
node.append(AliasNode(sig, self))
node.append(AliasNode(sig, env=self.env))
contentnode = addnodes.desc_content()
node.append(contentnode)

View File

@ -24,7 +24,6 @@ from sphinx.util.nodes import make_refnode
if False:
# For type annotation
from typing import Any, Dict, Iterator, List, Tuple # NOQA
from docutils import nodes # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.builders import Builder # NOQA
from sphinx.environment import BuildEnvironment # NOQA

View File

@ -11,7 +11,6 @@
from docutils import nodes
from docutils.nodes import make_id
from sphinx.addnodes import math_block as displaymath
from sphinx.domains import Domain
from sphinx.locale import __
from sphinx.roles import XRefRole
@ -20,7 +19,7 @@ from sphinx.util.nodes import make_refnode
if False:
# For type annotation
from typing import Any, Callable, Dict, Iterable, List, Tuple, Type, Union # NOQA
from typing import Any, Dict, Iterable, List, Tuple # NOQA
from sphinx import addnodes # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.builders import Builder # NOQA
@ -49,7 +48,6 @@ class MathDomain(Domain):
'eq': 'equation not found: %(target)s',
}
enumerable_nodes = { # node_class -> (figtype, title_getter)
displaymath: ('displaymath', None),
nodes.math_block: ('displaymath', None),
}
roles = {

View File

@ -13,8 +13,7 @@ import re
from docutils import nodes
from docutils.parsers.rst import directives
from sphinx import addnodes, locale
from sphinx.deprecation import DeprecatedDict, RemovedInSphinx30Warning
from sphinx import addnodes
from sphinx.directives import ObjectDescription
from sphinx.domains import Domain, ObjType, Index, IndexEntry
from sphinx.locale import _, __
@ -26,7 +25,7 @@ from sphinx.util.nodes import make_refnode
if False:
# For type annotation
from typing import Any, Dict, Iterable, Iterator, List, Tuple, Type, Union # NOQA
from typing import Any, Dict, Iterable, Iterator, List, Tuple, Type # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.builders import Builder # NOQA
from sphinx.environment import BuildEnvironment # NOQA
@ -55,13 +54,6 @@ pairindextypes = {
'builtin': _('built-in function'),
}
locale.pairindextypes = DeprecatedDict(
pairindextypes,
'sphinx.locale.pairindextypes is deprecated. '
'Please use sphinx.domains.python.pairindextypes instead.',
RemovedInSphinx30Warning
)
def _pseudo_parse_arglist(signode, arglist):
# type: (addnodes.desc_signature, str) -> None
@ -254,30 +246,30 @@ class PyObject(ObjectDescription):
m = py_sig_re.match(sig)
if m is None:
raise ValueError
name_prefix, name, arglist, retann = m.groups()
prefix, name, arglist, retann = m.groups()
# determine module and class name (if applicable), as well as full name
modname = self.options.get(
'module', self.env.ref_context.get('py:module'))
modname = self.options.get('module', self.env.ref_context.get('py:module'))
classname = self.env.ref_context.get('py:class')
if classname:
add_module = False
if name_prefix and name_prefix.startswith(classname):
fullname = name_prefix + name
if prefix and (prefix == classname or
prefix.startswith(classname + ".")):
fullname = prefix + name
# class name is given again in the signature
name_prefix = name_prefix[len(classname):].lstrip('.')
elif name_prefix:
prefix = prefix[len(classname):].lstrip('.')
elif prefix:
# class name is given in the signature, but different
# (shouldn't happen)
fullname = classname + '.' + name_prefix + name
fullname = classname + '.' + prefix + name
else:
# class name is not given in the signature
fullname = classname + '.' + name
else:
add_module = True
if name_prefix:
classname = name_prefix.rstrip('.')
fullname = name_prefix + name
if prefix:
classname = prefix.rstrip('.')
fullname = prefix + name
else:
classname = ''
fullname = name
@ -290,36 +282,31 @@ class PyObject(ObjectDescription):
if sig_prefix:
signode += addnodes.desc_annotation(sig_prefix, sig_prefix)
if name_prefix:
signode += addnodes.desc_addname(name_prefix, name_prefix)
# exceptions are a special case, since they are documented in the
# 'exceptions' module.
if prefix:
signode += addnodes.desc_addname(prefix, prefix)
elif add_module and self.env.config.add_module_names:
modname = self.options.get(
'module', self.env.ref_context.get('py:module'))
if modname and modname != 'exceptions':
# exceptions are a special case, since they are documented in the
# 'exceptions' module.
nodetext = modname + '.'
signode += addnodes.desc_addname(nodetext, nodetext)
anno = self.options.get('annotation')
signode += addnodes.desc_name(name, name)
if not arglist:
if arglist:
_pseudo_parse_arglist(signode, arglist)
else:
if self.needs_arglist():
# for callables, add an empty parameter list
signode += addnodes.desc_parameterlist()
if retann:
signode += addnodes.desc_returns(retann, retann)
if anno:
signode += addnodes.desc_annotation(' ' + anno, ' ' + anno)
return fullname, name_prefix
_pseudo_parse_arglist(signode, arglist)
if retann:
signode += addnodes.desc_returns(retann, retann)
anno = self.options.get('annotation')
if anno:
signode += addnodes.desc_annotation(' ' + anno, ' ' + anno)
return fullname, name_prefix
return fullname, prefix
def get_index_text(self, modname, name):
# type: (str, str) -> str

View File

@ -10,7 +10,6 @@
import re
import unicodedata
import warnings
from copy import copy
from typing import cast
@ -19,9 +18,9 @@ from docutils.parsers.rst import directives
from docutils.statemachine import StringList
from sphinx import addnodes
from sphinx.deprecation import RemovedInSphinx30Warning
from sphinx.directives import ObjectDescription
from sphinx.domains import Domain, ObjType
from sphinx.errors import NoUri
from sphinx.locale import _, __
from sphinx.roles import XRefRole
from sphinx.util import ws_re, logging, docname_join
@ -838,8 +837,6 @@ class StandardDomain(Domain):
def _resolve_citation_xref(self, env, fromdocname, builder, typ, target, node, contnode):
# type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA
from sphinx.environment import NoUri
docname, labelid, lineno = self.data['citations'].get(target, ('', '', 0))
if not docname:
if 'ids' in node:
@ -958,17 +955,6 @@ class StandardDomain(Domain):
figtype, _ = self.enumerable_nodes.get(node.__class__, (None, None))
return figtype
def get_figtype(self, node):
# type: (nodes.Node) -> str
"""Get figure type of nodes.
.. deprecated:: 1.8
"""
warnings.warn('StandardDomain.get_figtype() is deprecated. '
'Please use get_enumerable_node_type() instead.',
RemovedInSphinx30Warning, stacklevel=2)
return self.get_enumerable_node_type(node)
def get_fignumber(self, env, builder, figtype, docname, target_node):
# type: (BuildEnvironment, Builder, str, str, nodes.Element) -> Tuple[int, ...]
if figtype == 'section':

View File

@ -13,11 +13,10 @@ import pickle
import warnings
from collections import defaultdict
from copy import copy
from io import BytesIO
from os import path
from sphinx import addnodes
from sphinx.deprecation import RemovedInSphinx30Warning, RemovedInSphinx40Warning
from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.environment.adapters.toctree import TocTree
from sphinx.errors import SphinxError, BuildEnvironmentError, DocumentError, ExtensionError
from sphinx.locale import __
@ -25,12 +24,12 @@ from sphinx.transforms import SphinxTransformer
from sphinx.util import DownloadFiles, FilenameUniqDict
from sphinx.util import logging
from sphinx.util.docutils import LoggingReporter
from sphinx.util.i18n import find_catalog_files
from sphinx.util.i18n import CatalogRepository, docname_to_domain
from sphinx.util.nodes import is_translatable
if False:
# For type annotation
from typing import Any, Callable, Dict, IO, Iterator, List, Optional, Pattern, Set, Tuple, Type, Union, Generator # NOQA
from typing import Any, Callable, Dict, IO, Iterator, List, Optional, Set, Tuple, Union # NOQA
from docutils import nodes # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.builders import Builder # NOQA
@ -78,11 +77,6 @@ versioning_conditions = {
} # type: Dict[str, Union[bool, Callable]]
class NoUri(Exception):
"""Raised by get_relative_uri if there is no URI available."""
pass
class BuildEnvironment:
"""
The environment in which the ReST files are translated.
@ -311,10 +305,6 @@ class BuildEnvironment:
if docname in other.reread_always:
self.reread_always.add(docname)
for version, changes in other.versionchanges.items():
self.versionchanges.setdefault(version, []).extend(
change for change in changes if change[1] in docnames)
for domainname, domain in self.domains.items():
domain.merge_domaindata(docnames, other.domaindata[domainname])
app.emit('env-merge-info', self, docnames, other)
@ -395,15 +385,13 @@ class BuildEnvironment:
# move i18n process into the writing phase, and remove these lines.
if builder.use_message_catalog:
# add catalog mo file dependency
repo = CatalogRepository(self.srcdir, self.config.locale_dirs,
self.config.language, self.config.source_encoding)
for docname in self.found_docs:
catalog_files = find_catalog_files(
docname,
self.srcdir,
self.config.locale_dirs,
self.config.language,
self.config.gettext_compact)
for filename in catalog_files:
self.dependencies[docname].add(filename)
domain = docname_to_domain(docname, self.config.gettext_compact)
for catalog in repo.catalogs:
if catalog.domain == domain:
self.dependencies[docname].add(catalog.mo_path)
except OSError as exc:
raise DocumentError(__('Failed to scan documents in %s: %r') % (self.srcdir, exc))
@ -666,123 +654,3 @@ class BuildEnvironment:
for domain in self.domains.values():
domain.check_consistency()
self.app.emit('env-check-consistency', self)
# --------- METHODS FOR COMPATIBILITY --------------------------------------
def update(self, config, srcdir, doctreedir):
# type: (Config, str, str) -> List[str]
warnings.warn('env.update() is deprecated. Please use builder.read() instead.',
RemovedInSphinx30Warning, stacklevel=2)
return self.app.builder.read()
def _read_serial(self, docnames, app):
# type: (List[str], Sphinx) -> None
warnings.warn('env._read_serial() is deprecated. Please use builder.read() instead.',
RemovedInSphinx30Warning, stacklevel=2)
return self.app.builder._read_serial(docnames)
def _read_parallel(self, docnames, app, nproc):
# type: (List[str], Sphinx, int) -> None
warnings.warn('env._read_parallel() is deprecated. Please use builder.read() instead.',
RemovedInSphinx30Warning, stacklevel=2)
return self.app.builder._read_parallel(docnames, nproc)
def read_doc(self, docname, app=None):
# type: (str, Sphinx) -> None
warnings.warn('env.read_doc() is deprecated. Please use builder.read_doc() instead.',
RemovedInSphinx30Warning, stacklevel=2)
self.app.builder.read_doc(docname)
def write_doctree(self, docname, doctree):
# type: (str, nodes.document) -> None
warnings.warn('env.write_doctree() is deprecated. '
'Please use builder.write_doctree() instead.',
RemovedInSphinx30Warning, stacklevel=2)
self.app.builder.write_doctree(docname, doctree)
@property
def _nitpick_ignore(self):
# type: () -> List[str]
warnings.warn('env._nitpick_ignore is deprecated. '
'Please use config.nitpick_ignore instead.',
RemovedInSphinx30Warning, stacklevel=2)
return self.config.nitpick_ignore
@staticmethod
def load(f, app=None):
# type: (IO, Sphinx) -> BuildEnvironment
warnings.warn('BuildEnvironment.load() is deprecated. '
'Please use pickle.load() instead.',
RemovedInSphinx30Warning, stacklevel=2)
try:
env = pickle.load(f)
except Exception as exc:
# This can happen for example when the pickle is from a
# different version of Sphinx.
raise OSError(exc)
if app:
env.app = app
env.config.values = app.config.values
return env
@classmethod
def loads(cls, string, app=None):
# type: (bytes, Sphinx) -> BuildEnvironment
warnings.warn('BuildEnvironment.loads() is deprecated. '
'Please use pickle.loads() instead.',
RemovedInSphinx30Warning, stacklevel=2)
io = BytesIO(string)
return cls.load(io, app)
@classmethod
def frompickle(cls, filename, app):
# type: (str, Sphinx) -> BuildEnvironment
warnings.warn('BuildEnvironment.frompickle() is deprecated. '
'Please use pickle.load() instead.',
RemovedInSphinx30Warning, stacklevel=2)
with open(filename, 'rb') as f:
return cls.load(f, app)
@staticmethod
def dump(env, f):
# type: (BuildEnvironment, IO) -> None
warnings.warn('BuildEnvironment.dump() is deprecated. '
'Please use pickle.dump() instead.',
RemovedInSphinx30Warning, stacklevel=2)
pickle.dump(env, f, pickle.HIGHEST_PROTOCOL)
@classmethod
def dumps(cls, env):
# type: (BuildEnvironment) -> bytes
warnings.warn('BuildEnvironment.dumps() is deprecated. '
'Please use pickle.dumps() instead.',
RemovedInSphinx30Warning, stacklevel=2)
io = BytesIO()
cls.dump(env, io)
return io.getvalue()
def topickle(self, filename):
# type: (str) -> None
warnings.warn('env.topickle() is deprecated. '
'Please use pickle.dump() instead.',
RemovedInSphinx30Warning, stacklevel=2)
with open(filename, 'wb') as f:
self.dump(self, f)
@property
def versionchanges(self):
# type: () -> Dict[str, List[Tuple[str, str, int, str, str, str]]]
warnings.warn('env.versionchanges() is deprecated. '
'Please use ChangeSetDomain instead.',
RemovedInSphinx30Warning, stacklevel=2)
return self.domaindata['changeset']['changes']
def note_versionchange(self, type, version, node, lineno):
# type: (str, str, addnodes.versionmodified, int) -> None
warnings.warn('env.note_versionchange() is deprecated. '
'Please use ChangeSetDomain.note_changeset() instead.',
RemovedInSphinx30Warning, stacklevel=2)
node['type'] = type
node['version'] = version
node.line = lineno
self.get_domain('changeset').note_changeset(node) # type: ignore

View File

@ -12,6 +12,7 @@ import re
import unicodedata
from itertools import groupby
from sphinx.errors import NoUri
from sphinx.locale import _, __
from sphinx.util import split_into, logging
@ -33,8 +34,6 @@ class IndexEntries:
_fixre=re.compile(r'(.*) ([(][^()]*[)])')):
# type: (Builder, bool, Pattern) -> List[Tuple[str, List[Tuple[str, Any]]]]
"""Create the real index from the collected index entries."""
from sphinx.environment import NoUri
new = {} # type: Dict[str, List]
def add_entry(word, subword, main, link=True, dic=new, key=None):

View File

@ -24,8 +24,7 @@ from sphinx.util.images import guess_mimetype
if False:
# For type annotation
from typing import Dict, List, Set, Tuple # NOQA
from docutils import nodes # NOQA
from typing import Dict, List, Set # NOQA
from sphinx.sphinx import Sphinx # NOQA
from sphinx.environment import BuildEnvironment # NOQA

View File

@ -17,7 +17,6 @@ from sphinx.environment.collectors import EnvironmentCollector
if False:
# For type annotation
from typing import Dict, Set # NOQA
from docutils import nodes # NOQA
from sphinx.sphinx import Sphinx # NOQA
from sphinx.environment import BuildEnvironment # NOQA

View File

@ -16,7 +16,6 @@ from sphinx.transforms import SphinxContentsFilter
if False:
# For type annotation
from typing import Dict, Set # NOQA
from docutils import nodes # NOQA
from sphinx.sphinx import Sphinx # NOQA
from sphinx.environment import BuildEnvironment # NOQA

View File

@ -21,9 +21,8 @@ from sphinx.util import url_re, logging
if False:
# For type annotation
from typing import Any, Dict, List, Set, Tuple, Type, TypeVar # NOQA
from typing import Dict, List, Set, Tuple, Type, TypeVar # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.builders import Builder # NOQA
from sphinx.environment import BuildEnvironment # NOQA
N = TypeVar('N')

View File

@ -121,3 +121,8 @@ class PycodeError(Exception):
if len(self.args) > 1:
res += ' (exception was: %r)' % self.args[1]
return res
class NoUri(Exception):
"""Raised by builder.get_relative_uri() if there is no URI available."""
pass

View File

@ -45,7 +45,7 @@ else:
]
INITPY = '__init__.py'
PY_SUFFIXES = set(['.py', '.pyx'])
PY_SUFFIXES = {'.py', '.pyx'}
def makename(package, module):

View File

@ -18,9 +18,9 @@ from typing import Any
from docutils.statemachine import StringList
import sphinx
from sphinx.deprecation import RemovedInSphinx30Warning, RemovedInSphinx40Warning
from sphinx.ext.autodoc.importer import mock, import_object, get_object_members
from sphinx.ext.autodoc.importer import _MockImporter # to keep compatibility # NOQA
from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.ext.autodoc.importer import import_object, get_object_members
from sphinx.ext.autodoc.mock import mock
from sphinx.locale import _, __
from sphinx.pycode import ModuleAnalyzer, PycodeError
from sphinx.util import logging
@ -33,9 +33,7 @@ from sphinx.util.inspect import Signature, isdescriptor, safe_getmembers, \
if False:
# For type annotation
from types import ModuleType # NOQA
from typing import Any, Callable, Dict, Iterator, List, Sequence, Set, Tuple, Type, Union # NOQA
from docutils import nodes # NOQA
from docutils.utils import Reporter # NOQA
from typing import Callable, Dict, Iterator, List, Sequence, Set, Tuple, Type, Union # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.config import Config # NOQA
from sphinx.environment import BuildEnvironment # NOQA
@ -82,7 +80,7 @@ def members_set_option(arg):
"""Used to convert the :members: option to auto directives."""
if arg is None:
return ALL
return set(x.strip() for x in arg.split(','))
return {x.strip() for x in arg.split(',')}
SUPPRESS = object()
@ -1448,30 +1446,6 @@ def autodoc_attrgetter(app, obj, name, *defargs):
return safe_getattr(obj, name, *defargs)
def merge_autodoc_default_flags(app, config):
# type: (Sphinx, Config) -> None
"""This merges the autodoc_default_flags to autodoc_default_options."""
if not config.autodoc_default_flags:
return
# Note: this option will be removed in Sphinx-4.0. But I marked this as
# RemovedInSphinx *30* Warning because we have to emit warnings for users
# who will be still in use with Sphinx-3.x. So we should replace this by
# logger.warning() on 3.0.0 release.
warnings.warn('autodoc_default_flags is now deprecated. '
'Please use autodoc_default_options instead.',
RemovedInSphinx30Warning, stacklevel=2)
for option in config.autodoc_default_flags:
if isinstance(option, str):
config.autodoc_default_options[option] = None
else:
logger.warning(
__("Ignoring invalid option in autodoc_default_flags: %r"),
option, type='autodoc'
)
def setup(app):
# type: (Sphinx) -> Dict[str, Any]
app.add_autodocumenter(ModuleDocumenter)
@ -1496,6 +1470,4 @@ def setup(app):
app.add_event('autodoc-process-signature')
app.add_event('autodoc-skip-member')
app.connect('config-inited', merge_autodoc_default_flags)
return {'version': sphinx.__display_version__, 'parallel_read_safe': True}

View File

@ -17,9 +17,8 @@ from sphinx.util.nodes import nested_parse_with_titles
if False:
# For type annotation
from typing import Any, Callable, Dict, List, Set, Type # NOQA
from typing import Callable, Dict, List, Set, Type # NOQA
from docutils.parsers.rst.state import RSTState # NOQA
from docutils.statemachine import StateMachine, StringList # NOQA
from docutils.utils import Reporter # NOQA
from sphinx.config import Config # NOQA
from sphinx.environment import BuildEnvironment # NOQA

View File

@ -8,218 +8,22 @@
:license: BSD, see LICENSE for details.
"""
import contextlib
import os
import sys
import traceback
import warnings
from collections import namedtuple
from importlib.abc import Loader, MetaPathFinder
from importlib.machinery import ModuleSpec
from types import FunctionType, MethodType, ModuleType
from sphinx.deprecation import RemovedInSphinx30Warning
from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
from sphinx.util import logging
from sphinx.util.inspect import isenumclass, safe_getattr
if False:
# For type annotation
from typing import Any, Callable, Dict, Generator, Iterator, List, Optional, Sequence, Tuple, Union # NOQA
from typing import Any, Callable, Dict, List # NOQA
logger = logging.getLogger(__name__)
class _MockObject:
"""Used by autodoc_mock_imports."""
__display_name__ = '_MockObject'
def __new__(cls, *args, **kwargs):
# type: (Any, Any) -> Any
if len(args) == 3 and isinstance(args[1], tuple):
superclass = args[1][-1].__class__
if superclass is cls:
# subclassing MockObject
return _make_subclass(args[0], superclass.__display_name__,
superclass=superclass, attributes=args[2])
return super(_MockObject, cls).__new__(cls)
def __init__(self, *args, **kwargs):
# type: (Any, Any) -> None
self.__qualname__ = ''
def __len__(self):
# type: () -> int
return 0
def __contains__(self, key):
# type: (str) -> bool
return False
def __iter__(self):
# type: () -> Iterator
return iter([])
def __mro_entries__(self, bases):
# type: (Tuple) -> Tuple
return (self.__class__,)
def __getitem__(self, key):
# type: (str) -> _MockObject
return _make_subclass(key, self.__display_name__, self.__class__)()
def __getattr__(self, key):
# type: (str) -> _MockObject
return _make_subclass(key, self.__display_name__, self.__class__)()
def __call__(self, *args, **kw):
# type: (Any, Any) -> Any
if args and type(args[0]) in [FunctionType, MethodType]:
# Appears to be a decorator, pass through unchanged
return args[0]
return self
def __repr__(self):
# type: () -> str
return self.__display_name__
def _make_subclass(name, module, superclass=_MockObject, attributes=None):
# type: (str, str, Any, dict) -> Any
attrs = {'__module__': module, '__display_name__': module + '.' + name}
attrs.update(attributes or {})
return type(name, (superclass,), attrs)
class _MockModule(ModuleType):
"""Used by autodoc_mock_imports."""
__file__ = os.devnull
def __init__(self, name, loader=None):
# type: (str, _MockImporter) -> None
super().__init__(name)
self.__all__ = [] # type: List[str]
self.__path__ = [] # type: List[str]
if loader is not None:
warnings.warn('The loader argument for _MockModule is deprecated.',
RemovedInSphinx30Warning)
def __getattr__(self, name):
# type: (str) -> _MockObject
return _make_subclass(name, self.__name__)()
def __repr__(self):
# type: () -> str
return self.__name__
class _MockImporter(MetaPathFinder):
def __init__(self, names):
# type: (List[str]) -> None
self.names = names
self.mocked_modules = [] # type: List[str]
# enable hook by adding itself to meta_path
sys.meta_path.insert(0, self)
warnings.warn('_MockImporter is now deprecated.',
RemovedInSphinx30Warning)
def disable(self):
# type: () -> None
# remove `self` from `sys.meta_path` to disable import hook
sys.meta_path = [i for i in sys.meta_path if i is not self]
# remove mocked modules from sys.modules to avoid side effects after
# running auto-documenter
for m in self.mocked_modules:
if m in sys.modules:
del sys.modules[m]
def find_module(self, name, path=None):
# type: (str, Sequence[Union[bytes, str]]) -> Any
# check if name is (or is a descendant of) one of our base_packages
for n in self.names:
if n == name or name.startswith(n + '.'):
return self
return None
def load_module(self, name):
# type: (str) -> ModuleType
if name in sys.modules:
# module has already been imported, return it
return sys.modules[name]
else:
logger.debug('[autodoc] adding a mock module %s!', name)
module = _MockModule(name, self)
sys.modules[name] = module
self.mocked_modules.append(name)
return module
class MockLoader(Loader):
"""A loader for mocking."""
def __init__(self, finder):
# type: (MockFinder) -> None
super().__init__()
self.finder = finder
def create_module(self, spec):
# type: (ModuleSpec) -> ModuleType
logger.debug('[autodoc] adding a mock module as %s!', spec.name)
self.finder.mocked_modules.append(spec.name)
return _MockModule(spec.name)
def exec_module(self, module):
# type: (ModuleType) -> None
pass # nothing to do
class MockFinder(MetaPathFinder):
"""A finder for mocking."""
def __init__(self, modnames):
# type: (List[str]) -> None
super().__init__()
self.modnames = modnames
self.loader = MockLoader(self)
self.mocked_modules = [] # type: List[str]
def find_spec(self, fullname, path, target=None):
# type: (str, Sequence[Union[bytes, str]], ModuleType) -> ModuleSpec
for modname in self.modnames:
# check if fullname is (or is a descendant of) one of our targets
if modname == fullname or fullname.startswith(modname + '.'):
return ModuleSpec(fullname, self.loader)
return None
def invalidate_caches(self):
# type: () -> None
"""Invalidate mocked modules on sys.modules."""
for modname in self.mocked_modules:
sys.modules.pop(modname, None)
@contextlib.contextmanager
def mock(modnames):
# type: (List[str]) -> Generator[None, None, None]
"""Insert mock modules during context::
with mock(['target.module.name']):
# mock modules are enabled here
...
"""
try:
finder = MockFinder(modnames)
sys.meta_path.insert(0, finder)
yield
finally:
sys.meta_path.remove(finder)
finder.invalidate_caches()
def import_module(modname, warningiserror=False):
# type: (str, bool) -> Any
"""
@ -343,3 +147,18 @@ def get_object_members(subject, objpath, attrgetter, analyzer=None):
members[name] = Attribute(name, True, INSTANCEATTR)
return members
from sphinx.ext.autodoc.mock import ( # NOQA
_MockModule, _MockObject, MockFinder, MockLoader, mock
)
deprecated_alias('sphinx.ext.autodoc.importer',
{
'_MockModule': _MockModule,
'_MockObject': _MockObject,
'MockFinder': MockFinder,
'MockLoader': MockLoader,
'mock': mock,
},
RemovedInSphinx40Warning)

169
sphinx/ext/autodoc/mock.py Normal file
View File

@ -0,0 +1,169 @@
"""
sphinx.ext.autodoc.mock
~~~~~~~~~~~~~~~~~~~~~~~
mock for autodoc
:copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import contextlib
import os
import sys
from importlib.abc import Loader, MetaPathFinder
from importlib.machinery import ModuleSpec
from types import FunctionType, MethodType, ModuleType
from sphinx.util import logging
if False:
# For type annotation
from typing import Any, Generator, Iterator, List, Sequence, Tuple, Union # NOQA
logger = logging.getLogger(__name__)
class _MockObject:
"""Used by autodoc_mock_imports."""
__display_name__ = '_MockObject'
def __new__(cls, *args, **kwargs):
# type: (Any, Any) -> Any
if len(args) == 3 and isinstance(args[1], tuple):
superclass = args[1][-1].__class__
if superclass is cls:
# subclassing MockObject
return _make_subclass(args[0], superclass.__display_name__,
superclass=superclass, attributes=args[2])
return super().__new__(cls)
def __init__(self, *args, **kwargs):
# type: (Any, Any) -> None
self.__qualname__ = ''
def __len__(self):
# type: () -> int
return 0
def __contains__(self, key):
# type: (str) -> bool
return False
def __iter__(self):
# type: () -> Iterator
return iter([])
def __mro_entries__(self, bases):
# type: (Tuple) -> Tuple
return (self.__class__,)
def __getitem__(self, key):
# type: (str) -> _MockObject
return _make_subclass(key, self.__display_name__, self.__class__)()
def __getattr__(self, key):
# type: (str) -> _MockObject
return _make_subclass(key, self.__display_name__, self.__class__)()
def __call__(self, *args, **kw):
# type: (Any, Any) -> Any
if args and type(args[0]) in [FunctionType, MethodType]:
# Appears to be a decorator, pass through unchanged
return args[0]
return self
def __repr__(self):
# type: () -> str
return self.__display_name__
def _make_subclass(name, module, superclass=_MockObject, attributes=None):
# type: (str, str, Any, dict) -> Any
attrs = {'__module__': module, '__display_name__': module + '.' + name}
attrs.update(attributes or {})
return type(name, (superclass,), attrs)
class _MockModule(ModuleType):
"""Used by autodoc_mock_imports."""
__file__ = os.devnull
def __init__(self, name):
# type: (str) -> None
super().__init__(name)
self.__all__ = [] # type: List[str]
self.__path__ = [] # type: List[str]
def __getattr__(self, name):
# type: (str) -> _MockObject
return _make_subclass(name, self.__name__)()
def __repr__(self):
# type: () -> str
return self.__name__
class MockLoader(Loader):
"""A loader for mocking."""
def __init__(self, finder):
# type: (MockFinder) -> None
super().__init__()
self.finder = finder
def create_module(self, spec):
# type: (ModuleSpec) -> ModuleType
logger.debug('[autodoc] adding a mock module as %s!', spec.name)
self.finder.mocked_modules.append(spec.name)
return _MockModule(spec.name)
def exec_module(self, module):
# type: (ModuleType) -> None
pass # nothing to do
class MockFinder(MetaPathFinder):
"""A finder for mocking."""
def __init__(self, modnames):
# type: (List[str]) -> None
super().__init__()
self.modnames = modnames
self.loader = MockLoader(self)
self.mocked_modules = [] # type: List[str]
def find_spec(self, fullname, path, target=None):
# type: (str, Sequence[Union[bytes, str]], ModuleType) -> ModuleSpec
for modname in self.modnames:
# check if fullname is (or is a descendant of) one of our targets
if modname == fullname or fullname.startswith(modname + '.'):
return ModuleSpec(fullname, self.loader)
return None
def invalidate_caches(self):
# type: () -> None
"""Invalidate mocked modules on sys.modules."""
for modname in self.mocked_modules:
sys.modules.pop(modname, None)
@contextlib.contextmanager
def mock(modnames):
# type: (List[str]) -> Generator[None, None, None]
"""Insert mock modules during context::
with mock(['target.module.name']):
# mock modules are enabled here
...
"""
try:
finder = MockFinder(modnames)
sys.meta_path.insert(0, finder)
yield
finally:
sys.meta_path.remove(finder)
finder.invalidate_caches()

View File

@ -24,11 +24,6 @@ if False:
logger = logging.getLogger(__name__)
if False:
# For type annotation
from typing import Any, Dict # NOQA
from sphinx.application import Sphinx # NOQA
def get_node_depth(node):
i = 0

View File

@ -72,12 +72,13 @@ from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.environment.adapters.toctree import TocTree
from sphinx.ext.autodoc import get_documenters
from sphinx.ext.autodoc.directive import DocumenterBridge, Options
from sphinx.ext.autodoc.importer import import_module, mock
from sphinx.ext.autodoc.importer import import_module
from sphinx.ext.autodoc.mock import mock
from sphinx.locale import __
from sphinx.pycode import ModuleAnalyzer, PycodeError
from sphinx.util import import_object, rst, logging
from sphinx.util.docutils import (
NullReporter, SphinxDirective, new_document, switch_source_input
NullReporter, SphinxDirective, SphinxRole, new_document, switch_source_input
)
from sphinx.util.matching import Matcher
@ -642,6 +643,7 @@ def autolink_role(typ, rawtext, etext, lineno, inliner, options={}, content=[]):
Expands to ':obj:`text`' if `text` is an object that can be imported;
otherwise expands to '*text*'.
"""
warnings.warn('autolink_role() is deprecated.', RemovedInSphinx40Warning)
env = inliner.document.settings.env
pyobj_role = env.get_domain('py').role('obj')
objects, msg = pyobj_role('obj', rawtext, etext, lineno, inliner, options, content)
@ -660,6 +662,34 @@ def autolink_role(typ, rawtext, etext, lineno, inliner, options={}, content=[]):
return objects, msg
class AutoLink(SphinxRole):
"""Smart linking role.
Expands to ':obj:`text`' if `text` is an object that can be imported;
otherwise expands to '*text*'.
"""
def run(self):
# type: () -> Tuple[List[nodes.Node], List[nodes.system_message]]
pyobj_role = self.env.get_domain('py').role('obj')
objects, errors = pyobj_role('obj', self.rawtext, self.text, self.lineno,
self.inliner, self.options, self.content)
if errors:
return objects, errors
assert len(objects) == 1
pending_xref = cast(addnodes.pending_xref, objects[0])
try:
# try to import object by name
prefixes = get_import_prefixes_from_env(self.env)
import_by_name(pending_xref['reftarget'], prefixes)
except ImportError:
literal = cast(nodes.literal, pending_xref[0])
objects[0] = nodes.emphasis(self.rawtext, literal.astext(),
classes=literal['classes'])
return objects, errors
def get_rst_suffix(app):
# type: (Sphinx) -> str
def get_supported_format(suffix):
@ -730,7 +760,7 @@ def setup(app):
man=(autosummary_noop, autosummary_noop),
texinfo=(autosummary_noop, autosummary_noop))
app.add_directive('autosummary', Autosummary)
app.add_role('autolink', autolink_role)
app.add_role('autolink', AutoLink())
app.connect('doctree-read', process_autosummary_toc)
app.connect('builder-inited', process_generate_options)
app.add_config_value('autosummary_generate', [], True, [bool])

View File

@ -42,9 +42,7 @@ from sphinx.util.rst import escape as rst_escape
if False:
# For type annotation
from typing import Any, Callable, Dict, List, Tuple, Type, Union # NOQA
from sphinx import addnodes # NOQA
from sphinx.builders import Builder # NOQA
from sphinx.environment import BuildEnvironment # NOQA
from sphinx.ext.autodoc import Documenter # NOQA

View File

@ -23,7 +23,7 @@ from sphinx.util.inspect import safe_getattr
if False:
# For type annotation
from typing import Any, Callable, Dict, IO, List, Pattern, Set, Tuple # NOQA
from typing import Any, Dict, IO, List, Pattern, Set, Tuple # NOQA
from sphinx.application import Sphinx # NOQA
logger = logging.getLogger(__name__)

View File

@ -29,12 +29,11 @@ from sphinx.locale import __
from sphinx.util import logging
from sphinx.util.console import bold # type: ignore
from sphinx.util.docutils import SphinxDirective
from sphinx.util.nodes import set_source_info
from sphinx.util.osutil import relpath
if False:
# For type annotation
from typing import Any, Callable, Dict, IO, Iterable, List, Optional, Sequence, Set, Tuple, Type # NOQA
from typing import Any, Callable, Dict, Iterable, List, Optional, Sequence, Set, Tuple, Type # NOQA
from sphinx.application import Sphinx # NOQA
logger = logging.getLogger(__name__)
@ -83,16 +82,6 @@ class TestDirective(SphinxDirective):
def run(self):
# type: () -> List[nodes.Node]
if 'skipif' in self.options:
condition = self.options['skipif']
context = {} # type: Dict[str, Any]
if self.config.doctest_global_setup:
exec(self.config.doctest_global_setup, context)
should_skip = eval(condition, context)
if self.config.doctest_global_cleanup:
exec(self.config.doctest_global_cleanup, context)
if should_skip:
return []
# use ordinary docutils nodes for test code: they get special attributes
# so that our builder recognizes them, and the other builders are happy.
code = '\n'.join(self.content)
@ -114,7 +103,7 @@ class TestDirective(SphinxDirective):
else:
groups = ['default']
node = nodetype(code, code, testnodetype=self.name, groups=groups)
set_source_info(self, node)
self.set_source_info(node)
if test is not None:
# only save if it differs from code
node['test'] = test
@ -160,6 +149,8 @@ class TestDirective(SphinxDirective):
self.state.document.reporter.warning(
__("'%s' is not a valid pyversion option") % spec,
line=self.lineno)
if 'skipif' in self.options:
node['skipif'] = self.options['skipif']
return [node]
@ -404,6 +395,20 @@ Doctest summary
return node.line - 1
return None
def skipped(self, node):
# type: (nodes.Element) -> bool
if 'skipif' not in node:
return False
else:
condition = node['skipif']
context = {} # type: Dict[str, Any]
if self.config.doctest_global_setup:
exec(self.config.doctest_global_setup, context)
should_skip = eval(condition, context)
if self.config.doctest_global_cleanup:
exec(self.config.doctest_global_cleanup, context)
return should_skip
def test_doc(self, docname, doctree):
# type: (str, nodes.Node) -> None
groups = {} # type: Dict[str, TestGroup]
@ -429,8 +434,10 @@ Doctest summary
# type: (nodes.Node) -> bool
return isinstance(node, (nodes.literal_block, nodes.comment)) \
and 'testnodetype' in node
for node in doctree.traverse(condition): # type: nodes.Element
if self.skipped(node):
continue
source = node['test'] if 'test' in node else node.astext()
filename = self.get_filename_for_node(node, docname)
line_number = self.get_line_number(node)

View File

@ -196,9 +196,7 @@ class GraphvizSimple(SphinxDirective):
node = graphviz()
node['code'] = '%s %s {\n%s\n}\n' % \
(self.name, self.arguments[0], '\n'.join(self.content))
node['options'] = {
'docname': path.splitext(self.state.document.current_source)[0],
}
node['options'] = {'docname': self.env.docname}
if 'graphviz_dot' in self.options:
node['options']['graphviz_dot'] = self.options['graphviz_dot']
if 'alt' in self.options:

View File

@ -23,7 +23,6 @@ from docutils import nodes
import sphinx
from sphinx.util.docutils import SphinxDirective
from sphinx.util.nodes import set_source_info
if False:
# For type annotation
@ -47,7 +46,7 @@ class IfConfig(SphinxDirective):
# type: () -> List[nodes.Node]
node = ifconfig()
node.document = self.state.document
set_source_info(self, node)
self.set_source_info(node)
node['expr'] = self.arguments[0]
self.state.nested_parse(self.content, self.content_offset,
node, match_titles=True)
@ -56,7 +55,7 @@ class IfConfig(SphinxDirective):
def process_ifconfig_nodes(app, doctree, docname):
# type: (Sphinx, nodes.document, str) -> None
ns = dict((confval.name, confval.value) for confval in app.config)
ns = {confval.name: confval.value for confval in app.config}
ns.update(app.config.__dict__.copy())
ns['builder'] = app.builder.name
for node in doctree.traverse(ifconfig):

View File

@ -31,7 +31,6 @@ from sphinx.util.png import read_png_depth, write_png_depth
if False:
# For type annotation
from typing import Any, Dict, List, Tuple, Union # NOQA
from sphinx.addnodes import displaymath # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.builders import Builder # NOQA
from sphinx.config import Config # NOQA

View File

@ -55,7 +55,7 @@ from sphinx.util.docutils import SphinxDirective
if False:
# For type annotation
from typing import Any, Dict, List, Tuple, Dict, Optional # NOQA
from typing import Any, Dict, List, Optional, Tuple # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.environment import BuildEnvironment # NOQA
from sphinx.writers.html import HTMLTranslator # NOQA
@ -167,11 +167,17 @@ class InheritanceGraph:
"""Return name and bases for all classes that are ancestors of
*classes*.
*parts* gives the number of dotted name parts that is removed from the
displayed node names.
*parts* gives the number of dotted name parts to include in the
displayed node names, from right to left. If given as a negative, the
number of parts to drop from the left. A value of 0 displays the full
dotted name. E.g. ``sphinx.ext.inheritance_diagram.InheritanceGraph``
with ``parts=2`` or ``parts=-2`` gets displayed as
``inheritance_diagram.InheritanceGraph``, and as
``ext.inheritance_diagram.InheritanceGraph`` with ``parts=3`` or
``parts=-1``.
*top_classes* gives the name(s) of the top most ancestor class to traverse
to. Multiple names can be specified separated by comma.
*top_classes* gives the name(s) of the top most ancestor class to
traverse to. Multiple names can be specified separated by comma.
"""
all_classes = {}
py_builtins = vars(builtins).values()
@ -332,7 +338,7 @@ class InheritanceDiagram(SphinxDirective):
optional_arguments = 0
final_argument_whitespace = True
option_spec = {
'parts': directives.nonnegative_int,
'parts': int,
'private-bases': directives.flag,
'caption': directives.unchanged,
'top-classes': directives.unchanged_required,

View File

@ -41,7 +41,7 @@ from sphinx.util.inventory import InventoryFile
if False:
# For type annotation
from typing import Any, Dict, IO, List, Tuple, Union # NOQA
from typing import Any, Dict, IO, List, Tuple # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.config import Config # NOQA
from sphinx.environment import BuildEnvironment # NOQA
@ -150,9 +150,9 @@ def _get_safe_url(url):
else:
frags = list(parts)
if parts.port:
frags[1] = '{0}@{1}:{2}'.format(parts.username, parts.hostname, parts.port)
frags[1] = '{}@{}:{}'.format(parts.username, parts.hostname, parts.port)
else:
frags[1] = '{0}@{1}'.format(parts.username, parts.hostname)
frags[1] = '{}@{}'.format(parts.username, parts.hostname)
return urlunsplit(frags)
@ -173,7 +173,7 @@ def fetch_inventory(app, uri, inv):
f = open(path.join(app.srcdir, inv), 'rb')
except Exception as err:
err.args = ('intersphinx inventory %r not fetchable due to %s: %s',
inv, err.__class__, err)
inv, err.__class__, str(err))
raise
try:
if hasattr(f, 'url'):
@ -191,7 +191,7 @@ def fetch_inventory(app, uri, inv):
raise ValueError('unknown or unsupported inventory version: %r' % exc)
except Exception as err:
err.args = ('intersphinx inventory %r not readable due to %s: %s',
inv, err.__class__.__name__, err)
inv, err.__class__.__name__, str(err))
raise
else:
return invdata
@ -234,10 +234,9 @@ def load_mappings(app):
for fail in failures:
logger.info(*fail)
else:
issues = '\n'.join([f[0] % f[1:] for f in failures])
logger.warning(__("failed to reach any of the inventories "
"with the following issues:"))
for fail in failures:
logger.warning(*fail)
"with the following issues:") + "\n" + issues)
if update:
inventories.clear()

View File

@ -1,84 +0,0 @@
"""
sphinx.ext.mathbase
~~~~~~~~~~~~~~~~~~~
Set up math support in source files and LaTeX/text output.
:copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import warnings
from docutils import nodes
from docutils.parsers.rst.roles import math_role as math_role_base
from sphinx.addnodes import math, math_block as displaymath # NOQA # to keep compatibility
from sphinx.builders.latex.nodes import math_reference as eqref # NOQA # to keep compatibility
from sphinx.deprecation import RemovedInSphinx30Warning
from sphinx.directives.patches import MathDirective as MathDirectiveBase
from sphinx.domains.math import MathDomain # NOQA # to keep compatibility
from sphinx.domains.math import MathReferenceRole as EqXRefRole # NOQA # to keep compatibility
if False:
# For type annotation
from typing import Any, Callable, List, Tuple # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.writers.html import HTMLTranslator # NOQA
class MathDirective(MathDirectiveBase):
def run(self):
warnings.warn('sphinx.ext.mathbase.MathDirective is moved to '
'sphinx.directives.patches package.',
RemovedInSphinx30Warning, stacklevel=2)
return super().run()
def math_role(role, rawtext, text, lineno, inliner, options={}, content=[]):
warnings.warn('sphinx.ext.mathbase.math_role() is deprecated. '
'Please use docutils.parsers.rst.roles.math_role() instead.',
RemovedInSphinx30Warning, stacklevel=2)
return math_role_base(role, rawtext, text, lineno, inliner, options, content)
def get_node_equation_number(writer, node):
# type: (HTMLTranslator, nodes.math_block) -> str
warnings.warn('sphinx.ext.mathbase.get_node_equation_number() is moved to '
'sphinx.util.math package.',
RemovedInSphinx30Warning, stacklevel=2)
from sphinx.util.math import get_node_equation_number
return get_node_equation_number(writer, node)
def wrap_displaymath(text, label, numbering):
# type: (str, str, bool) -> str
warnings.warn('sphinx.ext.mathbase.wrap_displaymath() is moved to '
'sphinx.util.math package.',
RemovedInSphinx30Warning, stacklevel=2)
from sphinx.util.math import wrap_displaymath
return wrap_displaymath(text, label, numbering)
def is_in_section_title(node):
# type: (nodes.Element) -> bool
"""Determine whether the node is in a section title"""
from sphinx.util.nodes import traverse_parent
warnings.warn('is_in_section_title() is deprecated.',
RemovedInSphinx30Warning, stacklevel=2)
for ancestor in traverse_parent(node):
if isinstance(ancestor, nodes.title) and \
isinstance(ancestor.parent, nodes.section):
return True
return False
def setup_math(app, htmlinlinevisitors, htmldisplayvisitors):
# type: (Sphinx, Tuple[Callable, Callable], Tuple[Callable, Callable]) -> None
warnings.warn('setup_math() is deprecated. '
'Please use app.add_html_math_renderer() instead.',
RemovedInSphinx30Warning, stacklevel=2)
app.add_html_math_renderer('unknown', htmlinlinevisitors, htmldisplayvisitors)

View File

@ -18,11 +18,11 @@ from docutils.parsers.rst import directives
from docutils.parsers.rst.directives.admonitions import BaseAdmonition
import sphinx
from sphinx.environment import NoUri
from sphinx.errors import NoUri
from sphinx.locale import _, __
from sphinx.util import logging
from sphinx.util.docutils import SphinxDirective
from sphinx.util.nodes import set_source_info
from sphinx.util.nodes import make_refnode
from sphinx.util.texescape import tex_escape_map
if False:
@ -56,6 +56,7 @@ class Todo(BaseAdmonition, SphinxDirective):
final_argument_whitespace = False
option_spec = {
'class': directives.class_option,
'name': directives.unchanged,
}
def run(self):
@ -68,13 +69,10 @@ class Todo(BaseAdmonition, SphinxDirective):
return [todo]
elif isinstance(todo, todo_node):
todo.insert(0, nodes.title(text=_('Todo')))
set_source_info(self, todo)
targetid = 'index-%s' % self.env.new_serialno('index')
# Stash the target to be retrieved later in latex_visit_todo_node.
todo['targetref'] = '%s:%s' % (self.env.docname, targetid)
targetnode = nodes.target('', '', ids=[targetid])
return [targetnode, todo]
self.add_name(todo)
self.set_source_info(todo)
self.state.document.note_explicit_target(todo)
return [todo]
else:
raise RuntimeError # never reached here
@ -90,20 +88,14 @@ def process_todos(app, doctree):
for node in doctree.traverse(todo_node):
app.emit('todo-defined', node)
try:
targetnode = node.parent[node.parent.index(node) - 1]
if not isinstance(targetnode, nodes.target):
raise IndexError
except IndexError:
targetnode = None
newnode = node.deepcopy()
del newnode['ids']
newnode['ids'] = []
env.todo_all_todos.append({ # type: ignore
'docname': env.docname,
'source': node.source or env.doc2path(env.docname),
'lineno': node.line,
'todo': newnode,
'target': targetnode,
'target': node['ids'][0],
})
if env.config.todo_emit_warnings:
@ -168,27 +160,16 @@ def process_todo_nodes(app, doctree, fromdocname):
para += nodes.Text(desc1, desc1)
# Create a reference
newnode = nodes.reference('', '', internal=True)
innernode = nodes.emphasis(_('original entry'), _('original entry'))
try:
newnode['refuri'] = app.builder.get_relative_uri(
fromdocname, todo_info['docname'])
if 'refid' in todo_info['target']:
newnode['refuri'] += '#' + todo_info['target']['refid']
else:
newnode['refuri'] += '#' + todo_info['target']['ids'][0]
para += make_refnode(app.builder, fromdocname, todo_info['docname'],
todo_info['target'], innernode)
except NoUri:
# ignore if no URI can be determined, e.g. for LaTeX output
pass
newnode.append(innernode)
para += newnode
para += nodes.Text(desc2, desc2)
todo_entry = todo_info['todo']
# Remove targetref from the (copied) node to avoid emitting a
# duplicate label of the original entry when we walk this node.
if 'targetref' in todo_entry:
del todo_entry['targetref']
# (Recursively) resolve references in the todo content
env.resolve_references(todo_entry, todo_info['docname'],
@ -231,12 +212,7 @@ def depart_todo_node(self, node):
def latex_visit_todo_node(self, node):
# type: (LaTeXTranslator, todo_node) -> None
self.body.append('\n\\begin{sphinxadmonition}{note}{')
# If this is the original todo node, emit a label that will be referenced by
# a hyperref in the todolist.
target = node.get('targetref')
if target is not None:
self.body.append('\\label{%s}' % target)
self.body.append(self.hypertarget_to(node))
title_node = cast(nodes.title, node[0])
self.body.append('%s:}' % title_node.astext().translate(tex_escape_map))
node.pop(0)

View File

@ -9,13 +9,11 @@
"""
import traceback
import warnings
from docutils import nodes
import sphinx
from sphinx import addnodes
from sphinx.deprecation import RemovedInSphinx30Warning
from sphinx.locale import _, __
from sphinx.pycode import ModuleAnalyzer
from sphinx.util import get_full_modname, logging, status_iterator
@ -238,14 +236,6 @@ def collect_pages(app):
yield ('_modules/index', context, 'page.html')
def migrate_viewcode_import(app, config):
# type: (Sphinx, Config) -> None
if config.viewcode_import is not None:
warnings.warn('viewcode_import was renamed to viewcode_follow_imported_members. '
'Please update your configuration.',
RemovedInSphinx30Warning, stacklevel=2)
def setup(app):
# type: (Sphinx) -> Dict[str, Any]
app.add_config_value('viewcode_import', None, False)

View File

@ -8,21 +8,16 @@
:license: BSD, see LICENSE for details.
"""
import html
import warnings
from pygments import highlight
from pygments.filters import ErrorToken
from pygments.formatters import HtmlFormatter, LatexFormatter
from pygments.lexer import Lexer # NOQA
from pygments.lexer import Lexer
from pygments.lexers import get_lexer_by_name, guess_lexer
from pygments.lexers import PythonLexer, Python3Lexer, PythonConsoleLexer, \
CLexer, TextLexer, RstLexer
from pygments.styles import get_style_by_name
from pygments.util import ClassNotFound
from sphinx.deprecation import RemovedInSphinx30Warning
from sphinx.ext import doctest
from sphinx.locale import __
from sphinx.pygments_styles import SphinxStyle, NoneStyle
from sphinx.util import logging
@ -66,8 +61,8 @@ class PygmentsBridge:
html_formatter = HtmlFormatter
latex_formatter = LatexFormatter
def __init__(self, dest='html', stylename='sphinx', trim_doctest_flags=None):
# type: (str, str, bool) -> None
def __init__(self, dest='html', stylename='sphinx'):
# type: (str, str) -> None
self.dest = dest
if stylename is None or stylename == 'sphinx':
style = SphinxStyle
@ -86,30 +81,11 @@ class PygmentsBridge:
self.formatter = self.latex_formatter
self.formatter_args['commandprefix'] = 'PYG'
self.trim_doctest_flags = trim_doctest_flags
if trim_doctest_flags is not None:
warnings.warn('trim_doctest_flags option for PygmentsBridge is now deprecated.',
RemovedInSphinx30Warning, stacklevel=2)
def get_formatter(self, **kwargs):
# type: (Any) -> Formatter
kwargs.update(self.formatter_args)
return self.formatter(**kwargs)
def unhighlighted(self, source):
# type: (str) -> str
warnings.warn('PygmentsBridge.unhighlighted() is now deprecated.',
RemovedInSphinx30Warning, stacklevel=2)
if self.dest == 'html':
return '<pre>' + html.escape(source) + '</pre>\n'
else:
# first, escape highlighting characters like Pygments does
source = source.translate(escape_hl_chars)
# then, escape all characters nonrepresentable in LaTeX
source = source.translate(tex_hl_escape_map_new)
return '\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n' + \
source + '\\end{Verbatim}\n'
def highlight_block(self, source, lang, opts=None, location=None, force=False, **kwargs):
# type: (str, str, Any, Any, bool, Any) -> str
if not isinstance(source, str):
@ -145,11 +121,6 @@ class PygmentsBridge:
else:
lexer.add_filter('raiseonerror')
# trim doctest options if wanted
if isinstance(lexer, PythonConsoleLexer) and self.trim_doctest_flags:
source = doctest.blankline_re.sub('', source)
source = doctest.doctestopt_re.sub('', source)
# highlight via Pygments
formatter = self.get_formatter(**kwargs)
try:

View File

@ -8,45 +8,35 @@
:license: BSD, see LICENSE for details.
"""
import codecs
import warnings
from typing import Any
from docutils.core import Publisher
from docutils.io import FileInput, NullOutput
from docutils.parsers.rst import Parser as RSTParser
from docutils.readers import standalone
from docutils.statemachine import StringList, string2lines
from docutils.writers import UnfilteredWriter
from typing import Any, Union # NOQA
from sphinx.deprecation import RemovedInSphinx30Warning
from sphinx.transforms import (
ApplySourceWorkaround, ExtraTranslatableNodes, CitationReferences,
DefaultSubstitutions, MoveModuleTargets, HandleCodeBlocks, SortIds, FigureAligner,
AutoNumbering, AutoIndexUpgrader, FilterSystemMessages,
UnreferencedFootnotesDetector, SphinxSmartQuotes, DoctreeReadEvent, ManpageLink
AutoIndexUpgrader, DoctreeReadEvent, FigureAligner, SphinxTransformer
)
from sphinx.transforms import SphinxTransformer
from sphinx.transforms.compact_bullet_list import RefOnlyBulletListTransform
from sphinx.transforms.i18n import (
PreserveTranslatableMessages, Locale, RemoveTranslatableInline,
)
from sphinx.transforms.references import SphinxDomains, SubstitutionDefinitionsRemover
from sphinx.transforms.references import SphinxDomains
from sphinx.util import logging
from sphinx.util import UnicodeDecodeErrorHandler
from sphinx.util.docutils import LoggingReporter
from sphinx.util.rst import append_epilog, docinfo_re, prepend_prolog
from sphinx.versioning import UIDTransform
if False:
# For type annotation
from typing import Any, Dict, List, Tuple, Type, Union # NOQA
from typing import Dict, List, Tuple, Type # NOQA
from docutils import nodes # NOQA
from docutils.frontend import Values # NOQA
from docutils.io import Input # NOQA
from docutils.parsers import Parser # NOQA
from docutils.transforms import Transform # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.builders import Builder # NOQA
from sphinx.environment import BuildEnvironment # NOQA
@ -94,13 +84,6 @@ class SphinxStandaloneReader(SphinxBaseReader):
"""
A basic document reader for Sphinx.
"""
transforms = [ApplySourceWorkaround, ExtraTranslatableNodes, PreserveTranslatableMessages,
Locale, CitationReferences, DefaultSubstitutions, MoveModuleTargets,
HandleCodeBlocks, AutoNumbering, AutoIndexUpgrader, SortIds, FigureAligner,
RemoveTranslatableInline, FilterSystemMessages, RefOnlyBulletListTransform,
UnreferencedFootnotesDetector, SphinxSmartQuotes, ManpageLink,
SphinxDomains, SubstitutionDefinitionsRemover, DoctreeReadEvent,
UIDTransform]
def __init__(self, app, *args, **kwargs):
# type: (Sphinx, Any, Any) -> None
@ -137,25 +120,17 @@ class SphinxI18nReader(SphinxBaseReader):
Because the translated texts are partial and they don't have correct line numbers.
"""
transforms = [ApplySourceWorkaround, ExtraTranslatableNodes, CitationReferences,
DefaultSubstitutions, MoveModuleTargets, HandleCodeBlocks,
AutoNumbering, SortIds, RemoveTranslatableInline,
FilterSystemMessages, RefOnlyBulletListTransform,
UnreferencedFootnotesDetector, SphinxSmartQuotes, ManpageLink,
SubstitutionDefinitionsRemover]
def __init__(self, app, *args, **kwargs):
# type: (Sphinx, Any, Any) -> None
self.transforms = self.transforms + app.registry.get_transforms()
unused = [PreserveTranslatableMessages, Locale, RemoveTranslatableInline,
AutoIndexUpgrader, FigureAligner, SphinxDomains, DoctreeReadEvent,
UIDTransform]
for transform in unused:
if transform in self.transforms:
self.transforms.remove(transform)
def set_lineno_for_reporter(self, lineno):
# type: (int) -> None
"""Stores the source line number of original text."""
warnings.warn('SphinxI18nReader.set_lineno_for_reporter() is deprecated.',
RemovedInSphinx30Warning, stacklevel=2)
@property
def line(self):
# type: () -> int
warnings.warn('SphinxI18nReader.line is deprecated.',
RemovedInSphinx30Warning, stacklevel=2)
return 0
super().__init__(app, *args, **kwargs)
class SphinxDummyWriter(UnfilteredWriter):
@ -174,97 +149,12 @@ def SphinxDummySourceClass(source, *args, **kwargs):
return source
class SphinxBaseFileInput(FileInput):
"""A base class of SphinxFileInput.
It supports to replace unknown Unicode characters to '?'.
"""
def __init__(self, app, env, *args, **kwds):
# type: (Sphinx, BuildEnvironment, Any, Any) -> None
self.app = app
self.env = env
warnings.warn('%s is deprecated.' % self.__class__.__name__,
RemovedInSphinx30Warning, stacklevel=2)
kwds['error_handler'] = 'sphinx' # py3: handle error on open.
super().__init__(*args, **kwds)
def warn_and_replace(self, error):
# type: (Any) -> Tuple
return UnicodeDecodeErrorHandler(self.env.docname)(error)
class SphinxFileInput(FileInput):
"""A basic FileInput for Sphinx."""
supported = ('*',) # RemovedInSphinx30Warning
def __init__(self, *args, **kwargs):
# type: (Any, Any) -> None
kwargs['error_handler'] = 'sphinx'
super(SphinxFileInput, self).__init__(*args, **kwargs)
class SphinxRSTFileInput(SphinxBaseFileInput):
"""A reST FileInput for Sphinx.
This FileInput automatically prepends and appends text by :confval:`rst_prolog` and
:confval:`rst_epilog`.
.. important::
This FileInput uses an instance of ``StringList`` as a return value of ``read()``
method to indicate original source filename and line numbers after prepending and
appending.
For that reason, ``sphinx.parsers.RSTParser`` should be used with this to parse
a content correctly.
"""
supported = ('restructuredtext',)
def prepend_prolog(self, text, prolog):
# type: (StringList, str) -> None
docinfo = self.count_docinfo_lines(text)
if docinfo:
# insert a blank line after docinfo
text.insert(docinfo, '', '<generated>', 0)
docinfo += 1
# insert prolog (after docinfo if exists)
for lineno, line in enumerate(prolog.splitlines()):
text.insert(docinfo + lineno, line, '<rst_prolog>', lineno)
text.insert(docinfo + lineno + 1, '', '<generated>', 0)
def append_epilog(self, text, epilog):
# type: (StringList, str) -> None
# append a blank line and rst_epilog
text.append('', '<generated>', 0)
for lineno, line in enumerate(epilog.splitlines()):
text.append(line, '<rst_epilog>', lineno)
def read(self): # type: ignore
# type: () -> StringList
inputstring = super().read()
lines = string2lines(inputstring, convert_whitespace=True)
content = StringList()
for lineno, line in enumerate(lines):
content.append(line, self.source_path, lineno)
prepend_prolog(content, self.env.config.rst_prolog)
append_epilog(content, self.env.config.rst_epilog)
return content
def count_docinfo_lines(self, content):
# type: (StringList) -> int
if len(content) == 0:
return 0
else:
for lineno, line in enumerate(content.data):
if not docinfo_re.match(line):
break
return lineno
super().__init__(*args, **kwargs)
class FiletypeNotFoundError(Exception):

View File

@ -23,7 +23,7 @@ from sphinx.util.osutil import mtimes_of_files
if False:
# For type annotation
from typing import Any, Callable, Dict, List, Iterator, Tuple, Union # NOQA
from typing import Dict, List, Union # NOQA
from jinja2.environment import Environment # NOQA
from sphinx.builders import Builder # NOQA
from sphinx.theming import Theme # NOQA

View File

@ -10,15 +10,12 @@
import gettext
import locale
import warnings
from collections import UserString, defaultdict
from gettext import NullTranslations
from sphinx.deprecation import RemovedInSphinx30Warning
if False:
# For type annotation
from typing import Any, Callable, Dict, Iterable, Iterator, List, Tuple, Union # NOQA
from typing import Any, Callable, Dict, Iterable, List, Tuple, Union # NOQA
class _TranslationProxy(UserString):
@ -127,26 +124,6 @@ class _TranslationProxy(UserString):
return '<%s broken>' % self.__class__.__name__
def mygettext(string):
# type: (str) -> str
"""Used instead of _ when creating TranslationProxies, because _ is
not bound yet at that time.
"""
warnings.warn('sphinx.locale.mygettext() is deprecated. Please use `_()` instead.',
RemovedInSphinx30Warning, stacklevel=2)
return _(string)
def lazy_gettext(string):
# type: (str) -> str
"""A lazy version of `gettext`."""
# if isinstance(string, _TranslationProxy):
# return string
warnings.warn('sphinx.locale.laxy_gettext() is deprecated. Please use `_()` instead.',
RemovedInSphinx30Warning, stacklevel=2)
return _TranslationProxy(mygettext, string) # type: ignore
translators = defaultdict(NullTranslations) # type: Dict[Tuple[str, str], NullTranslations]
@ -255,20 +232,19 @@ def get_translation(catalog, namespace='general'):
import os
from sphinx.locale import get_translation
_ = get_translation(__name__)
MESSAGE_CATALOG_NAME = 'myextension' # name of *.pot, *.po and *.mo files
_ = get_translation(MESSAGE_CATALOG_NAME)
text = _('Hello Sphinx!')
def setup(app):
package_dir = path.abspath(path.dirname(__file__))
locale_dir = os.path.join(package_dir, 'locales')
app.add_message_catalog(__name__, locale_dir)
app.add_message_catalog(MESSAGE_CATALOG_NAME, locale_dir)
With this code, sphinx searches a message catalog from
``${package_dir}/locales/${language}/LC_MESSAGES/${__name__}.mo``
(ex. ``sphinxcontrib.applehelp.mo``). Of course, you can use
arbitrary catalog name instead of ``__name__``. The
:confval:`language` is used for the searching.
``${package_dir}/locales/${language}/LC_MESSAGES/myextension.mo``.
The :confval:`language` is used for the searching.
.. versionadded:: 1.8
"""
@ -296,12 +272,6 @@ _ = get_translation('sphinx')
__ = get_translation('sphinx', 'console')
def l_(*args):
warnings.warn('sphinx.locale.l_() is deprecated. Please use `_()` instead.',
RemovedInSphinx30Warning, stacklevel=2)
return _(*args)
# labels
admonitionlabels = {
'attention': _('Attention'),

View File

@ -1 +1 @@
Documentation.addTranslations({"locale": "ar", "messages": {"%(filename)s &#8212; %(docstitle)s": "", "&#169; <a href=\"%(path)s\">Copyright</a> %(copyright)s.": "&#169; <a href=\"%(path)s\"> \u062d\u0642\u0648\u0642 \u0627\u0644\u0646\u0634\u0631 </a> %(copyright)s", "&#169; Copyright %(copyright)s.": "&#169; \u062d\u0642\u0648\u0642 \u0627\u0644\u0646\u0634\u0631 %(copyright)s", ", in ": "", "About these documents": "", "Automatically generated list of changes in version %(version)s": "", "C API changes": "", "Changes in Version %(version)s &#8212; %(docstitle)s": "", "Collapse sidebar": "", "Complete Table of Contents": "", "Contents": "", "Copyright": "", "Created using <a href=\"http://sphinx-doc.org/\">Sphinx</a> %(sphinx_version)s.": "", "Expand sidebar": "", "From here you can search these documents. Enter your search\n words into the box below and click \"search\". Note that the search\n function will automatically search for all of the words. Pages\n containing fewer words won't appear in the result list.": "", "Full index on one page": "", "General Index": "", "Global Module Index": "", "Go": "", "Hide Search Matches": "", "Index": "", "Index &ndash; %(key)s": "", "Index pages by letter": "", "Indices and tables:": "", "Last updated on %(last_updated)s.": "", "Library changes": "", "Navigation": "", "Next topic": "\u0627\u0644\u0645\u0648\u0636\u0648\u0639 \u0627\u0644\u062a\u0627\u0644\u064a", "Other changes": "", "Overview": "", "Permalink to this definition": "", "Permalink to this headline": "", "Please activate JavaScript to enable the search\n functionality.": "", "Preparing search...": "", "Previous topic": "\u0627\u0644\u0645\u0648\u0636\u0648\u0639 \u0627\u0644\u0633\u0627\u0628\u0642", "Quick search": "", "Search": "", "Search Page": "", "Search Results": "", "Search finished, found %s page(s) matching the search query.": "", "Search within %(docstitle)s": "\u0627\u0644\u0628\u062d\u062b \u0636\u0645\u0646 %(docstitle)s", "Searching": "", "Show Source": "", "Table of Contents": "", "This Page": "", "Welcome! This is": "", "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories.": "", "all functions, classes, terms": "", "can be huge": "", "last updated": "", "lists all sections and subsections": "", "next chapter": "\u0627\u0644\u0641\u0635\u0644 \u0627\u0644\u062a\u0627\u0644\u064a", "previous chapter": "", "quick access to all modules": "", "search": "", "search this documentation": "", "the documentation for": ""}, "plural_expr": "n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5"});
Documentation.addTranslations({"locale": "ar", "messages": {"%(filename)s &#8212; %(docstitle)s": "", "&#169; <a href=\"%(path)s\">Copyright</a> %(copyright)s.": "&#169; <a href=\"%(path)s\"> \u062d\u0642\u0648\u0642 \u0627\u0644\u0646\u0634\u0631 </a> %(copyright)s", "&#169; Copyright %(copyright)s.": "&#169; \u062d\u0642\u0648\u0642 \u0627\u0644\u0646\u0634\u0631 %(copyright)s", ", in ": "", "About these documents": "", "Automatically generated list of changes in version %(version)s": "", "C API changes": "", "Changes in Version %(version)s &#8212; %(docstitle)s": "", "Collapse sidebar": "", "Complete Table of Contents": "", "Contents": "", "Copyright": "", "Created using <a href=\"http://sphinx-doc.org/\">Sphinx</a> %(sphinx_version)s.": "", "Expand sidebar": "", "From here you can search these documents. Enter your search\n words into the box below and click \"search\". Note that the search\n function will automatically search for all of the words. Pages\n containing fewer words won't appear in the result list.": "", "Full index on one page": "", "General Index": "", "Global Module Index": "", "Go": "", "Hide Search Matches": "", "Index": "", "Index &ndash; %(key)s": "", "Index pages by letter": "", "Indices and tables:": "", "Last updated on %(last_updated)s.": "", "Library changes": "", "Navigation": "", "Next topic": "\u0627\u0644\u0645\u0648\u0636\u0648\u0639 \u0627\u0644\u062a\u0627\u0644\u064a", "Other changes": "", "Overview": "", "Permalink to this definition": "", "Permalink to this headline": "", "Please activate JavaScript to enable the search\n functionality.": "", "Preparing search...": "", "Previous topic": "\u0627\u0644\u0645\u0648\u0636\u0648\u0639 \u0627\u0644\u0633\u0627\u0628\u0642", "Quick search": "", "Search": "", "Search Page": "", "Search Results": "", "Search finished, found %s page(s) matching the search query.": "", "Search within %(docstitle)s": "\u0627\u0644\u0628\u062d\u062b \u0636\u0645\u0646 %(docstitle)s", "Searching": "", "Show Source": "", "Table of Contents": "", "This Page": "", "Welcome! This is": "", "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories.": "", "all functions, classes, terms": "", "can be huge": "", "last updated": "", "lists all sections and subsections": "", "next chapter": "\u0627\u0644\u0641\u0635\u0644 \u0627\u0644\u062a\u0627\u0644\u064a", "previous chapter": "", "quick access to all modules": "", "search": "", "search this documentation": "", "the documentation for": ""}, "plural_expr": "n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5"});

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -1 +1 @@
Documentation.addTranslations({"locale": "ca", "messages": {"%(filename)s &#8212; %(docstitle)s": "", "&#169; <a href=\"%(path)s\">Copyright</a> %(copyright)s.": "", "&#169; Copyright %(copyright)s.": "", ", in ": "", "About these documents": "Quant a aquests documents", "Automatically generated list of changes in version %(version)s": "Llista de canvis de la versi\u00f3 %(version)s generada autom\u00e0ticament", "C API changes": "Canvis a la API de C", "Changes in Version %(version)s &#8212; %(docstitle)s": "", "Collapse sidebar": "", "Complete Table of Contents": "Taula de Contingut Completa", "Contents": "", "Copyright": "Copyright", "Created using <a href=\"http://sphinx-doc.org/\">Sphinx</a> %(sphinx_version)s.": "Creat amb <a href=\"http://sphinx-doc.org/\">Sphinx</a> %(sphinx_version)s.", "Expand sidebar": "", "From here you can search these documents. Enter your search\n words into the box below and click \"search\". Note that the search\n function will automatically search for all of the words. Pages\n containing fewer words won't appear in the result list.": "Des d'aqu\u00ed pots fer cerques en aquests documents. Entra les \nparaules de la teva cerca i clica el bot\u00f3 \"cerca\". Tingues en compte\nque la cerca inclour\u00e0 totes les paraules que posis. Les p\u00e0gines que no\ntenen totes les paraules no sortir\u00e0n.", "Full index on one page": "\u00cdndex complet en una p\u00e0gina", "General Index": "\u00cdndex General", "Global Module Index": "\u00cdndex Global de M\u00f2duls", "Go": "Ves a", "Hide Search Matches": "Oculta Resultats de Cerca", "Index": "\u00cdndex", "Index &ndash; %(key)s": "\u00cdndes &ndash; %(key)s", "Index pages by letter": "P\u00e0gines d'\u00edndex per lletra", "Indices and tables:": "\u00cdndexs i taules:", "Last updated on %(last_updated)s.": "\u00daltima actualitzaci\u00f3 el %(last_updated)s.", "Library changes": "Canvis a la llibreria", "Navigation": "Navegaci\u00f3", "Next topic": "Tema seg\u00fcent", "Other changes": "Altres canvis", "Overview": "Resum", "Permalink to this definition": "Link permanent a aquesta definici\u00f3", "Permalink to this headline": "Link permanent a aquest t\u00edtol", "Please activate JavaScript to enable the search\n functionality.": "Activa JavaScript per utilitzar la funcionalitat\nde cerca.", "Preparing search...": "", "Previous topic": "Tema anterior", "Quick search": "Cerca r\u00e0pida", "Search": "Cerca", "Search Page": "P\u00e0gina de Cerca", "Search Results": "Resultats de la Cerca", "Search finished, found %s page(s) matching the search query.": "", "Search within %(docstitle)s": "Cerca dins de %(docstitle)s", "Searching": "", "Show Source": "Mostra Codi Font", "Table of Contents": "", "This Page": "Aquesta P\u00e0gina", "Welcome! This is": "", "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories.": "", "all functions, classes, terms": "totes les funcions, classes, termes", "can be huge": "pot ser gegant", "last updated": "", "lists all sections and subsections": "llista totes les seccions i subseccions", "next chapter": "cap\u00edtol seg\u00fcent", "previous chapter": "cap\u00edtol anterior", "quick access to all modules": "acc\u00e9s r\u00e0pid a tots els m\u00f2duls", "search": "cerca", "search this documentation": "cerca aquesta documentaci\u00f3", "the documentation for": ""}, "plural_expr": "(n != 1)"});
Documentation.addTranslations({"locale": "ca", "messages": {"%(filename)s &#8212; %(docstitle)s": "", "&#169; <a href=\"%(path)s\">Copyright</a> %(copyright)s.": "", "&#169; Copyright %(copyright)s.": "", ", in ": "", "About these documents": "Quant a aquests documents", "Automatically generated list of changes in version %(version)s": "Llista de canvis de la versi\u00f3 %(version)s generada autom\u00e0ticament", "C API changes": "Canvis a la API de C", "Changes in Version %(version)s &#8212; %(docstitle)s": "", "Collapse sidebar": "", "Complete Table of Contents": "Taula de Contingut Completa", "Contents": "", "Copyright": "Copyright", "Created using <a href=\"http://sphinx-doc.org/\">Sphinx</a> %(sphinx_version)s.": "Creat amb <a href=\"http://sphinx-doc.org/\">Sphinx</a> %(sphinx_version)s.", "Expand sidebar": "", "From here you can search these documents. Enter your search\n words into the box below and click \"search\". Note that the search\n function will automatically search for all of the words. Pages\n containing fewer words won't appear in the result list.": "Des d'aqu\u00ed pots fer cerques en aquests documents. Entra les \nparaules de la teva cerca i clica el bot\u00f3 \"cerca\". Tingues en compte\nque la cerca inclour\u00e0 totes les paraules que posis. Les p\u00e0gines que no\ntenen totes les paraules no sortir\u00e0n.", "Full index on one page": "\u00cdndex complet en una p\u00e0gina", "General Index": "\u00cdndex General", "Global Module Index": "\u00cdndex Global de M\u00f2duls", "Go": "Ves a", "Hide Search Matches": "Oculta Resultats de Cerca", "Index": "\u00cdndex", "Index &ndash; %(key)s": "\u00cdndes &ndash; %(key)s", "Index pages by letter": "P\u00e0gines d'\u00edndex per lletra", "Indices and tables:": "\u00cdndexs i taules:", "Last updated on %(last_updated)s.": "\u00daltima actualitzaci\u00f3 el %(last_updated)s.", "Library changes": "Canvis a la llibreria", "Navigation": "Navegaci\u00f3", "Next topic": "Tema seg\u00fcent", "Other changes": "Altres canvis", "Overview": "Resum", "Permalink to this definition": "Link permanent a aquesta definici\u00f3", "Permalink to this headline": "Link permanent a aquest t\u00edtol", "Please activate JavaScript to enable the search\n functionality.": "Activa JavaScript per utilitzar la funcionalitat\nde cerca.", "Preparing search...": "", "Previous topic": "Tema anterior", "Quick search": "Cerca r\u00e0pida", "Search": "Cerca", "Search Page": "P\u00e0gina de Cerca", "Search Results": "Resultats de la Cerca", "Search finished, found %s page(s) matching the search query.": "", "Search within %(docstitle)s": "Cerca dins de %(docstitle)s", "Searching": "", "Show Source": "Mostra Codi Font", "Table of Contents": "", "This Page": "Aquesta P\u00e0gina", "Welcome! This is": "", "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories.": "", "all functions, classes, terms": "totes les funcions, classes, termes", "can be huge": "pot ser gegant", "last updated": "", "lists all sections and subsections": "llista totes les seccions i subseccions", "next chapter": "cap\u00edtol seg\u00fcent", "previous chapter": "cap\u00edtol anterior", "quick access to all modules": "acc\u00e9s r\u00e0pid a tots els m\u00f2duls", "search": "cerca", "search this documentation": "cerca aquesta documentaci\u00f3", "the documentation for": ""}, "plural_expr": "(n != 1)"});

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