mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge branch 'master' into feature-autosummary-packages
This commit is contained in:
commit
4004dd5635
@ -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
|
||||
|
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -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
|
||||
|
@ -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
180
CHANGES
@ -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)
|
||||
=====================================
|
||||
|
3
EXAMPLES
3
EXAMPLES
@ -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/>`__
|
||||
|
@ -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
|
||||
|
@ -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',
|
||||
|
11
doc/development/tutorials/examples/README.rst
Normal file
11
doc/development/tutorials/examples/README.rst
Normal 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.
|
19
doc/development/tutorials/examples/helloworld.py
Normal file
19
doc/development/tutorials/examples/helloworld.py
Normal 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,
|
||||
}
|
161
doc/development/tutorials/examples/recipe.py
Normal file
161
doc/development/tutorials/examples/recipe.py
Normal 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,
|
||||
}
|
124
doc/development/tutorials/examples/todo.py
Normal file
124
doc/development/tutorials/examples/todo.py
Normal 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,
|
||||
}
|
@ -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
|
||||
|
@ -9,3 +9,4 @@ Refer to the following tutorials to get started with extension development.
|
||||
|
||||
helloworld
|
||||
todo
|
||||
recipe
|
||||
|
217
doc/development/tutorials/recipe.rst
Normal file
217
doc/development/tutorials/recipe.rst
Normal 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/
|
@ -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
|
||||
|
@ -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
1032
doc/extdev/deprecated.rst
Normal file
File diff suppressed because it is too large
Load Diff
@ -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/
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
||||
|
32
doc/faq.rst
32
doc/faq.rst
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
-------------
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
2
setup.py
2
setup.py
@ -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',
|
||||
|
@ -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__))
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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:
|
||||
"""
|
||||
|
@ -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
|
||||
|
@ -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__)
|
||||
|
@ -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'),
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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'))
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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',
|
||||
|
@ -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'])
|
||||
|
@ -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)
|
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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]
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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 []
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 = []
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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 = {
|
||||
|
@ -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
|
||||
|
@ -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':
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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')
|
||||
|
@ -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
|
||||
|
@ -45,7 +45,7 @@ else:
|
||||
]
|
||||
|
||||
INITPY = '__init__.py'
|
||||
PY_SUFFIXES = set(['.py', '.pyx'])
|
||||
PY_SUFFIXES = {'.py', '.pyx'}
|
||||
|
||||
|
||||
def makename(package, module):
|
||||
|
@ -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}
|
||||
|
@ -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
|
||||
|
@ -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
169
sphinx/ext/autodoc/mock.py
Normal 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()
|
@ -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
|
||||
|
@ -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])
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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__)
|
||||
|
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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()
|
||||
|
@ -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)
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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:
|
||||
|
140
sphinx/io.py
140
sphinx/io.py
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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'),
|
||||
|
@ -1 +1 @@
|
||||
Documentation.addTranslations({"locale": "ar", "messages": {"%(filename)s — %(docstitle)s": "", "© <a href=\"%(path)s\">Copyright</a> %(copyright)s.": "© <a href=\"%(path)s\"> \u062d\u0642\u0648\u0642 \u0627\u0644\u0646\u0634\u0631 </a> %(copyright)s", "© Copyright %(copyright)s.": "© \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 — %(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 – %(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 — %(docstitle)s": "", "© <a href=\"%(path)s\">Copyright</a> %(copyright)s.": "© <a href=\"%(path)s\"> \u062d\u0642\u0648\u0642 \u0627\u0644\u0646\u0634\u0631 </a> %(copyright)s", "© Copyright %(copyright)s.": "© \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 — %(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 – %(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"});
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -1 +1 @@
|
||||
Documentation.addTranslations({"locale": "ca", "messages": {"%(filename)s — %(docstitle)s": "", "© <a href=\"%(path)s\">Copyright</a> %(copyright)s.": "", "© 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 — %(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 – %(key)s": "\u00cdndes – %(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 — %(docstitle)s": "", "© <a href=\"%(path)s\">Copyright</a> %(copyright)s.": "", "© 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 — %(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 – %(key)s": "\u00cdndes – %(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)"});
|
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user