Merge branch 'master' into 5637

This commit is contained in:
Takeshi KOMIYA 2020-02-12 01:42:05 +09:00
commit c5f0025ed9
689 changed files with 80985 additions and 67915 deletions

View File

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

View File

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

6
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@ -0,0 +1,6 @@
# Ref: https://help.github.com/en/github/building-a-strong-community/configuring-issue-templates-for-your-repository#configuring-the-template-chooser
blank_issues_enabled: false # default: true
contact_links:
- name: Question
url: https://groups.google.com/forum/#!forum/sphinx-users
about: For Q&A purpose, please use sphinx-users mailing list.

View File

@ -1,17 +0,0 @@
---
name: Question
about: For Q&A purpose, please use https://groups.google.com/forum/#!forum/sphinx-users
title: For Q&A purpose, please use sphinx-users group
labels: 'question'
assignees: ''
---
# Important
This is a list of issues for Sphinx, **not a forum**.
If you'd like to post a question, please move to sphinx-users group.
https://groups.google.com/forum/#!forum/sphinx-users
Thanks,

View File

@ -1,9 +1,20 @@
Subject: <short purpose of this pull request>
<!--
Before posting a pull request, please choose a appropriate branch:
- Breaking changes: master
- Critical or severe bugs: X.Y.Z
- Others: X.Y
For more details, see https://www.sphinx-doc.org/en/master/devguide.html#branch-model
-->
### Feature or Bugfix
<!-- please choose -->
- Feature
- Bugfix
- Refactoring
### Purpose
- <long purpose of this pull request>

View File

@ -19,12 +19,18 @@ matrix:
- TOXENV=du13
- python: '3.7'
env:
- TOXENV=py37
- TOXENV=du14
- python: '3.8'
env:
- TOXENV=du15
- PYTEST_ADDOPTS="--cov ./ --cov-append --cov-config setup.cfg"
- python: 'nightly'
env: TOXENV=py38
env:
- TOXENV=du16
- python: '3.6'
env: TOXENV=docs
- python: '3.6'
env: TOXENV=docslint
- python: '3.6'
env: TOXENV=mypy
- python: '3.6'
@ -37,6 +43,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

View File

@ -25,6 +25,7 @@ Other contributors, listed alphabetically, are:
* Henrique Bastos -- SVG support for graphviz extension
* Daniel Bültmann -- todo extension
* Marco Buttu -- doctest extension (pyversion option)
* Nathan Damon -- bugfix in validation of static paths in html builders
* Etienne Desautels -- apidoc module
* Michael Droettboom -- inheritance_diagram extension
* Charles Duffy -- original graphviz extension
@ -35,7 +36,7 @@ Other contributors, listed alphabetically, are:
* Hernan Grecco -- search improvements
* Horst Gutmann -- internationalization support
* Martin Hans -- autodoc improvements
* Zac Hatfield-Dodds -- doctest reporting improvements
* Zac Hatfield-Dodds -- doctest reporting improvements, intersphinx performance
* Doug Hellmann -- graphviz improvements
* Tim Hoffmann -- theme improvements
* Antti Kaihola -- doctest extension (skipif option)
@ -82,6 +83,7 @@ Other contributors, listed alphabetically, are:
* Stephen Finucane -- setup command improvements and documentation
* Daniel Pizetta -- inheritance diagram improvements
* KINEBUCHI Tomohiko -- typing Sphinx as well as docutils
* Adrián Chaves (Gallaecio) -- coverage builder improvements
Many thanks for all contributions!

1172
CHANGES

File diff suppressed because it is too large Load Diff

76
CODE_OF_CONDUCT Normal file
View File

@ -0,0 +1,76 @@
Like the technical community as a whole, the Sphinx team and community is made
up of volunteers from all over the world.
Diversity is a strength, but it can also lead to communication issues and
unhappiness. To that end, we have a few ground rules that we ask people to
adhere to.
* **Be friendly and patient.**
* **Be welcoming.**
We strive to be a community that welcomes and supports people of all
backgrounds and identities. This includes, but is not limited to members of
any race, ethnicity, culture, national origin, colour, immigration status,
social and economic class, educational level, sex, sexual orientation, gender
identity and expression, age, size, family status, political belief, religion,
and mental and physical ability.
* **Be considerate.**
Your work will be used by other people, and you in turn will depend on the
work of others. Any decision you take will affect users and colleagues, and
you should take those consequences into account when making decisions.
Remember that we're a world-wide community, so you might not be communicating
in someone else's primary language.
* **Be respectful.**
Not all of us will agree all the time, but disagreement is no excuse for poor
behavior and poor manners. We might all experience some frustration now and
then, but we cannot allow that frustration to turn into a personal attack.
Its important to remember that a community where people feel uncomfortable or
threatened is not a productive one. Members of the Sphinx community should be
respectful when dealing with other members as well as with people outside the
Sphinx community.
* **Be careful in the words that you choose.**
We are a community of professionals, and we conduct ourselves professionally.
Be kind to others. Do not insult or put down other participants. Harassment
and other exclusionary behavior aren't acceptable. This includes, but is not
limited to:
* Violent threats or language directed against another person.
* Discriminatory jokes and language.
* Posting sexually explicit or violent material.
* Posting (or threatening to post) other people's personally identifying
information ("doxing").
* Personal insults, especially those using racist or sexist terms.
* Unwelcome sexual attention.
* Advocating for, or encouraging, any of the above behavior.
* Repeated harassment of others. In general, if someone asks you to stop, then
stop.
* **When we disagree, try to understand why.**
Disagreements, both social and technical, happen all the time and Sphinx is no
exception. It is important that we resolve disagreements and differing views
constructively. Remember that were different. Different people have different
perspectives on issues. Being unable to understand why someone holds a
viewpoint doesnt mean that theyre wrong. Dont forget that it is human to
err and blaming each other doesnt get us anywhere. Instead, focus on helping
to resolve issues and learning from mistakes.
This isnt an exhaustive list of things that you cant do.
Rather, take it in the spirit in which its intended - a guide to make it easier
to enrich all of us and the technical communities in which we participate.
This code of conduct applies to all spaces of the Sphinx community.
Attribution
-----------
Original text courtesy of the Speak Up! project:
http://web.archive.org/web/20141109123859/http://speakup.io/coc.html.

View File

@ -250,7 +250,7 @@ the source and the compiled catalogs.
When a new locale is submitted, add a new directory with the ISO 639-1 language
identifier and put ``sphinx.po`` in there. Don't forget to update the possible
values for :confval:`language` in ``doc/config.rst``.
values for :confval:`language` in ``doc/usage/configuration.rst``.
The Sphinx core messages can also be translated on `Transifex
<https://www.transifex.com/>`_. There exists a client tool named ``tx`` in the
@ -303,8 +303,8 @@ Debugging Tips
* Set the debugging options in the `Docutils configuration file
<http://docutils.sourceforge.net/docs/user/config.html>`_.
* JavaScript stemming algorithms in ``sphinx/search/*.py`` (except ``en.py``) are
generated by this
* JavaScript stemming algorithms in ``sphinx/search/*.py`` (except ``en.py``)
are generated by this
`modified snowballcode generator <https://github.com/shibukawa/snowball>`_.
Generated `JSX <https://jsx.github.io/>`_ files are
in `this repository <https://github.com/shibukawa/snowball-stemmer.jsx>`_.

View File

@ -52,7 +52,7 @@ Documentation using the classic theme
* `Arb <http://arblib.org/>`__
* `Bazaar <http://doc.bazaar.canonical.com/>`__ (customized)
* `Beautiful Soup <https://www.crummy.com/software/BeautifulSoup/bs4/doc/>`__
* `Blender <https://docs.blender.org/api/current/>`__
* `Blender API <https://docs.blender.org/api/current/>`__
* `Bugzilla <https://bugzilla.readthedocs.io/>`__
* `Buildbot <https://docs.buildbot.net/latest/>`__
* `CMake <https://cmake.org/documentation/>`__ (customized)
@ -114,6 +114,7 @@ Documentation using the sphinxdoc theme
* `ABRT <https://abrt.readthedocs.io/>`__
* `cartopy <https://scitools.org.uk/cartopy/docs/latest/>`__
* `Jython <http://www.jython.org/docs/>`__
* `LLVM <https://llvm.org/docs/>`__
* `Matplotlib <https://matplotlib.org/>`__
* `MDAnalysis Tutorial <https://www.mdanalysis.org/MDAnalysisTutorial/>`__
* `NetworkX <https://networkx.github.io/>`__
@ -123,6 +124,7 @@ Documentation using the sphinxdoc theme
* `Pysparse <http://pysparse.sourceforge.net/>`__
* `PyTango <https://www.esrf.eu/computing/cs/tango/tango_doc/kernel_doc/pytango/latest/>`__
* `Python Wild Magic <https://vmlaker.github.io/pythonwildmagic/>`__ (customized)
* `RDKit <https://www.rdkit.org/docs/>`__
* `Reteisi <http://www.reteisi.org/contents.html>`__ (customized)
* `Sqlkit <http://sqlkit.argolinux.org/>`__ (customized)
* `Turbulenz <http://docs.turbulenz.com/>`__
@ -167,6 +169,7 @@ Documentation using sphinx_rtd_theme
* `ASE <https://wiki.fysik.dtu.dk/ase/>`__
* `Autofac <http://docs.autofac.org/>`__
* `BigchainDB <https://docs.bigchaindb.com/>`__
* `Blender Reference Manual <https://docs.blender.org/manual/>`__
* `Blocks <https://blocks.readthedocs.io/>`__
* `bootstrap-datepicker <https://bootstrap-datepicker.readthedocs.io/>`__
* `Certbot <https://letsencrypt.readthedocs.io/>`__
@ -180,17 +183,20 @@ Documentation using sphinx_rtd_theme
* `Databricks <https://docs.databricks.com/>`__ (customized)
* `Dataiku DSS <https://doc.dataiku.com/>`__
* `DNF <https://dnf.readthedocs.io/>`__
* `Django-cas-ng <https://djangocas.dev/docs/>`__
* `edX <https://docs.edx.org/>`__
* `Electrum <http://docs.electrum.org/>`__
* `Elemental <http://libelemental.org/documentation/dev/>`__
* `ESWP3 <https://eswp3.readthedocs.io/>`__
* `Ethereum Homestead <http://www.ethdocs.org/>`__
* `Exhale <https://exhale.readthedocs.io/>`__
* `Faker <https://faker.readthedocs.io/>`__
* `Fidimag <https://fidimag.readthedocs.io/>`__
* `Flake8 <http://flake8.pycqa.org/>`__
* `Flatpak <http://docs.flatpak.org/>`__
* `FluidDyn <https://fluiddyn.readthedocs.io/>`__
* `Fluidsim <https://fluidsim.readthedocs.io/>`__
* `Gallium <https://gallium.readthedocs.io/>`__
* `GeoNode <http://docs.geonode.org/>`__
* `Glances <https://glances.readthedocs.io/>`__
* `Godot <https://godot.readthedocs.io/>`__
@ -206,11 +212,13 @@ 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>`__
* `Mailman <http://docs.list.org/>`__
* `MathJax <https://docs.mathjax.org/>`__
* `MDTraj <http://mdtraj.org/latest/>`__ (customized)
* `MICrobial Community Analysis (micca) <http://micca.org/docs/latest/>`__
* `micca - MICrobial Community Analysis <https://micca.readthedocs.io/>`__
* `MicroPython <https://docs.micropython.org/>`__
* `Minds <https://www.minds.org/docs/>`__ (customized)
* `Mink <http://mink.behat.org/>`__
@ -252,6 +260,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/>`__
@ -285,6 +294,7 @@ Documentation using sphinx_bootstrap_theme
* `Hedge <https://documen.tician.de/hedge/>`__
* `ObsPy <https://docs.obspy.org/>`__
* `Open Dylan <https://opendylan.org/documentation/>`__
* `OPNFV <https://docs.opnfv.org/>`__
* `Pootle <http://docs.translatehouse.org/projects/pootle/>`__
* `PyUblas <https://documen.tician.de/pyublas/>`__
* `seaborn <https://seaborn.pydata.org/>`__
@ -321,6 +331,7 @@ Documentation using a custom theme or integrated in a website
* `MongoDB <https://docs.mongodb.com/>`__
* `Music21 <https://web.mit.edu/music21/doc/>`__
* `MyHDL <http://docs.myhdl.org/>`__
* `ndnSIM <https://ndnsim.net/current/>`__
* `nose <https://nose.readthedocs.io/>`__
* `ns-3 <https://www.nsnam.org/documentation/>`__
* `NumPy <https://docs.scipy.org/doc/numpy/reference/>`__
@ -394,6 +405,7 @@ Books produced using Sphinx
* `"Python Professional Programming" (in Japanese) <http://www.amazon.co.jp/dp/4798032948/>`__
* `"Python Professional Programming 2nd Edition" (in Japanese) <https://www.amazon.co.jp/dp/479804315X/>`__
* `"Python Professional Programming 3rd Edition" (in Japanese) <https://www.amazon.co.jp/dp/4798053821/>`__
* `Python Course by Yuri Petrov (Russian) <https://www.yuripetrov.ru/edu/python>`__
* `"Real World HTTP -- Learning The Internet and Web Technology via its history and code (Japanese)" <https://www.oreilly.co.jp/books/9784873118048/>`__
* `"Redmine Primer 5th Edition (in Japanese)" <https://www.shuwasystem.co.jp/products/7980html/4825.html>`__
* `"The repoze.bfg Web Application Framework" <https://www.amazon.com/repoze-bfg-Web-Application-Framework-Version/dp/0615345379>`__

View File

@ -59,6 +59,10 @@ style-check:
type-check:
mypy sphinx
.PHONY: doclinter
doclinter:
python utils/doclinter.py CHANGES *.rst doc/
.PHONY: pylint
pylint:
@pylint --rcfile utils/pylintrc sphinx

View File

@ -26,6 +26,10 @@
:target: https://codecov.io/gh/sphinx-doc/sphinx
:alt: Code Coverage Status (Codecov)
.. image:: https://img.shields.io/badge/License-BSD%203--Clause-blue.svg
:target: https://opensource.org/licenses/BSD-3-Clause
:alt: BSD 3 Clause
Sphinx is a tool that makes it easy to create intelligent and beautiful
documentation for Python projects (or other documents consisting of multiple
reStructuredText sources), written by Georg Brandl. It was originally created
@ -90,6 +94,10 @@ Get in touch
.. _on GitHub: https://github.com/sphinx-doc/sphinx
.. _mailing list: https://groups.google.com/forum/#!forum/sphinx-users
Please adhere to our `code of conduct`__.
__ http://www.sphinx-doc.org/en/master/code_of_conduct.html
Testing
=======

View File

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

4
doc/_static/Makefile vendored Normal file
View File

@ -0,0 +1,4 @@
translation.png: translation.puml
plantuml $<
clean:
rm translation.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 20 KiB

16
doc/_static/translation.puml vendored Normal file
View File

@ -0,0 +1,16 @@
@startuml
file "SphinxProject"
file ".rst"
database ".pot"
database ".po"
database ".mo"
actor translator
file TranslatedBuild
translator -l-> .po
SphinxProject -r-> .rst
.rst -r-> .pot : sphinx-build gettext
.pot -r-> .po : Pootle
.po -d-> .mo : msgfmt
.mo -l-> TranslatedBuild
.rst -d-> TranslatedBuild : "sphinx-buid -Dlanguage="
@enduml

View File

@ -97,6 +97,8 @@
<p>{%trans%}A Japanese book about Sphinx has been published by O'Reilly:
<a href="https://www.oreilly.co.jp/books/9784873116488/">Sphinxをはじめよう /
Learning Sphinx</a>.{%endtrans%}</p>
<p>{%trans%}In 2019 the second edition of a German book about Sphinx was published:
<a href="https://literatur.hasecke.com/post/software-dokumentation-mit-sphinx/">Software-Dokumentation mit Sphinx</a>.{%endtrans%}</p>
<!-- <p><img src="{{ pathto("_static/bookcover.png", 1) }}"/></p> -->
@ -118,4 +120,8 @@
<li>{%trans path=pathto("authors")%}<a href="{{ path }}">Sphinx Authors</a></li>{%endtrans%}
</ul>
<h2>{%trans%}Code of Conduct{%endtrans%}</h2>
{%trans path=pathto("code_of_conduct")%}Please adhere to our <a href="{{ path }}">Code of Conduct</a>.{%endtrans%}
{% endblock %}

View File

@ -30,7 +30,7 @@
.related { display: none; }
{% endif %}
</style>
<script type="text/javascript">
<script>
// intelligent scrolling of the sidebar content
$(window).scroll(function() {
var sb = $('.sphinxsidebarwrapper');

View File

@ -299,6 +299,11 @@ a.headerlink:hover {
color: white!important;
}
/* avoid font-size when :mod: role in headings */
h1 code, h2 code, h3 code, h4 code {
font-size: inherit;
}
cite, code, tt {
font-family: 'Consolas', 'DejaVu Sans Mono',
'Bitstream Vera Sans Mono', monospace;

8
doc/code_of_conduct.rst Normal file
View File

@ -0,0 +1,8 @@
:tocdepth: 2
.. _code_of_conduct:
Sphinx Code of Conduct
======================
.. include:: ../CODE_OF_CONDUCT

View File

@ -7,7 +7,7 @@ import sphinx
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.todo',
'sphinx.ext.autosummary', 'sphinx.ext.extlinks',
'sphinx.ext.viewcode']
'sphinx.ext.viewcode', 'sphinx.ext.inheritance_diagram']
master_doc = 'contents'
templates_path = ['_templates']
@ -48,7 +48,7 @@ epub_fix_images = False
epub_max_image_width = 0
epub_show_urls = 'inline'
epub_use_index = False
epub_guide = (('toc', 'contents.xhtml', u'Table of Contents'),)
epub_guide = (('toc', 'contents.xhtml', 'Table of Contents'),)
epub_description = 'Sphinx documentation generator system manual'
latex_documents = [('contents', 'sphinx.tex', 'Sphinx Documentation',
@ -146,6 +146,9 @@ def setup(app):
app.add_object_type('confval', 'confval',
objname='configuration value',
indextemplate='pair: %s; configuration value')
app.add_object_type('setuptools-confval', 'setuptools-confval',
objname='setuptools configuration value',
indextemplate='pair: %s; setuptools configuration value')
fdesc = GroupedField('parameter', label='Parameters',
names=['param'], can_collapse=True)
app.add_object_type('event', 'event', 'pair: %s; event', parse_event,

View File

@ -34,6 +34,7 @@ Sphinx documentation contents
changes
examples
authors
code_of_conduct

View File

@ -31,10 +31,12 @@ This is the current list of contributed extensions in that repository:
- actdiag: embed activity diagrams by using actdiag_
- adadomain: an extension for Ada support (Sphinx 1.0 needed)
- ansi: parse ANSI color sequences inside documents
- argdoc: automatically generate documentation for command-line arguments, descriptions, and help text
- argdoc: automatically generate documentation for command-line arguments,
descriptions and help text
- astah: embed diagram by using astah
- autoanysrc: Gather reST documentation from any source files
- autorun: Execute code in a ``runblock`` directive
- beamer_: A builder for Beamer (LaTeX) output.
- blockdiag: embed block diagrams by using blockdiag_
- cacoo: embed diagram from Cacoo
- cf3domain: a domain for CFEngine 3 policies
@ -45,7 +47,8 @@ This is the current list of contributed extensions in that repository:
- coffeedomain: a domain for (auto)documenting CoffeeScript source code
- context: a builder for ConTeXt
- disqus: embed Disqus comments in documents
- documentedlist: converts a Python list to a table in the generated documentation
- documentedlist: converts a Python list to a table in the generated
documentation
- doxylink: Link to external Doxygen-generated HTML documentation
- domaintools_: A tool for easy domain creation
- email: obfuscate email addresses
@ -64,7 +67,8 @@ This is the current list of contributed extensions in that repository:
- imgur: embed Imgur images, albums, and metadata in documents
- inlinesyntaxhighlight_: inline syntax highlighting
- lassodomain: a domain for documenting Lasso_ source code
- libreoffice: an extension to include any drawing supported by LibreOffice (e.g. odg, vsd, ...)
- libreoffice: an extension to include any drawing supported by LibreOffice
(e.g. odg, vsd, ...)
- lilypond: an extension inserting music scripts from Lilypond_ in PNG format
- makedomain_: a domain for `GNU Make`_
- matlabdomain: document MATLAB_ code
@ -74,7 +78,8 @@ This is the current list of contributed extensions in that repository:
- nicovideo: embed videos from nicovideo
- nwdiag: embed network diagrams by using nwdiag_
- omegat: support tools to collaborate with OmegaT_ (Sphinx 1.1 needed)
- osaka: convert standard Japanese doc to Osaka dialect (this is a joke extension)
- osaka: convert standard Japanese doc to Osaka dialect (this is a joke
extension)
- paverutils: an alternate integration of Sphinx with Paver_
- phpdomain: an extension for PHP support
- plantuml: embed UML diagram by using PlantUML_
@ -94,14 +99,15 @@ This is the current list of contributed extensions in that repository:
- sword: an extension inserting Bible verses from Sword_
- tikz: draw pictures with the `TikZ/PGF LaTeX package`_
- traclinks: create TracLinks_ to a Trac_ instance from within Sphinx
- versioning: Sphinx extension that allows building versioned docs for self-hosting
- versioning: Sphinx extension that allows building versioned docs for
self-hosting
- whooshindex: whoosh indexer extension
- youtube: embed videos from YouTube_
- zopeext: provide an ``autointerface`` directive for using `Zope interfaces`_
See the :doc:`extension tutorials <../development/tutorials/index>` on getting started with writing your
own extensions.
See the :doc:`extension tutorials <../development/tutorials/index>` on getting
started with writing your own extensions.
.. _aafigure: https://launchpad.net/aafigure
@ -143,3 +149,4 @@ own extensions.
.. _domaintools: https://bitbucket.org/klorenz/sphinxcontrib-domaintools
.. _restbuilder: https://pypi.org/project/sphinxcontrib-restbuilder/
.. _Lasso: http://www.lassosoft.com/
.. _beamer: https://pypi.org/project/sphinxcontrib-beamer/

View File

@ -77,7 +77,7 @@ create directives should extend this class.
.. seealso::
`The docutils documentation on creating directives <docutils directives>`_
`The docutils documentation on creating directives <docutils directives_>`_
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
@ -87,7 +87,7 @@ nodes available: text, paragraph, reference, table, etc.
.. seealso::
`The docutils documentation on nodes <docutils nodes>`_
`The docutils documentation on nodes <docutils nodes_>`_
The ``nodes.paragraph`` class creates a new paragraph node. A paragraph
node typically contains some text that we can set during instantiation using

View File

@ -23,8 +23,8 @@ We want the extension to add the following to Sphinx:
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
documentation.
* 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:
@ -107,6 +107,20 @@ is just a "general" node.
<http://docutils.sourceforge.net/docs/ref/doctree.html>`__ and :ref:`Sphinx
<nodes>`.
.. attention::
It is important to know that while you can extend Sphinx without
leaving your ``conf.py``, if you declare an inherited node right
there, you'll hit an unobvious :py:class:`PickleError`. So if
something goes wrong, please make sure that you put inherited nodes
into a separate Python module.
For more details, see:
- https://github.com/sphinx-doc/sphinx/issues/6751
- https://github.com/sphinx-doc/sphinx/issues/1493
- https://github.com/sphinx-doc/sphinx/issues/1424
.. rubric:: The directive classes
A directive class is a class deriving usually from

View File

@ -54,8 +54,6 @@ package.
.. automethod:: Sphinx.add_domain(domain)
.. automethod:: Sphinx.override_domain(domain)
.. method:: Sphinx.add_directive_to_domain(domain, name, func, content, arguments, \*\*options)
.. automethod:: Sphinx.add_directive_to_domain(domain, name, directiveclass)
@ -147,9 +145,9 @@ Sphinx core events
------------------
These events are known to the core. The arguments shown are given to the
registered event handlers. Use :meth:`.connect` in an extension's ``setup``
function (note that ``conf.py`` can also have a ``setup`` function) to connect
handlers to the events. Example:
registered event handlers. Use :meth:`.Sphinx.connect` in an extension's
``setup`` function (note that ``conf.py`` can also have a ``setup`` function) to
connect handlers to the events. Example:
.. code-block:: python
@ -218,6 +216,14 @@ handlers to the events. Example:
.. versionadded:: 0.5
.. event:: object-description-transform (app, domain, objtype, contentnode)
Emitted when an object description directive has run. The *domain* and
*objtype* arguments are strings indicating object description of the object.
And *contentnode* is a content for the object. It can be modified in-place.
.. versionadded:: 2.4
.. event:: doctree-read (app, doctree)
Emitted when a doctree has been parsed and read by the environment, and is

View File

@ -38,3 +38,8 @@ Builder API
.. automethod:: write_doc
.. automethod:: finish
**Attributes**
.. attribute:: events
An :class:`.EventManager` object.

1336
doc/extdev/deprecated.rst Normal file

File diff suppressed because it is too large Load Diff

View File

@ -27,6 +27,10 @@ Build environment API
Directory for storing pickled doctrees.
.. attribute:: events
An :class:`.EventManager` object.
.. attribute:: found_docs
A set of all existing docnames.

View File

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

View File

@ -97,7 +97,8 @@ extension. These are:
The config is available as ``app.config`` or ``env.config``.
To see an example of use of these objects, refer to :doc:`../development/tutorials/index`.
To see an example of use of these objects, refer to
:doc:`../development/tutorials/index`.
.. _build-phases:
@ -143,9 +144,9 @@ in which a Sphinx project is built: this works in several phases.
Now that the metadata and cross-reference data of all existing documents is
known, all temporary nodes are replaced by nodes that can be converted into
output using components called transforms. For example, links are created for
object references that exist, and simple literal nodes are created for those
that don't.
output using components called transforms. For example, links are created
for object references that exist, and simple literal nodes are created for
those that don't.
**Phase 4: Writing**
@ -187,6 +188,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,931 +207,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
* - ``sphinx.ext.autodoc.importer.MockFinder``
- 2.1
- 4.0
- ``sphinx.ext.autodoc.mock.MockFinder``
* - ``sphinx.ext.autodoc.importer.MockLoader``
- 2.1
- 4.0
- ``sphinx.ext.autodoc.mock.MockLoader``
* - ``sphinx.ext.autodoc.importer.mock()``
- 2.1
- 4.0
- ``sphinx.ext.autodoc.mock.mock()``
* - ``sphinx.ext.autosummary.autolink_role()``
- 2.1
- 4.0
- ``sphinx.ext.autosummary.AutoLink``
* - ``sphinx.util.i18n.find_catalog()``
- 2.1
- 4.0
- ``sphinx.util.i18n.docname_to_domain()``
* - ``sphinx.util.i18n.find_catalog_files()``
- 2.1
- 4.0
- ``sphinx.util.i18n.CatalogRepository``
* - ``sphinx.util.i18n.find_catalog_source_files()``
- 2.1
- 4.0
- ``sphinx.util.i18n.CatalogRepository``
* - ``encoding`` argument of ``autodoc.Documenter.get_doc()``,
``autodoc.DocstringSignatureMixin.get_doc()``,
``autodoc.DocstringSignatureMixin._find_signature()``, and
``autodoc.ClassDocumenter.get_doc()``
- 2.0
- 4.0
- N/A
* - arguments of ``EpubBuilder.build_mimetype()``,
``EpubBuilder.build_container()``, ``EpubBuilder.build_content()``,
``EpubBuilder.build_toc()`` and ``EpubBuilder.build_epub()``
- 2.0
- 4.0
- N/A
* - arguments of ``Epub3Builder.build_navigation_doc()``
- 2.0
- 4.0
- N/A
* - ``nodetype`` argument of
``sphinx.search.WordCollector.is_meta_keywords()``
- 2.0
- 4.0
- N/A
* - ``suffix`` argument of ``BuildEnvironment.doc2path()``
- 2.0
- 4.0
- N/A
* - string style ``base`` argument of ``BuildEnvironment.doc2path()``
- 2.0
- 4.0
- ``os.path.join()``
* - ``sphinx.addnodes.abbreviation``
- 2.0
- 4.0
- ``docutils.nodes.abbreviation``
* - ``sphinx.builders.applehelp``
- 2.0
- 4.0
- ``sphinxcontrib.applehelp``
* - ``sphinx.builders.devhelp``
- 2.0
- 4.0
- ``sphinxcontrib.devhelp``
* - ``sphinx.builders.epub3.Epub3Builder.validate_config_value()``
- 2.0
- 4.0
- ``sphinx.builders.epub3.validate_config_values()``
* - ``sphinx.builders.html.JSONHTMLBuilder``
- 2.0
- 4.0
- ``sphinx.builders.serializinghtml.JSONHTMLBuilder``
* - ``sphinx.builders.html.PickleHTMLBuilder``
- 2.0
- 4.0
- ``sphinx.builders.serializinghtml.PickleHTMLBuilder``
* - ``sphinx.builders.html.SerializingHTMLBuilder``
- 2.0
- 4.0
- ``sphinx.builders.serializinghtml.SerializingHTMLBuilder``
* - ``sphinx.builders.html.SingleFileHTMLBuilder``
- 2.0
- 4.0
- ``sphinx.builders.singlehtml.SingleFileHTMLBuilder``
* - ``sphinx.builders.html.WebHTMLBuilder``
- 2.0
- 4.0
- ``sphinx.builders.serializinghtml.PickleHTMLBuilder``
* - ``sphinx.builders.htmlhelp``
- 2.0
- 4.0
- ``sphinxcontrib.htmlhelp``
* - ``sphinx.builders.htmlhelp.HTMLHelpBuilder.open_file()``
- 2.0
- 4.0
- ``open()``
* - ``sphinx.builders.qthelp``
- 2.0
- 4.0
- ``sphinxcontrib.qthelp``
* - ``sphinx.cmd.quickstart.term_decode()``
- 2.0
- 4.0
- N/A
* - ``sphinx.cmd.quickstart.TERM_ENCODING``
- 2.0
- 4.0
- ``sys.stdin.encoding``
* - ``sphinx.config.check_unicode()``
- 2.0
- 4.0
- N/A
* - ``sphinx.config.string_classes``
- 2.0
- 4.0
- ``[str]``
* - ``sphinx.domains.cpp.DefinitionError.description``
- 2.0
- 4.0
- ``str(exc)``
* - ``sphinx.domains.cpp.NoOldIdError.description``
- 2.0
- 4.0
- ``str(exc)``
* - ``sphinx.domains.cpp.UnsupportedMultiCharacterCharLiteral.decoded``
- 2.0
- 4.0
- ``str(exc)``
* - ``sphinx.ext.autosummary.Autosummary.warn()``
- 2.0
- 4.0
- N/A
* - ``sphinx.ext.autosummary.Autosummary.genopt``
- 2.0
- 4.0
- N/A
* - ``sphinx.ext.autosummary.Autosummary.warnings``
- 2.0
- 4.0
- N/A
* - ``sphinx.ext.autosummary.Autosummary.result``
- 2.0
- 4.0
- N/A
* - ``sphinx.ext.doctest.doctest_encode()``
- 2.0
- 4.0
- N/A
* - ``sphinx.ext.jsmath``
- 2.0
- 4.0
- ``sphinxcontrib.jsmath``
* - ``sphinx.roles.abbr_role()``
- 2.0
- 4.0
- ``sphinx.roles.Abbreviation``
* - ``sphinx.roles.emph_literal_role()``
- 2.0
- 4.0
- ``sphinx.roles.EmphasizedLiteral``
* - ``sphinx.roles.menusel_role()``
- 2.0
- 4.0
- ``sphinx.roles.GUILabel`` or ``sphinx.roles.MenuSelection``
* - ``sphinx.roles.index_role()``
- 2.0
- 4.0
- ``sphinx.roles.Index``
* - ``sphinx.roles.indexmarkup_role()``
- 2.0
- 4.0
- ``sphinx.roles.PEP`` or ``sphinx.roles.RFC``
* - ``sphinx.testing.util.remove_unicode_literal()``
- 2.0
- 4.0
- N/A
* - ``sphinx.util.attrdict``
- 2.0
- 4.0
- N/A
* - ``sphinx.util.force_decode()``
- 2.0
- 4.0
- N/A
* - ``sphinx.util.get_matching_docs()``
- 2.0
- 4.0
- ``sphinx.util.get_matching_files()``
* - ``sphinx.util.inspect.Parameter``
- 2.0
- 3.0
- N/A
* - ``sphinx.util.jsonimpl``
- 2.0
- 4.0
- ``sphinxcontrib.serializinghtml.jsonimpl``
* - ``sphinx.util.osutil.EEXIST``
- 2.0
- 4.0
- ``errno.EEXIST`` or ``FileExistsError``
* - ``sphinx.util.osutil.EINVAL``
- 2.0
- 4.0
- ``errno.EINVAL``
* - ``sphinx.util.osutil.ENOENT``
- 2.0
- 4.0
- ``errno.ENOENT`` or ``FileNotFoundError``
* - ``sphinx.util.osutil.EPIPE``
- 2.0
- 4.0
- ``errno.ENOENT`` or ``BrokenPipeError``
* - ``sphinx.util.osutil.walk()``
- 2.0
- 4.0
- ``os.walk()``
* - ``sphinx.util.pycompat.NoneType``
- 2.0
- 4.0
- ``sphinx.util.typing.NoneType``
* - ``sphinx.util.pycompat.TextIOWrapper``
- 2.0
- 4.0
- ``io.TextIOWrapper``
* - ``sphinx.util.pycompat.UnicodeMixin``
- 2.0
- 4.0
- N/A
* - ``sphinx.util.pycompat.htmlescape()``
- 2.0
- 4.0
- ``html.escape()``
* - ``sphinx.util.pycompat.indent()``
- 2.0
- 4.0
- ``textwrap.indent()``
* - ``sphinx.util.pycompat.sys_encoding``
- 2.0
- 4.0
- ``sys.getdefaultencoding()``
* - ``sphinx.util.pycompat.terminal_safe()``
- 2.0
- 4.0
- ``sphinx.util.console.terminal_safe()``
* - ``sphinx.util.pycompat.u``
- 2.0
- 4.0
- N/A
* - ``sphinx.util.PeekableIterator``
- 2.0
- 4.0
- N/A
* - Omitting the ``filename`` argument in an overriddent
``IndexBuilder.feed()`` method.
- 2.0
- 4.0
- ``IndexBuilder.feed(docname, filename, title, doctree)``
* - ``sphinx.writers.latex.ExtBabel``
- 2.0
- 4.0
- ``sphinx.builders.latex.util.ExtBabel``
* - ``sphinx.writers.latex.LaTeXTranslator.babel_defmacro()``
- 2.0
- 4.0
- N/A
* - ``sphinx.application.Sphinx._setting_up_extension``
- 2.0
- 3.0
- N/A
* - The ``importer`` argument of ``sphinx.ext.autodoc.importer._MockModule``
- 2.0
- 3.0
- N/A
* - ``sphinx.ext.autodoc.importer._MockImporter``
- 2.0
- 3.0
- N/A
* - ``sphinx.io.SphinxBaseFileInput``
- 2.0
- 3.0
- N/A
* - ``sphinx.io.SphinxFileInput.supported``
- 2.0
- 3.0
- N/A
* - ``sphinx.io.SphinxRSTFileInput``
- 2.0
- 3.0
- N/A
* - ``sphinx.registry.SphinxComponentRegistry.add_source_input()``
- 2.0
- 3.0
- N/A
* - ``sphinx.writers.latex.LaTeXTranslator._make_visit_admonition()``
- 2.0
- 3.0
- N/A
* - ``sphinx.writers.latex.LaTeXTranslator.collect_footnotes()``
- 2.0
- 4.0
- N/A
* - ``sphinx.writers.texinfo.TexinfoTranslator._make_visit_admonition()``
- 2.0
- 3.0
- N/A
* - ``sphinx.writers.text.TextTranslator._make_depart_admonition()``
- 2.0
- 3.0
- N/A
* - ``sphinx.writers.latex.LaTeXTranslator.generate_numfig_format()``
- 2.0
- 4.0
- N/A
* - :rst:dir:`highlightlang`
- 1.8
- 4.0
- :rst:dir:`highlight`
* - :meth:`~sphinx.application.Sphinx.add_stylesheet()`
- 1.8
- 4.0
- :meth:`~sphinx.application.Sphinx.add_css_file()`
* - :meth:`~sphinx.application.Sphinx.add_javascript()`
- 1.8
- 4.0
- :meth:`~sphinx.application.Sphinx.add_js_file()`
* - :confval:`autodoc_default_flags`
- 1.8
- 4.0
- :confval:`autodoc_default_options`
* - ``content`` arguments of ``sphinx.util.image.guess_mimetype()``
- 1.8
- 3.0
- N/A
* - ``gettext_compact`` arguments of
``sphinx.util.i18n.find_catalog_source_files()``
- 1.8
- 3.0
- N/A
* - ``sphinx.io.SphinxI18nReader.set_lineno_for_reporter()``
- 1.8
- 3.0
- N/A
* - ``sphinx.io.SphinxI18nReader.line``
- 1.8
- 3.0
- N/A
* - ``sphinx.directives.other.VersionChanges``
- 1.8
- 3.0
- ``sphinx.domains.changeset.VersionChanges``
* - ``sphinx.highlighting.PygmentsBridge.unhighlight()``
- 1.8
- 3.0
- N/A
* - ``trim_doctest_flags`` arguments of
``sphinx.highlighting.PygmentsBridge``
- 1.8
- 3.0
- N/A
* - ``sphinx.ext.mathbase``
- 1.8
- 3.0
- N/A
* - ``sphinx.ext.mathbase.MathDomain``
- 1.8
- 3.0
- ``sphinx.domains.math.MathDomain``
* - ``sphinx.ext.mathbase.MathDirective``
- 1.8
- 3.0
- ``sphinx.directives.patches.MathDirective``
* - ``sphinx.ext.mathbase.math_role()``
- 1.8
- 3.0
- ``docutils.parsers.rst.roles.math_role()``
* - ``sphinx.ext.mathbase.setup_math()``
- 1.8
- 3.0
- :meth:`~sphinx.application.Sphinx.add_html_math_renderer()`
* - ``sphinx.ext.mathbase.is_in_section_title()``
- 1.8
- 3.0
- N/A
* - ``sphinx.ext.mathbase.get_node_equation_number()``
- 1.8
- 3.0
- ``sphinx.util.math.get_node_equation_number()``
* - ``sphinx.ext.mathbase.wrap_displaymath()``
- 1.8
- 3.0
- ``sphinx.util.math.wrap_displaymath()``
* - ``sphinx.ext.mathbase.math`` (node)
- 1.8
- 3.0
- ``docutils.nodes.math``
* - ``sphinx.ext.mathbase.displaymath`` (node)
- 1.8
- 3.0
- ``docutils.nodes.math_block``
* - ``sphinx.ext.mathbase.eqref`` (node)
- 1.8
- 3.0
- ``sphinx.builders.latex.nodes.math_reference``
* - ``viewcode_import`` (config value)
- 1.8
- 3.0
- :confval:`viewcode_follow_imported_members`
* - ``sphinx.writers.latex.Table.caption_footnotetexts``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.latex.Table.header_footnotetexts``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.latex.LaTeXTranslator.footnotestack``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.latex.LaTeXTranslator.in_container_literal_block``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.latex.LaTeXTranslator.next_section_ids``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.latex.LaTeXTranslator.next_hyperlink_ids``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.latex.LaTeXTranslator.restrict_footnote()``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.latex.LaTeXTranslator.unrestrict_footnote()``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.latex.LaTeXTranslator.push_hyperlink_ids()``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.latex.LaTeXTranslator.pop_hyperlink_ids()``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.latex.LaTeXTranslator.bibitems``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.latex.LaTeXTranslator.hlsettingstack``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.latex.ExtBabel.get_shorthandoff()``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.html.HTMLTranslator.highlightlang()``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.html.HTMLTranslator.highlightlang_base()``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.html.HTMLTranslator.highlightlangopts()``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.html.HTMLTranslator.highlightlinenothreshold()``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.html5.HTMLTranslator.highlightlang()``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.html5.HTMLTranslator.highlightlang_base()``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.html5.HTMLTranslator.highlightlangopts()``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.html5.HTMLTranslator.highlightlinenothreshold()``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.latex.LaTeXTranslator.check_latex_elements()``
- 1.8
- 3.0
- Nothing
* - ``sphinx.application.CONFIG_FILENAME``
- 1.8
- 3.0
- ``sphinx.config.CONFIG_FILENAME``
* - ``Config.check_unicode()``
- 1.8
- 3.0
- ``sphinx.config.check_unicode()``
* - ``Config.check_types()``
- 1.8
- 3.0
- ``sphinx.config.check_confval_types()``
* - ``dirname``, ``filename`` and ``tags`` arguments of
``Config.__init__()``
- 1.8
- 3.0
- ``Config.read()``
* - The value of :confval:`html_search_options`
- 1.8
- 3.0
- see :confval:`html_search_options`
* - ``sphinx.versioning.prepare()``
- 1.8
- 3.0
- ``sphinx.versioning.UIDTransform``
* - ``Sphinx.override_domain()``
- 1.8
- 3.0
- :meth:`~sphinx.application.Sphinx.add_domain()`
* - ``Sphinx.import_object()``
- 1.8
- 3.0
- ``sphinx.util.import_object()``
* - ``suffix`` argument of
:meth:`~sphinx.application.Sphinx.add_source_parser()`
- 1.8
- 3.0
- :meth:`~sphinx.application.Sphinx.add_source_suffix()`
* - ``BuildEnvironment.load()``
- 1.8
- 3.0
- ``pickle.load()``
* - ``BuildEnvironment.loads()``
- 1.8
- 3.0
- ``pickle.loads()``
* - ``BuildEnvironment.frompickle()``
- 1.8
- 3.0
- ``pickle.load()``
* - ``BuildEnvironment.dump()``
- 1.8
- 3.0
- ``pickle.dump()``
* - ``BuildEnvironment.dumps()``
- 1.8
- 3.0
- ``pickle.dumps()``
* - ``BuildEnvironment.topickle()``
- 1.8
- 3.0
- ``pickle.dump()``
* - ``BuildEnvironment._nitpick_ignore``
- 1.8
- 3.0
- :confval:`nitpick_ignore`
* - ``BuildEnvironment.versionchanges``
- 1.8
- 3.0
- N/A
* - ``BuildEnvironment.update()``
- 1.8
- 3.0
- ``Builder.read()``
* - ``BuildEnvironment.read_doc()``
- 1.8
- 3.0
- ``Builder.read_doc()``
* - ``BuildEnvironment._read_serial()``
- 1.8
- 3.0
- ``Builder.read()``
* - ``BuildEnvironment._read_parallel()``
- 1.8
- 3.0
- ``Builder.read()``
* - ``BuildEnvironment.write_doctree()``
- 1.8
- 3.0
- ``Builder.write_doctree()``
* - ``BuildEnvironment.note_versionchange()``
- 1.8
- 3.0
- ``ChangesDomain.note_changeset()``
* - ``warn()`` (template helper function)
- 1.8
- 3.0
- ``warning()``
* - :confval:`source_parsers`
- 1.8
- 3.0
- :meth:`~sphinx.application.Sphinx.add_source_parser()`
* - ``sphinx.util.docutils.directive_helper()``
- 1.8
- 3.0
- ``Directive`` class of docutils
* - ``sphinx.cmdline``
- 1.8
- 3.0
- ``sphinx.cmd.build``
* - ``sphinx.make_mode``
- 1.8
- 3.0
- ``sphinx.cmd.make_mode``
* - ``sphinx.locale.l_()``
- 1.8
- 3.0
- :func:`sphinx.locale._()`
* - ``sphinx.locale.lazy_gettext()``
- 1.8
- 3.0
- :func:`sphinx.locale._()`
* - ``sphinx.locale.mygettext()``
- 1.8
- 3.0
- :func:`sphinx.locale._()`
* - ``sphinx.util.copy_static_entry()``
- 1.5
- 3.0
- ``sphinx.util.fileutil.copy_asset()``
* - ``sphinx.build_main()``
- 1.7
- 2.0
- ``sphinx.cmd.build.build_main()``
* - ``sphinx.ext.intersphinx.debug()``
- 1.7
- 2.0
- ``sphinx.ext.intersphinx.inspect_main()``
* - ``sphinx.ext.autodoc.format_annotation()``
- 1.7
- 2.0
- ``sphinx.util.inspect.Signature``
* - ``sphinx.ext.autodoc.formatargspec()``
- 1.7
- 2.0
- ``sphinx.util.inspect.Signature``
* - ``sphinx.ext.autodoc.AutodocReporter``
- 1.7
- 2.0
- ``sphinx.util.docutils.switch_source_input()``
* - ``sphinx.ext.autodoc.add_documenter()``
- 1.7
- 2.0
- :meth:`~sphinx.application.Sphinx.add_autodocumenter()`
* - ``sphinx.ext.autodoc.AutoDirective._register``
- 1.7
- 2.0
- :meth:`~sphinx.application.Sphinx.add_autodocumenter()`
* - ``AutoDirective._special_attrgetters``
- 1.7
- 2.0
- :meth:`~sphinx.application.Sphinx.add_autodoc_attrgetter()`
* - ``Sphinx.warn()``, ``Sphinx.info()``
- 1.6
- 2.0
- :ref:`logging-api`
* - ``BuildEnvironment.set_warnfunc()``
- 1.6
- 2.0
- :ref:`logging-api`
* - ``BuildEnvironment.note_toctree()``
- 1.6
- 2.0
- ``Toctree.note()`` (in ``sphinx.environment.adapters.toctree``)
* - ``BuildEnvironment.get_toc_for()``
- 1.6
- 2.0
- ``Toctree.get_toc_for()`` (in ``sphinx.environment.adapters.toctree``)
* - ``BuildEnvironment.get_toctree_for()``
- 1.6
- 2.0
- ``Toctree.get_toctree_for()`` (in ``sphinx.environment.adapters.toctree``)
* - ``BuildEnvironment.create_index()``
- 1.6
- 2.0
- ``IndexEntries.create_index()`` (in ``sphinx.environment.adapters.indexentries``)
* - ``sphinx.websupport``
- 1.6
- 2.0
- `sphinxcontrib-websupport <https://pypi.org/project/sphinxcontrib-websupport/>`_
* - ``StandaloneHTMLBuilder.css_files``
- 1.6
- 2.0
- :meth:`~sphinx.application.Sphinx.add_stylesheet()`
* - ``document.settings.gettext_compact``
- 1.8
- 1.8
- :confval:`gettext_compact`
* - ``Sphinx.status_iterator()``
- 1.6
- 1.7
- ``sphinx.util.status_iterator()``
* - ``Sphinx.old_status_iterator()``
- 1.6
- 1.7
- ``sphinx.util.old_status_iterator()``
* - ``Sphinx._directive_helper()``
- 1.6
- 1.7
- ``sphinx.util.docutils.directive_helper()``
* - ``sphinx.util.compat.Directive``
- 1.6
- 1.7
- ``docutils.parsers.rst.Directive``
* - ``sphinx.util.compat.docutils_version``
- 1.6
- 1.7
- ``sphinx.util.docutils.__version_info__``
.. note:: On deprecating on public APIs (internal functions and classes),
we also follow the policy as much as possible.
deprecated

View File

@ -56,8 +56,8 @@ Logging API
:meth:`SphinxLoggerAdapter.warning`.
**color**
The color of logs. By default, info and verbose level logs are not colored,
and debug level ones are colored as ``"darkgray"``.
The color of logs. By default, info and verbose level logs are not
colored, and debug level ones are colored as ``"darkgray"``.
.. autofunction:: pending_logging()

View File

@ -138,8 +138,8 @@ Both APIs parse the content into a given node. They are used like this::
.. deprecated:: 1.7
Until Sphinx-1.6, ``sphinx.ext.autodoc.AutodocReporter`` is used for this purpose.
For now, it is replaced by ``switch_source_input()``.
Until Sphinx-1.6, ``sphinx.ext.autodoc.AutodocReporter`` is used for this
purpose. For now, it is replaced by ``switch_source_input()``.
If you don't need the wrapping node, you can use any concrete node type and
return ``node.children`` from the Directive.
@ -147,5 +147,6 @@ return ``node.children`` from the Directive.
.. seealso::
`Creating directives <http://docutils.sourceforge.net/docs/howto/rst-directives.html>`_
HOWTO of the Docutils documentation
`Creating directives`_ HOWTO of the Docutils documentation
.. _Creating directives: http://docutils.sourceforge.net/docs/howto/rst-directives.html

View File

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

View File

@ -37,9 +37,6 @@ How do I...
You'll still need to mark up classes and such, but the headings and code
examples come through cleanly.
... create HTML slides from Sphinx documents?
See the "Hieroglyph" package at https://github.com/nyergler/hieroglyph.
For many more extensions and other contributed stuff, see the sphinx-contrib_
repository.
@ -51,12 +48,10 @@ Using Sphinx with...
--------------------
Read the Docs
https://readthedocs.org is a documentation hosting service based around
Sphinx. They will host sphinx documentation, along with supporting a number
of other features including version support, PDF generation, and more. The
`Getting Started
<https://read-the-docs.readthedocs.io/en/latest/getting_started.html>`_
guide is a good place to start.
`Read the Docs <https://readthedocs.org>`_ is a documentation hosting
service based around Sphinx. They will host sphinx documentation, along
with supporting a number of other features including version support, PDF
generation, and more. The `Getting Started`_ guide is a good place to start.
Epydoc
There's a third-party extension providing an `api role`_ which refers to
@ -77,13 +72,13 @@ PyPI
https://pythonhosted.org/.
GitHub Pages
Directories starting with underscores are ignored by default which breaks
static files in Sphinx. GitHub's preprocessor can be `disabled
<https://github.com/blog/572-bypassing-jekyll-on-github-pages>`_ to support
Sphinx HTML output properly.
Please add :py:mod:`sphinx.ext.githubpages` to your project. It allows you
to publish your document in GitHub Pages. It generates helper files for
GitHub Pages on building HTML document automatically.
MediaWiki
See https://bitbucket.org/kevindunn/sphinx-wiki/wiki/Home, a project by Kevin Dunn.
See https://bitbucket.org/kevindunn/sphinx-wiki/wiki/Home, a project by
Kevin Dunn.
Google Analytics
You can use a custom ``layout.html`` template, like this:
@ -94,7 +89,7 @@ Google Analytics
{%- block extrahead %}
{{ super() }}
<script type="text/javascript">
<script>
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'XXX account number XXX']);
_gaq.push(['_trackPageview']);
@ -106,7 +101,7 @@ Google Analytics
<div class="footer">This page uses <a href="https://analytics.google.com/">
Google Analytics</a> to collect statistics. You can disable it by blocking
the JavaScript coming from www.google-analytics.com.
<script type="text/javascript">
<script>
(function() {
var ga = document.createElement('script');
ga.src = ('https:' == document.location.protocol ?
@ -137,7 +132,6 @@ Google Search
(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];
@ -149,6 +143,7 @@ Google Search
3. Add ``searchbox.html`` to the :confval:`html_sidebars` configuration value.
.. _Getting Started: https://docs.readthedocs.io/en/stable/intro/getting-started-with-sphinx.html
.. _api role: https://git.savannah.gnu.org/cgit/kenozooid.git/tree/doc/extapi.py
.. _xhtml to reST: http://docutils.sourceforge.net/sandbox/xhtml2rest/xhtml2rest.py

View File

@ -17,10 +17,10 @@ docs have a look at `Epydoc <http://epydoc.sourceforge.net/>`_, which also
understands reST.
For a great "introduction" to writing docs in general -- the whys and hows, see
also `Write the docs <https://write-the-docs.readthedocs.io/>`_, written by Eric
Holscher.
also `Write the docs`_, written by Eric Holscher.
.. _rinohtype: https://github.com/brechtm/rinohtype
.. _Write the docs: http://www.writethedocs.org/guide/writing/beginners-guide-to-docs/
Conversion from other systems
-----------------------------
@ -55,9 +55,9 @@ See the :ref:`pertinent section in the FAQ list <usingwith>`.
Prerequisites
-------------
Sphinx needs at least **Python 3.5** to run, as well as the docutils_ and
Jinja2_ libraries. Sphinx should work with docutils version 0.12 or some (not
broken) SVN trunk snapshot.
Sphinx needs at least **Python 3.5** to run.
It also depends on 3rd party libraries such as docutils_ and jinja2_, but they
are automatically installed when sphinx is installed.
.. _reStructuredText: http://docutils.sourceforge.net/rst.html
.. _docutils: http://docutils.sourceforge.net/

View File

@ -226,6 +226,25 @@ into the generated ``.tex`` files. Its ``'sphinxsetup'`` key is described
.. versionadded:: 1.5
``'extrapackages'``
Additional LaTeX packages. For example:
.. code-block:: python
latex_elements = {
'packages': r'\usepackage{isodate}'
}
It defaults to empty.
The specified LaTeX packages will be loaded before
hyperref package and packages loaded from Sphinx extensions.
.. hint:: If you'd like to load additional LaTeX packages after hyperref, use
``'preamble'`` key instead.
.. versionadded:: 2.3
``'footer'``
Additional footer content (before the indices), default empty.
@ -289,6 +308,11 @@ into the generated ``.tex`` files. Its ``'sphinxsetup'`` key is described
.. attention::
If Greek is main language, do not use this key. Since Sphinx 2.2.1,
``xelatex`` will be used automatically as :confval:`latex_engine`.
Formerly, Sphinx did not support producing PDF via LaTeX with Greek as
main language.
Prior to 2.0, Unicode Greek letters were escaped to use LaTeX math
mark-up. This is not the case anymore, and the above must be used
(only in case of ``'pdflatex'`` engine) if the source contains such
@ -310,12 +334,19 @@ into the generated ``.tex`` files. Its ``'sphinxsetup'`` key is described
.. versionchanged:: 2.0
``'lualatex'`` executes
``\defaultfontfeatures[\rmfamily,\sffamily]{}`` to disable TeX
ligatures.
ligatures transforming `<<` and `>>` as escaping working with
``pdflatex/xelatex`` failed with ``lualatex``.
.. versionchanged:: 2.0
Detection of ``LGR``, ``T2A``, ``X2`` to trigger support of
occasional Greek or Cyrillic (``'pdflatex'`` only, as this support
is provided natively by ``'platex'`` and only requires suitable
font with ``'xelatex'/'lualatex'``).
.. versionchanged:: 2.3.0
``'xelatex'`` also executes
``\defaultfontfeatures[\rmfamily,\sffamily]{}`` in order to avoid
contractions of ``--`` into en-dash or transforms of straight quotes
into curly ones in PDF (in non-literal text paragraphs) despite
:confval:`smartquotes` being set to ``False``.
``'textgreek'``
The default (``'pdflatex'`` only) is
@ -595,12 +626,15 @@ macros may be significant.
default ``true``. Allows linebreaks inside inline literals: but extra
potential break-points (additionally to those allowed by LaTeX at spaces
or for hyphenation) are currently inserted only after the characters
``. , ; ? ! /``. Due to TeX internals, white space in the line will be
stretched (or shrunk) in order to accomodate the linebreak.
``. , ; ? ! /`` and ``\``. Due to TeX internals, white space in the line
will be stretched (or shrunk) in order to accomodate the linebreak.
.. versionadded:: 1.5
set this option value to ``false`` to recover former behaviour.
.. versionchanged:: 2.3.0
added potential breakpoint at ``\`` characters.
``verbatimvisiblespace``
default ``\textcolor{red}{\textvisiblespace}``. When a long code line is
split, the last space character from the source code line right before the
@ -740,19 +774,41 @@ thus allowing redefinitions. Check the respective files for the defaults.
Macros
~~~~~~
- text styling commands ``\sphinx<foo>`` with ``<foo>`` being one of
``strong``, ``bfcode``, ``email``, ``tablecontinued``, ``titleref``,
``menuselection``, ``accelerator``, ``crossref``, ``termref``, ``optional``.
- text styling commands:
- ``\sphinxstrong``,
- ``\sphinxbfcode``,
- ``\sphinxemail``,
- ``\sphinxtablecontinued``,
- ``\sphinxtitleref``,
- ``\sphinxmenuselection``,
- ``\sphinxaccelerator``,
- ``\sphinxcrossref``,
- ``\sphinxtermref``,
- ``\sphinxoptional``.
.. versionadded:: 1.4.5
Use of ``\sphinx`` prefixed macro names to limit possibilities of conflict
with LaTeX packages.
- more text styling: ``\sphinxstyle<bar>`` with ``<bar>`` one of
``indexentry``, ``indexextra``, ``indexpageref``, ``topictitle``,
``sidebartitle``, ``othertitle``, ``sidebarsubtitle``, ``theadfamily``,
``emphasis``, ``literalemphasis``, ``strong``, ``literalstrong``,
``abbreviation``, ``literalintitle``, ``codecontinued``, ``codecontinues``
- more text styling:
- ``\sphinxstyleindexentry``,
- ``\sphinxstyleindexextra``,
- ``\sphinxstyleindexpageref``,
- ``\sphinxstyletopictitle``,
- ``\sphinxstylesidebartitle``,
- ``\sphinxstyleothertitle``,
- ``\sphinxstylesidebarsubtitle``,
- ``\sphinxstyletheadfamily``,
- ``\sphinxstyleemphasis``,
- ``\sphinxstyleliteralemphasis``,
- ``\sphinxstylestrong``,
- ``\sphinxstyleliteralstrong``,
- ``\sphinxstyleabbreviation``,
- ``\sphinxstyleliteralintitle``,
- ``\sphinxstylecodecontinued``,
- ``\sphinxstylecodecontinues``.
.. versionadded:: 1.5
these macros were formerly hard-coded as non customizable ``\texttt``,
``\emph``, etc...
@ -761,7 +817,7 @@ Macros
multiple paragraphs in header cells of tables.
.. versionadded:: 1.6.3
``\sphinxstylecodecontinued`` and ``\sphinxstylecodecontinues``.
- the table of contents is typeset via ``\sphinxtableofcontents`` which is a
- ``\sphinxtableofcontents``: it is a
wrapper (defined differently in :file:`sphinxhowto.cls` and in
:file:`sphinxmanual.cls`) of standard ``\tableofcontents``. The macro
``\sphinxtableofcontentshook`` is executed during its expansion right before
@ -774,22 +830,22 @@ Macros
done during loading of ``'manual'`` docclass are now executed later via
``\sphinxtableofcontentshook``. This macro is also executed by the
``'howto'`` docclass, but defaults to empty with it.
- a custom ``\sphinxmaketitle`` is defined in the class files
- ``\sphinxmaketitle``: it is defined in the class files
:file:`sphinxmanual.cls` and :file:`sphinxhowto.cls` and is used as
default setting of ``'maketitle'`` :confval:`latex_elements` key.
.. versionchanged:: 1.8.3
formerly, ``\maketitle`` from LaTeX document class was modified by
Sphinx.
- for ``'manual'`` docclass a macro ``\sphinxbackoftitlepage``, if it is
defined, gets executed at end of ``\sphinxmaketitle``, before the final
- ``\sphinxbackoftitlepage``: for ``'manual'`` docclass, and if it is
defined, it gets executed at end of ``\sphinxmaketitle``, before the final
``\clearpage``. Use either the ``'maketitle'`` key or the ``'preamble'`` key
of :confval:`latex_elements` to add a custom definition of
``\sphinxbackoftitlepage``.
.. versionadded:: 1.8.3
- the citation reference is typeset via ``\sphinxcite`` which is a wrapper
of standard ``\cite``.
- ``\sphinxcite``: it is a wrapper of standard ``\cite`` for citation
references.
Environments
~~~~~~~~~~~~
@ -801,12 +857,23 @@ Environments
.. versionadded:: 1.5.6
formerly, the ``\small`` was hardcoded in LaTeX writer and the ending
``\par`` was lacking.
- for each admonition type ``<foo>``, the
used environment is named ``sphinx<foo>``. They may be ``\renewenvironment``
- environments associated with admonitions:
- ``sphinxnote``,
- ``sphinxhint``,
- ``sphinximportant``,
- ``sphinxtip``,
- ``sphinxwarning``,
- ``sphinxcaution``,
- ``sphinxattention``,
- ``sphinxdanger``,
- ``sphinxerror``.
They may be ``\renewenvironment``
'd individually, and must then be defined with one argument (it is the heading
of the notice, for example ``Warning:`` for :dudir:`warning` directive, if
English is the document language). Their default definitions use either the
*sphinxheavybox* (for the first listed directives) or the *sphinxlightbox*
*sphinxheavybox* (for the last 5 ones) or the *sphinxlightbox*
environments, configured to use the parameters (colours, border thickness)
specific to each type, which can be set via ``'sphinxsetup'`` string.

View File

@ -4,7 +4,8 @@ sphinx-apidoc
Synopsis
--------
**sphinx-apidoc** [*OPTIONS*] -o <*OUTPUT_PATH*> <*MODULE_PATH*> [*EXCLUDE_PATTERN*, ...]
**sphinx-apidoc** [*OPTIONS*] -o <*OUTPUT_PATH*> <*MODULE_PATH*>
[*EXCLUDE_PATTERN* ...]
Description
-----------
@ -38,6 +39,11 @@ Options
Directory to place the output files. If it does not exist, it is created.
.. option:: -q
Do not output anything on standard output, only write warnings and errors to
standard error.
.. option:: -f, --force
Force overwriting of any existing generated files.
@ -125,6 +131,30 @@ These options are used when :option:`--full` is specified:
Sets the project release to put in generated files (see :confval:`release`).
.. rubric:: Project templating
.. versionadded:: 2.2
Project templating options for sphinx-apidoc
.. option:: -t, --templatedir=TEMPLATEDIR
Template directory for template files. You can modify the templates of
sphinx project files generated by apidoc. Following Jinja2 template
files are allowed:
* ``module.rst_t``
* ``package.rst_t``
* ``toc.rst_t``
* ``master_doc.rst_t``
* ``conf.py_t``
* ``Makefile_t``
* ``Makefile.new_t``
* ``make.bat_t``
* ``make.bat.new_t``
In detail, please refer the system template files Sphinx provides.
(``sphinx/templates/apidoc`` and ``sphinx/templates/quickstart``)
Environment
-----------

View File

@ -302,7 +302,8 @@ variables to customize behavior:
.. describe:: SPHINXOPTS
Additional options for :program:`sphinx-build`.
Additional options for :program:`sphinx-build`. These options can
also be set via the shortcut variable **O** (capital 'o').
.. _when-deprecation-warnings-are-displayed:

View File

@ -297,7 +297,8 @@ in the future.
The "page name" of the current file, i.e. either the document name if the
file is generated from a reST source, or the equivalent hierarchical name
relative to the output directory (``[directory/]filename_without_extension``).
relative to the output directory
(``[directory/]filename_without_extension``).
.. data:: project
@ -354,8 +355,8 @@ are in HTML form), these variables are also available:
.. data:: body
A string containing the content of the page in HTML form as produced by the HTML builder,
before the theme is applied.
A string containing the content of the page in HTML form as produced by the
HTML builder, before the theme is applied.
.. data:: display_toc
@ -382,8 +383,9 @@ are in HTML form), these variables are also available:
.. data:: page_source_suffix
The suffix of the file that was rendered. Since we support a list of :confval:`source_suffix`,
this will allow you to properly link to the original source file.
The suffix of the file that was rendered. Since we support a list of
:confval:`source_suffix`, this will allow you to properly link to the
original source file.
.. data:: parents

View File

@ -49,8 +49,9 @@ Python :mod:`ConfigParser` module) and has the following structure:
* The **inherit** setting gives the name of a "base theme", or ``none``. The
base theme will be used to locate missing templates (most themes will not have
to supply most templates if they use ``basic`` as the base theme), its options
will be inherited, and all of its static files will be used as well. If you want
to also inherit the stylesheet, include it via CSS' ``@import`` in your own.
will be inherited, and all of its static files will be used as well. If you
want to also inherit the stylesheet, include it via CSS' ``@import`` in your
own.
* The **stylesheet** setting gives the name of a CSS file which will be
referenced in the HTML header. If you need more than one CSS file, either

View File

@ -12,8 +12,8 @@ in itself. See the :ref:`intl-options` for details on configuration.
.. figure:: /_static/translation.png
:width: 100%
Workflow visualization of translations in Sphinx. (The stick-figure is taken
from an `XKCD comic <https://xkcd.com/779/>`_.)
Workflow visualization of translations in Sphinx. (The figure is created by
`plantuml <http://plantuml.com>`_.)
.. contents::
:local:

View File

@ -57,7 +57,7 @@ Once configured, call this by calling the relevant command on ``setup.py``::
Options for setuptools integration
----------------------------------
.. confval:: fresh-env
.. setuptools-confval:: fresh-env
A boolean that determines whether the saved environment should be discarded
on build. Default is false.
@ -68,7 +68,7 @@ Options for setuptools integration
$ python setup.py build_sphinx -E
.. confval:: all-files
.. setuptools-confval:: all-files
A boolean that determines whether all files should be built from scratch.
Default is false.
@ -79,7 +79,7 @@ Options for setuptools integration
$ python setup.py build_sphinx -a
.. confval:: source-dir
.. setuptools-confval:: source-dir
The target source directory. This can be relative to the ``setup.py`` or
``setup.cfg`` file, or it can be absolute. It defaults to ``./doc`` or
@ -92,12 +92,12 @@ Options for setuptools integration
$ python setup.py build_sphinx -s $SOURCE_DIR
.. confval:: build-dir
.. setuptools-confval:: build-dir
The target build directory. This can be relative to the ``setup.py`` or
``setup.cfg`` file, or it can be absolute. Default is ``./build/sphinx``.
.. confval:: config-dir
.. setuptools-confval:: config-dir
Location of the configuration directory. This can be relative to the
``setup.py`` or ``setup.cfg`` file, or it can be absolute. Default is to use
@ -111,7 +111,7 @@ Options for setuptools integration
.. versionadded:: 1.0
.. confval:: builder
.. setuptools-confval:: builder
The builder or list of builders to use. Default is ``html``.
@ -124,7 +124,7 @@ Options for setuptools integration
.. versionchanged:: 1.6
This can now be a comma- or space-separated list of builders
.. confval:: warning-is-error
.. setuptools-confval:: warning-is-error
A boolean that ensures Sphinx warnings will result in a failed build.
Default is false.
@ -137,32 +137,32 @@ Options for setuptools integration
.. versionadded:: 1.5
.. confval:: project
.. setuptools-confval:: project
The documented project's name. Default is ``''``.
.. versionadded:: 1.0
.. confval:: version
.. setuptools-confval:: version
The short X.Y version. Default is ``''``.
.. versionadded:: 1.0
.. confval:: release
.. setuptools-confval:: release
The full version, including alpha/beta/rc tags. Default is ``''``.
.. versionadded:: 1.0
.. confval:: today
.. setuptools-confval:: today
How to format the current date, used as the replacement for ``|today|``.
Default is ``''``.
.. versionadded:: 1.0
.. confval:: link-index
.. setuptools-confval:: link-index
A boolean that ensures index.html will be linked to the master doc. Default
is false.
@ -175,13 +175,13 @@ Options for setuptools integration
.. versionadded:: 1.0
.. confval:: copyright
.. setuptools-confval:: copyright
The copyright string. Default is ``''``.
.. versionadded:: 1.3
.. confval:: nitpicky
.. setuptools-confval:: nitpicky
Run in nit-picky mode. Currently, this generates warnings for all missing
references. See the config value :confval:`nitpick_ignore` for a way to
@ -189,7 +189,7 @@ Options for setuptools integration
.. versionadded:: 1.8
.. confval:: pdb
.. setuptools-confval:: pdb
A boolean to configure ``pdb`` on exception. Default is false.

View File

@ -63,7 +63,7 @@ This dict can then be used as context for templates. The goal is to be easy to
integrate with your existing templating system. An example using `Jinja2
<http://jinja.pocoo.org/>`_ is:
.. sourcecode:: html+jinja
.. code-block:: html+jinja
{%- extends "layout.html" %}

View File

@ -61,7 +61,7 @@ The builder's "name" must be given to the **-b** command-line option of
.. versionadded:: 1.0
.. module:: sphinx.builders.htmlhelp
.. module:: sphinxcontrib.htmlhelp
.. class:: HTMLHelpBuilder
This builder produces the same output as the standalone HTML builder, but

View File

@ -296,25 +296,26 @@ General configuration
Sphinx supports following warning types:
* app.add_node
* app.add_directive
* app.add_role
* app.add_generic_role
* app.add_source_parser
* download.not_readable
* image.not_readable
* ref.term
* ref.ref
* ref.numref
* ref.keyword
* ref.option
* ref.citation
* ref.footnote
* ref.doc
* ref.python
* misc.highlighting_failure
* toc.secnum
* epub.unknown_project_files
* ``app.add_node``
* ``app.add_directive``
* ``app.add_role``
* ``app.add_generic_role``
* ``app.add_source_parser``
* ``download.not_readable``
* ``image.not_readable``
* ``ref.term``
* ``ref.ref``
* ``ref.numref``
* ``ref.keyword``
* ``ref.option``
* ``ref.citation``
* ``ref.footnote``
* ``ref.doc``
* ``ref.python``
* ``misc.highlighting_failure``
* ``toc.secnum``
* ``epub.unknown_project_files``
* ``autosectionlabel.*``
You can choose from these types.
@ -334,6 +335,10 @@ General configuration
Added ``ref.footnote``
.. versionchanged:: 2.1
Added ``autosectionlabel.*``
.. confval:: needs_sphinx
If set to a ``major.minor`` version string like ``'1.1'``, Sphinx will
@ -492,8 +497,27 @@ General configuration
direct usage of :program:`sphinx-build` as it caches
(in its default usage) the parsed source files in per builder locations.
.. hint:: An alternative way to effectively deactivate (or customize) the
smart quotes for a given builder, for example ``latex``, is to use
``make`` this way:
.. code-block:: console
make latex O="-D smartquotes_action="
This can follow some ``make html`` with no problem, in contrast to the
situation from the prior note. It requires Docutils 0.14 or later.
.. versionadded:: 1.6.6
.. confval:: user_agent
A User-Agent of Sphinx. It is used for a header on HTTP access (ex.
linkcheck, intersphinx and so on). Default is ``"Sphinx/X.Y.Z
requests/X.Y.Z python/X.Y.Z"``.
.. versionadded:: 2.3
.. confval:: tls_verify
If true, Sphinx verifies server certifications. Default is ``True``.
@ -514,7 +538,7 @@ General configuration
directory pointed ``REQUESTS_CA_BUNDLE`` environment
variable if ``tls_cacerts`` not set.
.. _requests: http://docs.python-requests.org/en/master/
.. _requests: https://requests.readthedocs.io/en/master/
.. confval:: today
today_fmt
@ -550,7 +574,7 @@ General configuration
A dictionary of options that modify how the lexer specified by
:confval:`highlight_language` generates highlighted source code. These are
lexer-specific; for the options understood by each, see the
`Pygments documentation <http://pygments.org/docs/lexers/>`_.
`Pygments documentation <https://pygments.org/docs/lexers.html>`_.
.. versionadded:: 1.3
@ -638,12 +662,17 @@ documentation on :ref:`intl` for details.
Currently supported languages by Sphinx are:
* ``ar`` -- Arabic
* ``bn`` -- Bengali
* ``ca`` -- Catalan
* ``cak`` -- Kaqchikel
* ``cs`` -- Czech
* ``cy`` -- Welsh
* ``da`` -- Danish
* ``de`` -- German
* ``el`` -- Greek
* ``en`` -- English
* ``eo`` -- Esperanto
* ``es`` -- Spanish
* ``et`` -- Estonian
* ``eu`` -- Basque
@ -651,6 +680,7 @@ documentation on :ref:`intl` for details.
* ``fi`` -- Finnish
* ``fr`` -- French
* ``he`` -- Hebrew
* ``hi`` -- Hindi
* ``hr`` -- Croatian
* ``hu`` -- Hungarian
* ``id`` -- Indonesian
@ -664,15 +694,20 @@ documentation on :ref:`intl` for details.
* ``ne`` -- Nepali
* ``nl`` -- Dutch
* ``pl`` -- Polish
* ``pt`` -- Portuguese
* ``pt_BR`` -- Brazilian Portuguese
* ``pt_PT`` -- European Portuguese
* ``ro`` -- Romanian
* ``ru`` -- Russian
* ``si`` -- Sinhala
* ``sk`` -- Slovak
* ``sl`` -- Slovenian
* ``sr`` -- Serbian
* ``sv`` -- Swedish
* ``ta`` -- Tamil
* ``tr`` -- Turkish
* ``uk_UA`` -- Ukrainian
* ``ur`` -- Urdu
* ``vi`` -- Vietnamese
* ``zh_CN`` -- Simplified Chinese
* ``zh_TW`` -- Traditional Chinese
@ -746,7 +781,7 @@ documentation on :ref:`intl` for details.
i18n additionally. You can specify below names:
:index: index terms
:literal-block: literal blocks: ``::`` and ``code-block``.
:literal-block: literal blocks (``::`` annotation and ``code-block`` directive)
:doctest-block: doctest block
:raw: raw content
:image: image/figure uri and alt
@ -914,7 +949,7 @@ that use Sphinx's HTMLWriter class.
Example::
html_css_files = ['custom.css'
html_css_files = ['custom.css',
'https://example.com/css/custom.css',
('print.css', {'media': 'print'})]
@ -1812,6 +1847,7 @@ These options influence LaTeX output.
* ``'xelatex'`` -- XeLaTeX
* ``'lualatex'`` -- LuaLaTeX
* ``'platex'`` -- pLaTeX (default if :confval:`language` is ``'ja'``)
* ``'uplatex'`` -- upLaTeX (experimental)
``'pdflatex'``\ 's support for Unicode characters is limited.
@ -1825,7 +1861,21 @@ These options influence LaTeX output.
``'xelatex'`` or ``'lualatex'`` and making sure to use an OpenType font
with wide-enough glyph coverage is often easier than trying to make
``'pdflatex'`` work with the extra Unicode characters. Since Sphinx 2.0
the default is the GNU FreeFont which covers well Latin, Cyrillic and Greek.
the default is the GNU FreeFont which covers well Latin, Cyrillic and
Greek.
.. versionchanged:: 2.1.0
Use ``xelatex`` (and LaTeX package ``xeCJK``) by default for Chinese
documents.
.. versionchanged:: 2.2.1
Use ``xelatex`` by default for Greek documents.
.. versionchanged:: 2.3
Add ``uplatex`` support.
Contrarily to :ref:`MathJaX math rendering in HTML output <math-support>`,
LaTeX requires some extra configuration to support Unicode literals in
@ -2330,6 +2380,34 @@ Options for the linkcheck builder
.. versionadded:: 1.5
.. confval:: linkcheck_auth
Pass authentication information when doing a ``linkcheck`` build.
A list of ``(regex_pattern, auth_info)`` tuples where the items are:
*regex_pattern*
A regular expression that matches a URI.
*auth_info*
Authentication information to use for that URI. The value can be anything
that is understood by the ``requests`` library (see `requests
Authentication <requests-auth>`_ for details).
.. _requests-auth: https://requests.readthedocs.io/en/master/user/authentication/
The ``linkcheck`` builder will use the first matching ``auth_info`` value
it can find in the :confval:`linkcheck_auth` list, so values earlier in the
list have higher priority.
Example::
linkcheck_auth = [
('https://foo\.yourcompany\.com/.+', ('johndoe', 'secret')),
('https://.+\.yourcompany\.com/.+', HTTPDigestAuth(...)),
]
.. versionadded:: 2.3
Options for the XML builder
---------------------------

View File

@ -40,10 +40,8 @@ you can also enable the :mod:`napoleon <sphinx.ext.napoleon>` extension.
:mod:`napoleon <sphinx.ext.napoleon>` is a preprocessor that converts your
docstrings to correct reStructuredText before :mod:`autodoc` processes them.
.. _Google:
https://github.com/google/styleguide/blob/gh-pages/pyguide.md#38-comments-and-docstrings
.. _NumPy:
https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt
.. _Google: https://github.com/google/styleguide/blob/gh-pages/pyguide.md#38-comments-and-docstrings
.. _NumPy: https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt
Directives
@ -142,6 +140,20 @@ inserting them into the page source under a suitable :rst:dir:`py:module`,
.. versionadded:: 1.1
* autodoc considers a member private if its docstring contains
``:meta private:`` in its :ref:`info-field-lists`.
For example:
.. code-block:: rst
def my_function(my_arg, my_other_arg):
"""blah blah blah
:meta private:
"""
.. versionadded:: 3.0
* Python "special" members (that is, those named like ``__special__``) will
be included if the ``special-members`` flag option is given::
@ -159,7 +171,7 @@ inserting them into the page source under a suitable :rst:dir:`py:module`,
* For classes and exceptions, members inherited from base classes will be
left out when documenting all members, unless you give the
``inherited-members`` flag option, in addition to ``members``::
``inherited-members`` option, in addition to ``members``::
.. autoclass:: Noodle
:members:
@ -168,11 +180,29 @@ inserting them into the page source under a suitable :rst:dir:`py:module`,
This can be combined with ``undoc-members`` to document *all* available
members of the class or module.
It can take an anchestor class not to document inherited members from it.
By default, members of ``object`` class are not documented. To show them
all, give ``None`` to the option.
For example; If your class ``Foo`` is derived from ``list`` class and
you don't want to document ``list.__len__()``, you should specify a
option ``:inherited-members: list`` to avoid special members of list
class.
Another example; If your class Foo has ``__str__`` special method and
autodoc directive has both ``inherited-members`` and ``special-members``,
``__str__`` will be documented as in the past, but other special method
that are not implemented in your class ``Foo``.
Note: this will lead to markup errors if the inherited members come from a
module whose docstrings are not reST formatted.
.. versionadded:: 0.3
.. versionchanged:: 3.0
It takes an anchestor class name as an argument.
* It's possible to override the signature for explicitly documented callable
objects (functions, methods, classes) with the regular syntax that will
override the signature gained from introspection::
@ -245,21 +275,23 @@ inserting them into the page source under a suitable :rst:dir:`py:module`,
These work exactly like :rst:dir:`autoclass` etc.,
but do not offer the options used for automatic member documentation.
:rst:dir:`autodata` and :rst:dir:`autoattribute` support
the ``annotation`` option.
Without this option, the representation of the object
will be shown in the documentation.
When the option is given without arguments,
only the name of the object will be printed::
:rst:dir:`autodata` and :rst:dir:`autoattribute` support the ``annotation``
option. The option controls how the value of variable is shown. If specified
without arguments, only the name of the variable will be printed, and its value
is not shown::
.. autodata:: CD_DRIVE
:annotation:
You can tell sphinx what should be printed after the name::
If the option specified with arguments, it is printed after the name as a value
of the variable::
.. autodata:: CD_DRIVE
:annotation: = your CD device name
By default, without ``annotation`` option, Sphinx tries to obtain the value of
the variable and print it after the name.
For module data members and class attributes, documentation can either be put
into a comment with special formatting (using a ``#:`` to start the comment
instead of just ``#``), or in a docstring *after* the definition. Comments
@ -315,7 +347,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
@ -387,14 +419,17 @@ There are also new config values that you can set:
The supported options are ``'members'``, ``'member-order'``,
``'undoc-members'``, ``'private-members'``, ``'special-members'``,
``'inherited-members'``, ``'show-inheritance'``, ``'ignore-module-all'`` and
``'exclude-members'``.
``'inherited-members'``, ``'show-inheritance'``, ``'ignore-module-all'``,
``'imported-members'`` and ``'exclude-members'``.
.. versionadded:: 1.8
.. versionchanged:: 2.0
Accepts ``True`` as a value.
.. versionchanged:: 2.1
Added ``'imported-members'``.
.. confval:: autodoc_docstring_signature
Functions imported from C modules cannot be introspected, and therefore the
@ -428,6 +463,16 @@ There are also new config values that you can set:
This config value only requires to declare the top-level modules that
should be mocked.
.. confval:: autodoc_typehints
This value controls how to represents typehints. The setting takes the
following values:
* ``'signature'`` -- Show typehints as its signature (default)
* ``'none'`` -- Do not show typehints
.. versionadded:: 2.1
.. confval:: autodoc_warningiserror
This value controls the behavior of :option:`sphinx-build -W` during
@ -481,6 +526,17 @@ autodoc provides the following additional events:
auto directive
:param lines: the lines of the docstring, see above
.. event:: autodoc-before-process-signature (app, obj, bound_method)
.. versionadded:: 2.4
Emitted before autodoc formats a signature for an object. The event handler
can modify an object to change its signature.
:param app: the Sphinx application object
:param obj: the object itself
:param bound_method: a boolean indicates an object is bound method or not
.. event:: autodoc-process-signature (app, what, name, obj, options, signature, return_annotation)
.. versionadded:: 0.5
@ -543,3 +599,24 @@ member should be included in the documentation by using the following event:
``inherited_members``, ``undoc_members``, ``show_inheritance`` and
``noindex`` that are true if the flag option of same name was given to the
auto directive
Generating documents from type annotations
------------------------------------------
As an experimental feature, autodoc provides ``sphinx.ext.autodoc.typehints`` as
an additional extension. It extends autodoc itself to generate function document
from its type annotations.
To enable the feature, please add ``sphinx.ext.autodoc.typehints`` to list of
extensions and set `'description'` to :confval:`autodoc_typehints`:
.. code-block:: python
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.autodoc.typehints']
autodoc_typehints = 'description'
.. versionadded:: 2.4
Added as an experimental feature. This will be integrated into autodoc core
in Sphinx-3.0.

View File

@ -131,7 +131,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
@ -143,12 +143,34 @@ also use this new config value:
The new files will be placed in the directories specified in the
``:toctree:`` options of the directives.
.. versionchanged:: 2.3
Emits :event:`autodoc-skip-member` event as :mod:`~sphinx.ext.autodoc`
does.
.. confval:: autosummary_generate_overwrite
If true, autosummary already overwrites stub files by generated contents.
Defaults to true (enabled).
.. versionadded:: 3.0
.. confval:: autosummary_mock_imports
This value contains a list of modules to be mocked up. See
:confval:`autodoc_mock_imports` for more details. It defaults to
:confval:`autodoc_mock_imports`.
.. versionadded:: 2.0
.. confval:: autosummary_imported_members
A boolean flag indicating whether to document classes and functions imported
in modules. Default is ``False``
.. versionadded:: 2.1
Customizing templates
---------------------

View File

@ -13,7 +13,7 @@ This extension features one additional builder, the :class:`CoverageBuilder`.
.. todo:: Write this section.
Several new configuration values can be used to specify what the builder
Several configuration values can be used to specify what the builder
should check:
.. confval:: coverage_ignore_modules
@ -22,6 +22,16 @@ should check:
.. confval:: coverage_ignore_classes
.. confval:: coverage_ignore_pyobjects
List of `Python regular expressions`_.
If any of these regular expressions matches any part of the full import path
of a Python object, that Python object is excluded from the documentation
coverage report.
.. versionadded:: 2.1
.. confval:: coverage_c_path
.. confval:: coverage_c_regexes
@ -40,3 +50,5 @@ should check:
``False`` by default.
.. versionadded:: 1.1
.. _Python regular expressions: https://docs.python.org/library/re

View File

@ -11,11 +11,15 @@
pair: testing; snippets
This extension allows you to test snippets in the documentation in a natural
way. It works by collecting specially-marked up code blocks and running them as
doctest tests.
It is often helpful to include snippets of code in your documentation and
demonstrate the results of executing them. But it is important to ensure that
the documentation stays up-to-date with the code.
Within one document, test code is partitioned in *groups*, where each group
This extension allows you to test such code snippets in the documentation in
a natural way. If you mark the code blocks as shown here, the ``doctest``
builder will collect them and run them as doctest tests.
Within each document, you can assign each snippet to a *group*. Each group
consists of:
* zero or more *setup code* blocks (e.g. importing the module to test)

View File

@ -0,0 +1,11 @@
:mod:`sphinx.ext.duration` -- Measure durations of Sphinx processing
====================================================================
.. module:: sphinx.ext.duration
:synopsis: Measure durations of Sphinx processing
.. versionadded:: 2.4
This extension measures durations of Sphinx processing and show its
result at end of the build. It is useful for inspecting what document
is slowly built.

View File

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

View File

@ -39,6 +39,56 @@ It adds these directives:
.. versionchanged:: 1.1
Added support for external files.
.. rubric:: options
.. rst:directive:option:: alt: alternate text
:type: text
The alternate text of the graph. By default, the graph code is used to
the alternate text.
.. versionadded:: 1.0
.. rst:directive:option:: align: alignment of the graph
:type: left, center or right
The horizontal alignment of the graph.
.. versionadded:: 1.5
.. rst:directive:option:: caption: caption of the graph
:type: text
The caption of the graph.
.. versionadded:: 1.1
.. rst:directive:option:: layout: layout type of the graph
:type: text
The layout of the graph (ex. ``dot``, ``neato`` and so on). A path to the
graphviz commands are also allowed. By default, :confval:`graphviz_dot`
is used.
.. versionadded:: 1.4
.. versionchanged:: 2.2
Renamed from ``graphviz_dot``
.. rst:directive:option:: name: label
:type: text
The label of the graph.
.. versionadded:: 1.6
.. rst:directive:option:: class: class names
:type: a list of class names separeted by spaces
The class name of the graph.
.. versionadded:: 2.4
.. rst:directive:: graph
@ -56,6 +106,45 @@ It adds these directives:
non-alphanumeric characters (e.g. a dash), you will have to double-quote
it.
.. rubric:: options
Same as :rst:dir:`graphviz`.
.. rst:directive:option:: alt: alternate text
:type: text
.. versionadded:: 1.0
.. rst:directive:option:: align: alignment of the graph
:type: left, center or right
.. versionadded:: 1.5
.. rst:directive:option:: caption: caption of the graph
:type: text
.. versionadded:: 1.1
.. rst:directive:option:: layout: layout type of the graph
:type: text
.. versionadded:: 1.4
.. versionchanged:: 2.2
Renamed from ``graphviz_dot``
.. rst:directive:option:: name: label
:type: text
.. versionadded:: 1.6
.. rst:directive:option:: class: class names
:type: a list of class names separeted by spaces
The class name of the graph.
.. versionadded:: 2.4
.. rst:directive:: digraph
@ -69,28 +158,47 @@ It adds these directives:
"bar" -> "baz" -> "quux";
.. rubric:: options
.. versionadded:: 1.0
All three directives support an ``alt`` option that determines the image's
alternate text for HTML output. If not given, the alternate text defaults to
the graphviz code.
Same as :rst:dir:`graphviz`.
.. versionadded:: 1.1
All three directives support a ``caption`` option that can be used to give a
caption to the diagram.
.. rst:directive:option:: alt: alternate text
:type: text
.. versionchanged:: 1.4
All three directives support a ``graphviz_dot`` option that can be switch the
``dot`` command within the directive.
.. versionadded:: 1.0
.. versionadded:: 1.5
All three directives support a ``align`` option to align the graph horizontal.
The values "left", "center", "right" are allowed.
.. rst:directive:option:: align: alignment of the graph
:type: left, center or right
.. versionadded:: 1.6
All three directives support a ``name`` option to set the label to graph.
.. versionadded:: 1.5
There are also these new config values:
.. rst:directive:option:: caption: caption of the graph
:type: text
.. versionadded:: 1.1
.. rst:directive:option:: layout: layout type of the graph
:type: text
.. versionadded:: 1.4
.. versionchanged:: 2.2
Renamed from ``graphviz_dot``
.. rst:directive:option:: name: label
:type: text
.. versionadded:: 1.6
.. rst:directive:option:: class: class names
:type: a list of class names separeted by spaces
The class name of the graph.
.. versionadded:: 2.4
There are also these config values:
.. confval:: graphviz_dot

View File

@ -8,6 +8,11 @@
This extension is quite simple, and features only one directive:
.. warning::
This directive is designed to control only content of document. It could
not control sections, labels and so on.
.. rst:directive:: ifconfig
Include content of the directive only if the Python expression given as an
@ -28,6 +33,6 @@ This extension is quite simple, and features only one directive:
def setup(app):
app.add_config_value('releaselevel', '', 'env')
The second argument is the default value, the third should always be ``'env'``
for such values (it selects if Sphinx re-reads the documents if the value
changes).
The second argument is the default value, the third should always be
``'env'`` for such values (it selects if Sphinx re-reads the documents if the
value changes).

View File

@ -23,6 +23,7 @@ These extensions are built in and can be activated by respective entries in the
autosummary
coverage
doctest
duration
extlinks
githubpages
graphviz

View File

@ -25,13 +25,19 @@ 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
classes (those whose name starts with ``_``) will be included.
.. versionchanged:: 2.0
The value of for ``parts`` can also be negative, indicating how many
parts to drop from the left. For example, if all your class names start
with ``lib.``, you can give ``:parts: -1`` to remove that prefix from the
displayed node names.
The directive also supports a ``private-bases`` flag option; if given,
private base classes (those whose name starts with ``_``) will be included.
You can use ``caption`` option to give a caption to the diagram.
@ -92,6 +98,41 @@ It adds this directive:
Added ``top-classes`` option to limit the scope of inheritance graphs.
Examples
--------
The following are different inheritance diagrams for the internal
``InheritanceDiagram`` class that implements the directive.
With full names::
.. inheritance-diagram:: sphinx.ext.inheritance_diagram.InheritanceDiagram
.. inheritance-diagram:: sphinx.ext.inheritance_diagram.InheritanceDiagram
Showing class names only::
.. inheritance-diagram:: sphinx.ext.inheritance_diagram.InheritanceDiagram
:parts: 1
.. inheritance-diagram:: sphinx.ext.inheritance_diagram.InheritanceDiagram
:parts: 1
Stopping the diagram at :class:`sphinx.util.docutils.SphinxDirective` (the
highest superclass still part of Sphinx), and dropping the common left-most
part (``sphinx``) from all names::
.. inheritance-diagram:: sphinx.ext.inheritance_diagram.InheritanceDiagram
:top-classes: sphinx.util.docutils.SphinxDirective
:parts: -1
.. inheritance-diagram:: sphinx.ext.inheritance_diagram.InheritanceDiagram
:top-classes: sphinx.util.docutils.SphinxDirective
:parts: -1
Configuration
-------------

View File

@ -43,7 +43,7 @@ Configuration
-------------
To use Intersphinx linking, add ``'sphinx.ext.intersphinx'`` to your
:confval:`extensions` config value, and use these new config values to activate
:confval:`extensions` config value, and use these config values to activate
linking:
.. confval:: intersphinx_mapping
@ -148,3 +148,13 @@ project. The following example prints the Intersphinx mapping of the Python 3
documentation::
$ python -msphinx.ext.intersphinx https://docs.python.org/3/objects.inv
Using Intersphinx with inventory file under Basic Authorization
---------------------------------------------------------------
Intersphinx supports Basic Authorization like this::
intersphinx_mapping = {'python': ('https://user:password@docs.python.org/3',
None)}
The user and password will be stripped from the URL when generating the links.

View File

@ -15,7 +15,8 @@ Math support for HTML outputs in Sphinx
So mathbase extension is no longer needed.
Since mathematical notation isn't natively supported by HTML in any way, Sphinx
gives a math support to HTML document with several extensions.
gives a math support to HTML document with several extensions. These use the
reStructuredText math :rst:dir:`directive <math>` and :rst:role:`role <math>`.
:mod:`sphinx.ext.imgmath` -- Render math as images
--------------------------------------------------
@ -29,13 +30,39 @@ This extension renders math via LaTeX and dvipng_ or dvisvgm_ into PNG or SVG
images. This of course means that the computer where the docs are built must
have both programs available.
There are various config values you can set to influence how the images are
built:
There are various configuration values you can set to influence how the images
are built:
.. confval:: imgmath_image_format
The output image format. The default is ``'png'``. It should be either
``'png'`` or ``'svg'``.
The output image format. The default is ``'png'``. It should be either
``'png'`` or ``'svg'``. The image is produced by first executing ``latex``
on the TeX mathematical mark-up then (depending on the requested format)
either `dvipng`_ or `dvisvgm`_.
.. confval:: imgmath_use_preview
``dvipng`` and ``dvisvgm`` both have the ability to collect from LaTeX the
"depth" of the rendered math: an inline image should use this "depth" in a
``vertical-align`` style to get correctly aligned with surrounding text.
This mechanism requires the `LaTeX preview package`_ (available as
``preview-latex-style`` on Ubuntu xenial). Therefore, the default for this
option is ``False`` but it is strongly recommended to set it to ``True``.
.. versionchanged:: 2.2
This option can be used with the ``'svg'`` :confval:`imgmath_image_format`.
.. confval:: imgmath_add_tooltips
Default: ``True``. If false, do not add the LaTeX code as an "alt" attribute
for math images.
.. confval:: imgmath_font_size
The font size (in ``pt``) of the displayed math. The default value is
``12``. It must be a positive integer.
.. confval:: imgmath_latex
@ -53,19 +80,13 @@ built:
This value should only contain the path to the latex executable, not further
arguments; use :confval:`imgmath_latex_args` for that purpose.
.. confval:: imgmath_dvipng
.. hint::
The command name with which to invoke ``dvipng``. The default is
``'dvipng'``; you may need to set this to a full path if ``dvipng`` is not in
the executable search path. This option is only used when
``imgmath_image_format`` is set to ``'png'``.
.. confval:: imgmath_dvisvgm
The command name with which to invoke ``dvisvgm``. The default is
``'dvisvgm'``; you may need to set this to a full path if ``dvisvgm`` is not
in the executable search path. This option is only used when
``imgmath_image_format`` is ``'svg'``.
Some fancy LaTeX mark-up (an example was reported which used TikZ to add
various decorations to the equation) require multiple runs of the LaTeX
executable. To handle this, set this configuration setting to
``'latexmk'`` (or a full path to it) as this Perl script reliably
chooses dynamically how many latex runs are needed.
.. confval:: imgmath_latex_args
@ -74,48 +95,43 @@ built:
.. confval:: imgmath_latex_preamble
Additional LaTeX code to put into the preamble of the short LaTeX files that
are used to translate the math snippets. This is empty by default. Use it
e.g. to add more packages whose commands you want to use in the math.
Additional LaTeX code to put into the preamble of the LaTeX files used to
translate the math snippets. This is left empty by default. Use it
e.g. to add packages which modify the fonts used for math, such as
``'\\usepackage{newtxsf}'`` for sans-serif fonts, or
``'\\usepackage{fouriernc}'`` for serif fonts. Indeed, the default LaTeX
math fonts have rather thin glyphs which (in HTML output) often do not
match well with the font for text.
.. confval:: imgmath_dvipng
The command name to invoke ``dvipng``. The default is
``'dvipng'``; you may need to set this to a full path if ``dvipng`` is not in
the executable search path. This option is only used when
``imgmath_image_format`` is set to ``'png'``.
.. confval:: imgmath_dvipng_args
Additional arguments to give to dvipng, as a list. The default value is
``['-gamma', '1.5', '-D', '110', '-bg', 'Transparent']`` which makes the
image a bit darker and larger then it is by default, and produces PNGs with a
image a bit darker and larger then it is by default (this compensates
somewhat for the thinness of default LaTeX math fonts), and produces PNGs with a
transparent background. This option is used only when
``imgmath_image_format`` is ``'png'``.
.. confval:: imgmath_dvisvgm
The command name to invoke ``dvisvgm``. The default is
``'dvisvgm'``; you may need to set this to a full path if ``dvisvgm`` is not
in the executable search path. This option is only used when
``imgmath_image_format`` is ``'svg'``.
.. confval:: imgmath_dvisvgm_args
Additional arguments to give to dvisvgm, as a list. The default value is
``['--no-fonts']``. This option is used only when ``imgmath_image_format``
is ``'svg'``.
.. confval:: imgmath_use_preview
``dvipng`` has the ability to determine the "depth" of the rendered text: for
example, when typesetting a fraction inline, the baseline of surrounding text
should not be flush with the bottom of the image, rather the image should
extend a bit below the baseline. This is what TeX calls "depth". When this
is enabled, the images put into the HTML document will get a
``vertical-align`` style that correctly aligns the baselines.
Unfortunately, this only works when the `preview-latex package`_ is
installed. Therefore, the default for this option is ``False``.
Currently this option is only used when ``imgmath_image_format`` is
``'png'``.
.. confval:: imgmath_add_tooltips
Default: ``True``. If false, do not add the LaTeX code as an "alt" attribute
for math images.
.. confval:: imgmath_font_size
The font size (in ``pt``) of the displayed math. The default value is
``12``. It must be a positive integer.
Additional arguments to give to dvisvgm, as a list. The default value is
``['--no-fonts']``, which means that ``dvisvgm`` will render glyphs as path
elements (cf the `dvisvgm FAQ`_). This option is used only when
``imgmath_image_format`` is ``'svg'``.
:mod:`sphinx.ext.mathjax` -- Render math via JavaScript
@ -131,7 +147,13 @@ MathJax_ is then loaded and transforms the LaTeX markup to readable math live in
the browser.
Because MathJax (and the necessary fonts) is very large, it is not included in
Sphinx.
Sphinx but is set to automatically include it from a third-party site.
.. attention::
You should use the math :rst:dir:`directive <math>` and
:rst:role:`role <math>`, not the native MathJax ``$$``, ``\(``, etc.
.. confval:: mathjax_path
@ -140,8 +162,9 @@ Sphinx.
The default is the ``https://`` URL that loads the JS files from the
`cdnjs`__ Content Delivery Network. See the `MathJax Getting Started
page`__ for details. If you want MathJax to be available offline, you have
to download it and set this value to a different path.
page`__ for details. If you want MathJax to be available offline or
without including resources from a third-party site, you have to
download it and set this value to a different path.
__ https://cdnjs.com
@ -168,6 +191,8 @@ Sphinx.
The default is empty (``{}``).
.. versionadded:: 1.8
.. confval:: mathjax_config
The inline configuration options for mathjax. The value is used as a
@ -183,6 +208,8 @@ Sphinx.
The default is empty (not configured).
.. versionadded:: 1.8
.. _Using in-line configuration options: https://docs.mathjax.org/en/latest/configuration.html#using-in-line-configuration-options
:mod:`sphinx.ext.jsmath` -- Render math via JavaScript
@ -209,7 +236,8 @@ package jsMath_. It provides this config value:
.. _dvipng: https://savannah.nongnu.org/projects/dvipng/
.. _dvisvgm: http://dvisvgm.bplaced.net/
.. _dvisvgm: https://dvisvgm.de/
.. _dvisvgm FAQ: https://dvisvgm.de/FAQ
.. _MathJax: https://www.mathjax.org/
.. _jsMath: http://www.math.union.edu/~dpvc/jsmath/
.. _preview-latex package: https://www.gnu.org/software/auctex/preview-latex.html
.. _LaTeX preview package: https://www.gnu.org/software/auctex/preview-latex.html

View File

@ -56,7 +56,7 @@ source code files.
.. _Google:
https://google.github.io/styleguide/pyguide.html#Comments
.. _NumPy:
https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt
https://numpydoc.readthedocs.io/en/latest/format.html#docstring-standard
.. _Khan Academy:
https://github.com/Khan/style-guides/blob/master/style/python.md#docstrings

View File

@ -17,8 +17,9 @@ There are two additional directives when using this extension:
``True``.
.. versionadded:: 1.3.2
This directive supports an ``class`` option that determines the class attribute
for HTML output. If not given, the class defaults to ``admonition-todo``.
This directive supports an ``class`` option that determines the class
attribute for HTML output. If not given, the class defaults to
``admonition-todo``.
.. rst:directive:: todolist
@ -46,8 +47,8 @@ Configuration
.. confval:: todo_link_only
If this is ``True``, :rst:dir:`todolist` produce output without file path and line,
The default is ``False``.
If this is ``True``, :rst:dir:`todolist` produce output without file path and
line, The default is ``False``.
.. versionadded:: 1.4
@ -57,5 +58,5 @@ autodoc provides the following an additional event:
.. versionadded:: 1.5
Emitted when a todo is defined. *node* is the defined ``sphinx.ext.todo.todo_node``
node.
Emitted when a todo is defined. *node* is the defined
``sphinx.ext.todo.todo_node`` node.

View File

@ -108,12 +108,13 @@ Windows
.. todo:: Could we start packaging this?
Most Windows users do not have Python installed by default, so we begin with
the installation of Python itself. If you are unsure, open the *Command
Prompt* (:kbd:`⊞Win-r` and type :command:`cmd`). Once the command prompt is
open, type :command:`python --version` and press Enter. If Python is
available, you will see the version of Python printed to the screen. If you do
not have Python installed, refer to the `Hitchhikers Guide to Python's`__
Python on Windows installation guides. You must install `Python 3`__.
the installation of Python itself. To check if you already have Python
installed, open the *Command Prompt* (:kbd:`⊞Win-r` and type :command:`cmd`).
Once the command prompt is open, type :command:`python --version` and press
Enter. If Python is installed, you will see the version of Python printed to
the screen. If you do not have Python installed, refer to the `Hitchhikers
Guide to Python's`__ Python on Windows installation guides. You must install
`Python 3`__.
Once Python is installed, you can install Sphinx using :command:`pip`. Refer
to the :ref:`pip installation instructions <install-pypi>` below for more

View File

@ -6,7 +6,7 @@ Once Sphinx is :doc:`installed </usage/installation>`, you can proceed with
setting up your first Sphinx project. To ease the process of getting started,
Sphinx provides a tool, :program:`sphinx-quickstart`, which will generate a
documentation source directory and populate it with some defaults. We're going
to use the :program:`sphinx-quickstart` tool here, though it's use by no means
to use the :program:`sphinx-quickstart` tool here, though its use is by no means
necessary.
@ -26,7 +26,7 @@ configuration values from a few questions it asks you. To use this, run:
$ sphinx-quickstart
Answer each question asked. Be sure to say yes to the ``autodoc`` extension, as
Answer each question asked. Be sure to say "yes" to the ``autodoc`` extension, as
we will use this later.
There is also an automatic "API documentation" generator called
@ -103,7 +103,7 @@ In Sphinx source files, you can use most features of standard
For example, you can add cross-file references in a portable way (which works
for all output types) using the :rst:role:`ref` role.
For an example, if you are viewing the HTML version you can look at the source
For an example, if you are viewing the HTML version, you can look at the source
for this document -- use the "Show Source" link in the sidebar.
.. todo:: Update the below link when we add new guides on these.

View File

@ -372,7 +372,8 @@ Docutils supports the following directives:
* HTML specifics:
- :dudir:`meta` (generation of HTML ``<meta>`` tags)
- :dudir:`meta`
(generation of HTML ``<meta>`` tags, see also :ref:`html-meta` below)
- :dudir:`title <metadata-document-title>` (override document title)
* Influencing markup:
@ -538,6 +539,45 @@ You can indent text after a comment start to form multiline comments::
Still in the comment.
.. _html-meta:
HTML Metadata
-------------
The :rst:dir:`meta` directive (:dudir:`ref <meta>`) allows specifying the HTML
`metadata element`_ of a Sphinx documentation page. For example, the
directive::
.. meta::
:description: The Sphinx documentation builder
:keywords: Sphinx, documentation, builder
will generate the following HTML output:
.. code:: html
<meta name="description" content="The Sphinx documentation builder">
<meta name="keywords" content="Sphinx, documentation, builder">
Also, Sphinx will add the keywords as specified in the meta directive to the
search index. Thereby, the ``lang`` attribute of the meta element is
considered. For example, the directive::
.. meta::
:keywords: backup
:keywords lang=en: pleasefindthiskey pleasefindthiskeytoo
:keywords lang=de: bittediesenkeyfinden
adds the following words to the search indices of builds with different language
configurations:
* ``pleasefindthiskey``, ``pleasefindthiskeytoo`` to *English* builds;
* ``bittediesenkeyfinden`` to *German* builds;
* ``backup`` to builds in all languages.
.. _metadata element: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta
Source encoding
---------------

View File

@ -36,8 +36,8 @@ tables of contents. The ``toctree`` directive is the central element.
.. note::
For local tables of contents, use the standard reST :dudir:`contents
directive <table-of-contents>`.
To create table of contents for current document (.rst file), use the
standard reST :dudir:`contents directive <table-of-contents>`.
.. rst:directive:: toctree
@ -441,7 +441,7 @@ If highlighting with the selected language fails (i.e. Pygments emits an
want to ensure consistent highlighting, you should fix your version of
Pygments.
__ http://pygments.org/docs/lexers/
__ http://pygments.org/docs/lexers
.. rst:directive:: .. highlight:: language
@ -453,15 +453,29 @@ __ http://pygments.org/docs/lexers/
As discussed previously, *language* can be any lexer alias supported by
Pygments.
**Additional options**
.. rubric:: options
Pygments can generate line numbers for code blocks. To enable this, use the
``linenothreshold`` option. ::
.. rst:directive:option:: linenothreshold: threshold
:type: number (optional)
.. highlight:: python
:linenothreshold: 5
Enable to generate line numbers for code blocks.
This will produce line numbers for all code blocks longer than five lines.
This option takes an optional number as threshold parameter. If any
threshold given, the directive will produce line numbers only for the code
blocks longer than N lines. If not given, line numbers will be produced
for all of code blocks.
Example::
.. highlight:: python
:linenothreshold: 5
.. rst:directive:option:: force
:type: no value
If given, minor errors on highlighting are ignored.
.. versionadded:: 2.1
.. rst:directive:: .. code-block:: [language]
@ -477,67 +491,99 @@ __ http://pygments.org/docs/lexers/
:rst:dir:`highlight` directive will be used. If not set,
:confval:`highlight_language` will be used.
**Additional options**
Pygments can generate line numbers for code blocks. To enable this for, use
the ``linenos`` flag option. ::
.. code-block:: ruby
:linenos:
Some more Ruby code.
The first line number can be selected with the ``lineno-start`` option. If
present, ``linenos`` flag is automatically activated::
.. code-block:: ruby
:lineno-start: 10
Some more Ruby code, with line numbering starting at 10.
Additionally, an ``emphasize-lines`` option can be given to have Pygments
emphasize particular lines::
.. code-block:: python
:emphasize-lines: 3,5
def some_function():
interesting = False
print 'This line is highlighted.'
print 'This one is not...'
print '...but this one is.'
A ``caption`` option can be given to show that name before the code block.
A ``name`` option can be provided implicit target name that can be
referenced by using :rst:role:`ref`. For example::
.. code-block:: python
:caption: this.py
:name: this-py
print 'Explicit is better than implicit.'
A ``dedent`` option can be given to strip indentation characters from the
code block. For example::
.. code-block:: ruby
:dedent: 4
some ruby code
.. versionchanged:: 1.1
The ``emphasize-lines`` option has been added.
.. versionchanged:: 1.3
The ``lineno-start``, ``caption``, ``name`` and ``dedent`` options have
been added.
.. versionchanged:: 1.6.6
LaTeX supports the ``emphasize-lines`` option.
.. versionchanged:: 2.0
The ``language`` argument becomes optional.
.. rubric:: options
.. rst:directive:option:: linenos
:type: no value
Enable to generate line numbers for the code block::
.. code-block:: ruby
:linenos:
Some more Ruby code.
.. rst:directive:option:: lineno-start: number
:type: number
Set the first line number of the code block. If present, ``linenos``
option is also automatically activated::
.. code-block:: ruby
:lineno-start: 10
Some more Ruby code, with line numbering starting at 10.
.. versionadded:: 1.3
.. rst:directive:option:: emphasize-lines: line numbers
:type: comma separated numbers
Emphasize particular lines of the code block::
.. code-block:: python
:emphasize-lines: 3,5
def some_function():
interesting = False
print 'This line is highlighted.'
print 'This one is not...'
print '...but this one is.'
.. versionadded:: 1.1
.. versionchanged:: 1.6.6
LaTeX supports the ``emphasize-lines`` option.
.. rst:directive:option: force
:type: no value
Ignore minor errors on highlighting
.. versionchanged:: 2.1
.. rst:directive:option:: caption: caption of code block
:type: text
Set a caption to the code block.
.. versionadded:: 1.3
.. rst:directive:option:: name: a label for hyperlink
:type: text
Define implicit target name that can be referenced by using
:rst:role:`ref`. For example::
.. code-block:: python
:caption: this.py
:name: this-py
print 'Explicit is better than implicit.'
.. versionadded:: 1.3
.. rst:directive:option:: dedent: number
:type: number
Strip indentation characters from the code block. For example::
.. code-block:: ruby
:dedent: 4
some ruby code
.. versionadded:: 1.3
.. rst:directive:option:: force
:type: no value
If given, minor errors on highlighting are ignored.
.. versionadded:: 2.1
.. rst:directive:: .. literalinclude:: filename
Longer displays of verbatim text may be included by storing the example text
@ -630,6 +676,8 @@ __ http://pygments.org/docs/lexers/
This shows the diff between ``example.py`` and ``example.py.orig`` with
unified diff format.
A ``force`` option can ignore minor errors on highlighting.
.. versionchanged:: 0.4.3
Added the ``encoding`` option.
@ -651,6 +699,9 @@ __ http://pygments.org/docs/lexers/
With both ``start-after`` and ``lines`` in use, the first line as per
``start-after`` is considered to be with line number ``1`` for ``lines``.
.. versionchanged:: 2.1
Added the ``force`` option.
.. _glossary-directive:
Glossary
@ -686,8 +737,8 @@ Glossary
(When the glossary is sorted, the first term determines the sort order.)
If you want to specify "grouping key" for general index entries, you can put a "key"
as "term : key". For example::
If you want to specify "grouping key" for general index entries, you can put
a "key" as "term : key". For example::
.. glossary::
@ -697,12 +748,12 @@ Glossary
Note that "key" is used for grouping key as is.
The "key" isn't normalized; key "A" and "a" become different groups.
The whole characters in "key" is used instead of a first character; it is used for
"Combining Character Sequence" and "Surrogate Pairs" grouping key.
The whole characters in "key" is used instead of a first character; it is
used for "Combining Character Sequence" and "Surrogate Pairs" grouping key.
In i18n situation, you can specify "localized term : key" even if original text only
have "term" part. In this case, translated "localized term" will be categorized in
"key" group.
In i18n situation, you can specify "localized term : key" even if original
text only have "term" part. In this case, translated "localized term" will be
categorized in "key" group.
.. versionadded:: 0.6
You can now give the glossary directive a ``:sorted:`` flag that will
@ -958,16 +1009,16 @@ this reason, the following directive exists:
.. warning::
Tables with more than 30 rows are rendered using ``longtable``, not
``tabulary``, in order to allow pagebreaks. The ``L``, ``R``, ... specifiers
do not work for these tables.
``tabulary``, in order to allow pagebreaks. The ``L``, ``R``, ...
specifiers do not work for these tables.
Tables that contain list-like elements such as object descriptions,
blockquotes or any kind of lists cannot be set out of the box with
``tabulary``. They are therefore set with the standard LaTeX ``tabular`` (or
``longtable``) environment if you don't give a ``tabularcolumns`` directive.
If you do, the table will be set with ``tabulary`` but you must use the
``p{width}`` construct (or Sphinx's ``\X`` and ``\Y`` specifiers described
below) for the columns containing these elements.
``tabulary``. They are therefore set with the standard LaTeX ``tabular``
(or ``longtable``) environment if you don't give a ``tabularcolumns``
directive. If you do, the table will be set with ``tabulary`` but you
must use the ``p{width}`` construct (or Sphinx's ``\X`` and ``\Y``
specifiers described below) for the columns containing these elements.
Literal blocks do not work with ``tabulary`` at all, so tables containing
a literal block are always set with ``tabular``. The verbatim environment
@ -996,10 +1047,11 @@ this reason, the following directive exists:
.. versionchanged:: 1.6
Merged cells from complex grid tables (either multi-row, multi-column, or
both) now allow blockquotes, lists, literal blocks, ... as do regular cells.
both) now allow blockquotes, lists, literal blocks, ... as do regular
cells.
Sphinx's merged cells interact well with ``p{width}``, ``\X{a}{b}``, ``Y{f}``
and tabulary's columns.
Sphinx's merged cells interact well with ``p{width}``, ``\X{a}{b}``,
``\Y{f}`` and tabulary's columns.
.. note::
@ -1087,7 +1139,7 @@ derived forms), but provides enough to allow context-free grammars to be
displayed in a way that causes uses of a symbol to be rendered as hyperlinks to
the definition of the symbol. There is this directive:
.. rst:directive:: .. productionlist:: [name]
.. rst:directive:: .. productionlist:: [productionGroup]
This directive is used to enclose a group of productions. Each production
is given on a single line and consists of a name, separated by a colon from
@ -1095,16 +1147,24 @@ the definition of the symbol. There is this directive:
continuation line must begin with a colon placed at the same column as in
the first line.
The argument to :rst:dir:`productionlist` serves to distinguish different
sets of production lists that belong to different grammars.
The *productionGroup* argument to :rst:dir:`productionlist` serves to
distinguish different sets of production lists that belong to different
grammars. Multiple production lists with the same *productionGroup* thus
define rules in the same scope.
Blank lines are not allowed within ``productionlist`` directive arguments.
The definition can contain token names which are marked as interpreted text
(e.g. ``sum ::= `integer` "+" `integer```) -- this generates
(e.g. "``sum ::= `integer` "+" `integer```") -- this generates
cross-references to the productions of these tokens. Outside of the
production list, you can reference to token productions using
:rst:role:`token`.
However, if you have given a *productionGroup* argument you must prefix the
token name in the cross-reference with the group name and a colon,
e.g., "``myGroup:sum``" instead of just "``sum``".
If the group should not be shown in the title of the link either
an explicit title can be given (e.g., "``myTitle <myGroup:sum>``"),
or the target can be prefixed with a tilde (e.g., "``~myGroup:sum``").
Note that no further reST parsing is done in the production, so that you
don't have to escape ``*`` or ``|`` characters.

View File

@ -128,17 +128,28 @@ declarations:
This directive will also cause an entry in the global module index.
The ``platform`` option, if present, is a comma-separated list of the
platforms on which the module is available (if it is available on all
platforms, the option should be omitted). The keys are short identifiers;
examples that are in use include "IRIX", "Mac", "Windows", and "Unix". It is
important to use a key which has already been used when applicable.
.. rubric:: options
The ``synopsis`` option should consist of one sentence describing the
module's purpose -- it is currently only used in the Global Module Index.
.. rst:directive:option:: platform: platforms
:type: comma separated list
The ``deprecated`` option can be given (with no value) to mark a module as
deprecated; it will be designated as such in various locations then.
Indicate platforms which the module is available (if it is available on
all platforms, the option should be omitted). The keys are short
identifiers; examples that are in use include "IRIX", "Mac", "Windows"
and "Unix". It is important to use a key which has already been used when
applicable.
.. rst:directive:option:: synopsis: purpose
:type: text
Consist of one sentence describing the module's purpose -- it is currently
only used in the Global Module Index.
.. rst:directive:option:: deprecated
:type: no argument
Mark a module as deprecated; it will be designated as such in various
locations then.
.. rst:directive:: .. py:currentmodule:: name
@ -169,12 +180,33 @@ The following directives are provided for module and class contents:
This information can (in any ``py`` directive) optionally be given in a
structured form, see :ref:`info-field-lists`.
.. rubric:: options
.. rst:directive:option:: async
:type: no value
Indicate the function is an async function.
.. versionadded:: 2.1
.. rst:directive:: .. py:data:: name
Describes global data in a module, including both variables and values used
as "defined constants." Class and object attributes are not documented
using this environment.
.. rubric:: options
.. rst:directive:option:: type: type of the variable
:type: text
.. versionadded:: 2.4
.. rst:directive:option:: value: initial value of the variable
:type: text
.. versionadded:: 2.4
.. rst:directive:: .. py:exception:: name
Describes an exception class. The signature can, but need not include
@ -209,6 +241,18 @@ The following directives are provided for module and class contents:
information about the type of the data to be expected and whether it may be
changed directly.
.. rubric:: options
.. rst:directive:option:: type: type of the attribute
:type: text
.. versionadded:: 2.4
.. rst:directive:option:: value: initial value of the attribute
:type: text
.. versionadded:: 2.4
.. rst:directive:: .. py:method:: name(parameters)
Describes an object method. The parameters should not include the ``self``
@ -216,6 +260,44 @@ The following directives are provided for module and class contents:
described for ``function``. See also :ref:`signatures` and
:ref:`info-field-lists`.
.. rubric:: options
.. rst:directive:option:: abstractmethod
:type: no value
Indicate the method is an abstract method.
.. versionadded:: 2.1
.. rst:directive:option:: async
:type: no value
Indicate the method is an async method.
.. versionadded:: 2.1
.. rst:directive:option:: classmethod
:type: no value
Indicate the method is a class method.
.. versionadded:: 2.1
.. rst:directive:option:: property
:type: no value
Indicate the method is a property.
.. versionadded:: 2.1
.. rst:directive:option:: staticmethod
:type: no value
Indicate the method is a static method.
.. versionadded:: 2.1
.. rst:directive:: .. py:staticmethod:: name(parameters)
Like :rst:dir:`py:method`, but indicates that the method is a static method.
@ -296,6 +378,9 @@ Info field lists
~~~~~~~~~~~~~~~~
.. versionadded:: 0.4
.. versionchanged:: 3.0
meta fields are added.
Inside Python object description directives, reST field lists with these fields
are recognized and formatted nicely:
@ -309,6 +394,10 @@ are recognized and formatted nicely:
* ``vartype``: Type of a variable. Creates a link if possible.
* ``returns``, ``return``: Description of the return value.
* ``rtype``: Return type. Creates a link if possible.
* ``meta``: Add metadata to description of the python object. The metadata will
not be shown on output document. For example, ``:meta private:`` indicates
the python object is private member. It is used in
:py:mod:`sphinx.ext.autodoc` for filtering members.
.. note::
@ -699,7 +788,8 @@ visibility statement (``public``, ``private`` or ``protected``).
.. cpp:enum-struct:: protected MyScopedVisibilityEnum : std::underlying_type<MySpecificEnum>::type
A scoped enum with non-default visibility, and with a specified underlying type.
A scoped enum with non-default visibility, and with a specified
underlying type.
.. rst:directive:: .. cpp:enumerator:: name
.. cpp:enumerator:: name = constant
@ -739,7 +829,8 @@ visibility statement (``public``, ``private`` or ``protected``).
**Valid Expressions**
- :cpp:expr:`*r`, when :cpp:expr:`r` is dereferenceable.
- :cpp:expr:`++r`, with return type :cpp:expr:`It&`, when :cpp:expr:`r` is incrementable.
- :cpp:expr:`++r`, with return type :cpp:expr:`It&`, when
:cpp:expr:`r` is incrementable.
This will render as follows:
@ -778,11 +869,12 @@ Anonymous Entities
~~~~~~~~~~~~~~~~~~
C++ supports anonymous namespaces, classes, enums, and unions.
For the sake of documentation they must be given some name that starts with ``@``,
e.g., ``@42`` or ``@data``.
For the sake of documentation they must be given some name that starts with
``@``, e.g., ``@42`` or ``@data``.
These names can also be used in cross-references and (type) expressions,
though nested symbols will be found even when omitted.
The ``@...`` name will always be rendered as **[anonymous]** (possibly as a link).
The ``@...`` name will always be rendered as **[anonymous]** (possibly as a
link).
Example::
@ -814,8 +906,8 @@ Explicit ref: :cpp:var:`Data::@data::a`. Short-hand ref: :cpp:var:`Data::a`.
Aliasing Declarations
~~~~~~~~~~~~~~~~~~~~~
Sometimes it may be helpful list declarations elsewhere than their main documentation,
e.g., when creating a synopsis of a class interface.
Sometimes it may be helpful list declarations elsewhere than their main
documentation, e.g., when creating a synopsis of a class interface.
The following directive can be used for this purpose.
.. rst:directive:: .. cpp:alias:: name or function signature
@ -1058,19 +1150,21 @@ These roles link to the given declaration types:
be properly qualified relative to the position of the link.
.. versionadded:: 2.0
The :rst:role:`cpp:struct` role as alias for the :rst:role:`cpp:class` role.
The :rst:role:`cpp:struct` role as alias for the :rst:role:`cpp:class`
role.
.. admonition:: Note on References with Templates Parameters/Arguments
These roles follow the Sphinx :ref:`xref-syntax` rules. This means care must be
taken when referencing a (partial) template specialization, e.g. if the link looks like
this: ``:cpp:class:`MyClass<int>```.
These roles follow the Sphinx :ref:`xref-syntax` rules. This means care must
be taken when referencing a (partial) template specialization, e.g. if the
link looks like this: ``:cpp:class:`MyClass<int>```.
This is interpreted as a link to ``int`` with a title of ``MyClass``.
In this case, escape the opening angle bracket with a backslash,
like this: ``:cpp:class:`MyClass\<int>```.
When a custom title is not needed it may be useful to use the roles for inline expressions,
:rst:role:`cpp:expr` and :rst:role:`cpp:texpr`, where angle brackets do not need escaping.
When a custom title is not needed it may be useful to use the roles for
inline expressions, :rst:role:`cpp:expr` and :rst:role:`cpp:texpr`, where
angle brackets do not need escaping.
Declarations without template parameters and template arguments
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -1084,7 +1178,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:
@ -1104,7 +1198,8 @@ References using the :rst:role:`cpp:func` role:
- Specific overload: ``void C::f()``, :cpp:func:`void C::f()`
- Specific overload: ``void C::f(int)``, :cpp:func:`void C::f(int)`
- Specific overload: ``void C::f(double)``, :cpp:func:`void C::f(double)`
- Specific overload: ``void C::f(double) const``, :cpp:func:`void C::f(double) const`
- Specific overload: ``void C::f(double) const``,
:cpp:func:`void C::f(double) const`
Note that the :confval:`add_function_parentheses` configuration variable
does not influence specific overload references.
@ -1133,8 +1228,9 @@ and template arguments for the prefix of qualified names. For example:
- ``template\<typename TOuter> template\<typename TInner> Wrapper::Outer<TOuter>::Inner``
(:cpp:class:`template\<typename TOuter> template\<typename TInner> Wrapper::Outer<TOuter>::Inner`)
Currently the lookup only succeed if the template parameter identifiers are equal strings.
That is, ``template\<typename UOuter> Wrapper::Outer`` will not work.
Currently the lookup only succeed if the template parameter identifiers are
equal strings. That is, ``template\<typename UOuter> Wrapper::Outer`` will not
work.
As a shorthand notation, if a template parameter list is omitted,
then the lookup will assume either a primary template or a non-template,
@ -1295,8 +1391,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)
@ -1404,6 +1498,43 @@ The reStructuredText domain (name **rst**) provides the following directives:
Bar description.
.. rst:directive:: .. rst:directive:option:: name
Describes an option for reST directive. The *name* can be a single option
name or option name with arguments which separated with colon (``:``).
For example::
.. rst:directive:: toctree
.. rst:directive:option:: caption: caption of ToC
.. rst:directive:option:: glob
will be rendered as:
.. rst:directive:: toctree
:noindex:
.. rst:directive:option:: caption: caption of ToC
.. rst:directive:option:: glob
.. rubric:: options
.. rst:directive:option:: type: description of argument
:type: text
Describe the type of option value.
For example::
.. rst:directive:: toctree
.. rst:directive:option:: maxdepth
:type: integer or no value
.. versionadded:: 2.1
.. rst:directive:: .. rst:role:: name
Describes a reST role. For example::
@ -1430,7 +1561,7 @@ These roles are provided to refer to the described objects:
The Math Domain
---------------
The math domain (name **math**) provides the following roles::
The math domain (name **math**) provides the following roles:
.. rst:role:: math:numref

View File

@ -30,6 +30,12 @@ At the moment, these metadata fields are recognized:
:tocdepth: 2
.. note::
This metadata effects to the depth of local toctree. But it does not
effect to the depth of *global* toctree. So this would not be change
the sidebar of some themes which uses global one.
.. versionadded:: 0.4
``nocomments``

View File

@ -189,8 +189,8 @@ Referencing downloadable files
When you use this role, the referenced file is automatically marked for
inclusion in the output when building (obviously, for HTML output only).
All downloadable files are put into the ``_downloads`` subdirectory of the
output directory; duplicate filenames are handled.
All downloadable files are put into a ``_downloads/<unique hash>/``
subdirectory of the output directory; duplicate filenames are handled.
An example::

View File

@ -151,6 +151,10 @@ These themes are:
dimension string such as '70em' or '50%'. Use 'none' if you don't
want a width limit. Defaults may depend on the theme (often 800px).
- **navigation_with_keys** (true or false): Allow navigating to the
previous/next page using the keyboard's left and right arrows. Defaults to
``False``.
**alabaster**
`Alabaster theme`_ is a modified "Kr" Sphinx theme from @kennethreitz
(especially as used in his Requests project), which was itself originally
@ -237,6 +241,8 @@ These themes are:
- **documentwidth** (CSS length): Width of the document (without sidebar),
default 50em.
- **sidebarwidth** (CSS length): Width of the sidebar, default 20em.
- **rightsidebar** (true or false): Put the sidebar on the right side.
Defaults to ``True``.
- **bgcolor** (CSS color): Background color.
- **headerbg** (CSS value for "background"): background for the header area,
default a grayish gradient.

2323
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -11,10 +11,10 @@
"url": "https://github.com/sphinx-doc/sphinx/issues"
},
"devDependencies": {
"jasmine-core": "^3.1.0",
"karma": "^3.0.0",
"karma-chrome-launcher": "^2.2.0",
"jasmine-core": "^3.4.0",
"karma": "^4.0.0",
"karma-chrome-launcher": "^3.0.0",
"karma-firefox-launcher": "^1.1.0",
"karma-jasmine": "^1.1.2"
"karma-jasmine": "^2.0.0"
}
}

View File

@ -5,9 +5,6 @@ license_file = LICENSE
tag_build = .dev
tag_date = true
[bdist_wheel]
universal = 1
[aliases]
release = egg_info -Db ''
upload = upload --sign --identity=36580288
@ -43,6 +40,8 @@ paths =
.
[mypy]
python_version = 3.5
disallow_incomplete_defs = True
show_column_numbers = True
show_error_context = True
ignore_missing_imports = True
@ -55,6 +54,11 @@ strict_optional = False
filterwarnings =
all
ignore::DeprecationWarning:docutils.io
markers =
sphinx
apidoc
setup_command
test_params
[coverage:run]
branch = True

View File

@ -25,7 +25,7 @@ install_requires = [
'Pygments>=2.0',
'docutils>=0.12',
'snowballstemmer>=1.1',
'babel>=1.3,!=2.0',
'babel>=1.3',
'alabaster>=0.7,<0.8',
'imagesize',
'requests>=2.5.0',
@ -38,16 +38,21 @@ extras_require = {
':sys_platform=="win32"': [
'colorama>=0.3.5',
],
'test': [
'mock',
'pytest',
'pytest-cov',
'html5lib',
'docs': [
'sphinxcontrib-websupport',
],
'lint': [
'flake8>=3.5.0',
'flake8-import-order',
'mypy>=0.590',
'mypy>=0.761',
'docutils-stubs',
],
'test': [
'pytest < 5.3.3',
'pytest-cov',
'html5lib',
'typed_ast', # for py35-37
],
}
# Provide a "compile_catalog" command that also creates the translated
@ -173,6 +178,11 @@ setup(
author_email='georg@python.org',
description='Python documentation generator',
long_description=long_desc,
long_description_content_type='text/x-rst',
project_urls={
"Code": "https://github.com/sphinx-doc/sphinx",
"Issue tracker": "https://github.com/sphinx-doc/sphinx/issues",
},
zip_safe=False,
classifiers=[
'Development Status :: 5 - Production/Stable',
@ -213,6 +223,9 @@ setup(
],
platforms='any',
packages=find_packages(exclude=['tests', 'utils']),
package_data = {
'sphinx': ['py.typed'],
},
include_package_data=True,
entry_points={
'console_scripts': [

View File

@ -4,7 +4,7 @@
The Sphinx documentation toolchain.
:copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@ -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__))
@ -56,8 +56,8 @@ if __version__.endswith('+'):
__version__ = __version__[:-1] # remove '+' for PEP-440 version spec.
try:
ret = subprocess.run(['git', 'show', '-s', '--pretty=format:%h'],
stdout=PIPE, stderr=PIPE, encoding='ascii')
stdout=PIPE, stderr=PIPE)
if ret.stdout:
__display_version__ += '/' + ret.stdout.strip()
__display_version__ += '/' + ret.stdout.decode('ascii').strip()
except Exception:
pass

View File

@ -4,7 +4,7 @@
The Sphinx documentation toolchain.
:copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -4,20 +4,21 @@
Additional docutils nodes.
:copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import warnings
from typing import Any, Dict, List, Sequence
from docutils import nodes
from docutils.nodes import Node
from sphinx.deprecation import RemovedInSphinx30Warning, RemovedInSphinx40Warning
from sphinx.deprecation import RemovedInSphinx40Warning
if False:
# For type annotation
from typing import Any, Dict, List, Sequence # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.application import Sphinx
class translatable(nodes.Node):
@ -34,18 +35,15 @@ class translatable(nodes.Node):
Because they are used at final step; extraction.
"""
def preserve_original_messages(self):
# type: () -> None
def preserve_original_messages(self) -> None:
"""Preserve original translatable messages."""
raise NotImplementedError
def apply_translated_message(self, original_message, translated_message):
# type: (str, str) -> None
def apply_translated_message(self, original_message: str, translated_message: str) -> None:
"""Apply translated message."""
raise NotImplementedError
def extract_original_messages(self):
# type: () -> Sequence[str]
def extract_original_messages(self) -> Sequence[str]:
"""Extract translation messages.
:returns: list of extracted messages or messages generator
@ -61,22 +59,37 @@ class not_smartquotable:
class toctree(nodes.General, nodes.Element, translatable):
"""Node for inserting a "TOC tree"."""
def preserve_original_messages(self):
# type: () -> None
def preserve_original_messages(self) -> None:
# toctree entries
rawentries = self.setdefault('rawentries', [])
for title, docname in self['entries']:
if title:
rawentries.append(title)
# :caption: option
if self.get('caption'):
self['rawcaption'] = self['caption']
def apply_translated_message(self, original_message, translated_message):
# type: (str, str) -> None
def apply_translated_message(self, original_message: str, translated_message: str) -> None:
# toctree entries
for i, (title, docname) in enumerate(self['entries']):
if title == original_message:
self['entries'][i] = (translated_message, docname)
# :caption: option
if self.get('rawcaption') == original_message:
self['caption'] = translated_message
def extract_original_messages(self):
# type: () -> List[str]
def extract_original_messages(self) -> List[str]:
messages = [] # type: List[str]
# toctree entries
messages.extend(self.get('rawentries', []))
# :caption: option
if 'rawcaption' in self:
return [self['rawcaption']]
else:
return []
messages.append(self['rawcaption'])
return messages
# domain-specific object descriptions (class, function etc.)
@ -125,8 +138,7 @@ class desc_type(nodes.Part, nodes.Inline, nodes.FixedTextElement):
class desc_returns(desc_type):
"""Node for a "returns" annotation (a la -> in Python)."""
def astext(self):
# type: () -> str
def astext(self) -> str:
return ' -> ' + super().astext()
@ -147,8 +159,7 @@ class desc_optional(nodes.Part, nodes.Inline, nodes.FixedTextElement):
"""Node for marking optional parts of the parameter list."""
child_text_separator = ', '
def astext(self):
# type: () -> str
def astext(self) -> str:
return '[' + super().astext() + ']'
@ -188,59 +199,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):
@ -348,8 +306,8 @@ class abbreviation(nodes.abbreviation):
.. deprecated:: 2.0
"""
def __init__(self, rawsource='', text='', *children, **attributes):
# type: (str, str, *nodes.Node, **Any) -> None
def __init__(self, rawsource: str = '', text: str = '',
*children: Node, **attributes: Any) -> None:
warnings.warn("abbrevition node for Sphinx was replaced by docutils'.",
RemovedInSphinx40Warning, stacklevel=2)
@ -360,8 +318,7 @@ class manpage(nodes.Inline, nodes.FixedTextElement):
"""Node for references to manpages."""
def setup(app):
# type: (Sphinx) -> Dict[str, Any]
def setup(app: "Sphinx") -> Dict[str, Any]:
app.add_node(toctree)
app.add_node(desc)
app.add_node(desc_signature)
@ -379,7 +336,6 @@ def setup(app):
app.add_node(seealso)
app.add_node(productionlist)
app.add_node(production)
app.add_node(displaymath)
app.add_node(index)
app.add_node(centered)
app.add_node(acks)

View File

@ -6,58 +6,59 @@
Gracefully adapted from the TextPress system by Armin.
:copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import os
import pickle
import platform
import sys
import warnings
from collections import deque
from inspect import isclass
from io import StringIO
from os import path
from typing import Any, Callable, Dict, IO, List, Tuple, Union
from docutils import nodes
from docutils.nodes import Element, TextElement
from docutils.parsers.rst import Directive, roles
from docutils.transforms import Transform
from pygments.lexer import Lexer
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.domains import Domain, Index
from sphinx.environment import BuildEnvironment
from sphinx.environment.collectors import EnvironmentCollector
from sphinx.errors import ApplicationError, ConfigError, VersionRequirementError
from sphinx.events import EventManager
from sphinx.extension import Extension
from sphinx.highlighting import lexer_classes, lexers
from sphinx.locale import __
from sphinx.project import Project
from sphinx.registry import SphinxComponentRegistry
from sphinx.roles import XRefRole
from sphinx.theming import Theme
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 CatalogRepository
from sphinx.util.logging import prefixed_warnings
from sphinx.util.osutil import abspath, ensuredir, relpath
from sphinx.util.tags import Tags
from sphinx.util.typing import RoleFunction, TitleGetter
if False:
# For type annotation
from typing import Any, Callable, Dict, IO, Iterable, Iterator, List, Tuple, Type, Union # NOQA
from docutils import nodes # NOQA
from docutils.parsers import Parser # NOQA
from docutils.transforms import Transform # NOQA
from sphinx.builders import Builder # NOQA
from sphinx.domains import Domain, Index # NOQA
from sphinx.environment.collectors import EnvironmentCollector # NOQA
from sphinx.extension import Extension # NOQA
from sphinx.roles import XRefRole # NOQA
from sphinx.theming import Theme # NOQA
from sphinx.util.typing import RoleFunction, TitleGetter # NOQA
from docutils.nodes import Node # NOQA
from typing import Type # for python3.5.1
from sphinx.builders import Builder
builtin_extensions = (
'sphinx.addnodes',
@ -77,7 +78,9 @@ builtin_extensions = (
'sphinx.config',
'sphinx.domains.c',
'sphinx.domains.changeset',
'sphinx.domains.citation',
'sphinx.domains.cpp',
'sphinx.domains.index',
'sphinx.domains.javascript',
'sphinx.domains.math',
'sphinx.domains.python',
@ -91,18 +94,21 @@ 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',
'sphinx.environment.collectors.metadata',
'sphinx.environment.collectors.title',
'sphinx.environment.collectors.toctree',
'sphinx.environment.collectors.indexentries',
# 1st party extensions
'sphinxcontrib.applehelp',
'sphinxcontrib.devhelp',
@ -128,11 +134,11 @@ class Sphinx:
:ivar outdir: Directory for storing build documents.
"""
def __init__(self, srcdir, confdir, outdir, doctreedir, buildername,
confoverrides=None, status=sys.stdout, warning=sys.stderr,
freshenv=False, warningiserror=False, tags=None, verbosity=0,
parallel=0, keep_going=False):
# type: (str, str, str, str, str, Dict, IO, IO, bool, bool, List[str], int, int, bool) -> None # NOQA
def __init__(self, srcdir: str, confdir: str, outdir: str, doctreedir: str,
buildername: str, confoverrides: Dict = None,
status: IO = sys.stdout, warning: IO = sys.stderr,
freshenv: bool = False, warningiserror: bool = False, tags: List[str] = None,
verbosity: int = 0, parallel: int = 0, keep_going: bool = False) -> None:
self.phase = BuildPhase.INITIALIZATION
self.verbosity = verbosity
self.extensions = {} # type: Dict[str, Extension]
@ -182,7 +188,7 @@ class Sphinx:
self.warningiserror = warningiserror
logging.setup(self, self._status, self._warning)
self.events = EventManager()
self.events = EventManager(self)
# keep last few messages for traceback
# This will be filled by sphinx.util.logging.LastMessagesWriter
@ -191,6 +197,12 @@ class Sphinx:
# say hello to the world
logger.info(bold(__('Running Sphinx v%s') % sphinx.__display_version__))
# notice for parallel build on macOS and py38+
if sys.version_info > (3, 8) and platform.system() == 'Darwin' and parallel > 1:
logger.info(bold(__("For security reason, parallel mode is disabled on macOS and "
"python3.8 and above. For more details, please read "
"https://github.com/sphinx-doc/sphinx/issues/6803")))
# status code for command-line application
self.statuscode = 0
@ -249,7 +261,7 @@ class Sphinx:
# now that we know all config values, collect them from conf.py
self.config.init_values()
self.emit('config-inited', self.config)
self.events.emit('config-inited', self.config)
# create the project
self.project = Project(self.srcdir, self.config.source_suffix)
@ -260,8 +272,7 @@ class Sphinx:
# set up the builder
self._init_builder()
def _init_i18n(self):
# type: () -> None
def _init_i18n(self) -> None:
"""Load translated strings from the configured localedirs if enabled in
the configuration.
"""
@ -286,8 +297,7 @@ class Sphinx:
else:
logger.info(__('not available for built-in messages'))
def _init_env(self, freshenv):
# type: (bool) -> None
def _init_env(self, freshenv: bool) -> None:
filename = path.join(self.doctreedir, ENV_PICKLE_FILENAME)
if freshenv or not os.path.exists(filename):
self.env = BuildEnvironment()
@ -303,28 +313,24 @@ class Sphinx:
logger.info(__('failed: %s'), err)
self._init_env(freshenv=True)
def preload_builder(self, name):
# type: (str) -> None
def preload_builder(self, name: str) -> None:
self.registry.preload_builder(self, name)
def create_builder(self, name):
# type: (str) -> Builder
def create_builder(self, name: str) -> "Builder":
if name is None:
logger.info(__('No builder selected, using default: html'))
name = 'html'
return self.registry.create_builder(self, name)
def _init_builder(self):
# type: () -> None
def _init_builder(self) -> None:
self.builder.set_environment(self.env)
self.builder.init()
self.emit('builder-inited')
self.events.emit('builder-inited')
# ---- main "build" method -------------------------------------------------
def build(self, force_all=False, filenames=None):
# type: (bool, List[str]) -> None
def build(self, force_all: bool = False, filenames: List[str] = None) -> None:
self.phase = BuildPhase.READING
try:
if force_all:
@ -340,12 +346,19 @@ class Sphinx:
if self._warncount and self.keep_going:
self.statuscode = 1
status = (self.statuscode == 0 and
__('succeeded') or __('finished with problems'))
status = (__('succeeded') if self.statuscode == 0
else __('finished with problems'))
if self._warncount:
logger.info(bold(__('build %s, %s warning.',
'build %s, %s warnings.', self._warncount) %
(status, self._warncount)))
if self.warningiserror:
msg = __('build %s, %s warning (with warnings treated as errors).',
'build %s, %s warnings (with warnings treated as errors).',
self._warncount)
else:
msg = __('build %s, %s warning.',
'build %s, %s warnings.',
self._warncount)
logger.info(bold(msg % (status, self._warncount)))
else:
logger.info(bold(__('build %s.') % status))
@ -360,16 +373,15 @@ class Sphinx:
envfile = path.join(self.doctreedir, ENV_PICKLE_FILENAME)
if path.isfile(envfile):
os.unlink(envfile)
self.emit('build-finished', err)
self.events.emit('build-finished', err)
raise
else:
self.emit('build-finished', None)
self.events.emit('build-finished', None)
self.builder.cleanup()
# ---- general extensibility interface -------------------------------------
def setup_extension(self, extname):
# type: (str) -> None
def setup_extension(self, extname: str) -> None:
"""Import and setup a Sphinx extension module.
Load the extension given by the module *name*. Use this if your
@ -379,8 +391,7 @@ class Sphinx:
logger.debug('[app] setting up extension: %r', extname)
self.registry.load_extension(self, extname)
def require_sphinx(self, version):
# type: (str) -> None
def require_sphinx(self, version: str) -> None:
"""Check the Sphinx version if requested.
Compare *version* (which must be a ``major.minor`` version string, e.g.
@ -392,68 +403,53 @@ 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
def connect(self, event: str, callback: Callable, priority: int = 500) -> int:
"""Register *callback* to be called when *event* is emitted.
For details on available core events and the arguments of callback
functions, please see :ref:`events`.
Registered callbacks will be invoked on event in the order of *priority* and
registration. The priority is ascending order.
The method returns a "listener ID" that can be used as an argument to
:meth:`disconnect`.
.. versionchanged:: 3.0
Support *priority*
"""
listener_id = self.events.connect(event, callback)
logger.debug('[app] connecting event %r: %r [id=%s]', event, callback, listener_id)
listener_id = self.events.connect(event, callback, priority)
logger.debug('[app] connecting event %r (%d): %r [id=%s]',
event, priority, callback, listener_id)
return listener_id
def disconnect(self, listener_id):
# type: (int) -> None
def disconnect(self, listener_id: int) -> None:
"""Unregister callback by *listener_id*."""
logger.debug('[app] disconnecting event: [id=%s]', listener_id)
self.events.disconnect(listener_id)
def emit(self, event, *args):
# type: (str, Any) -> List
def emit(self, event: str, *args: Any) -> List:
"""Emit *event* and pass *arguments* to the callback functions.
Return the return values of all callbacks as a list. Do not emit core
Sphinx events in extensions!
"""
try:
logger.debug('[app] emitting event: %r%s', event, repr(args)[:100])
except Exception:
# not every object likes to be repr()'d (think
# random stuff coming via autodoc)
pass
return self.events.emit(event, self, *args)
return self.events.emit(event, *args)
def emit_firstresult(self, event, *args):
# type: (str, Any) -> Any
def emit_firstresult(self, event: str, *args: Any) -> Any:
"""Emit *event* and pass *arguments* to the callback functions.
Return the result of the first callback that doesn't return ``None``.
.. versionadded:: 0.5
"""
return self.events.emit_firstresult(event, self, *args)
return self.events.emit_firstresult(event, *args)
# registering addon parts
def add_builder(self, builder, override=False):
# type: (Type[Builder], bool) -> None
def add_builder(self, builder: "Type[Builder]", override: bool = False) -> None:
"""Register a new builder.
*builder* must be a class that inherits from
@ -465,8 +461,8 @@ class Sphinx:
self.registry.add_builder(builder, override=override)
# TODO(stephenfin): Describe 'types' parameter
def add_config_value(self, name, default, rebuild, types=()):
# type: (str, Any, Union[bool, str], Any) -> None
def add_config_value(self, name: str, default: Any, rebuild: Union[bool, str],
types: Any = ()) -> None:
"""Register a configuration value.
This is necessary for Sphinx to recognize new values and set default
@ -495,11 +491,10 @@ class Sphinx:
logger.debug('[app] adding config value: %r',
(name, default, rebuild) + ((types,) if types else ())) # type: ignore
if rebuild in (False, True):
rebuild = rebuild and 'env' or ''
rebuild = 'env' if rebuild else ''
self.config.add(name, default, rebuild, types)
def add_event(self, name):
# type: (str) -> None
def add_event(self, name: str) -> None:
"""Register an event called *name*.
This is needed to be able to emit it.
@ -507,8 +502,8 @@ class Sphinx:
logger.debug('[app] adding event: %r', name)
self.events.add(name)
def set_translator(self, name, translator_class, override=False):
# type: (str, Type[nodes.NodeVisitor], bool) -> None
def set_translator(self, name: str, translator_class: "Type[nodes.NodeVisitor]",
override: bool = False) -> None:
"""Register or override a Docutils translator class.
This is used to register a custom output translator or to replace a
@ -521,8 +516,8 @@ class Sphinx:
"""
self.registry.add_translator(name, translator_class, override=override)
def add_node(self, node, override=False, **kwds):
# type: (Type[nodes.Element], bool, Any) -> None
def add_node(self, node: "Type[Element]", override: bool = False,
**kwargs: Tuple[Callable, Callable]) -> None:
"""Register a Docutils node class.
This is necessary for Docutils internals. It may also be used in the
@ -552,16 +547,17 @@ class Sphinx:
.. versionchanged:: 0.5
Added the support for keyword arguments giving visit functions.
"""
logger.debug('[app] adding node: %r', (node, kwds))
logger.debug('[app] adding node: %r', (node, kwargs))
if not override and docutils.is_node_registered(node):
logger.warning(__('node class %r is already registered, '
'its visitors will be overridden'),
node.__name__, type='app', subtype='add_node')
docutils.register_node(node)
self.registry.add_translation_handlers(node, **kwds)
self.registry.add_translation_handlers(node, **kwargs)
def add_enumerable_node(self, node, figtype, title_getter=None, override=False, **kwds):
# type: (Type[nodes.Element], str, TitleGetter, bool, Any) -> None
def add_enumerable_node(self, node: "Type[Element]", figtype: str,
title_getter: TitleGetter = None, override: bool = False,
**kwargs: Tuple[Callable, Callable]) -> None:
"""Register a Docutils node class as a numfig target.
Sphinx numbers the node automatically. And then the users can refer it
@ -586,38 +582,15 @@ class Sphinx:
.. versionadded:: 1.4
"""
self.registry.add_enumerable_node(node, figtype, title_getter, override=override)
self.add_node(node, override=override, **kwds)
self.add_node(node, override=override, **kwargs)
@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: str, cls: "Type[Directive]", override: bool = False) -> 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,20 +621,14 @@ 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
def add_role(self, name: str, role: Any, override: bool = False) -> None:
"""Register a Docutils role.
*name* must be the role name that occurs in the source, *role* the role
@ -678,8 +645,7 @@ class Sphinx:
name, type='app', subtype='add_role')
docutils.register_role(name, role)
def add_generic_role(self, name, nodeclass, override=False):
# type: (str, Any, bool) -> None
def add_generic_role(self, name: str, nodeclass: Any, override: bool = False) -> None:
"""Register a generic Docutils role.
Register a Docutils role that does nothing but wrap its contents in the
@ -698,8 +664,7 @@ class Sphinx:
role = roles.GenericRole(name, nodeclass)
docutils.register_role(name, role)
def add_domain(self, domain, override=False):
# type: (Type[Domain], bool) -> None
def add_domain(self, domain: "Type[Domain]", override: bool = False) -> None:
"""Register a domain.
Make the given *domain* (which must be a class; more precisely, a
@ -711,26 +676,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: str, name: str,
cls: "Type[Directive]", override: bool = False) -> None:
"""Register a Docutils directive in a domain.
Like :meth:`add_directive`, but the directive is added to the domain
@ -740,12 +687,10 @@ 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
def add_role_to_domain(self, domain: str, name: str, role: Union[RoleFunction, XRefRole],
override: bool = False) -> None:
"""Register a Docutils role in a domain.
Like :meth:`add_role`, but the role is added to the domain named
@ -757,8 +702,8 @@ class Sphinx:
"""
self.registry.add_role_to_domain(domain, name, role, override=override)
def add_index_to_domain(self, domain, index, override=False):
# type: (str, Type[Index], bool) -> None
def add_index_to_domain(self, domain: str, index: "Type[Index]", override: bool = False
) -> None:
"""Register a custom index for a domain.
Add a custom *index* class to the domain named *domain*. *index* must
@ -770,10 +715,10 @@ class Sphinx:
"""
self.registry.add_index_to_domain(domain, index)
def add_object_type(self, directivename, rolename, indextemplate='',
parse_node=None, ref_nodeclass=None, objname='',
doc_field_types=[], override=False):
# type: (str, str, str, Callable, Type[nodes.TextElement], str, List, bool) -> None
def add_object_type(self, directivename: str, rolename: str, indextemplate: str = '',
parse_node: Callable = None, ref_nodeclass: "Type[TextElement]" = None,
objname: str = '', doc_field_types: List = [], override: bool = False
) -> None:
"""Register a new object type.
This method is a very convenient way to add a new :term:`object` type
@ -827,9 +772,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.
"""
@ -837,9 +779,9 @@ class Sphinx:
ref_nodeclass, objname, doc_field_types,
override=override)
def add_crossref_type(self, directivename, rolename, indextemplate='',
ref_nodeclass=None, objname='', override=False):
# type: (str, str, str, Type[nodes.TextElement], str, bool) -> None
def add_crossref_type(self, directivename: str, rolename: str, indextemplate: str = '',
ref_nodeclass: "Type[TextElement]" = None, objname: str = '',
override: bool = False) -> None:
"""Register a new crossref object type.
This method is very similar to :meth:`add_object_type` except that the
@ -873,8 +815,7 @@ class Sphinx:
indextemplate, ref_nodeclass, objname,
override=override)
def add_transform(self, transform):
# type: (Type[Transform]) -> None
def add_transform(self, transform: "Type[Transform]") -> None:
"""Register a Docutils transform to be applied after parsing.
Add the standard docutils :class:`Transform` subclass *transform* to
@ -907,8 +848,7 @@ class Sphinx:
""" # NOQA
self.registry.add_transform(transform)
def add_post_transform(self, transform):
# type: (Type[Transform]) -> None
def add_post_transform(self, transform: "Type[Transform]") -> None:
"""Register a Docutils transform to be applied before writing.
Add the standard docutils :class:`Transform` subclass *transform* to
@ -917,22 +857,22 @@ class Sphinx:
"""
self.registry.add_post_transform(transform)
def add_javascript(self, filename, **kwargs):
# type: (str, **str) -> None
def add_javascript(self, filename: str, **kwargs: str) -> None:
"""An alias of :meth:`add_js_file`."""
warnings.warn('The app.add_javascript() is deprecated. '
'Please use app.add_js_file() instead.',
RemovedInSphinx40Warning, stacklevel=2)
self.add_js_file(filename, **kwargs)
def add_js_file(self, filename, **kwargs):
# type: (str, **str) -> None
def add_js_file(self, filename: str, **kwargs: str) -> None:
"""Register a JavaScript file to include in the HTML output.
Add *filename* to the list of JavaScript files that the default HTML
template will include. The filename must be relative to the HTML
static path , or a full URI with scheme. The keyword arguments are
also accepted for attributes of ``<script>`` tag.
static path , or a full URI with scheme. If the keyword argument
``body`` is given, its value will be added between the
``<script>`` tags. Extra keyword arguments are included as
attributes of the ``<script>`` tag.
Example::
@ -942,6 +882,9 @@ class Sphinx:
app.add_js_file('example.js', async="async")
# => <script src="_static/example.js" async="async"></script>
app.add_js_file(None, body="var myVariable = 'foo';")
# => <script>var myVariable = 'foo';</script>
.. versionadded:: 0.5
.. versionchanged:: 1.8
@ -952,8 +895,7 @@ class Sphinx:
if hasattr(self.builder, 'add_js_file'):
self.builder.add_js_file(filename, **kwargs) # type: ignore
def add_css_file(self, filename, **kwargs):
# type: (str, **str) -> None
def add_css_file(self, filename: str, **kwargs: str) -> None:
"""Register a stylesheet to include in the HTML output.
Add *filename* to the list of CSS files that the default HTML template
@ -992,8 +934,8 @@ class Sphinx:
if hasattr(self.builder, 'add_css_file'):
self.builder.add_css_file(filename, **kwargs) # type: ignore
def add_stylesheet(self, filename, alternate=False, title=None):
# type: (str, bool, str) -> None
def add_stylesheet(self, filename: str, alternate: bool = False, title: str = None
) -> None:
"""An alias of :meth:`add_css_file`."""
warnings.warn('The app.add_stylesheet() is deprecated. '
'Please use app.add_css_file() instead.',
@ -1010,8 +952,7 @@ class Sphinx:
self.add_css_file(filename, **attributes)
def add_latex_package(self, packagename, options=None):
# type: (str, str) -> None
def add_latex_package(self, packagename: str, options: str = None) -> None:
r"""Register a package to include in the LaTeX source code.
Add *packagename* to the list of packages that LaTeX source code will
@ -1029,23 +970,26 @@ class Sphinx:
"""
self.registry.add_latex_package(packagename, options)
def add_lexer(self, alias, lexer):
# type: (str, Any) -> None
def add_lexer(self, alias: str, lexer: Union[Lexer, "Type[Lexer]"]) -> None:
"""Register a new lexer for source code.
Use *lexer*, which must be an instance of a Pygments lexer class, to
highlight code blocks with the given language *alias*.
Use *lexer* to highlight code blocks with the given language *alias*.
.. versionadded:: 0.6
.. versionchanged:: 2.1
Take a lexer class as an argument. An instance of lexers are
still supported until Sphinx-3.x.
"""
logger.debug('[app] adding lexer: %r', (alias, lexer))
from sphinx.highlighting import lexers
if lexers is None:
return
lexers[alias] = lexer
if isinstance(lexer, Lexer):
warnings.warn('app.add_lexer() API changed; '
'Please give lexer class instead instance',
RemovedInSphinx40Warning)
lexers[alias] = lexer
else:
lexer_classes[alias] = lexer
def add_autodocumenter(self, cls):
# type: (Any) -> None
def add_autodocumenter(self, cls: Any, override: bool = False) -> None:
"""Register a new documenter class for the autodoc extension.
Add *cls* as a new documenter class for the :mod:`sphinx.ext.autodoc`
@ -1057,14 +1001,16 @@ class Sphinx:
.. todo:: Add real docs for Documenter and subclassing
.. versionadded:: 0.6
.. versionchanged:: 2.2
Add *override* keyword.
"""
logger.debug('[app] adding autodocumenter: %r', cls)
from sphinx.ext.autodoc.directive import AutodocDirective
self.registry.add_documenter(cls.objtype, cls)
self.add_directive('auto' + cls.objtype, AutodocDirective)
self.add_directive('auto' + cls.objtype, AutodocDirective, override=override)
def add_autodoc_attrgetter(self, typ, getter):
# type: (Type, Callable[[Any, str, Any], Any]) -> None
def add_autodoc_attrgetter(self, typ: "Type", getter: Callable[[Any, str, Any], Any]
) -> None:
"""Register a new ``getattr``-like function for the autodoc extension.
Add *getter*, which must be a function with an interface compatible to
@ -1078,8 +1024,7 @@ class Sphinx:
logger.debug('[app] adding autodoc attrgetter: %r', (typ, getter))
self.registry.add_autodoc_attrgetter(typ, getter)
def add_search_language(self, cls):
# type: (Any) -> None
def add_search_language(self, cls: Any) -> None:
"""Register a new language for the HTML search index.
Add *cls*, which must be a subclass of
@ -1095,8 +1040,7 @@ class Sphinx:
assert issubclass(cls, SearchLanguage)
languages[cls.lang] = cls
def add_source_suffix(self, suffix, filetype, override=False):
# type: (str, str, bool) -> None
def add_source_suffix(self, suffix: str, filetype: str, override: bool = False) -> None:
"""Register a suffix of source files.
Same as :confval:`source_suffix`. The users can override this
@ -1106,8 +1050,7 @@ class Sphinx:
"""
self.registry.add_source_suffix(suffix, filetype, override=override)
def add_source_parser(self, *args, **kwargs):
# type: (Any, Any) -> None
def add_source_parser(self, *args: Any, **kwargs: Any) -> None:
"""Register a parser class.
.. versionadded:: 1.4
@ -1119,8 +1062,7 @@ class Sphinx:
"""
self.registry.add_source_parser(*args, **kwargs)
def add_env_collector(self, collector):
# type: (Type[EnvironmentCollector]) -> None
def add_env_collector(self, collector: "Type[EnvironmentCollector]") -> None:
"""Register an environment collector class.
Refer to :ref:`collector-api`.
@ -1130,8 +1072,7 @@ class Sphinx:
logger.debug('[app] adding environment collector: %r', collector)
collector().enable(self)
def add_html_theme(self, name, theme_path):
# type: (str, str) -> None
def add_html_theme(self, name: str, theme_path: str) -> None:
"""Register a HTML Theme.
The *name* is a name of theme, and *path* is a full path to the theme
@ -1142,8 +1083,9 @@ class Sphinx:
logger.debug('[app] adding HTML theme: %r, %r', name, theme_path)
self.html_themes[name] = theme_path
def add_html_math_renderer(self, name, inline_renderers=None, block_renderers=None):
# type: (str, Tuple[Callable, Callable], Tuple[Callable, Callable]) -> None
def add_html_math_renderer(self, name: str,
inline_renderers: Tuple[Callable, Callable] = None,
block_renderers: Tuple[Callable, Callable] = None) -> None:
"""Register a math renderer for HTML.
The *name* is a name of math renderer. Both *inline_renderers* and
@ -1157,8 +1099,7 @@ class Sphinx:
"""
self.registry.add_html_math_renderer(name, inline_renderers, block_renderers)
def add_message_catalog(self, catalog, locale_dir):
# type: (str, str) -> None
def add_message_catalog(self, catalog: str, locale_dir: str) -> None:
"""Register a message catalog.
The *catalog* is a name of catalog, and *locale_dir* is a base path
@ -1171,45 +1112,41 @@ class Sphinx:
locale.init_console(locale_dir, catalog)
# ---- other methods -------------------------------------------------
def is_parallel_allowed(self, typ):
# type: (str) -> bool
def is_parallel_allowed(self, typ: str) -> bool:
"""Check parallel processing is allowed or not.
``typ`` is a type of processing; ``'read'`` or ``'write'``.
"""
if typ == 'read':
attrname = 'parallel_read_safe'
message = __("the %s extension does not declare if it is safe "
"for parallel reading, assuming it isn't - please "
"ask the extension author to check and make it "
"explicit")
message_not_declared = __("the %s extension does not declare if it "
"is safe for parallel reading, assuming "
"it isn't - please ask the extension author "
"to check and make it explicit")
message_not_safe = __("the %s extension is not safe for parallel reading")
elif typ == 'write':
attrname = 'parallel_write_safe'
message = __("the %s extension does not declare if it is safe "
"for parallel writing, assuming it isn't - please "
"ask the extension author to check and make it "
"explicit")
message_not_declared = __("the %s extension does not declare if it "
"is safe for parallel writing, assuming "
"it isn't - please ask the extension author "
"to check and make it explicit")
message_not_safe = __("the %s extension is not safe for parallel writing")
else:
raise ValueError('parallel type %s is not supported' % typ)
for ext in self.extensions.values():
allowed = getattr(ext, attrname, None)
if allowed is None:
logger.warning(message, ext.name)
logger.warning(message_not_declared, ext.name)
logger.warning(__('doing serial %s'), typ)
return False
elif not allowed:
logger.warning(message_not_safe, ext.name)
logger.warning(__('doing serial %s'), typ)
return False
return True
@property
def _setting_up_extension(self):
# type: () -> List[str]
warnings.warn('app._setting_up_extension is deprecated.',
RemovedInSphinx30Warning)
return ['?']
class TemplateBridge:
"""
@ -1217,8 +1154,7 @@ class TemplateBridge:
that renders templates given a template name and a context.
"""
def init(self, builder, theme=None, dirs=None):
# type: (Builder, Theme, List[str]) -> None
def init(self, builder: "Builder", theme: Theme = None, dirs: List[str] = None) -> None:
"""Called by the builder to initialize the template system.
*builder* is the builder object; you'll probably want to look at the
@ -1229,23 +1165,20 @@ class TemplateBridge:
"""
raise NotImplementedError('must be implemented in subclasses')
def newest_template_mtime(self):
# type: () -> float
def newest_template_mtime(self) -> float:
"""Called by the builder to determine if output files are outdated
because of template changes. Return the mtime of the newest template
file that was changed. The default implementation returns ``0``.
"""
return 0
def render(self, template, context):
# type: (str, Dict) -> None
def render(self, template: str, context: Dict) -> None:
"""Called by the builder to render a template given as a filename with
a specified context (a Python dictionary).
"""
raise NotImplementedError('must be implemented in subclasses')
def render_string(self, template, context):
# type: (str, Dict) -> str
def render_string(self, template: str, context: Dict) -> str:
"""Called by the builder to render a template given as a string with a
specified context (a Python dictionary).
"""

View File

@ -4,29 +4,34 @@
Builder superclass for all builders.
:copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import pickle
import time
from os import path
from typing import Any, Dict, Iterable, List, Sequence, Set, Tuple, Union
from docutils import nodes
from docutils.nodes import Node
from sphinx.environment import CONFIG_OK, CONFIG_CHANGED_REASON
from sphinx.config import Config
from sphinx.environment import BuildEnvironment, CONFIG_OK, CONFIG_CHANGED_REASON
from sphinx.environment.adapters.asset import ImageAdapter
from sphinx.errors import SphinxError
from sphinx.events import EventManager
from sphinx.io import read_doc
from sphinx.locale import __
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 CatalogRepository, docname_to_domain
from sphinx.util.i18n import CatalogInfo, 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
from sphinx.util.tags import Tags
# side effect: registers roles and directives
from sphinx import roles # noqa
@ -39,12 +44,8 @@ except ImportError:
if False:
# For type annotation
from typing import Any, Callable, 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
from sphinx.util.i18n import CatalogInfo # NOQA
from sphinx.util.tags import Tags # NOQA
from typing import Type # for python3.5.1
from sphinx.application import Sphinx
logger = logging.getLogger(__name__)
@ -83,8 +84,7 @@ class Builder:
#: The builder supports data URIs or not.
supported_data_uri_images = False
def __init__(self, app):
# type: (Sphinx) -> None
def __init__(self, app: "Sphinx") -> None:
self.srcdir = app.srcdir
self.confdir = app.confdir
self.outdir = app.outdir
@ -93,6 +93,7 @@ class Builder:
self.app = app # type: Sphinx
self.env = None # type: BuildEnvironment
self.events = app.events # type: EventManager
self.config = app.config # type: Config
self.tags = app.tags # type: Tags
self.tags.add(self.format)
@ -111,20 +112,17 @@ class Builder:
self.parallel_ok = False
self.finish_tasks = None # type: Any
def set_environment(self, env):
# type: (BuildEnvironment) -> None
def set_environment(self, env: BuildEnvironment) -> None:
"""Store BuildEnvironment object."""
self.env = env
self.env.set_versioning_method(self.versioning_method,
self.versioning_compare)
def get_translator_class(self, *args):
# type: (Any) -> Type[nodes.NodeVisitor]
def get_translator_class(self, *args: Any) -> "Type[nodes.NodeVisitor]":
"""Return a class of translator."""
return self.app.registry.get_translator_class(self)
def create_translator(self, *args):
# type: (Any) -> nodes.NodeVisitor
def create_translator(self, *args: Any) -> nodes.NodeVisitor:
"""Return an instance of translator.
This method returns an instance of ``default_translator_class`` by default.
@ -133,15 +131,13 @@ class Builder:
return self.app.registry.create_translator(self, *args)
# helper methods
def init(self):
# type: () -> None
def init(self) -> None:
"""Load necessary templates and perform initialization. The default
implementation does nothing.
"""
pass
def create_template_bridge(self):
# type: () -> None
def create_template_bridge(self) -> None:
"""Return the template bridge configured."""
if self.config.template_bridge:
self.templates = import_object(self.config.template_bridge,
@ -150,8 +146,7 @@ class Builder:
from sphinx.jinja2glue import BuiltinTemplateLoader
self.templates = BuiltinTemplateLoader()
def get_target_uri(self, docname, typ=None):
# type: (str, str) -> str
def get_target_uri(self, docname: str, typ: str = None) -> str:
"""Return the target URI for a document name.
*typ* can be used to qualify the link characteristic for individual
@ -159,8 +154,7 @@ class Builder:
"""
raise NotImplementedError
def get_relative_uri(self, from_, to, typ=None):
# type: (str, str, str) -> str
def get_relative_uri(self, from_: str, to: str, typ: str = None) -> str:
"""Return a relative URI between two source filenames.
May raise environment.NoUri if there's no way to return a sensible URI.
@ -168,8 +162,7 @@ class Builder:
return relative_uri(self.get_target_uri(from_),
self.get_target_uri(to, typ))
def get_outdated_docs(self):
# type: () -> Union[str, Iterable[str]]
def get_outdated_docs(self) -> Union[str, Iterable[str]]:
"""Return an iterable of output files that are outdated, or a string
describing what an update build will build.
@ -179,13 +172,11 @@ class Builder:
"""
raise NotImplementedError
def get_asset_paths(self):
# type: () -> List[str]
def get_asset_paths(self) -> List[str]:
"""Return list of paths for assets (ex. templates, CSS, etc.)."""
return []
def post_process_images(self, doctree):
# type: (nodes.Node) -> None
def post_process_images(self, doctree: Node) -> None:
"""Pick the best candidate for all image URIs."""
images = ImageAdapter(self.env)
for node in doctree.traverse(nodes.image):
@ -218,13 +209,11 @@ class Builder:
# compile po methods
def compile_catalogs(self, catalogs, message):
# type: (Set[CatalogInfo], str) -> None
def compile_catalogs(self, catalogs: Set[CatalogInfo], message: str) -> None:
if not self.config.gettext_auto_build:
return
def cat2relpath(cat):
# type: (CatalogInfo) -> str
def cat2relpath(cat: CatalogInfo) -> str:
return relpath(cat.mo_path, self.env.srcdir).replace(path.sep, SEP)
logger.info(bold(__('building [mo]: ')) + message)
@ -233,17 +222,14 @@ class Builder:
stringify_func=cat2relpath):
catalog.write_mo(self.config.language)
def compile_all_catalogs(self):
# type: () -> None
def compile_all_catalogs(self) -> None:
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
def to_domain(fpath):
# type: (str) -> str
def compile_specific_catalogs(self, specified_files: List[str]) -> None:
def to_domain(fpath: str) -> str:
docname = self.env.path2doc(path.abspath(fpath))
if docname:
return docname_to_domain(docname, self.config.gettext_compact)
@ -260,8 +246,7 @@ class Builder:
message = __('targets for %d po files that are specified') % len(catalogs)
self.compile_catalogs(catalogs, message)
def compile_update_catalogs(self):
# type: () -> None
def compile_update_catalogs(self) -> None:
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()}
@ -270,13 +255,11 @@ class Builder:
# build methods
def build_all(self):
# type: () -> None
def build_all(self) -> None:
"""Build all source files."""
self.build(None, summary=__('all source files'), method='all')
def build_specific(self, filenames):
# type: (List[str]) -> None
def build_specific(self, filenames: List[str]) -> None:
"""Only rebuild as much as needed for changes in the *filenames*."""
# bring the filenames to the canonical format, that is,
# relative to the source directory and without source_suffix.
@ -304,8 +287,7 @@ class Builder:
self.build(to_write, method='specific',
summary=__('%d source files given on command line') % len(to_write))
def build_update(self):
# type: () -> None
def build_update(self) -> None:
"""Only rebuild what was changed or added since last build."""
to_build = self.get_outdated_docs()
if isinstance(to_build, str):
@ -316,14 +298,13 @@ class Builder:
summary=__('targets for %d source files that are out of date') %
len(to_build))
def build(self, docnames, summary=None, method='update'):
# type: (Iterable[str], str, str) -> None
def build(self, docnames: Iterable[str], summary: str = None, method: str = 'update') -> None: # NOQA
"""Main build method.
First updates the environment, and then calls :meth:`write`.
"""
if summary:
logger.info(bold(__('building [%s]') % self.name) + ': ' + summary)
logger.info(bold(__('building [%s]: ') % self.name) + summary)
# while reading, collect all warnings from docutils
with logging.pending_warnings():
@ -385,8 +366,7 @@ class Builder:
# wait for all tasks
self.finish_tasks.join()
def read(self):
# type: () -> List[str]
def read(self) -> List[str]:
"""(Re-)read all files new or changed since last update.
Store all environment docnames in the canonical format (ie using SEP as
@ -399,7 +379,7 @@ class Builder:
added, changed, removed = self.env.get_outdated_files(updated)
# allow user intervention as well
for docs in self.app.emit('env-get-outdated', self, added, changed, removed):
for docs in self.events.emit('env-get-outdated', self, added, changed, removed):
changed.update(set(docs) & self.env.found_docs)
# if files were added or removed, all documents with globbed toctrees
@ -408,21 +388,23 @@ class Builder:
# ... but not those that already were removed
changed.update(self.env.glob_toctrees & self.env.found_docs)
if changed:
reason = CONFIG_CHANGED_REASON.get(self.env.config_status, '')
if updated: # explain the change iff build config status was not ok
reason = (CONFIG_CHANGED_REASON.get(self.env.config_status, '') +
(self.env.config_status_extra or ''))
logger.info('[%s] ', reason, nonl=True)
logger.info(__('%s added, %s changed, %s removed'),
len(added), len(changed), len(removed))
# clear all files no longer present
for docname in removed:
self.app.emit('env-purge-doc', self.env, docname)
self.events.emit('env-purge-doc', self.env, docname)
self.env.clear_doc(docname)
# read all new and changed files
docnames = sorted(added | changed)
# allow changing and reordering the list of docs to read
self.app.emit('env-before-read-docs', self.env, docnames)
self.events.emit('env-before-read-docs', self.env, docnames)
# check if we should do parallel or serial read
if parallel_available and len(docnames) > 5 and self.app.parallel > 1:
@ -439,7 +421,7 @@ class Builder:
raise SphinxError('master file %s not found' %
self.env.doc2path(self.config.master_doc))
for retval in self.app.emit('env-updated', self.env):
for retval in self.events.emit('env-updated', self.env):
if retval is not None:
docnames.extend(retval)
@ -448,32 +430,28 @@ class Builder:
return sorted(docnames)
def _read_serial(self, docnames):
# type: (List[str]) -> None
def _read_serial(self, docnames: List[str]) -> None:
for docname in status_iterator(docnames, __('reading sources... '), "purple",
len(docnames), self.app.verbosity):
# remove all inventory entries for that file
self.app.emit('env-purge-doc', self.env, docname)
self.events.emit('env-purge-doc', self.env, docname)
self.env.clear_doc(docname)
self.read_doc(docname)
def _read_parallel(self, docnames, nproc):
# type: (List[str], int) -> None
def _read_parallel(self, docnames: List[str], nproc: int) -> None:
# clear all outdated docs at once
for docname in docnames:
self.app.emit('env-purge-doc', self.env, docname)
self.events.emit('env-purge-doc', self.env, docname)
self.env.clear_doc(docname)
def read_process(docs):
# type: (List[str]) -> bytes
def read_process(docs: List[str]) -> bytes:
self.env.app = self.app
for docname in docs:
self.read_doc(docname)
# allow pickling self to send it back
return pickle.dumps(self.env, pickle.HIGHEST_PROTOCOL)
def merge(docs, otherenv):
# type: (List[str], bytes) -> None
def merge(docs: List[str], otherenv: bytes) -> None:
env = pickle.loads(otherenv)
self.env.merge_info_from(docs, env, self.app)
@ -488,8 +466,7 @@ class Builder:
logger.info(bold(__('waiting for workers...')))
tasks.join()
def read_doc(self, docname):
# type: (str) -> None
def read_doc(self, docname: str) -> None:
"""Parse a file and add/update inventory entries for the doctree."""
self.env.prepare_settings(docname)
@ -514,8 +491,7 @@ class Builder:
self.write_doctree(docname, doctree)
def write_doctree(self, docname, doctree):
# type: (str, nodes.document) -> None
def write_doctree(self, docname: str, doctree: nodes.document) -> None:
"""Write the doctree to a file."""
# make it picklable
doctree.reporter = None
@ -529,8 +505,7 @@ class Builder:
with open(doctree_filename, 'wb') as f:
pickle.dump(doctree, f, pickle.HIGHEST_PROTOCOL)
def write(self, build_docnames, updated_docnames, method='update'):
# type: (Iterable[str], Sequence[str], str) -> None
def write(self, build_docnames: Iterable[str], updated_docnames: Sequence[str], method: str = 'update') -> None: # NOQA
if build_docnames is None or build_docnames == ['__all__']:
# build_all
build_docnames = self.env.found_docs
@ -559,8 +534,7 @@ class Builder:
else:
self._write_serial(sorted(docnames))
def _write_serial(self, docnames):
# type: (Sequence[str]) -> None
def _write_serial(self, docnames: Sequence[str]) -> None:
with logging.pending_warnings():
for docname in status_iterator(docnames, __('writing output... '), "darkgreen",
len(docnames), self.app.verbosity):
@ -570,10 +544,8 @@ class Builder:
self.write_doc_serialized(docname, doctree)
self.write_doc(docname, doctree)
def _write_parallel(self, docnames, nproc):
# type: (Sequence[str], int) -> None
def write_process(docs):
# type: (List[Tuple[str, nodes.document]]) -> None
def _write_parallel(self, docnames: Sequence[str], nproc: int) -> None:
def write_process(docs: List[Tuple[str, nodes.document]]) -> None:
self.app.phase = BuildPhase.WRITING
for docname, doctree in docs:
self.write_doc(docname, doctree)
@ -603,41 +575,35 @@ class Builder:
logger.info(bold(__('waiting for workers...')))
tasks.join()
def prepare_writing(self, docnames):
# type: (Set[str]) -> None
def prepare_writing(self, docnames: Set[str]) -> None:
"""A place where you can add logic before :meth:`write_doc` is run"""
raise NotImplementedError
def write_doc(self, docname, doctree):
# type: (str, nodes.document) -> None
def write_doc(self, docname: str, doctree: nodes.document) -> None:
"""Where you actually write something to the filesystem."""
raise NotImplementedError
def write_doc_serialized(self, docname, doctree):
# type: (str, nodes.document) -> None
def write_doc_serialized(self, docname: str, doctree: nodes.document) -> None:
"""Handle parts of write_doc that must be called in the main process
if parallel build is active.
"""
pass
def finish(self):
# type: () -> None
def finish(self) -> None:
"""Finish the building process.
The default implementation does nothing.
"""
pass
def cleanup(self):
# type: () -> None
def cleanup(self) -> None:
"""Cleanup any resources.
The default implementation does nothing.
"""
pass
def get_builder_config(self, option, default):
# type: (str, str) -> Any
def get_builder_config(self, option: str, default: str) -> Any:
"""Return a builder specific option.
This method allows customization of common builder settings by

View File

@ -4,18 +4,21 @@
Base class of epub2/epub3 builders.
:copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import html
import os
import re
import warnings
from collections import namedtuple
from os import path
from typing import Any, Dict, List, Set, Tuple
from zipfile import ZIP_DEFLATED, ZIP_STORED, ZipFile
from docutils import nodes
from docutils.nodes import Element, Node
from docutils.utils import smartquotes
from sphinx import addnodes
@ -33,11 +36,6 @@ try:
except ImportError:
Image = None
if False:
# For type annotation
from typing import Any, Dict, List, Set, Tuple # NOQA
from sphinx.application import Sphinx # NOQA
logger = logging.getLogger(__name__)
@ -93,8 +91,7 @@ Guide = namedtuple('Guide', ['type', 'title', 'uri'])
NavPoint = namedtuple('NavPoint', ['navpoint', 'playorder', 'text', 'refuri', 'children'])
def sphinx_smarty_pants(t, language='en'):
# type: (str, str) -> str
def sphinx_smarty_pants(t: str, language: str = 'en') -> str:
t = t.replace('&quot;', '"')
t = smartquotes.educateDashesOldSchool(t)
t = smartquotes.educateQuotes(t, language)
@ -145,8 +142,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
template_dir = ""
doctype = ""
def init(self):
# type: () -> None
def init(self) -> None:
super().init()
# the output files for epub must be .html only
self.out_suffix = '.xhtml'
@ -157,17 +153,14 @@ class EpubBuilder(StandaloneHTMLBuilder):
self.use_index = self.get_builder_config('use_index', 'epub')
self.refnodes = [] # type: List[Dict[str, Any]]
def create_build_info(self):
# type: () -> BuildInfo
def create_build_info(self) -> BuildInfo:
return BuildInfo(self.config, self.tags, ['html', 'epub'])
def get_theme_config(self):
# type: () -> Tuple[str, Dict]
def get_theme_config(self) -> Tuple[str, Dict]:
return self.config.epub_theme, self.config.epub_theme_options
# generic support functions
def make_id(self, name):
# type: (str) -> str
def make_id(self, name: str) -> str:
# id_cache is intentionally mutable
"""Return a unique id for name."""
id = self.id_cache.get(name)
@ -176,10 +169,11 @@ class EpubBuilder(StandaloneHTMLBuilder):
self.id_cache[name] = id
return id
def esc(self, name):
# type: (str) -> str
def esc(self, name: str) -> str:
"""Replace all characters not allowed in text an attribute values."""
# Like cgi.escape, but also replace apostrophe
warnings.warn(
'%s.esc() is deprecated. Use html.escape() instead.' % self.__class__.__name__,
RemovedInSphinx40Warning)
name = name.replace('&', '&amp;')
name = name.replace('<', '&lt;')
name = name.replace('>', '&gt;')
@ -187,8 +181,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
name = name.replace('\'', '&#39;')
return name
def get_refnodes(self, doctree, result):
# type: (nodes.Node, List[Dict[str, Any]]) -> List[Dict[str, Any]]
def get_refnodes(self, doctree: Node, result: List[Dict[str, Any]]) -> List[Dict[str, Any]]: # NOQA
"""Collect section titles, their depth in the toc and the refuri."""
# XXX: is there a better way than checking the attribute
# toctree-l[1-8] on the parent node?
@ -202,8 +195,8 @@ class EpubBuilder(StandaloneHTMLBuilder):
if (self.toctree_template % level) in classes:
result.append({
'level': level,
'refuri': self.esc(refuri),
'text': ssp(self.esc(doctree.astext()))
'refuri': html.escape(refuri),
'text': ssp(html.escape(doctree.astext()))
})
break
elif isinstance(doctree, nodes.Element):
@ -211,8 +204,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
result = self.get_refnodes(elem, result)
return result
def check_refnodes(self, nodes):
# type: (List[Dict[str, Any]]) -> None
def check_refnodes(self, nodes: List[Dict[str, Any]]) -> None:
appeared = set() # type: Set[str]
for node in nodes:
if node['refuri'] in appeared:
@ -220,8 +212,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
else:
appeared.add(node['refuri'])
def get_toc(self):
# type: () -> None
def get_toc(self) -> None:
"""Get the total table of contents, containing the master_doc
and pre and post files not managed by sphinx.
"""
@ -236,36 +227,33 @@ class EpubBuilder(StandaloneHTMLBuilder):
item['refuri'] = master_dir + item['refuri']
self.toc_add_files(self.refnodes)
def toc_add_files(self, refnodes):
# type: (List[Dict[str, Any]]) -> None
def toc_add_files(self, refnodes: List[Dict[str, Any]]) -> None:
"""Add the master_doc, pre and post files to a list of refnodes.
"""
refnodes.insert(0, {
'level': 1,
'refuri': self.esc(self.config.master_doc + self.out_suffix),
'text': ssp(self.esc(
'refuri': html.escape(self.config.master_doc + self.out_suffix),
'text': ssp(html.escape(
self.env.titles[self.config.master_doc].astext()))
})
for file, text in reversed(self.config.epub_pre_files):
refnodes.insert(0, {
'level': 1,
'refuri': self.esc(file),
'text': ssp(self.esc(text))
'refuri': html.escape(file),
'text': ssp(html.escape(text))
})
for file, text in self.config.epub_post_files:
refnodes.append({
'level': 1,
'refuri': self.esc(file),
'text': ssp(self.esc(text))
'refuri': html.escape(file),
'text': ssp(html.escape(text))
})
def fix_fragment(self, prefix, fragment):
# type: (str, str) -> str
def fix_fragment(self, prefix: str, fragment: str) -> str:
"""Return a href/id attribute with colons replaced by hyphens."""
return prefix + fragment.replace(':', '-')
def fix_ids(self, tree):
# type: (nodes.document) -> None
def fix_ids(self, tree: nodes.document) -> None:
"""Replace colons with hyphens in href and id attributes.
Some readers crash because they interpret the part as a
@ -284,7 +272,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
if ':' in node_id:
target['ids'][i] = self.fix_fragment('', node_id)
next_node = target.next_node(siblings=True) # type: nodes.Node
next_node = target.next_node(ascend=True) # type: Node
if isinstance(next_node, nodes.Element):
for i, node_id in enumerate(next_node['ids']):
if ':' in node_id:
@ -297,20 +285,17 @@ class EpubBuilder(StandaloneHTMLBuilder):
newids.append(self.fix_fragment('', id))
desc_signature.attributes['ids'] = newids
def add_visible_links(self, tree, show_urls='inline'):
# type: (nodes.document, str) -> None
def add_visible_links(self, tree: nodes.document, show_urls: str = 'inline') -> None:
"""Add visible link targets for external links"""
def make_footnote_ref(doc, label):
# type: (nodes.document, str) -> nodes.footnote_reference
def make_footnote_ref(doc: nodes.document, label: str) -> nodes.footnote_reference:
"""Create a footnote_reference node with children"""
footnote_ref = nodes.footnote_reference('[#]_')
footnote_ref.append(nodes.Text(label))
doc.note_autofootnote_ref(footnote_ref)
return footnote_ref
def make_footnote(doc, label, uri):
# type: (nodes.document, str, str) -> nodes.footnote
def make_footnote(doc: nodes.document, label: str, uri: str) -> nodes.footnote:
"""Create a footnote node with children"""
footnote = nodes.footnote(uri)
para = nodes.paragraph()
@ -320,8 +305,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
doc.note_autofootnote(footnote)
return footnote
def footnote_spot(tree):
# type: (nodes.document) -> Tuple[nodes.Element, int]
def footnote_spot(tree: nodes.document) -> Tuple[Element, int]:
"""Find or create a spot to place footnotes.
The function returns the tuple (parent, index)."""
@ -369,8 +353,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
footnote.add_backref(footnote_ref['ids'][0])
fn_idx += 1
def write_doc(self, docname, doctree):
# type: (str, nodes.document) -> None
def write_doc(self, docname: str, doctree: nodes.document) -> None:
"""Write one document file.
This method is overwritten in order to fix fragment identifiers
@ -380,8 +363,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
self.add_visible_links(doctree, self.config.epub_show_urls)
super().write_doc(docname, doctree)
def fix_genindex(self, tree):
# type: (List[Tuple[str, List[Tuple[str, Any]]]]) -> None
def fix_genindex(self, tree: List[Tuple[str, List[Tuple[str, Any]]]]) -> None:
"""Fix href attributes for genindex pages."""
# XXX: modifies tree inline
# Logic modeled from themes/basic/genindex.html
@ -399,14 +381,12 @@ class EpubBuilder(StandaloneHTMLBuilder):
subentrylinks[i] = (ismain,
self.fix_fragment(m.group(1), m.group(2)))
def is_vector_graphics(self, filename):
# type: (str) -> bool
def is_vector_graphics(self, filename: str) -> bool:
"""Does the filename extension indicate a vector graphic format?"""
ext = path.splitext(filename)[-1]
return ext in VECTOR_GRAPHICS_EXTENSIONS
def copy_image_files_pil(self):
# type: () -> None
def copy_image_files_pil(self) -> None:
"""Copy images using Pillow, the Python Imaging Libary.
The method tries to read and write the files with Pillow, converting
the format and resizing the image if necessary/possible.
@ -445,8 +425,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
logger.warning(__('cannot write image file %r: %s'),
path.join(self.srcdir, src), err)
def copy_image_files(self):
# type: () -> None
def copy_image_files(self) -> None:
"""Copy image files to destination directory.
This overwritten method can use Pillow to convert image files.
"""
@ -460,13 +439,11 @@ class EpubBuilder(StandaloneHTMLBuilder):
else:
super().copy_image_files()
def copy_download_files(self):
# type: () -> None
def copy_download_files(self) -> None:
pass
def handle_page(self, pagename, addctx, templatename='page.html',
outfilename=None, event_arg=None):
# type: (str, Dict, str, str, Any) -> None
def handle_page(self, pagename: str, addctx: Dict, templatename: str = 'page.html',
outfilename: str = None, event_arg: Any = None) -> None:
"""Create a rendered page.
This method is overwritten for genindex pages in order to fix href link
@ -479,8 +456,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
addctx['doctype'] = self.doctype
super().handle_page(pagename, addctx, templatename, outfilename, event_arg)
def build_mimetype(self, outdir=None, outname='mimetype'):
# type: (str, str) -> None
def build_mimetype(self, outdir: str = None, outname: str = 'mimetype') -> None:
"""Write the metainfo file mimetype."""
if outdir:
warnings.warn('The arguments of EpubBuilder.build_mimetype() is deprecated.',
@ -492,8 +468,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
copy_asset_file(path.join(self.template_dir, 'mimetype'),
path.join(outdir, outname))
def build_container(self, outdir=None, outname='META-INF/container.xml'):
# type: (str, str) -> None
def build_container(self, outdir: str = None, outname: str = 'META-INF/container.xml') -> None: # NOQA
"""Write the metainfo file META-INF/container.xml."""
if outdir:
warnings.warn('The arguments of EpubBuilder.build_container() is deprecated.',
@ -506,28 +481,26 @@ class EpubBuilder(StandaloneHTMLBuilder):
ensuredir(path.dirname(filename))
copy_asset_file(path.join(self.template_dir, 'container.xml'), filename)
def content_metadata(self):
# type: () -> Dict[str, Any]
def content_metadata(self) -> Dict[str, Any]:
"""Create a dictionary with all metadata for the content.opf
file properly escaped.
"""
metadata = {} # type: Dict[str, Any]
metadata['title'] = self.esc(self.config.epub_title)
metadata['author'] = self.esc(self.config.epub_author)
metadata['uid'] = self.esc(self.config.epub_uid)
metadata['lang'] = self.esc(self.config.epub_language)
metadata['publisher'] = self.esc(self.config.epub_publisher)
metadata['copyright'] = self.esc(self.config.epub_copyright)
metadata['scheme'] = self.esc(self.config.epub_scheme)
metadata['id'] = self.esc(self.config.epub_identifier)
metadata['date'] = self.esc(format_date("%Y-%m-%d"))
metadata['title'] = html.escape(self.config.epub_title)
metadata['author'] = html.escape(self.config.epub_author)
metadata['uid'] = html.escape(self.config.epub_uid)
metadata['lang'] = html.escape(self.config.epub_language)
metadata['publisher'] = html.escape(self.config.epub_publisher)
metadata['copyright'] = html.escape(self.config.epub_copyright)
metadata['scheme'] = html.escape(self.config.epub_scheme)
metadata['id'] = html.escape(self.config.epub_identifier)
metadata['date'] = html.escape(format_date("%Y-%m-%d"))
metadata['manifest_items'] = []
metadata['spines'] = []
metadata['guides'] = []
return metadata
def build_content(self, outdir=None, outname='content.opf'):
# type: (str, str) -> None
def build_content(self, outdir: str = None, outname: str = 'content.opf') -> None:
"""Write the metainfo file content.opf It contains bibliographic data,
a file list and the spine (the reading order).
"""
@ -567,9 +540,9 @@ class EpubBuilder(StandaloneHTMLBuilder):
type='epub', subtype='unknown_project_files')
continue
filename = filename.replace(os.sep, '/')
item = ManifestItem(self.esc(filename),
self.esc(self.make_id(filename)),
self.esc(self.media_types[ext]))
item = ManifestItem(html.escape(filename),
html.escape(self.make_id(filename)),
html.escape(self.media_types[ext]))
metadata['manifest_items'].append(item)
self.files.append(filename)
@ -580,21 +553,21 @@ class EpubBuilder(StandaloneHTMLBuilder):
continue
if refnode['refuri'] in self.ignored_files:
continue
spine = Spine(self.esc(self.make_id(refnode['refuri'])), True)
spine = Spine(html.escape(self.make_id(refnode['refuri'])), True)
metadata['spines'].append(spine)
spinefiles.add(refnode['refuri'])
for info in self.domain_indices:
spine = Spine(self.esc(self.make_id(info[0] + self.out_suffix)), True)
spine = Spine(html.escape(self.make_id(info[0] + self.out_suffix)), True)
metadata['spines'].append(spine)
spinefiles.add(info[0] + self.out_suffix)
if self.use_index:
spine = Spine(self.esc(self.make_id('genindex' + self.out_suffix)), True)
spine = Spine(html.escape(self.make_id('genindex' + self.out_suffix)), True)
metadata['spines'].append(spine)
spinefiles.add('genindex' + self.out_suffix)
# add auto generated files
for name in self.files:
if name not in spinefiles and name.endswith(self.out_suffix):
spine = Spine(self.esc(self.make_id(name)), False)
spine = Spine(html.escape(self.make_id(name)), False)
metadata['spines'].append(spine)
# add the optional cover
@ -602,18 +575,18 @@ class EpubBuilder(StandaloneHTMLBuilder):
if self.config.epub_cover:
image, html_tmpl = self.config.epub_cover
image = image.replace(os.sep, '/')
metadata['cover'] = self.esc(self.make_id(image))
metadata['cover'] = html.escape(self.make_id(image))
if html_tmpl:
spine = Spine(self.esc(self.make_id(self.coverpage_name)), True)
spine = Spine(html.escape(self.make_id(self.coverpage_name)), True)
metadata['spines'].insert(0, spine)
if self.coverpage_name not in self.files:
ext = path.splitext(self.coverpage_name)[-1]
self.files.append(self.coverpage_name)
item = ManifestItem(self.esc(self.coverpage_name),
self.esc(self.make_id(self.coverpage_name)),
self.esc(self.media_types[ext]))
item = ManifestItem(html.escape(self.coverpage_name),
html.escape(self.make_id(self.coverpage_name)),
html.escape(self.media_types[ext]))
metadata['manifest_items'].append(item)
ctx = {'image': self.esc(image), 'title': self.config.project}
ctx = {'image': html.escape(image), 'title': self.config.project}
self.handle_page(
path.splitext(self.coverpage_name)[0], ctx, html_tmpl)
spinefiles.add(self.coverpage_name)
@ -629,25 +602,24 @@ class EpubBuilder(StandaloneHTMLBuilder):
auto_add_cover = False
if type == 'toc':
auto_add_toc = False
metadata['guides'].append(Guide(self.esc(type),
self.esc(title),
self.esc(uri)))
metadata['guides'].append(Guide(html.escape(type),
html.escape(title),
html.escape(uri)))
if auto_add_cover and html_tmpl:
metadata['guides'].append(Guide('cover',
self.guide_titles['cover'],
self.esc(self.coverpage_name)))
html.escape(self.coverpage_name)))
if auto_add_toc and self.refnodes:
metadata['guides'].append(Guide('toc',
self.guide_titles['toc'],
self.esc(self.refnodes[0]['refuri'])))
html.escape(self.refnodes[0]['refuri'])))
# write the project file
copy_asset_file(path.join(self.template_dir, 'content.opf_t'),
path.join(outdir, outname),
metadata)
def new_navpoint(self, node, level, incr=True):
# type: (Dict[str, Any], int, bool) -> NavPoint
def new_navpoint(self, node: Dict[str, Any], level: int, incr: bool = True) -> NavPoint:
"""Create a new entry in the toc from the node at given level."""
# XXX Modifies the node
if incr:
@ -656,8 +628,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
return NavPoint('navPoint%d' % self.tocid, self.playorder,
node['text'], node['refuri'], [])
def build_navpoints(self, nodes):
# type: (List[Dict[str, Any]]) -> List[NavPoint]
def build_navpoints(self, nodes: List[Dict[str, Any]]) -> List[NavPoint]:
"""Create the toc navigation structure.
Subelements of a node are nested inside the navpoint. For nested nodes
@ -701,20 +672,18 @@ class EpubBuilder(StandaloneHTMLBuilder):
return navstack[0].children
def toc_metadata(self, level, navpoints):
# type: (int, List[NavPoint]) -> Dict[str, Any]
def toc_metadata(self, level: int, navpoints: List[NavPoint]) -> Dict[str, Any]:
"""Create a dictionary with all metadata for the toc.ncx file
properly escaped.
"""
metadata = {} # type: Dict[str, Any]
metadata['uid'] = self.config.epub_uid
metadata['title'] = self.esc(self.config.epub_title)
metadata['title'] = html.escape(self.config.epub_title)
metadata['level'] = level
metadata['navpoints'] = navpoints
return metadata
def build_toc(self, outdir=None, outname='toc.ncx'):
# type: (str, str) -> None
def build_toc(self, outdir: str = None, outname: str = 'toc.ncx') -> None:
"""Write the metainfo file toc.ncx."""
if outdir:
warnings.warn('The arguments of EpubBuilder.build_toc() is deprecated.',
@ -741,8 +710,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
path.join(outdir, outname),
self.toc_metadata(level, navpoints))
def build_epub(self, outdir=None, outname=None):
# type: (str, str) -> None
def build_epub(self, outdir: str = None, outname: str = None) -> None:
"""Write the epub file.
It is a zip file with the mimetype file stored uncompressed as the first

View File

@ -4,11 +4,12 @@
Build Apple help books.
:copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import warnings
from typing import Any, Dict
from sphinxcontrib.applehelp import (
AppleHelpCodeSigningFailed,
@ -16,13 +17,9 @@ from sphinxcontrib.applehelp import (
AppleHelpBuilder,
)
from sphinx.application import Sphinx
from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
if False:
# For type annotation
from typing import Any, Dict # NOQA
from sphinx.application import Sphinx # NOQA
deprecated_alias('sphinx.builders.applehelp',
{
@ -33,8 +30,7 @@ deprecated_alias('sphinx.builders.applehelp',
RemovedInSphinx40Warning)
def setup(app):
# type: (Sphinx) -> Dict[str, Any]
def setup(app: Sphinx) -> Dict[str, Any]:
warnings.warn('sphinx.builders.applehelp has been moved to sphinxcontrib-applehelp.',
RemovedInSphinx40Warning)
app.setup_extension('sphinxcontrib.applehelp')

View File

@ -4,15 +4,17 @@
Changelog builder.
:copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import html
from os import path
from typing import Any, Dict, List, Tuple
from typing import cast
from sphinx import package_dir
from sphinx.application import Sphinx
from sphinx.builders import Builder
from sphinx.domains.changeset import ChangeSetDomain
from sphinx.locale import _, __
@ -22,11 +24,6 @@ from sphinx.util.console import bold # type: ignore
from sphinx.util.fileutil import copy_asset_file
from sphinx.util.osutil import ensuredir, os_path
if False:
# For type annotation
from typing import Any, Dict, List, Tuple # NOQA
from sphinx.application import Sphinx # NOQA
logger = logging.getLogger(__name__)
@ -38,15 +35,13 @@ class ChangesBuilder(Builder):
name = 'changes'
epilog = __('The overview file is in %(outdir)s.')
def init(self):
# type: () -> None
def init(self) -> None:
self.create_template_bridge()
theme_factory = HTMLThemeFactory(self.app)
self.theme = theme_factory.create('default')
self.templates.init(self, self.theme)
def get_outdated_docs(self):
# type: () -> str
def get_outdated_docs(self) -> str:
return self.outdir
typemap = {
@ -55,8 +50,7 @@ class ChangesBuilder(Builder):
'deprecated': 'deprecated',
}
def write(self, *ignored):
# type: (Any) -> None
def write(self, *ignored: Any) -> None:
version = self.config.version
domain = cast(ChangeSetDomain, self.env.get_domain('changeset'))
libchanges = {} # type: Dict[str, List[Tuple[str, str, int]]]
@ -83,8 +77,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:
@ -122,8 +115,7 @@ class ChangesBuilder(Builder):
'.. versionchanged:: %s' % version,
'.. deprecated:: %s' % version]
def hl(no, line):
# type: (int, str) -> str
def hl(no: int, line: str) -> str:
line = '<a name="L%s"> </a>' % no + html.escape(line)
for x in hltext:
if x in line:
@ -149,28 +141,25 @@ 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'),
self.outdir)
def hl(self, text, version):
# type: (str, str) -> str
def hl(self, text: str, version: str) -> str:
text = html.escape(text)
for directive in ['versionchanged', 'versionadded', 'deprecated']:
text = text.replace('.. %s:: %s' % (directive, version),
'<b>.. %s:: %s</b>' % (directive, version))
return text
def finish(self):
# type: () -> None
def finish(self) -> None:
pass
def setup(app):
# type: (Sphinx) -> Dict[str, Any]
def setup(app: Sphinx) -> Dict[str, Any]:
app.add_builder(ChangesBuilder)
return {

View File

@ -6,23 +6,19 @@
.. _Devhelp: https://wiki.gnome.org/Apps/Devhelp
:copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import warnings
from typing import Any, Dict
from sphinxcontrib.devhelp import DevhelpBuilder
from sphinx.application import Sphinx
from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
if False:
# For type annotation
from typing import Any, Dict # NOQA
from sphinx.application import Sphinx # NOQA
deprecated_alias('sphinx.builders.devhelp',
{
'DevhelpBuilder': DevhelpBuilder,
@ -30,8 +26,7 @@ deprecated_alias('sphinx.builders.devhelp',
RemovedInSphinx40Warning)
def setup(app):
# type: (Sphinx) -> Dict[str, Any]
def setup(app: Sphinx) -> Dict[str, Any]:
warnings.warn('sphinx.builders.devhelp has been moved to sphinxcontrib-devhelp.',
RemovedInSphinx40Warning)
app.setup_extension('sphinxcontrib.devhelp')

View File

@ -4,21 +4,19 @@
Directory HTML builders.
:copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from os import path
from typing import Any, Dict
from sphinx.application import Sphinx
from sphinx.builders.html import StandaloneHTMLBuilder
from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
from sphinx.util import logging
from sphinx.util.osutil import SEP, os_path
if False:
# For type annotation
from typing import Any, Dict, Set # NOQA
from sphinx.application import Sphinx # NOQA
logger = logging.getLogger(__name__)
@ -30,16 +28,14 @@ class DirectoryHTMLBuilder(StandaloneHTMLBuilder):
"""
name = 'dirhtml'
def get_target_uri(self, docname, typ=None):
# type: (str, str) -> str
def get_target_uri(self, docname: str, typ: str = None) -> str:
if docname == 'index':
return ''
if docname.endswith(SEP + 'index'):
return docname[:-5] # up to sep
return docname + SEP
def get_outfilename(self, pagename):
# type: (str) -> str
def get_outfilename(self, pagename: str) -> str:
if pagename == 'index' or pagename.endswith(SEP + 'index'):
outfilename = path.join(self.outdir, os_path(pagename) +
self.out_suffix)
@ -49,14 +45,16 @@ class DirectoryHTMLBuilder(StandaloneHTMLBuilder):
return outfilename
def prepare_writing(self, docnames):
# type: (Set[str]) -> None
super().prepare_writing(docnames)
self.globalcontext['no_search_suffix'] = True
# for compatibility
deprecated_alias('sphinx.builders.html',
{
'DirectoryHTMLBuilder': DirectoryHTMLBuilder,
},
RemovedInSphinx40Warning)
def setup(app):
# type: (Sphinx) -> Dict[str, Any]
def setup(app: Sphinx) -> Dict[str, Any]:
app.setup_extension('sphinx.builders.html')
app.add_builder(DirectoryHTMLBuilder)

View File

@ -8,16 +8,14 @@
:license: BSD, see LICENSE for details.
"""
from typing import Any, Dict, Set
from docutils.nodes import Node
from sphinx.application import Sphinx
from sphinx.builders import Builder
from sphinx.locale import __
if False:
# For type annotation
from typing import Any, Dict, Set # NOQA
from docutils import nodes # NOQA
from sphinx.application import Sphinx # NOQA
class DummyBuilder(Builder):
name = 'dummy'
@ -25,33 +23,26 @@ class DummyBuilder(Builder):
allow_parallel = True
def init(self):
# type: () -> None
def init(self) -> None:
pass
def get_outdated_docs(self):
# type: () -> Set[str]
def get_outdated_docs(self) -> Set[str]:
return self.env.found_docs
def get_target_uri(self, docname, typ=None):
# type: (str, str) -> str
def get_target_uri(self, docname: str, typ: str = None) -> str:
return ''
def prepare_writing(self, docnames):
# type: (Set[str]) -> None
def prepare_writing(self, docnames: Set[str]) -> None:
pass
def write_doc(self, docname, doctree):
# type: (str, nodes.Node) -> None
def write_doc(self, docname: str, doctree: Node) -> None:
pass
def finish(self):
# type: () -> None
def finish(self) -> None:
pass
def setup(app):
# type: (Sphinx) -> Dict[str, Any]
def setup(app: Sphinx) -> Dict[str, Any]:
app.add_builder(DummyBuilder)
return {

View File

@ -9,13 +9,16 @@
:license: BSD, see LICENSE for details.
"""
import html
import warnings
from collections import namedtuple
from os import path
from typing import Any, Dict, List, Set, Tuple
from sphinx import package_dir
from sphinx.application import Sphinx
from sphinx.builders import _epub_base
from sphinx.config import ENUM
from sphinx.config import Config, ENUM
from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.locale import __
from sphinx.util import logging, xmlname_checker
@ -23,13 +26,6 @@ from sphinx.util.fileutil import copy_asset_file
from sphinx.util.i18n import format_date
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 sphinx.application import Sphinx # NOQA
from sphinx.config import Config # NOQA
logger = logging.getLogger(__name__)
@ -75,8 +71,7 @@ class Epub3Builder(_epub_base.EpubBuilder):
use_meta_charset = True
# Finish by building the epub file
def handle_finish(self):
# type: () -> None
def handle_finish(self) -> None:
"""Create the metainfo files and finally the epub."""
self.get_toc()
self.build_mimetype()
@ -86,30 +81,27 @@ class Epub3Builder(_epub_base.EpubBuilder):
self.build_toc()
self.build_epub()
def validate_config_value(self):
# type: () -> None
def validate_config_value(self) -> None:
warnings.warn('Epub3Builder.validate_config_value() is deprecated.',
RemovedInSphinx40Warning, stacklevel=2)
def content_metadata(self):
# type: () -> Dict
def content_metadata(self) -> Dict:
"""Create a dictionary with all metadata for the content.opf
file properly escaped.
"""
writing_mode = self.config.epub_writing_mode
metadata = super().content_metadata()
metadata['description'] = self.esc(self.config.epub_description)
metadata['contributor'] = self.esc(self.config.epub_contributor)
metadata['description'] = html.escape(self.config.epub_description)
metadata['contributor'] = html.escape(self.config.epub_contributor)
metadata['page_progression_direction'] = PAGE_PROGRESSION_DIRECTIONS.get(writing_mode)
metadata['ibook_scroll_axis'] = IBOOK_SCROLL_AXIS.get(writing_mode)
metadata['date'] = self.esc(format_date("%Y-%m-%dT%H:%M:%SZ"))
metadata['version'] = self.esc(self.config.version)
metadata['date'] = html.escape(format_date("%Y-%m-%dT%H:%M:%SZ"))
metadata['version'] = html.escape(self.config.version)
metadata['epub_version'] = self.config.epub_version
return metadata
def prepare_writing(self, docnames):
# type: (Set[str]) -> None
def prepare_writing(self, docnames: Set[str]) -> None:
super().prepare_writing(docnames)
writing_mode = self.config.epub_writing_mode
@ -118,8 +110,7 @@ class Epub3Builder(_epub_base.EpubBuilder):
self.globalcontext['use_meta_charset'] = self.use_meta_charset
self.globalcontext['skip_ua_compatible'] = True
def build_navlist(self, navnodes):
# type: (List[Dict[str, Any]]) -> List[NavPoint]
def build_navlist(self, navnodes: List[Dict[str, Any]]) -> List[NavPoint]:
"""Create the toc navigation structure.
This method is almost same as build_navpoints method in epub.py.
@ -161,19 +152,17 @@ class Epub3Builder(_epub_base.EpubBuilder):
return navstack[0].children
def navigation_doc_metadata(self, navlist):
# type: (List[NavPoint]) -> Dict
def navigation_doc_metadata(self, navlist: List[NavPoint]) -> Dict:
"""Create a dictionary with all metadata for the nav.xhtml file
properly escaped.
"""
metadata = {} # type: Dict
metadata['lang'] = self.esc(self.config.epub_language)
metadata['toc_locale'] = self.esc(self.guide_titles['toc'])
metadata['lang'] = html.escape(self.config.epub_language)
metadata['toc_locale'] = html.escape(self.guide_titles['toc'])
metadata['navlist'] = navlist
return metadata
def build_navigation_doc(self, outdir=None, outname='nav.xhtml'):
# type: (str, str) -> None
def build_navigation_doc(self, outdir: str = None, outname: str = 'nav.xhtml') -> None:
"""Write the metainfo file nav.xhtml."""
if outdir:
warnings.warn('The arguments of Epub3Builder.build_navigation_doc() '
@ -202,8 +191,7 @@ class Epub3Builder(_epub_base.EpubBuilder):
self.files.append(outname)
def validate_config_values(app):
# type: (Sphinx) -> None
def validate_config_values(app: Sphinx) -> None:
if app.builder.name != 'epub':
return
@ -242,8 +230,7 @@ def validate_config_values(app):
logger.warning(__('conf value "version" should not be empty for EPUB3'))
def convert_epub_css_files(app, config):
# type: (Sphinx, Config) -> None
def convert_epub_css_files(app: Sphinx, config: Config) -> None:
"""This converts string styled epub_css_files to tuple styled one."""
epub_css_files = [] # type: List[Tuple[str, Dict]]
for entry in config.epub_css_files:
@ -260,8 +247,7 @@ def convert_epub_css_files(app, config):
config.epub_css_files = epub_css_files # type: ignore
def setup(app):
# type: (Sphinx) -> Dict[str, Any]
def setup(app: Sphinx) -> Dict[str, Any]:
app.add_builder(Epub3Builder)
# config values

View File

@ -4,36 +4,39 @@
The MessageCatalogBuilder class.
:copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from codecs import open
from collections import defaultdict, OrderedDict
from datetime import datetime, tzinfo, timedelta
from io import StringIO
from os import path, walk, getenv
from time import time
from typing import Any, Dict, Iterable, Generator, List, Set, Tuple, Union
from uuid import uuid4
from docutils import nodes
from docutils.nodes import Element
from sphinx import addnodes
from sphinx import package_dir
from sphinx.application import Sphinx
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 docname_to_domain
from sphinx.util.i18n import CatalogInfo, 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.osutil import ensuredir, canon_path, relpath
from sphinx.util.tags import Tags
from sphinx.util.template import SphinxRenderer
if False:
# For type annotation
from typing import Any, DefaultDict, Dict, Iterable, List, Set, Tuple, Union # NOQA
from docutils import nodes # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.util.i18n import CatalogInfo # NOQA
from typing import DefaultDict # for python3.5.1
logger = logging.getLogger(__name__)
@ -56,21 +59,27 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"""[1:]
"""[1:] # RemovedInSphinx40Warning
class Message:
"""An entry of translatable message."""
def __init__(self, text: str, locations: List[Tuple[str, int]], uuids: List[str]):
self.text = text
self.locations = locations
self.uuids = uuids
class Catalog:
"""Catalog of translatable messages."""
def __init__(self):
# type: () -> None
def __init__(self) -> None:
self.messages = [] # type: List[str]
# retain insertion order, a la OrderedDict
self.metadata = OrderedDict() # type: Dict[str, List[Tuple[str, int, str]]]
# msgid -> file, line, uid
def add(self, msg, origin):
# type: (str, Union[nodes.Element, MsgOrigin]) -> None
def add(self, msg: str, origin: Union[Element, "MsgOrigin"]) -> None:
if not hasattr(origin, 'uid'):
# Nodes that are replicated like todo don't have a uid,
# however i18n is also unnecessary.
@ -80,27 +89,55 @@ class Catalog:
self.metadata[msg] = []
self.metadata[msg].append((origin.source, origin.line, origin.uid)) # type: ignore
def __iter__(self) -> Generator[Message, None, None]:
for message in self.messages:
positions = [(source, line) for source, line, uuid in self.metadata[message]]
uuids = [uuid for source, line, uuid in self.metadata[message]]
yield Message(message, positions, uuids)
class MsgOrigin:
"""
Origin holder for Catalog message origin.
"""
def __init__(self, source, line):
# type: (str, int) -> None
def __init__(self, source: str, line: int) -> None:
self.source = source
self.line = line
self.uid = uuid4().hex
class GettextRenderer(SphinxRenderer):
def __init__(self, template_path: str = None, outdir: str = None) -> None:
self.outdir = outdir
if template_path is None:
template_path = path.join(package_dir, 'templates', 'gettext')
super().__init__(template_path)
def escape(s: str) -> str:
s = s.replace('\\', r'\\')
s = s.replace('"', r'\"')
return s.replace('\n', '\\n"\n"')
# use texescape as escape filter
self.env.filters['e'] = escape
self.env.filters['escape'] = escape
def render(self, filename: str, context: Dict) -> str:
def _relpath(s: str) -> str:
return canon_path(relpath(s, self.outdir))
context['relpath'] = _relpath
return super().render(filename, context)
class I18nTags(Tags):
"""Dummy tags module for I18nBuilder.
To translate all text inside of only nodes, this class
always returns True value even if no tags are defined.
"""
def eval_condition(self, condition):
# type: (Any) -> bool
def eval_condition(self, condition: Any) -> bool:
return True
@ -114,34 +151,33 @@ class I18nBuilder(Builder):
# be set by `gettext_uuid`
use_message_catalog = False
def init(self):
# type: () -> None
def init(self) -> None:
super().init()
self.env.set_versioning_method(self.versioning_method,
self.env.config.gettext_uuid)
self.tags = I18nTags()
self.catalogs = defaultdict(Catalog) # type: DefaultDict[str, Catalog]
def get_target_uri(self, docname, typ=None):
# type: (str, str) -> str
def get_target_uri(self, docname: str, typ: str = None) -> str:
return ''
def get_outdated_docs(self):
# type: () -> Set[str]
def get_outdated_docs(self) -> Set[str]:
return self.env.found_docs
def prepare_writing(self, docnames):
# type: (Set[str]) -> None
def prepare_writing(self, docnames: Set[str]) -> None:
return
def compile_catalogs(self, catalogs, message):
# type: (Set[CatalogInfo], str) -> None
def compile_catalogs(self, catalogs: Set[CatalogInfo], message: str) -> None:
return
def write_doc(self, docname, doctree):
# type: (str, nodes.document) -> None
def write_doc(self, docname: str, doctree: nodes.document) -> None:
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)
@ -170,26 +206,21 @@ if source_date_epoch is not None:
class LocalTimeZone(tzinfo):
def __init__(self, *args, **kw):
# type: (Any, Any) -> None
super().__init__(*args, **kw) # type: ignore
def __init__(self, *args: Any, **kwargs: Any) -> None:
super().__init__(*args, **kwargs) # type: ignore
self.tzdelta = tzdelta
def utcoffset(self, dt):
# type: (datetime) -> timedelta
def utcoffset(self, dt: datetime) -> timedelta:
return self.tzdelta
def dst(self, dt):
# type: (datetime) -> timedelta
def dst(self, dt: datetime) -> timedelta:
return timedelta(0)
ltz = LocalTimeZone()
def should_write(filepath, new_content):
# type: (str, str) -> bool
def should_write(filepath: str, new_content: str) -> bool:
if not path.exists(filepath):
return True
try:
@ -214,14 +245,12 @@ class MessageCatalogBuilder(I18nBuilder):
name = 'gettext'
epilog = __('The message catalogs are in %(outdir)s.')
def init(self):
# type: () -> None
def init(self) -> None:
super().init()
self.create_template_bridge()
self.templates.init(self)
def _collect_templates(self):
# type: () -> Set[str]
def _collect_templates(self) -> Set[str]:
template_files = set()
for template_path in self.config.templates_path:
tmpl_abs_path = path.join(self.app.srcdir, template_path)
@ -232,8 +261,7 @@ class MessageCatalogBuilder(I18nBuilder):
template_files.add(filename)
return template_files
def _extract_from_template(self):
# type: () -> None
def _extract_from_template(self) -> None:
files = list(self._collect_templates())
files.sort()
logger.info(bold(__('building [%s]: ') % self.name), nonl=True)
@ -252,20 +280,21 @@ class MessageCatalogBuilder(I18nBuilder):
except Exception as exc:
raise ThemeError('%s: %r' % (template, exc))
def build(self, docnames, summary=None, method='update'):
# type: (Iterable[str], str, str) -> None
def build(self, docnames: Iterable[str], summary: str = None, method: str = 'update') -> None: # NOQA
self._extract_from_template()
super().build(docnames, summary, method)
def finish(self):
# type: () -> None
def finish(self) -> None:
super().finish()
data = {
context = {
'version': self.config.version,
'copyright': self.config.copyright,
'project': self.config.project,
'ctime': datetime.fromtimestamp(
timestamp, ltz).strftime('%Y-%m-%d %H:%M%z'),
'last_translator': self.config.gettext_last_translator,
'language_team': self.config.gettext_language_team,
'ctime': datetime.fromtimestamp(timestamp, ltz).strftime('%Y-%m-%d %H:%M%z'),
'display_location': self.config.gettext_location,
'display_uuid': self.config.gettext_uuid,
}
for textdomain, catalog in status_iterator(self.catalogs.items(),
__("writing message catalogs... "),
@ -275,37 +304,16 @@ class MessageCatalogBuilder(I18nBuilder):
# noop if config.gettext_compact is set
ensuredir(path.join(self.outdir, path.dirname(textdomain)))
context['messages'] = list(catalog)
content = GettextRenderer(outdir=self.outdir).render('message.pot_t', context)
pofn = path.join(self.outdir, textdomain + '.pot')
output = StringIO()
output.write(POHEADER % data)
for message in catalog.messages:
positions = catalog.metadata[message]
if self.config.gettext_location:
# generate "#: file1:line1\n#: file2:line2 ..."
output.write("#: %s\n" % "\n#: ".join(
"%s:%s" % (canon_path(relpath(source, self.outdir)), line)
for source, line, _ in positions))
if self.config.gettext_uuid:
# generate "# uuid1\n# uuid2\n ..."
output.write("# %s\n" % "\n# ".join(uid for _, _, uid in positions))
# message contains *one* line of text ready for translation
message = message.replace('\\', r'\\'). \
replace('"', r'\"'). \
replace('\n', '\\n"\n"')
output.write('msgid "%s"\nmsgstr ""\n\n' % message)
content = output.getvalue()
if should_write(pofn, content):
with open(pofn, 'w', encoding='utf-8') as pofile:
pofile.write(content)
def setup(app):
# type: (Sphinx) -> Dict[str, Any]
def setup(app: Sphinx) -> Dict[str, Any]:
app.add_builder(MessageCatalogBuilder)
app.add_config_value('gettext_compact', True, 'gettext')
@ -313,6 +321,8 @@ def setup(app):
app.add_config_value('gettext_uuid', False, 'gettext')
app.add_config_value('gettext_auto_build', True, 'env')
app.add_config_value('gettext_additional_targets', [], 'env')
app.add_config_value('gettext_last_translator', 'FULL NAME <EMAIL@ADDRESS>', 'gettext')
app.add_config_value('gettext_language_team', 'LANGUAGE <LL@li.org>', 'gettext')
return {
'version': 'builtin',

View File

@ -4,7 +4,7 @@
Several HTML builders.
:copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@ -15,18 +15,21 @@ import sys
import warnings
from hashlib import md5
from os import path
from typing import Any, Dict, IO, Iterable, Iterator, List, Set, Tuple
from docutils import nodes
from docutils.core import publish_parts
from docutils.frontend import OptionParser
from docutils.io import DocTreeInput, StringOutput
from docutils.nodes import Node
from docutils.utils import relative_path
from sphinx import package_dir, __display_version__
from sphinx.application import Sphinx
from sphinx.builders import Builder
from sphinx.deprecation import (
RemovedInSphinx30Warning, RemovedInSphinx40Warning, deprecated_alias
)
from sphinx.config import Config
from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.domains import Domain, Index, IndexEntry
from sphinx.environment.adapters.asset import ImageAdapter
from sphinx.environment.adapters.indexentries import IndexEntries
from sphinx.environment.adapters.toctree import TocTree
@ -35,25 +38,22 @@ from sphinx.highlighting import PygmentsBridge
from sphinx.locale import _, __
from sphinx.search import js_index
from sphinx.theming import HTMLThemeFactory
from sphinx.util import logging, status_iterator
from sphinx.util.console import bold # type: ignore
from sphinx.util import logging, progress_message, status_iterator
from sphinx.util.docutils import is_html5_writer_available, new_document
from sphinx.util.fileutil import copy_asset
from sphinx.util.i18n import format_date
from sphinx.util.inventory import InventoryFile
from sphinx.util.matching import patmatch, Matcher, DOTFILES
from sphinx.util.osutil import os_path, relative_uri, ensuredir, movefile, copyfile
from sphinx.util.tags import Tags
from sphinx.writers.html import HTMLWriter, HTMLTranslator
if False:
# For type annotation
from typing import Any, Dict, IO, Iterable, Iterator, List, Set, Type, Tuple # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.config import Config # NOQA
from sphinx.domains import Domain, Index, IndexEntry # NOQA
from sphinx.util.tags import Tags # NOQA
from typing import Type # for python3.5.1
# HTML5 Writer is avialable or not
# HTML5 Writer is available or not
if is_html5_writer_available():
from sphinx.writers.html5 import HTML5Translator
html5_ready = True
@ -67,8 +67,7 @@ logger = logging.getLogger(__name__)
return_codes_re = re.compile('[\r\n]+')
def get_stable_hash(obj):
# type: (Any) -> str
def get_stable_hash(obj: Any) -> str:
"""
Return a stable hash for a Python data structure. We can't just use
the md5 of str(obj) since for example dictionary items are enumerated
@ -91,8 +90,7 @@ class Stylesheet(str):
attributes = None # type: Dict[str, str]
filename = None # type: str
def __new__(cls, filename, *args, **attributes):
# type: (str, str, str) -> None
def __new__(cls, filename: str, *args: str, **attributes: str) -> "Stylesheet":
self = str.__new__(cls, filename) # type: ignore
self.filename = filename
self.attributes = attributes
@ -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.
@ -148,12 +113,10 @@ class JavaScript(str):
attributes = None # type: Dict[str, str]
filename = None # type: str
def __new__(cls, filename, **attributes):
# type: (str, **str) -> None
def __new__(cls, filename: str, **attributes: str) -> "JavaScript":
self = str.__new__(cls, filename) # type: ignore
self.filename = filename
self.attributes = attributes
self.attributes.setdefault('type', 'text/javascript')
return self
@ -166,8 +129,7 @@ class BuildInfo:
"""
@classmethod
def load(cls, f):
# type: (IO) -> BuildInfo
def load(cls, f: IO) -> "BuildInfo":
try:
lines = f.readlines()
assert lines[0].rstrip() == '# Sphinx build info version 1'
@ -181,25 +143,22 @@ class BuildInfo:
except Exception as exc:
raise ValueError(__('build info file is broken: %r') % exc)
def __init__(self, config=None, tags=None, config_categories=[]):
# type: (Config, Tags, List[str]) -> None
def __init__(self, config: Config = None, tags: Tags = None, config_categories: List[str] = []) -> None: # NOQA
self.config_hash = ''
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:
self.tags_hash = get_stable_hash(sorted(tags))
def __eq__(self, other): # type: ignore
# type: (BuildInfo) -> bool
def __eq__(self, other: "BuildInfo") -> bool: # type: ignore
return (self.config_hash == other.config_hash and
self.tags_hash == other.tags_hash)
def dump(self, f):
# type: (IO) -> None
def dump(self, f: IO) -> None:
f.write('# Sphinx build info version 1\n'
'# This file hashes the configuration used when building these files.'
' When it is not found, a full rebuild will be done.\n'
@ -239,18 +198,16 @@ class StandaloneHTMLBuilder(Builder):
imgpath = None # type: str
domain_indices = [] # type: List[Tuple[str, Type[Index], List[Tuple[str, List[IndexEntry]]], bool]] # NOQA
def __init__(self, app):
# type: (Sphinx) -> None
def __init__(self, app: Sphinx) -> None:
super().__init__(app)
# CSS files
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
def init(self) -> None:
self.build_info = self.create_build_info()
# basename of images directory
self.imagedir = '_images'
@ -276,12 +233,10 @@ class StandaloneHTMLBuilder(Builder):
self.use_index = self.get_builder_config('use_index', 'html')
def create_build_info(self):
# type: () -> BuildInfo
def create_build_info(self) -> BuildInfo:
return BuildInfo(self.config, self.tags, ['html'])
def _get_translations_js(self):
# type: () -> str
def _get_translations_js(self) -> str:
candidates = [path.join(dir, self.config.language,
'LC_MESSAGES', 'sphinx.js')
for dir in self.config.locale_dirs] + \
@ -295,12 +250,10 @@ class StandaloneHTMLBuilder(Builder):
return jsfile
return None
def get_theme_config(self):
# type: () -> Tuple[str, Dict]
def get_theme_config(self) -> Tuple[str, Dict]:
return self.config.html_theme, self.config.html_theme_options
def init_templates(self):
# type: () -> None
def init_templates(self) -> None:
theme_factory = HTMLThemeFactory(self.app)
themename, themeoptions = self.get_theme_config()
self.theme = theme_factory.create(themename)
@ -308,8 +261,7 @@ class StandaloneHTMLBuilder(Builder):
self.create_template_bridge()
self.templates.init(self, self.theme)
def init_highlighter(self):
# type: () -> None
def init_highlighter(self) -> None:
# determine Pygments style and create the highlighter
if self.config.pygments_style is not None:
style = self.config.pygments_style
@ -319,23 +271,20 @@ class StandaloneHTMLBuilder(Builder):
style = 'sphinx'
self.highlighter = PygmentsBridge('html', style)
def init_css_files(self):
# type: () -> None
def init_css_files(self) -> None:
for filename, attrs in self.app.registry.css_files:
self.add_css_file(filename, **attrs)
for filename, attrs in self.get_builder_config('css_files', 'html'):
self.add_css_file(filename, **attrs)
def add_css_file(self, filename, **kwargs):
# type: (str, **str) -> None
def add_css_file(self, filename: str, **kwargs: str) -> None:
if '://' not in filename:
filename = posixpath.join('_static', filename)
self.css_files.append(Stylesheet(filename, **kwargs)) # type: ignore
def init_js_files(self):
# type: () -> None
def init_js_files(self) -> None:
self.add_js_file('jquery.js')
self.add_js_file('underscore.js')
self.add_js_file('doctools.js')
@ -350,24 +299,21 @@ class StandaloneHTMLBuilder(Builder):
if self.config.language and self._get_translations_js():
self.add_js_file('translations.js')
def add_js_file(self, filename, **kwargs):
# type: (str, **str) -> None
def add_js_file(self, filename: str, **kwargs: str) -> None:
if filename and '://' not in filename:
filename = posixpath.join('_static', filename)
self.script_files.append(JavaScript(filename, **kwargs))
@property
def default_translator_class(self): # type: ignore
# type: () -> Type[nodes.NodeVisitor]
def default_translator_class(self) -> "Type[nodes.NodeVisitor]": # type: ignore
if not html5_ready or self.config.html4_writer:
return HTMLTranslator
else:
return HTML5Translator
@property
def math_renderer_name(self):
# type: () -> str
def math_renderer_name(self) -> str:
name = self.get_builder_config('math_renderer', 'html')
if name is not None:
# use given name
@ -386,8 +332,7 @@ class StandaloneHTMLBuilder(Builder):
# many math_renderers are registered. can't choose automatically!
return None
def get_outdated_docs(self):
# type: () -> Iterator[str]
def get_outdated_docs(self) -> Iterator[str]:
try:
with open(path.join(self.outdir, '.buildinfo')) as fp:
buildinfo = BuildInfo.load(fp)
@ -423,12 +368,10 @@ class StandaloneHTMLBuilder(Builder):
# source doesn't exist anymore
pass
def get_asset_paths(self):
# type: () -> List[str]
def get_asset_paths(self) -> List[str]:
return self.config.html_extra_path + self.config.html_static_path
def render_partial(self, node):
# type: (nodes.Node) -> Dict[str, str]
def render_partial(self, node: Node) -> Dict[str, str]:
"""Utility: Render a lone doctree node."""
if node is None:
return {'fragment': ''}
@ -442,8 +385,7 @@ class StandaloneHTMLBuilder(Builder):
settings_overrides={'output_encoding': 'unicode'},
source=doc)
def prepare_writing(self, docnames):
# type: (Set[str]) -> None
def prepare_writing(self, docnames: Set[str]) -> None:
# create the search indexer
self.indexer = None
if self.search:
@ -490,11 +432,8 @@ class StandaloneHTMLBuilder(Builder):
else:
self.last_updated = None
logo = self.config.html_logo and \
path.basename(self.config.html_logo) or ''
favicon = self.config.html_favicon and \
path.basename(self.config.html_favicon) or ''
logo = path.basename(self.config.html_logo) if self.config.html_logo else ''
favicon = path.basename(self.config.html_favicon) if self.config.html_favicon else ''
if not isinstance(self.config.html_use_opensearch, str):
logger.warning(__('html_use_opensearch config value must now be a string'))
@ -552,8 +491,7 @@ class StandaloneHTMLBuilder(Builder):
self.theme.get_options(self.theme_options).items())
self.globalcontext.update(self.config.html_context)
def get_doc_context(self, docname, body, metatags):
# type: (str, str, str) -> Dict[str, Any]
def get_doc_context(self, docname: str, body: str, metatags: str) -> Dict[str, Any]:
"""Collect items for the template context of a page."""
# find out relations
prev = next = None
@ -597,7 +535,7 @@ class StandaloneHTMLBuilder(Builder):
# title rendered as HTML
title_node = self.env.longtitles.get(docname)
title = title_node and self.render_partial(title_node)['title'] or ''
title = self.render_partial(title_node)['title'] if title_node else ''
# Suffix for the document
source_suffix = path.splitext(self.env.doc2path(docname))[1]
@ -633,8 +571,7 @@ class StandaloneHTMLBuilder(Builder):
'page_source_suffix': source_suffix,
}
def write_doc(self, docname, doctree):
# type: (str, nodes.document) -> None
def write_doc(self, docname: str, doctree: nodes.document) -> None:
destination = StringOutput(encoding='utf-8')
doctree.settings = self.docsettings
@ -651,17 +588,16 @@ class StandaloneHTMLBuilder(Builder):
ctx = self.get_doc_context(docname, body, metatags)
self.handle_page(docname, ctx, event_arg=doctree)
def write_doc_serialized(self, docname, doctree):
# type: (str, nodes.document) -> None
def write_doc_serialized(self, docname: str, doctree: nodes.document) -> None:
self.imgpath = relative_uri(self.get_target_uri(docname), self.imagedir)
self.post_process_images(doctree)
title_node = self.env.longtitles.get(docname)
title = title_node and self.render_partial(title_node)['title'] or ''
title = self.render_partial(title_node)['title'] if title_node else ''
self.index_page(docname, doctree, title)
def finish(self):
# type: () -> None
def finish(self) -> None:
self.finish_tasks.add_task(self.gen_indices)
self.finish_tasks.add_task(self.gen_pages_from_extensions)
self.finish_tasks.add_task(self.gen_additional_pages)
self.finish_tasks.add_task(self.copy_image_files)
self.finish_tasks.add_task(self.copy_download_files)
@ -672,10 +608,8 @@ class StandaloneHTMLBuilder(Builder):
# dump the search index
self.handle_finish()
def gen_indices(self):
# type: () -> None
logger.info(bold(__('generating indices...')), nonl=True)
@progress_message(__('generating indices'))
def gen_indices(self) -> None:
# the global general index
if self.use_index:
self.write_genindex()
@ -683,17 +617,14 @@ class StandaloneHTMLBuilder(Builder):
# the global domain-specific indices
self.write_domain_indices()
logger.info('')
def gen_additional_pages(self):
# type: () -> None
def gen_pages_from_extensions(self) -> None:
# pages from extensions
for pagelist in self.app.emit('html-collect-pages'):
for pagelist in self.events.emit('html-collect-pages'):
for pagename, context, template in pagelist:
self.handle_page(pagename, context, template)
logger.info(bold(__('writing additional pages...')), nonl=True)
@progress_message(__('writing additional pages'))
def gen_additional_pages(self) -> None:
# additional pages from conf.py
for pagename, template in self.config.html_additional_pages.items():
logger.info(' ' + pagename, nonl=True)
@ -710,10 +641,7 @@ class StandaloneHTMLBuilder(Builder):
fn = path.join(self.outdir, '_static', 'opensearch.xml')
self.handle_page('opensearch', {}, 'opensearch.xml', outfilename=fn)
logger.info('')
def write_genindex(self):
# type: () -> None
def write_genindex(self) -> None:
# the total count of lines for each index letter, used to distribute
# the entries into two columns
genindex = IndexEntries(self.env).create_index(self)
@ -742,8 +670,7 @@ class StandaloneHTMLBuilder(Builder):
else:
self.handle_page('genindex', genindexcontext, 'genindex.html')
def write_domain_indices(self):
# type: () -> None
def write_domain_indices(self) -> None:
for indexname, indexcls, content, collapse in self.domain_indices:
indexcontext = {
'indextitle': indexcls.localname,
@ -753,8 +680,7 @@ class StandaloneHTMLBuilder(Builder):
logger.info(' ' + indexname, nonl=True)
self.handle_page(indexname, indexcontext, 'domainindex.html')
def copy_image_files(self):
# type: () -> None
def copy_image_files(self) -> None:
if self.images:
stringify_func = ImageAdapter(self.app.env).get_original_image_uri
ensuredir(path.join(self.outdir, self.imagedir))
@ -769,11 +695,10 @@ class StandaloneHTMLBuilder(Builder):
logger.warning(__('cannot copy image file %r: %s'),
path.join(self.srcdir, src), err)
def copy_download_files(self):
# type: () -> None
def to_relpath(f):
# type: (str) -> str
def copy_download_files(self) -> None:
def to_relpath(f: str) -> str:
return relative_path(self.srcdir, f)
# copy downloadable files
if self.env.dlfiles:
ensuredir(path.join(self.outdir, '_downloads'))
@ -788,105 +713,93 @@ class StandaloneHTMLBuilder(Builder):
logger.warning(__('cannot copy downloadable file %r: %s'),
path.join(self.srcdir, src), err)
def copy_static_files(self):
# type: () -> None
def create_pygments_style_file(self) -> None:
"""create a style file for pygments."""
with open(path.join(self.outdir, '_static', 'pygments.css'), 'w') as f:
f.write(self.highlighter.get_stylesheet())
def copy_translation_js(self) -> None:
"""Copy a JavaScript file for translations."""
if self.config.language is not None:
jsfile = self._get_translations_js()
if jsfile:
copyfile(jsfile, path.join(self.outdir, '_static', 'translations.js'))
def copy_stemmer_js(self) -> None:
"""Copy a JavaScript file for stemmer."""
if self.indexer is not None:
jsfile = self.indexer.get_js_stemmer_rawcode()
if jsfile:
copyfile(jsfile, path.join(self.outdir, '_static', '_stemmer.js'))
def copy_theme_static_files(self, context: Dict) -> None:
if self.theme:
for entry in self.theme.get_theme_dirs()[::-1]:
copy_asset(path.join(entry, 'static'),
path.join(self.outdir, '_static'),
excluded=DOTFILES, context=context, renderer=self.templates)
def copy_html_static_files(self, context: Dict) -> None:
excluded = Matcher(self.config.exclude_patterns + ["**/.*"])
for entry in self.config.html_static_path:
copy_asset(path.join(self.confdir, entry),
path.join(self.outdir, '_static'),
excluded, context=context, renderer=self.templates)
def copy_html_logo(self) -> None:
if self.config.html_logo:
copy_asset(path.join(self.confdir, self.config.html_logo),
path.join(self.outdir, '_static'))
def copy_html_favicon(self) -> None:
if self.config.html_favicon:
copy_asset(path.join(self.confdir, self.config.html_favicon),
path.join(self.outdir, '_static'))
def copy_static_files(self) -> None:
try:
# copy static files
logger.info(bold(__('copying static files... ')), nonl=True)
ensuredir(path.join(self.outdir, '_static'))
# first, create pygments style file
with open(path.join(self.outdir, '_static', 'pygments.css'), 'w') as f:
f.write(self.highlighter.get_stylesheet())
# then, copy translations JavaScript file
if self.config.language is not None:
jsfile = self._get_translations_js()
if jsfile:
copyfile(jsfile, path.join(self.outdir, '_static',
'translations.js'))
with progress_message(__('copying static files... ')):
ensuredir(path.join(self.outdir, '_static'))
# copy non-minified stemmer JavaScript file
if self.indexer is not None:
jsfile = self.indexer.get_js_stemmer_rawcode()
if jsfile:
copyfile(jsfile, path.join(self.outdir, '_static', '_stemmer.js'))
# prepare context for templates
context = self.globalcontext.copy()
if self.indexer is not None:
context.update(self.indexer.context_for_searchtool())
ctx = self.globalcontext.copy()
# add context items for search function used in searchtools.js_t
if self.indexer is not None:
ctx.update(self.indexer.context_for_searchtool())
# then, copy over theme-supplied static files
if self.theme:
for theme_path in self.theme.get_theme_dirs()[::-1]:
entry = path.join(theme_path, 'static')
copy_asset(entry, path.join(self.outdir, '_static'), excluded=DOTFILES,
context=ctx, renderer=self.templates)
# then, copy over all user-supplied static files
excluded = Matcher(self.config.exclude_patterns + ["**/.*"])
for static_path in self.config.html_static_path:
entry = path.join(self.confdir, static_path)
if not path.exists(entry):
logger.warning(__('html_static_path entry %r does not exist'), entry)
continue
copy_asset(entry, path.join(self.outdir, '_static'), excluded,
context=ctx, renderer=self.templates)
# copy logo and favicon files if not already in static path
if self.config.html_logo:
logobase = path.basename(self.config.html_logo)
logotarget = path.join(self.outdir, '_static', logobase)
if not path.isfile(path.join(self.confdir, self.config.html_logo)):
logger.warning(__('logo file %r does not exist'), self.config.html_logo)
elif not path.isfile(logotarget):
copyfile(path.join(self.confdir, self.config.html_logo),
logotarget)
if self.config.html_favicon:
iconbase = path.basename(self.config.html_favicon)
icontarget = path.join(self.outdir, '_static', iconbase)
if not path.isfile(path.join(self.confdir, self.config.html_favicon)):
logger.warning(__('favicon file %r does not exist'),
self.config.html_favicon)
elif not path.isfile(icontarget):
copyfile(path.join(self.confdir, self.config.html_favicon),
icontarget)
logger.info(__('done'))
self.create_pygments_style_file()
self.copy_translation_js()
self.copy_stemmer_js()
self.copy_theme_static_files(context)
self.copy_html_static_files(context)
self.copy_html_logo()
self.copy_html_favicon()
except OSError as err:
logger.warning(__('cannot copy static file %r'), err)
def copy_extra_files(self):
# type: () -> None
def copy_extra_files(self) -> None:
"""copy html_extra_path files."""
try:
# copy html_extra_path files
logger.info(bold(__('copying extra files... ')), nonl=True)
excluded = Matcher(self.config.exclude_patterns)
for extra_path in self.config.html_extra_path:
entry = path.join(self.confdir, extra_path)
if not path.exists(entry):
logger.warning(__('html_extra_path entry %r does not exist'), entry)
continue
copy_asset(entry, self.outdir, excluded)
logger.info(__('done'))
with progress_message(__('copying extra files')):
excluded = Matcher(self.config.exclude_patterns)
for extra_path in self.config.html_extra_path:
entry = path.join(self.confdir, extra_path)
copy_asset(entry, self.outdir, excluded)
except OSError as err:
logger.warning(__('cannot copy extra file %r'), err)
def write_buildinfo(self):
# type: () -> None
def write_buildinfo(self) -> None:
try:
with open(path.join(self.outdir, '.buildinfo'), 'w') as fp:
self.build_info.dump(fp)
except OSError as exc:
logger.warning(__('Failed to write build info file: %r'), exc)
def cleanup(self):
# type: () -> None
def cleanup(self) -> None:
# clean up theme stuff
if self.theme:
self.theme.cleanup()
def post_process_images(self, doctree):
# type: (nodes.Node) -> None
def post_process_images(self, doctree: Node) -> None:
"""Pick the best candidate for an image and link down-scaled images to
their high res version.
"""
@ -911,8 +824,7 @@ class StandaloneHTMLBuilder(Builder):
node.replace_self(reference)
reference.append(node)
def load_indexer(self, docnames):
# type: (Iterable[str]) -> None
def load_indexer(self, docnames: Iterable[str]) -> None:
keep = set(self.env.all_docs) - set(docnames)
try:
searchindexfn = path.join(self.outdir, self.searchindex_filename)
@ -930,8 +842,7 @@ class StandaloneHTMLBuilder(Builder):
# delete all entries for files that will be rebuilt
self.indexer.prune(keep)
def index_page(self, pagename, doctree, title):
# type: (str, nodes.document, str) -> None
def index_page(self, pagename: str, doctree: nodes.document, title: str) -> None:
# only index pages with title
if self.indexer is not None and title:
filename = self.env.doc2path(pagename, base=None)
@ -947,22 +858,19 @@ class StandaloneHTMLBuilder(Builder):
indexer_name, indexer_name),
RemovedInSphinx40Warning)
def _get_local_toctree(self, docname, collapse=True, **kwds):
# type: (str, bool, Any) -> str
if 'includehidden' not in kwds:
kwds['includehidden'] = False
def _get_local_toctree(self, docname: str, collapse: bool = True, **kwargs: Any) -> str:
if 'includehidden' not in kwargs:
kwargs['includehidden'] = False
return self.render_partial(TocTree(self.env).get_toctree_for(
docname, self, collapse, **kwds))['fragment']
docname, self, collapse, **kwargs))['fragment']
def get_outfilename(self, pagename):
# type: (str) -> str
def get_outfilename(self, pagename: str) -> str:
return path.join(self.outdir, os_path(pagename) + self.out_suffix)
def add_sidebars(self, pagename, ctx):
# type: (str, Dict) -> None
def has_wildcard(pattern):
# type: (str) -> bool
def add_sidebars(self, pagename: str, ctx: Dict) -> None:
def has_wildcard(pattern: str) -> bool:
return any(char in pattern for char in '*?[')
sidebars = None
matched = None
customsidebar = None
@ -1011,13 +919,11 @@ class StandaloneHTMLBuilder(Builder):
# --------- these are overwritten by the serialization builder
def get_target_uri(self, docname, typ=None):
# type: (str, str) -> str
def get_target_uri(self, docname: str, typ: str = None) -> str:
return docname + self.link_suffix
def handle_page(self, pagename, addctx, templatename='page.html',
outfilename=None, event_arg=None):
# type: (str, Dict, str, str, Any) -> None
def handle_page(self, pagename: str, addctx: Dict, templatename: str = 'page.html',
outfilename: str = None, event_arg: Any = None) -> None:
ctx = self.globalcontext.copy()
# current_page_name is backwards compatibility
ctx['pagename'] = ctx['current_page_name'] = pagename
@ -1033,8 +939,7 @@ class StandaloneHTMLBuilder(Builder):
else:
ctx['pageurl'] = None
def pathto(otheruri, resource=False, baseuri=default_baseuri):
# type: (str, bool, str) -> str
def pathto(otheruri: str, resource: bool = False, baseuri: str = default_baseuri) -> str: # NOQA
if resource and '://' in otheruri:
# allow non-local resources given by scheme
return otheruri
@ -1046,8 +951,7 @@ class StandaloneHTMLBuilder(Builder):
return uri
ctx['pathto'] = pathto
def css_tag(css):
# type: (Stylesheet) -> str
def css_tag(css: Stylesheet) -> str:
attrs = []
for key in sorted(css.attributes):
value = css.attributes[key]
@ -1057,8 +961,7 @@ class StandaloneHTMLBuilder(Builder):
return '<link %s />' % ' '.join(attrs)
ctx['css_tag'] = css_tag
def hasdoc(name):
# type: (str) -> bool
def hasdoc(name: str) -> bool:
if name in self.env.all_docs:
return True
elif name == 'search' and self.search:
@ -1068,17 +971,7 @@ 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)
ctx['toctree'] = lambda **kwargs: self._get_local_toctree(pagename, **kwargs)
self.add_sidebars(pagename, ctx)
ctx.update(addctx)
@ -1116,43 +1009,35 @@ class StandaloneHTMLBuilder(Builder):
ensuredir(path.dirname(source_name))
copyfile(self.env.doc2path(pagename), source_name)
def update_page_context(self, pagename, templatename, ctx, event_arg):
# type: (str, str, Dict, Any) -> None
def update_page_context(self, pagename: str, templatename: str,
ctx: Dict, event_arg: Any) -> None:
pass
def handle_finish(self):
# type: () -> None
def handle_finish(self) -> None:
if self.indexer:
self.finish_tasks.add_task(self.dump_search_index)
self.finish_tasks.add_task(self.dump_inventory)
def dump_inventory(self):
# type: () -> None
logger.info(bold(__('dumping object inventory... ')), nonl=True)
@progress_message(__('dumping object inventory'))
def dump_inventory(self) -> None:
InventoryFile.dump(path.join(self.outdir, INVENTORY_FILENAME), self.env, self)
logger.info(__('done'))
def dump_search_index(self):
# type: () -> None
logger.info(
bold(__('dumping search index in %s ... ') % self.indexer.label()),
nonl=True)
self.indexer.prune(self.env.all_docs)
searchindexfn = path.join(self.outdir, self.searchindex_filename)
# first write to a temporary file, so that if dumping fails,
# the existing index won't be overwritten
if self.indexer_dumps_unicode:
with open(searchindexfn + '.tmp', 'w', encoding='utf-8') as ft:
self.indexer.dump(ft, self.indexer_format)
else:
with open(searchindexfn + '.tmp', 'wb') as fb:
self.indexer.dump(fb, self.indexer_format)
movefile(searchindexfn + '.tmp', searchindexfn)
logger.info(__('done'))
def dump_search_index(self) -> None:
with progress_message(__('dumping search index in %s') % self.indexer.label()):
self.indexer.prune(self.env.all_docs)
searchindexfn = path.join(self.outdir, self.searchindex_filename)
# first write to a temporary file, so that if dumping fails,
# the existing index won't be overwritten
if self.indexer_dumps_unicode:
with open(searchindexfn + '.tmp', 'w', encoding='utf-8') as ft:
self.indexer.dump(ft, self.indexer_format)
else:
with open(searchindexfn + '.tmp', 'wb') as fb:
self.indexer.dump(fb, self.indexer_format)
movefile(searchindexfn + '.tmp', searchindexfn)
def convert_html_css_files(app, config):
# type: (Sphinx, Config) -> None
def convert_html_css_files(app: Sphinx, config: Config) -> None:
"""This converts string styled html_css_files to tuple styled one."""
html_css_files = [] # type: List[Tuple[str, Dict]]
for entry in config.html_css_files:
@ -1169,8 +1054,7 @@ def convert_html_css_files(app, config):
config.html_css_files = html_css_files # type: ignore
def convert_html_js_files(app, config):
# type: (Sphinx, Config) -> None
def convert_html_js_files(app: Sphinx, config: Config) -> None:
"""This converts string styled html_js_files to tuple styled one."""
html_js_files = [] # type: List[Tuple[str, Dict]]
for entry in config.html_js_files:
@ -1187,16 +1071,15 @@ def convert_html_js_files(app, config):
config.html_js_files = html_js_files # type: ignore
def setup_js_tag_helper(app, pagename, templatexname, context, doctree):
# type: (Sphinx, str, str, Dict, nodes.Node) -> None
def setup_js_tag_helper(app: Sphinx, pagename: str, templatexname: str,
context: Dict, doctree: Node) -> None:
"""Set up js_tag() template helper.
.. note:: This set up function is added to keep compatibility with webhelper.
"""
pathto = context.get('pathto')
def js_tag(js):
# type: (JavaScript) -> str
def js_tag(js: JavaScript) -> str:
attrs = []
body = ''
if isinstance(js, JavaScript):
@ -1211,15 +1094,13 @@ def setup_js_tag_helper(app, pagename, templatexname, context, doctree):
attrs.append('src="%s"' % pathto(js.filename, resource=True))
else:
# str value (old styled)
attrs.append('type="text/javascript"')
attrs.append('src="%s"' % pathto(js, resource=True))
return '<script %s>%s</script>' % (' '.join(attrs), body)
context['js_tag'] = js_tag
def validate_math_renderer(app):
# type: (Sphinx) -> None
def validate_math_renderer(app: Sphinx) -> None:
if app.builder.format != 'html':
return
@ -1231,28 +1112,53 @@ def validate_math_renderer(app):
raise ConfigError(__('Unknown math_renderer %r is given.') % name)
def validate_html_extra_path(app: Sphinx, config: Config) -> None:
"""Check html_extra_paths setting."""
for entry in config.html_extra_path[:]:
extra_path = path.normpath(path.join(app.confdir, entry))
if not path.exists(extra_path):
logger.warning(__('html_extra_path entry %r does not exist'), entry)
config.html_extra_path.remove(entry)
elif (path.splitdrive(app.outdir)[0] == path.splitdrive(extra_path)[0] and
path.commonpath([app.outdir, extra_path]) == app.outdir):
logger.warning(__('html_extra_path entry %r is placed inside outdir'), entry)
config.html_extra_path.remove(entry)
def validate_html_static_path(app: Sphinx, config: Config) -> None:
"""Check html_static_paths setting."""
for entry in config.html_static_path[:]:
static_path = path.normpath(path.join(app.confdir, entry))
if not path.exists(static_path):
logger.warning(__('html_static_path entry %r does not exist'), entry)
config.html_static_path.remove(entry)
elif (path.splitdrive(app.outdir)[0] == path.splitdrive(static_path)[0] and
path.commonpath([app.outdir, static_path]) == app.outdir):
logger.warning(__('html_static_path entry %r is placed inside outdir'), entry)
config.html_static_path.remove(entry)
def validate_html_logo(app: Sphinx, config: Config) -> None:
"""Check html_logo setting."""
if config.html_logo and not path.isfile(path.join(app.confdir, config.html_logo)):
logger.warning(__('logo file %r does not exist'), config.html_logo)
config.html_logo = None # type: ignore
def validate_html_favicon(app: Sphinx, config: Config) -> None:
"""Check html_favicon setting."""
if config.html_favicon and not path.isfile(path.join(app.confdir, config.html_favicon)):
logger.warning(__('favicon file %r does not exist'), config.html_favicon)
config.html_favicon = None # type: ignore
# for compatibility
from sphinx.builders.dirhtml import DirectoryHTMLBuilder # NOQA
from sphinx.builders.singlehtml import SingleFileHTMLBuilder # NOQA
from sphinxcontrib.serializinghtml import ( # NOQA
LAST_BUILD_FILENAME, JSONHTMLBuilder, PickleHTMLBuilder, SerializingHTMLBuilder
)
deprecated_alias('sphinx.builders.html',
{
'LAST_BUILD_FILENAME': LAST_BUILD_FILENAME,
'DirectoryHTMLBuilder': DirectoryHTMLBuilder,
'JSONHTMLBuilder': JSONHTMLBuilder,
'PickleHTMLBuilder': PickleHTMLBuilder,
'SerializingHTMLBuilder': SerializingHTMLBuilder,
'SingleFileHTMLBuilder': SingleFileHTMLBuilder,
'WebHTMLBuilder': PickleHTMLBuilder,
},
RemovedInSphinx40Warning)
import sphinx.builders.dirhtml # NOQA
import sphinx.builders.singlehtml # NOQA
import sphinxcontrib.serializinghtml # NOQA
def setup(app):
# type: (Sphinx) -> Dict[str, Any]
def setup(app: Sphinx) -> Dict[str, Any]:
# builders
app.add_builder(StandaloneHTMLBuilder)
@ -1301,6 +1207,10 @@ def setup(app):
# event handlers
app.connect('config-inited', convert_html_css_files)
app.connect('config-inited', convert_html_js_files)
app.connect('config-inited', validate_html_extra_path)
app.connect('config-inited', validate_html_static_path)
app.connect('config-inited', validate_html_logo)
app.connect('config-inited', validate_html_favicon)
app.connect('builder-inited', validate_math_renderer)
app.connect('html-page-context', setup_js_tag_helper)

View File

@ -5,26 +5,22 @@
Build HTML help support files.
Parts adapted from Python's Doc/tools/prechm.py.
:copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import warnings
from typing import Any, Dict
from sphinxcontrib.htmlhelp import (
chm_locales, chm_htmlescape, HTMLHelpBuilder, default_htmlhelp_basename
)
from sphinx.application import Sphinx
from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
if False:
# For type annotation
from typing import Any, Dict # NOQA
from sphinx.application import Sphinx # NOQA
deprecated_alias('sphinx.builders.devhelp',
deprecated_alias('sphinx.builders.htmlhelp',
{
'chm_locales': chm_locales,
'chm_htmlescape': chm_htmlescape,
@ -34,8 +30,7 @@ deprecated_alias('sphinx.builders.devhelp',
RemovedInSphinx40Warning)
def setup(app):
# type: (Sphinx) -> Dict[str, Any]
def setup(app: Sphinx) -> Dict[str, Any]:
warnings.warn('sphinx.builders.htmlhelp has been moved to sphinxcontrib-htmlhelp.',
RemovedInSphinx40Warning)
app.setup_extension('sphinxcontrib.htmlhelp')

View File

@ -4,30 +4,29 @@
LaTeX builder.
:copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import os
import warnings
from os import path
from typing import Any, Dict, Iterable, List, Tuple, Union
from docutils.frontend import OptionParser
from docutils.nodes import Node
import sphinx.builders.latex.nodes # NOQA # Workaround: import this before writer to avoid ImportError
from sphinx import package_dir, addnodes, highlighting
from sphinx.application import Sphinx
from sphinx.builders import Builder
from sphinx.builders.latex.transforms import (
BibliographyTransform, CitationReferenceTransform, MathReferenceTransform,
FootnoteDocnameUpdater, LaTeXFootnoteTransform, LiteralBlockTransform,
ShowUrlsTransform, DocumentTargetTransform,
)
from sphinx.builders.latex.constants import ADDITIONAL_SETTINGS, DEFAULT_SETTINGS
from sphinx.builders.latex.util import ExtBabel
from sphinx.config import ENUM
from sphinx.environment import NoUri
from sphinx.config import Config, ENUM
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
@ -36,16 +35,10 @@ from sphinx.util.i18n import format_date
from sphinx.util.nodes import inline_all_toctrees
from sphinx.util.osutil import SEP, make_filename_from_project
from sphinx.util.template import LaTeXRenderer
from sphinx.writers.latex import (
ADDITIONAL_SETTINGS, DEFAULT_SETTINGS, LaTeXWriter, LaTeXTranslator
)
from sphinx.writers.latex import LaTeXWriter, LaTeXTranslator
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
# load docutils.nodes after loading sphinx.builders.latex.nodes
from docutils import nodes # NOQA
XINDY_LANG_OPTIONS = {
@ -128,8 +121,7 @@ class LaTeXBuilder(Builder):
supported_remote_images = False
default_translator_class = LaTeXTranslator
def init(self):
# type: () -> None
def init(self) -> None:
self.babel = None # type: ExtBabel
self.context = {} # type: Dict[str, Any]
self.docnames = [] # type: Iterable[str]
@ -140,24 +132,20 @@ class LaTeXBuilder(Builder):
self.init_context()
self.init_babel()
def get_outdated_docs(self):
# type: () -> Union[str, List[str]]
def get_outdated_docs(self) -> Union[str, List[str]]:
return 'all documents' # for now
def get_target_uri(self, docname, typ=None):
# type: (str, str) -> str
def get_target_uri(self, docname: str, typ: str = None) -> str:
if docname not in self.docnames:
raise NoUri
raise NoUri(docname, typ)
else:
return '%' + docname
def get_relative_uri(self, from_, to, typ=None):
# type: (str, str, str) -> str
def get_relative_uri(self, from_: str, to: str, typ: str = None) -> str:
# ignore source path
return self.get_target_uri(to, typ)
def init_document_data(self):
# type: () -> None
def init_document_data(self) -> None:
preliminary_document_data = [list(x) for x in self.config.latex_documents]
if not preliminary_document_data:
logger.warning(__('no "latex_documents" config value found; no documents '
@ -176,19 +164,16 @@ class LaTeXBuilder(Builder):
docname = docname[:-5]
self.titles.append((docname, entry[2]))
def init_context(self):
# type: () -> None
def init_context(self) -> None:
self.context = DEFAULT_SETTINGS.copy()
# Add special settings for latex_engine
self.context.update(ADDITIONAL_SETTINGS.get(self.config.latex_engine, {}))
# for xelatex+French, don't use polyglossia by default
if self.config.latex_engine == 'xelatex':
if self.config.language:
if self.config.language[:2] == 'fr':
self.context['polyglossia'] = ''
self.context['babel'] = r'\usepackage{babel}'
# Add special settings for (latex_engine, language_code)
if self.config.language:
key = (self.config.latex_engine, self.config.language[:2])
self.context.update(ADDITIONAL_SETTINGS.get(key, {}))
# Apply extension settings to context
self.context['packages'] = self.usepackages
@ -211,10 +196,9 @@ class LaTeXBuilder(Builder):
self.context['indexname'] = _('Index')
if self.config.release:
# Show the release label only if release value exists
self.context['releasename'] = _('Release')
self.context.setdefault('releasename', _('Release'))
def init_babel(self):
# type: () -> None
def init_babel(self) -> None:
self.babel = ExtBabel(self.config.language, not self.context['babel'])
if self.config.language and not self.babel.is_supported_language():
# emit warning if specified language is invalid
@ -222,8 +206,7 @@ class LaTeXBuilder(Builder):
logger.warning(__('no Babel option known for language %r'),
self.config.language)
def write_stylesheet(self):
# type: () -> None
def write_stylesheet(self) -> None:
highlighter = highlighting.PygmentsBridge('latex', self.config.pygments_style)
stylesheet = path.join(self.outdir, 'sphinxhighlight.sty')
with open(stylesheet, 'w') as f:
@ -232,13 +215,13 @@ class LaTeXBuilder(Builder):
'[2016/05/29 stylesheet for highlighting with pygments]\n\n')
f.write(highlighter.get_stylesheet())
def write(self, *ignored):
# type: (Any) -> None
def write(self, *ignored: Any) -> None:
docwriter = LaTeXWriter(self)
docsettings = OptionParser(
defaults=self.env.settings,
components=(docwriter,),
read_config_files=True).get_default_values() # type: Any
patch_settings(docsettings)
self.init_document_data()
self.write_stylesheet()
@ -251,34 +234,33 @@ class LaTeXBuilder(Builder):
destination = SphinxFileOutput(destination_path=path.join(self.outdir, targetname),
encoding='utf-8', overwrite_if_changed=True)
with progress_message(__("processing %s") % targetname):
toctrees = self.env.get_doctree(docname).traverse(addnodes.toctree)
if toctrees:
if toctrees[0].get('maxdepth') > 0:
tocdepth = toctrees[0].get('maxdepth')
else:
tocdepth = None
doctree = self.env.get_doctree(docname)
toctree = next(iter(doctree.traverse(addnodes.toctree)), None)
if toctree and toctree.get('maxdepth') > 0:
tocdepth = toctree.get('maxdepth')
else:
tocdepth = None
doctree = self.assemble_doctree(
docname, toctree_only,
appendices=((docclass != 'howto') and self.config.latex_appendices or []))
appendices=(self.config.latex_appendices if docclass != 'howto' else []))
doctree['docclass'] = docclass
doctree['contentsname'] = self.get_contentsname(docname)
doctree['tocdepth'] = tocdepth
self.apply_transforms(doctree)
self.post_process_images(doctree)
self.update_doc_context(title, author)
with progress_message(__("writing")):
docsettings.author = author
docsettings.title = title
docsettings.contentsname = self.get_contentsname(docname)
docsettings.docname = docname
docsettings.docclass = docclass
docsettings._author = author
docsettings._title = title
docsettings._contentsname = doctree['contentsname']
docsettings._docname = docname
docsettings._docclass = docclass
doctree.settings = docsettings
docwriter.write(doctree, destination)
def get_contentsname(self, indexfile):
# type: (str) -> str
def get_contentsname(self, indexfile: str) -> str:
tree = self.env.get_doctree(indexfile)
contentsname = None
for toctree in tree.traverse(addnodes.toctree):
@ -288,14 +270,11 @@ class LaTeXBuilder(Builder):
return contentsname
def update_doc_context(self, title, author):
# type: (str, str) -> None
def update_doc_context(self, title: str, author: str) -> None:
self.context['title'] = title
self.context['author'] = author
def assemble_doctree(self, indexfile, toctree_only, appendices):
# type: (str, bool, List[str]) -> nodes.document
from docutils import nodes # NOQA
def assemble_doctree(self, indexfile: str, toctree_only: bool, appendices: List[str]) -> nodes.document: # NOQA
self.docnames = set([indexfile] + appendices)
logger.info(darkgreen(indexfile) + " ", nonl=True)
tree = self.env.get_doctree(indexfile)
@ -326,7 +305,7 @@ class LaTeXBuilder(Builder):
for pendingnode in largetree.traverse(addnodes.pending_xref):
docname = pendingnode['refdocname']
sectname = pendingnode['refsectname']
newnodes = [nodes.emphasis(sectname, sectname)] # type: List[nodes.Node]
newnodes = [nodes.emphasis(sectname, sectname)] # type: List[Node]
for subdir, title in self.titles:
if docname.startswith(subdir):
newnodes.append(nodes.Text(_(' (in '), _(' (in ')))
@ -338,19 +317,11 @@ class LaTeXBuilder(Builder):
pendingnode.replace_self(newnodes)
return largetree
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()
def apply_transforms(self, doctree: nodes.document) -> None:
warnings.warn('LaTeXBuilder.apply_transforms() is deprecated.',
RemovedInSphinx40Warning)
def finish(self):
# type: () -> None
def finish(self) -> None:
self.copy_image_files()
self.write_message_catalog()
self.copy_support_files()
@ -359,8 +330,7 @@ class LaTeXBuilder(Builder):
self.copy_latex_additional_files()
@progress_message(__('copying TeX support files'))
def copy_support_files(self):
# type: () -> None
def copy_support_files(self) -> None:
"""copy TeX support files from texinputs."""
# configure usage of xindy (impacts Makefile and latexmkrc)
# FIXME: convert this rather to a confval with suitable default
@ -392,22 +362,13 @@ class LaTeXBuilder(Builder):
copy_asset_file(path.join(staticdirname, 'Makefile_t'),
self.outdir, context=context)
# the logo is handled differently
if self.config.latex_logo:
if not path.isfile(path.join(self.confdir, self.config.latex_logo)):
raise SphinxError(__('logo file %r does not exist') % self.config.latex_logo)
else:
copy_asset_file(path.join(self.confdir, self.config.latex_logo), self.outdir)
@progress_message(__('copying additional files'))
def copy_latex_additional_files(self):
# type: () -> None
def copy_latex_additional_files(self) -> None:
for filename in self.config.latex_additional_files:
logger.info(' ' + filename, nonl=True)
copy_asset_file(path.join(self.confdir, filename), self.outdir)
def copy_image_files(self):
# type: () -> None
def copy_image_files(self) -> None:
if self.images:
stringify_func = ImageAdapter(self.app.env).get_original_image_uri
for src in status_iterator(self.images, __('copying images... '), "brown",
@ -420,9 +381,13 @@ class LaTeXBuilder(Builder):
except Exception as err:
logger.warning(__('cannot copy image file %r: %s'),
path.join(self.srcdir, src), err)
if self.config.latex_logo:
if not path.isfile(path.join(self.confdir, self.config.latex_logo)):
raise SphinxError(__('logo file %r does not exist') % self.config.latex_logo)
else:
copy_asset_file(path.join(self.confdir, self.config.latex_logo), self.outdir)
def write_message_catalog(self):
# type: () -> None
def write_message_catalog(self) -> None:
formats = self.config.numfig_format
context = {
'addtocaptions': r'\@iden',
@ -438,8 +403,45 @@ class LaTeXBuilder(Builder):
copy_asset_file(filename, self.outdir, context=context, renderer=LaTeXRenderer())
def validate_config_values(app, config):
# type: (Sphinx, Config) -> None
def patch_settings(settings: Any) -> Any:
"""Make settings object to show deprecation messages."""
class Values(type(settings)): # type: ignore
@property
def author(self):
warnings.warn('settings.author is deprecated',
RemovedInSphinx40Warning, stacklevel=2)
return self._author
@property
def title(self):
warnings.warn('settings.title is deprecated',
RemovedInSphinx40Warning, stacklevel=2)
return self._title
@property
def contentsname(self):
warnings.warn('settings.contentsname is deprecated',
RemovedInSphinx40Warning, stacklevel=2)
return self._contentsname
@property
def docname(self):
warnings.warn('settings.docname is deprecated',
RemovedInSphinx40Warning, stacklevel=2)
return self._docname
@property
def docclass(self):
warnings.warn('settings.docclass is deprecated',
RemovedInSphinx40Warning, stacklevel=2)
return self._docclass
# dynamic subclassing
settings.__class__ = Values
def validate_config_values(app: Sphinx, config: Config) -> None:
for key in list(config.latex_elements):
if key not in DEFAULT_SETTINGS:
msg = __("Unknown configure key: latex_elements[%r]. ignored.")
@ -447,56 +449,60 @@ def validate_config_values(app, config):
config.latex_elements.pop(key)
def default_latex_engine(config):
# type: (Config) -> str
def default_latex_engine(config: Config) -> str:
""" Better default latex_engine settings for specific languages. """
if config.language == 'ja':
return 'platex'
elif (config.language or '').startswith('zh'):
return 'xelatex'
elif config.language == 'el':
return 'xelatex'
else:
return 'pdflatex'
def default_latex_docclass(config):
# type: (Config) -> Dict[str, str]
def default_latex_docclass(config: Config) -> Dict[str, str]:
""" Better default latex_docclass settings for specific languages. """
if config.language == 'ja':
return {'manual': 'jsbook',
'howto': 'jreport'}
if config.latex_engine == 'uplatex':
return {'manual': 'ujbook',
'howto': 'ujreport'}
else:
return {'manual': 'jsbook',
'howto': 'jreport'}
else:
return {}
def default_latex_use_xindy(config):
# type: (Config) -> bool
def default_latex_use_xindy(config: Config) -> bool:
""" Better default latex_use_xindy settings for specific engines. """
return config.latex_engine in {'xelatex', 'lualatex'}
def default_latex_documents(config):
# type: (Config) -> List[Tuple[str, str, str, str, str]]
def default_latex_documents(config: Config) -> List[Tuple[str, str, str, str, str]]:
""" Better default latex_documents settings. """
project = texescape.escape(config.project, config.latex_engine)
author = texescape.escape(config.author, config.latex_engine)
return [(config.master_doc,
make_filename_from_project(config.project) + '.tex',
texescape.escape_abbr(texescape.escape(config.project)),
texescape.escape_abbr(texescape.escape(config.author)),
texescape.escape_abbr(project),
texescape.escape_abbr(author),
'manual')]
def setup(app):
# type: (Sphinx) -> Dict[str, Any]
def setup(app: 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'))
ENUM('pdflatex', 'xelatex', 'lualatex', 'platex', 'uplatex'))
app.add_config_value('latex_documents', default_latex_documents, None)
app.add_config_value('latex_logo', None, None, [str])
app.add_config_value('latex_appendices', [], None)
app.add_config_value('latex_use_latex_multicolumn', False, None)
app.add_config_value('latex_use_xindy', default_latex_use_xindy, None)
app.add_config_value('latex_use_xindy', default_latex_use_xindy, None, [bool])
app.add_config_value('latex_toplevel_sectioning', None, None,
ENUM(None, 'part', 'chapter', 'section'))
app.add_config_value('latex_domain_indices', True, None, [list])

View File

@ -0,0 +1,192 @@
"""
sphinx.builders.latex.constants
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
consntants for LaTeX builder.
:copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from typing import Any, Dict
PDFLATEX_DEFAULT_FONTPKG = r'''
\usepackage{times}
\expandafter\ifx\csname T@LGR\endcsname\relax
\else
% LGR was declared as font encoding
\substitutefont{LGR}{\rmdefault}{cmr}
\substitutefont{LGR}{\sfdefault}{cmss}
\substitutefont{LGR}{\ttdefault}{cmtt}
\fi
\expandafter\ifx\csname T@X2\endcsname\relax
\expandafter\ifx\csname T@T2A\endcsname\relax
\else
% T2A was declared as font encoding
\substitutefont{T2A}{\rmdefault}{cmr}
\substitutefont{T2A}{\sfdefault}{cmss}
\substitutefont{T2A}{\ttdefault}{cmtt}
\fi
\else
% X2 was declared as font encoding
\substitutefont{X2}{\rmdefault}{cmr}
\substitutefont{X2}{\sfdefault}{cmss}
\substitutefont{X2}{\ttdefault}{cmtt}
\fi
'''
XELATEX_DEFAULT_FONTPKG = r'''
\setmainfont{FreeSerif}[
Extension = .otf,
UprightFont = *,
ItalicFont = *Italic,
BoldFont = *Bold,
BoldItalicFont = *BoldItalic
]
\setsansfont{FreeSans}[
Extension = .otf,
UprightFont = *,
ItalicFont = *Oblique,
BoldFont = *Bold,
BoldItalicFont = *BoldOblique,
]
\setmonofont{FreeMono}[
Extension = .otf,
UprightFont = *,
ItalicFont = *Oblique,
BoldFont = *Bold,
BoldItalicFont = *BoldOblique,
]
'''
XELATEX_GREEK_DEFAULT_FONTPKG = (XELATEX_DEFAULT_FONTPKG +
'\n\\newfontfamily\\greekfont{FreeSerif}' +
'\n\\newfontfamily\\greekfontsf{FreeSans}' +
'\n\\newfontfamily\\greekfonttt{FreeMono}')
LUALATEX_DEFAULT_FONTPKG = XELATEX_DEFAULT_FONTPKG
DEFAULT_SETTINGS = {
'latex_engine': 'pdflatex',
'papersize': 'letterpaper',
'pointsize': '10pt',
'pxunit': '.75bp',
'classoptions': '',
'extraclassoptions': '',
'maxlistdepth': '',
'sphinxpkgoptions': '',
'sphinxsetup': '',
'fvset': '\\fvset{fontsize=\\small}',
'passoptionstopackages': '',
'geometry': '\\usepackage{geometry}',
'inputenc': '',
'utf8extra': '',
'cmappkg': '\\usepackage{cmap}',
'fontenc': '\\usepackage[T1]{fontenc}',
'amsmath': '\\usepackage{amsmath,amssymb,amstext}',
'multilingual': '',
'babel': '\\usepackage{babel}',
'polyglossia': '',
'fontpkg': PDFLATEX_DEFAULT_FONTPKG,
'substitutefont': '',
'textcyrillic': '',
'textgreek': '\\usepackage{textalpha}',
'fncychap': '\\usepackage[Bjarne]{fncychap}',
'hyperref': ('% Include hyperref last.\n'
'\\usepackage{hyperref}\n'
'% Fix anchor placement for figures with captions.\n'
'\\usepackage{hypcap}% it must be loaded after hyperref.\n'
'% Set up styles of URL: it should be placed after hyperref.\n'
'\\urlstyle{same}'),
'contentsname': '',
'extrapackages': '',
'preamble': '',
'title': '',
'release': '',
'author': '',
'releasename': '',
'makeindex': '\\makeindex',
'shorthandoff': '',
'maketitle': '\\sphinxmaketitle',
'tableofcontents': '\\sphinxtableofcontents',
'atendofbody': '',
'printindex': '\\printindex',
'transition': '\n\n\\bigskip\\hrule\\bigskip\n\n',
'figure_align': 'htbp',
'tocdepth': '',
'secnumdepth': '',
} # type: Dict[str, Any]
ADDITIONAL_SETTINGS = {
'pdflatex': {
'inputenc': '\\usepackage[utf8]{inputenc}',
'utf8extra': ('\\ifdefined\\DeclareUnicodeCharacter\n'
'% support both utf8 and utf8x syntaxes\n'
' \\ifdefined\\DeclareUnicodeCharacterAsOptional\n'
' \\def\\sphinxDUC#1{\\DeclareUnicodeCharacter{"#1}}\n'
' \\else\n'
' \\let\\sphinxDUC\\DeclareUnicodeCharacter\n'
' \\fi\n'
' \\sphinxDUC{00A0}{\\nobreakspace}\n'
' \\sphinxDUC{2500}{\\sphinxunichar{2500}}\n'
' \\sphinxDUC{2502}{\\sphinxunichar{2502}}\n'
' \\sphinxDUC{2514}{\\sphinxunichar{2514}}\n'
' \\sphinxDUC{251C}{\\sphinxunichar{251C}}\n'
' \\sphinxDUC{2572}{\\textbackslash}\n'
'\\fi'),
},
'xelatex': {
'latex_engine': 'xelatex',
'polyglossia': '\\usepackage{polyglossia}',
'babel': '',
'fontenc': ('\\usepackage{fontspec}\n'
'\\defaultfontfeatures[\\rmfamily,\\sffamily,\\ttfamily]{}'),
'fontpkg': XELATEX_DEFAULT_FONTPKG,
'textgreek': '',
'utf8extra': ('\\catcode`^^^^00a0\\active\\protected\\def^^^^00a0'
'{\\leavevmode\\nobreak\\ }'),
},
'lualatex': {
'latex_engine': 'lualatex',
'polyglossia': '\\usepackage{polyglossia}',
'babel': '',
'fontenc': ('\\usepackage{fontspec}\n'
'\\defaultfontfeatures[\\rmfamily,\\sffamily,\\ttfamily]{}'),
'fontpkg': LUALATEX_DEFAULT_FONTPKG,
'textgreek': '',
'utf8extra': ('\\catcode`^^^^00a0\\active\\protected\\def^^^^00a0'
'{\\leavevmode\\nobreak\\ }'),
},
'platex': {
'latex_engine': 'platex',
'babel': '',
'classoptions': ',dvipdfmx',
'fontpkg': '\\usepackage{times}',
'textgreek': '',
'fncychap': '',
'geometry': '\\usepackage[dvipdfm]{geometry}',
},
'uplatex': {
'latex_engine': 'uplatex',
'babel': '',
'classoptions': ',dvipdfmx',
'fontpkg': '\\usepackage{times}',
'textgreek': '',
'fncychap': '',
'geometry': '\\usepackage[dvipdfm]{geometry}',
},
# special settings for latex_engine + language_code
('xelatex', 'fr'): {
# use babel instead of polyglossia by default
'polyglossia': '',
'babel': '\\usepackage{babel}',
},
('xelatex', 'zh'): {
'fontenc': '\\usepackage{xeCJK}',
},
('xelatex', 'el'): {
'fontpkg': XELATEX_GREEK_DEFAULT_FONTPKG,
},
} # type: Dict[Any, Dict[str, Any]]

View File

@ -4,7 +4,7 @@
Additional nodes for LaTeX writer.
:copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

View File

@ -4,25 +4,26 @@
Transforms for LaTeX builder.
:copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from typing import Any, Dict, List, Set, Tuple
from typing import cast
from docutils import nodes
from docutils.nodes import Element, Node
from sphinx import addnodes
from sphinx.application import Sphinx
from sphinx.builders.latex.nodes import (
captioned_literal_block, footnotemark, footnotetext, math_reference, thebibliography
)
from sphinx.domains.citation import CitationDomain
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
URI_SCHEMES = ('mailto:', 'http:', 'https:', 'ftp:')
@ -31,14 +32,13 @@ class FootnoteDocnameUpdater(SphinxTransform):
default_priority = 700
TARGET_NODES = (nodes.footnote, nodes.footnote_reference)
def apply(self, **kwargs):
# type: (Any) -> None
def apply(self, **kwargs: Any) -> None:
matcher = NodeMatcher(*self.TARGET_NODES)
for node in self.document.traverse(matcher): # type: nodes.Element
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,12 +46,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):
# type: (Any) -> None
def run(self, **kwargs: Any) -> None:
try:
# replace id_prefix temporarily
settings = self.document.settings # type: Any
@ -65,8 +65,7 @@ class ShowUrlsTransform(SphinxTransform):
# restore id_prefix
settings.id_prefix = id_prefix
def expand_show_urls(self):
# type: () -> None
def expand_show_urls(self) -> None:
show_urls = self.config.latex_show_urls
if show_urls is False or show_urls == 'no':
return
@ -89,8 +88,7 @@ class ShowUrlsTransform(SphinxTransform):
textnode = nodes.Text(" (%s)" % uri)
node.parent.insert(index + 1, textnode)
def get_docname_for_node(self, node):
# type: (nodes.Node) -> str
def get_docname_for_node(self, node: Node) -> str:
while node:
if isinstance(node, nodes.document):
return self.env.path2doc(node['source'])
@ -101,8 +99,7 @@ class ShowUrlsTransform(SphinxTransform):
return None # never reached here. only for type hinting
def create_footnote(self, uri, docname):
# type: (str, str) -> Tuple[nodes.footnote, nodes.footnote_reference]
def create_footnote(self, uri: str, docname: str) -> Tuple[nodes.footnote, nodes.footnote_reference]: # NOQA
reference = nodes.reference('', nodes.Text(uri), refuri=uri, nolinkurl=True)
footnote = nodes.footnote(uri, auto=1, docname=docname)
footnote['names'].append('#')
@ -118,8 +115,7 @@ class ShowUrlsTransform(SphinxTransform):
return footnote, footnote_ref
def renumber_footnotes(self):
# type: () -> None
def renumber_footnotes(self) -> None:
collector = FootnoteCollector(self.document)
self.document.walkabout(collector)
@ -149,35 +145,30 @@ class ShowUrlsTransform(SphinxTransform):
class FootnoteCollector(nodes.NodeVisitor):
"""Collect footnotes and footnote references on the document"""
def __init__(self, document):
# type: (nodes.document) -> None
def __init__(self, document: nodes.document) -> None:
self.auto_footnotes = [] # type: List[nodes.footnote]
self.used_footnote_numbers = set() # type: Set[str]
self.footnote_refs = [] # type: List[nodes.footnote_reference]
super().__init__(document)
def unknown_visit(self, node):
# type: (nodes.Node) -> None
def unknown_visit(self, node: Node) -> None:
pass
def unknown_departure(self, node):
# type: (nodes.Node) -> None
def unknown_departure(self, node: Node) -> None:
pass
def visit_footnote(self, node):
# type: (nodes.footnote) -> None
def visit_footnote(self, node: nodes.footnote) -> None:
if node.get('auto'):
self.auto_footnotes.append(node)
else:
for name in node['names']:
self.used_footnote_numbers.add(name)
def visit_footnote_reference(self, node):
# type: (nodes.footnote_reference) -> None
def visit_footnote_reference(self, node: nodes.footnote_reference) -> None:
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,9 +336,9 @@ class LaTeXFootnoteTransform(SphinxTransform):
"""
default_priority = 600
builders = ('latex',)
def apply(self, **kwargs):
# type: (Any) -> None
def run(self, **kwargs: Any) -> None:
footnotes = list(self.document.traverse(nodes.footnote))
for node in footnotes:
node.parent.remove(node)
@ -357,8 +348,7 @@ class LaTeXFootnoteTransform(SphinxTransform):
class LaTeXFootnoteVisitor(nodes.NodeVisitor):
def __init__(self, document, footnotes):
# type: (nodes.document, List[nodes.footnote]) -> None
def __init__(self, document: nodes.document, footnotes: List[nodes.footnote]) -> None:
self.appeared = set() # type: Set[Tuple[str, str]]
self.footnotes = footnotes # type: List[nodes.footnote]
self.pendings = [] # type: List[nodes.footnote]
@ -366,21 +356,17 @@ class LaTeXFootnoteVisitor(nodes.NodeVisitor):
self.restricted = None # type: nodes.Element
super().__init__(document)
def unknown_visit(self, node):
# type: (nodes.Node) -> None
def unknown_visit(self, node: Node) -> None:
pass
def unknown_departure(self, node):
# type: (nodes.Node) -> None
def unknown_departure(self, node: Node) -> None:
pass
def restrict(self, node):
# type: (nodes.Element) -> None
def restrict(self, node: Element) -> None:
if self.restricted is None:
self.restricted = node
def unrestrict(self, node):
# type: (nodes.Element) -> None
def unrestrict(self, node: Element) -> None:
if self.restricted == node:
self.restricted = None
pos = node.parent.index(node)
@ -389,37 +375,29 @@ class LaTeXFootnoteVisitor(nodes.NodeVisitor):
node.parent.insert(pos + i + 1, fntext)
self.pendings = []
def visit_figure(self, node):
# type: (nodes.figure) -> None
def visit_figure(self, node: nodes.figure) -> None:
self.restrict(node)
def depart_figure(self, node):
# type: (nodes.figure) -> None
def depart_figure(self, node: nodes.figure) -> None:
self.unrestrict(node)
def visit_term(self, node):
# type: (nodes.term) -> None
def visit_term(self, node: nodes.term) -> None:
self.restrict(node)
def depart_term(self, node):
# type: (nodes.term) -> None
def depart_term(self, node: nodes.term) -> None:
self.unrestrict(node)
def visit_caption(self, node):
# type: (nodes.caption) -> None
def visit_caption(self, node: nodes.caption) -> None:
self.restrict(node)
def depart_caption(self, node):
# type: (nodes.caption) -> None
def depart_caption(self, node: nodes.caption) -> None:
self.unrestrict(node)
def visit_title(self, node):
# type: (nodes.title) -> None
def visit_title(self, node: nodes.title) -> None:
if isinstance(node.parent, (nodes.section, nodes.table)):
self.restrict(node)
def depart_title(self, node):
# type: (nodes.title) -> None
def depart_title(self, node: nodes.title) -> None:
if isinstance(node.parent, nodes.section):
self.unrestrict(node)
elif isinstance(node.parent, nodes.table):
@ -427,18 +405,15 @@ class LaTeXFootnoteVisitor(nodes.NodeVisitor):
self.pendings = []
self.unrestrict(node)
def visit_thead(self, node):
# type: (nodes.thead) -> None
def visit_thead(self, node: nodes.thead) -> None:
self.restrict(node)
def depart_thead(self, node):
# type: (nodes.thead) -> None
def depart_thead(self, node: nodes.thead) -> None:
self.table_footnotes += self.pendings
self.pendings = []
self.unrestrict(node)
def depart_table(self, node):
# type: (nodes.table) -> None
def depart_table(self, node: nodes.table) -> None:
tbody = list(node.traverse(nodes.tbody))[0]
for footnote in reversed(self.table_footnotes):
fntext = footnotetext('', *footnote.children)
@ -446,16 +421,13 @@ class LaTeXFootnoteVisitor(nodes.NodeVisitor):
self.table_footnotes = []
def visit_footnote(self, node):
# type: (nodes.footnote) -> None
def visit_footnote(self, node: nodes.footnote) -> None:
self.restrict(node)
def depart_footnote(self, node):
# type: (nodes.footnote) -> None
def depart_footnote(self, node: nodes.footnote) -> None:
self.unrestrict(node)
def visit_footnote_reference(self, node):
# type: (nodes.footnote_reference) -> None
def visit_footnote_reference(self, node: nodes.footnote_reference) -> None:
number = node.astext().strip()
docname = node['docname']
if self.restricted:
@ -476,8 +448,7 @@ class LaTeXFootnoteVisitor(nodes.NodeVisitor):
self.appeared.add((docname, number))
raise nodes.SkipNode
def get_footnote_by_reference(self, node):
# type: (nodes.footnote_reference) -> nodes.footnote
def get_footnote_by_reference(self, node: nodes.footnote_reference) -> nodes.footnote:
docname = node['docname']
for footnote in self.footnotes:
if docname == footnote['docname'] and footnote['ids'][0] == node['refid']:
@ -486,7 +457,7 @@ class LaTeXFootnoteVisitor(nodes.NodeVisitor):
return None
class BibliographyTransform(SphinxTransform):
class BibliographyTransform(SphinxPostTransform):
"""Gather bibliography entries to tail of document.
Before::
@ -517,9 +488,9 @@ class BibliographyTransform(SphinxTransform):
...
"""
default_priority = 750
builders = ('latex',)
def apply(self, **kwargs):
# type: (Any) -> None
def run(self, **kwargs: Any) -> None:
citations = thebibliography()
for node in self.document.traverse(nodes.citation):
node.parent.remove(node)
@ -529,42 +500,36 @@ 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):
# 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']
def run(self, **kwargs: Any) -> None:
domain = cast(CitationDomain, self.env.get_domain('citation'))
matcher = NodeMatcher(addnodes.pending_xref, refdomain='citation', reftype='ref')
for node in self.document.traverse(matcher): # type: addnodes.pending_xref
docname, labelid, _ = citations.get(node['reftarget'], ('', '', 0))
docname, labelid, _ = domain.citations.get(node['reftarget'], ('', '', 0))
if docname:
citation_ref = nodes.citation_reference('', '', *node.children,
docname=docname, refname=labelid)
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):
# type: (Any) -> None
if self.app.builder.name != 'latex':
return
def run(self, **kwargs: Any) -> None:
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 +539,80 @@ 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):
# type: (Any) -> None
if self.app.builder.name != 'latex':
return
def run(self, **kwargs: Any) -> None:
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):
# type: (Any) -> None
if self.app.builder.name != 'latex':
return
def run(self, **kwargs: Any) -> None:
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: Sphinx) -> Dict[str, Any]:
app.add_transform(FootnoteDocnameUpdater)
app.add_post_transform(BibliographyTransform)
app.add_post_transform(CitationReferenceTransform)
app.add_post_transform(DocumentTargetTransform)
app.add_post_transform(IndexInSectionTitleTransform)
app.add_post_transform(LaTeXFootnoteTransform)
app.add_post_transform(LiteralBlockTransform)
app.add_post_transform(MathReferenceTransform)
app.add_post_transform(ShowUrlsTransform)
return {
'version': 'builtin',
'parallel_read_safe': True,
'parallel_write_safe': True,
}

View File

@ -8,53 +8,39 @@
: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')
def __init__(self, language_code, use_polyglossia=False):
# type: (str, bool) -> None
def __init__(self, language_code: str, use_polyglossia: bool = False) -> None:
self.language_code = language_code
self.use_polyglossia = use_polyglossia
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
def uses_cyrillic(self) -> bool:
return self.language in self.cyrillic_languages
def is_supported_language(self):
# type: () -> bool
def is_supported_language(self) -> bool:
return self.supported
def language_name(self, language_code):
# type: (str) -> str
def language_name(self, language_code: str) -> str:
language = super().language_name(language_code)
if language == 'ngerman' and self.use_polyglossia:
# polyglossia calls new orthography (Neue Rechtschreibung) as
# german (with new spelling option).
return 'german'
elif not language:
elif language:
return language
elif language_code.startswith('zh'):
return 'english' # fallback to english (behaves like supported)
else:
self.supported = False
return 'english' # fallback to english
else:
return language
def get_mainlanguage_options(self):
# type: () -> str
def get_mainlanguage_options(self) -> str:
"""Return options for polyglossia's ``\\setmainlanguage``."""
if self.use_polyglossia is False:
return None

View File

@ -4,7 +4,7 @@
The CheckExternalLinksBuilder class.
:copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@ -14,26 +14,23 @@ import socket
import threading
from html.parser import HTMLParser
from os import path
from typing import Any, Dict, List, Set, Tuple
from urllib.parse import unquote
from docutils import nodes
from docutils.nodes import Node
from requests.exceptions import HTTPError
from sphinx.application import Sphinx
from sphinx.builders import Builder
from sphinx.locale import __
from sphinx.util import encode_uri, requests, logging
from sphinx.util.console import ( # type: ignore
purple, red, darkgreen, darkgray, darkred, turquoise
purple, red, darkgreen, darkgray, turquoise
)
from sphinx.util.nodes import get_node_line
from sphinx.util.requests import is_ssl_error
if False:
# For type annotation
from typing import Any, Dict, List, Set, Tuple, Union # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.util.requests.requests import Response # NOQA
logger = logging.getLogger(__name__)
@ -41,23 +38,20 @@ logger = logging.getLogger(__name__)
class AnchorCheckParser(HTMLParser):
"""Specialized HTML parser that looks for a specific anchor."""
def __init__(self, search_anchor):
# type: (str) -> None
def __init__(self, search_anchor: str) -> None:
super().__init__()
self.search_anchor = search_anchor
self.found = False
def handle_starttag(self, tag, attrs):
# type: (Any, Any) -> None
def handle_starttag(self, tag: Any, attrs: Any) -> None:
for key, value in attrs:
if key in ('id', 'name') and value == self.search_anchor:
self.found = True
break
def check_anchor(response, anchor):
# type: (Response, str) -> bool
def check_anchor(response: requests.requests.Response, anchor: str) -> bool:
"""Reads HTML data from a response object `response` searching for `anchor`.
Returns True if anchor was found, False otherwise.
"""
@ -65,6 +59,9 @@ def check_anchor(response, anchor):
# Read file in chunks. If we find a matching anchor, we break
# the loop early in hopes not to have to download the whole thing.
for chunk in response.iter_content(chunk_size=4096, decode_unicode=True):
if isinstance(chunk, bytes): # requests failed to decode
chunk = chunk.decode() # manually try to decode it
parser.feed(chunk)
if parser.found:
break
@ -80,11 +77,12 @@ class CheckExternalLinksBuilder(Builder):
epilog = __('Look for any errors in the above output or in '
'%(outdir)s/output.txt')
def init(self):
# type: () -> None
def init(self) -> None:
self.to_ignore = [re.compile(x) for x in self.app.config.linkcheck_ignore]
self.anchors_ignore = [re.compile(x)
for x in self.app.config.linkcheck_anchors_ignore]
self.auth = [(re.compile(pattern), auth_info) for pattern, auth_info
in self.app.config.linkcheck_auth]
self.good = set() # type: Set[str]
self.broken = {} # type: Dict[str, str]
self.redirected = {} # type: Dict[str, Tuple[str, int]]
@ -103,19 +101,17 @@ class CheckExternalLinksBuilder(Builder):
thread.start()
self.workers.append(thread)
def check_thread(self):
# type: () -> None
def check_thread(self) -> None:
kwargs = {
'allow_redirects': True,
'headers': {
'Accept': 'text/html,application/xhtml+xml;q=0.9,*/*;q=0.8'
'Accept': 'text/html,application/xhtml+xml;q=0.9,*/*;q=0.8',
},
}
if self.app.config.linkcheck_timeout:
kwargs['timeout'] = self.app.config.linkcheck_timeout
def check_uri():
# type: () -> Tuple[str, str, int]
def check_uri() -> Tuple[str, str, int]:
# split off anchor
if '#' in uri:
req_url, anchor = uri.split('#', 1)
@ -133,11 +129,18 @@ class CheckExternalLinksBuilder(Builder):
except UnicodeError:
req_url = encode_uri(req_url)
# Get auth info, if any
for pattern, auth_info in self.auth:
if pattern.match(uri):
break
else:
auth_info = None
try:
if anchor and self.app.config.linkcheck_anchors:
# Read the whole document and see if #anchor exists
response = requests.get(req_url, stream=True, config=self.app.config,
**kwargs)
auth=auth_info, **kwargs)
found = check_anchor(response, unquote(anchor))
if not found:
@ -146,18 +149,22 @@ class CheckExternalLinksBuilder(Builder):
try:
# try a HEAD request first, which should be easier on
# the server and the network
response = requests.head(req_url, config=self.app.config, **kwargs)
response = requests.head(req_url, config=self.app.config,
auth=auth_info, **kwargs)
response.raise_for_status()
except HTTPError:
# retry with GET request if that fails, some servers
# don't like HEAD requests.
response = requests.get(req_url, stream=True, config=self.app.config,
**kwargs)
auth=auth_info, **kwargs)
response.raise_for_status()
except HTTPError as err:
if err.response.status_code == 401:
# We'll take "Unauthorized" as working.
return 'working', ' - unauthorized', 0
elif err.response.status_code == 503:
# We'll take "Service Unavailable" as ignored.
return 'ignored', str(err), 0
else:
return 'broken', str(err), 0
except Exception as err:
@ -178,8 +185,7 @@ class CheckExternalLinksBuilder(Builder):
else:
return 'redirected', new_url, 0
def check():
# type: () -> Tuple[str, str, int]
def check() -> Tuple[str, str, int]:
# check for various conditions without bothering the network
if len(uri) == 0 or uri.startswith(('#', 'mailto:', 'ftp:')):
return 'unchecked', '', 0
@ -217,8 +223,7 @@ class CheckExternalLinksBuilder(Builder):
status, info, code = check()
self.rqueue.put((uri, docname, lineno, status, info, code))
def process_result(self, result):
# type: (Tuple[str, str, int, str, str, int]) -> None
def process_result(self, result: Tuple[str, str, int, str, str, int]) -> None:
uri, docname, lineno, status, info, code = result
if status == 'unchecked':
return
@ -244,31 +249,30 @@ class CheckExternalLinksBuilder(Builder):
else:
logger.info(red('broken ') + uri + red(' - ' + info))
elif status == 'redirected':
text, color = {
301: ('permanently', darkred),
302: ('with Found', purple),
303: ('with See Other', purple),
307: ('temporarily', turquoise),
0: ('with unknown code', purple),
}[code]
try:
text, color = {
301: ('permanently', purple),
302: ('with Found', purple),
303: ('with See Other', purple),
307: ('temporarily', turquoise),
308: ('permanently', purple),
}[code]
except KeyError:
text, color = ('with unknown code', purple)
self.write_entry('redirected ' + text, docname, lineno,
uri + ' to ' + info)
logger.info(color('redirect ') + uri + color(' - ' + text + ' to ' + info))
def get_target_uri(self, docname, typ=None):
# type: (str, str) -> str
def get_target_uri(self, docname: str, typ: str = None) -> str:
return ''
def get_outdated_docs(self):
# type: () -> Set[str]
def get_outdated_docs(self) -> Set[str]:
return self.env.found_docs
def prepare_writing(self, docnames):
# type: (Set[str]) -> None
def prepare_writing(self, docnames: Set[str]) -> None:
return
def write_doc(self, docname, doctree):
# type: (str, nodes.Node) -> None
def write_doc(self, docname: str, doctree: Node) -> None:
logger.info('')
n = 0
@ -297,23 +301,21 @@ class CheckExternalLinksBuilder(Builder):
if self.broken:
self.app.statuscode = 1
def write_entry(self, what, docname, line, uri):
# type: (str, str, int, str) -> None
def write_entry(self, what: str, docname: str, line: int, uri: str) -> None:
with open(path.join(self.outdir, 'output.txt'), 'a', encoding='utf-8') as output:
output.write("%s:%s: [%s] %s\n" % (self.env.doc2path(docname, None),
line, what, uri))
def finish(self):
# type: () -> None
def finish(self) -> None:
for worker in self.workers:
self.wqueue.put((None, None, None), False)
def setup(app):
# type: (Sphinx) -> Dict[str, Any]
def setup(app: Sphinx) -> Dict[str, Any]:
app.add_builder(CheckExternalLinksBuilder)
app.add_config_value('linkcheck_ignore', [], None)
app.add_config_value('linkcheck_auth', [], None)
app.add_config_value('linkcheck_retries', 1, None)
app.add_config_value('linkcheck_timeout', None, None, [int])
app.add_config_value('linkcheck_workers', 5, None)

View File

@ -4,18 +4,21 @@
Manual pages builder.
:copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from os import path
from typing import Any, Dict, List, Set, Tuple, Union
from docutils.frontend import OptionParser
from docutils.io import FileOutput
from sphinx import addnodes
from sphinx.application import Sphinx
from sphinx.builders import Builder
from sphinx.environment import NoUri
from sphinx.config import Config
from sphinx.errors import NoUri
from sphinx.locale import __
from sphinx.util import logging
from sphinx.util import progress_message
@ -24,12 +27,6 @@ from sphinx.util.nodes import inline_all_toctrees
from sphinx.util.osutil import make_filename_from_project
from sphinx.writers.manpage import ManualPageWriter, ManualPageTranslator
if False:
# For type annotation
from typing import Any, Dict, List, Set, Tuple, Union # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.config import Config # NOQA
logger = logging.getLogger(__name__)
@ -45,25 +42,21 @@ class ManualPageBuilder(Builder):
default_translator_class = ManualPageTranslator
supported_image_types = [] # type: List[str]
def init(self):
# type: () -> None
def init(self) -> None:
if not self.config.man_pages:
logger.warning(__('no "man_pages" config value found; no manual pages '
'will be written'))
def get_outdated_docs(self):
# type: () -> Union[str, List[str]]
def get_outdated_docs(self) -> Union[str, List[str]]:
return 'all manpages' # for now
def get_target_uri(self, docname, typ=None):
# type: (str, str) -> str
def get_target_uri(self, docname: str, typ: str = None) -> str:
if typ == 'token':
return ''
raise NoUri
raise NoUri(docname, typ)
@progress_message(__('writing'))
def write(self, *ignored):
# type: (Any) -> None
def write(self, *ignored: Any) -> None:
docwriter = ManualPageWriter(self)
docsettings = OptionParser(
defaults=self.env.settings,
@ -106,21 +99,18 @@ class ManualPageBuilder(Builder):
docwriter.write(largetree, destination)
def finish(self):
# type: () -> None
def finish(self) -> None:
pass
def default_man_pages(config):
# type: (Config) -> List[Tuple[str, str, str, List[str], int]]
def default_man_pages(config: Config) -> List[Tuple[str, str, str, List[str], int]]:
""" Better default man_pages settings. """
filename = make_filename_from_project(config.project)
return [(config.master_doc, filename, '%s %s' % (config.project, config.release),
[config.author], 1)]
def setup(app):
# type: (Sphinx) -> Dict[str, Any]
def setup(app: Sphinx) -> Dict[str, Any]:
app.add_builder(ManualPageBuilder)
app.add_config_value('man_pages', default_man_pages, None)

View File

@ -4,22 +4,19 @@
Build input files for the Qt collection generator.
:copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import warnings
from typing import Any, Dict
from sphinxcontrib.qthelp import QtHelpBuilder, render_file
import sphinx
from sphinx.application import Sphinx
from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
if False:
# For type annotation
from typing import Any, Dict # NOQA
from sphinx.application import Sphinx # NOQA
deprecated_alias('sphinx.builders.qthelp',
{
@ -29,8 +26,7 @@ deprecated_alias('sphinx.builders.qthelp',
RemovedInSphinx40Warning)
def setup(app):
# type: (Sphinx) -> Dict[str, Any]
def setup(app: Sphinx) -> Dict[str, Any]:
warnings.warn('sphinx.builders.qthelp has been moved to sphinxcontrib-qthelp.',
RemovedInSphinx40Warning)

View File

@ -4,15 +4,19 @@
Single HTML builders.
:copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from os import path
from typing import Any, Dict, List, Tuple, Union
from docutils import nodes
from docutils.nodes import Node
from sphinx.application import Sphinx
from sphinx.builders.html import StandaloneHTMLBuilder
from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
from sphinx.environment.adapters.toctree import TocTree
from sphinx.locale import __
from sphinx.util import logging
@ -20,11 +24,6 @@ from sphinx.util import progress_message
from sphinx.util.console import darkgreen # type: ignore
from sphinx.util.nodes import inline_all_toctrees
if False:
# For type annotation
from typing import Any, Dict, List, Tuple, Union # NOQA
from sphinx.application import Sphinx # NOQA
logger = logging.getLogger(__name__)
@ -38,12 +37,10 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
copysource = False
def get_outdated_docs(self): # type: ignore
# type: () -> Union[str, List[str]]
def get_outdated_docs(self) -> Union[str, List[str]]: # type: ignore
return 'all documents'
def get_target_uri(self, docname, typ=None):
# type: (str, str) -> str
def get_target_uri(self, docname: str, typ: str = None) -> str:
if docname in self.env.all_docs:
# all references are on the same page...
return self.config.master_doc + self.out_suffix + \
@ -52,13 +49,11 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
# chances are this is a html_additional_page
return docname + self.out_suffix
def get_relative_uri(self, from_, to, typ=None):
# type: (str, str, str) -> str
def get_relative_uri(self, from_: str, to: str, typ: str = None) -> str:
# ignore source
return self.get_target_uri(to, typ)
def fix_refuris(self, tree):
# type: (nodes.Node) -> None
def fix_refuris(self, tree: Node) -> None:
# fix refuris with double anchor
fname = self.config.master_doc + self.out_suffix
for refnode in tree.traverse(nodes.reference):
@ -72,17 +67,15 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
if hashindex >= 0:
refnode['refuri'] = fname + refuri[hashindex:]
def _get_local_toctree(self, docname, collapse=True, **kwds):
# type: (str, bool, Any) -> str
if 'includehidden' not in kwds:
kwds['includehidden'] = False
toctree = TocTree(self.env).get_toctree_for(docname, self, collapse, **kwds)
def _get_local_toctree(self, docname: str, collapse: bool = True, **kwargs: Any) -> str:
if 'includehidden' not in kwargs:
kwargs['includehidden'] = False
toctree = TocTree(self.env).get_toctree_for(docname, self, collapse, **kwargs)
if toctree is not None:
self.fix_refuris(toctree)
return self.render_partial(toctree)['fragment']
def assemble_doctree(self):
# type: () -> nodes.document
def assemble_doctree(self) -> nodes.document:
master = self.config.master_doc
tree = self.env.get_doctree(master)
tree = inline_all_toctrees(self, set(), master, tree, darkgreen, [master])
@ -91,8 +84,7 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
self.fix_refuris(tree)
return tree
def assemble_toc_secnumbers(self):
# type: () -> Dict[str, Dict[str, Tuple[int, ...]]]
def assemble_toc_secnumbers(self) -> Dict[str, Dict[str, Tuple[int, ...]]]:
# Assemble toc_secnumbers to resolve section numbers on SingleHTML.
# Merge all secnumbers to single secnumber.
#
@ -110,8 +102,7 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
return {self.config.master_doc: new_secnumbers}
def assemble_toc_fignumbers(self):
# type: () -> Dict[str, Dict[str, Dict[str, Tuple[int, ...]]]]
def assemble_toc_fignumbers(self) -> Dict[str, Dict[str, Dict[str, Tuple[int, ...]]]]:
# Assemble toc_fignumbers to resolve figure numbers on SingleHTML.
# Merge all fignumbers to single fignumber.
#
@ -132,8 +123,7 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
return {self.config.master_doc: new_fignumbers}
def get_doc_context(self, docname, body, metatags):
# type: (str, str, str) -> Dict
def get_doc_context(self, docname: str, body: str, metatags: str) -> Dict:
# no relation links...
toctree = TocTree(self.env).get_toctree_for(self.config.master_doc, self, False)
# if there is no toctree, toc is None
@ -159,8 +149,7 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
'display_toc': display_toc,
}
def write(self, *ignored):
# type: (Any) -> None
def write(self, *ignored: Any) -> None:
docnames = self.env.all_docs
with progress_message(__('preparing documents')):
@ -175,8 +164,7 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
self.write_doc_serialized(self.config.master_doc, doctree)
self.write_doc(self.config.master_doc, doctree)
def finish(self):
# type: () -> None
def finish(self) -> None:
self.write_additional_files()
self.copy_image_files()
self.copy_download_files()
@ -186,8 +174,7 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
self.dump_inventory()
@progress_message(__('writing additional files'))
def write_additional_files(self):
# type: () -> None
def write_additional_files(self) -> None:
# no indices or search pages are supported
# additional pages from conf.py
@ -201,8 +188,15 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
self.handle_page('opensearch', {}, 'opensearch.xml', outfilename=fn)
def setup(app):
# type: (Sphinx) -> Dict[str, Any]
# for compatibility
deprecated_alias('sphinx.builders.html',
{
'SingleFileHTMLBuilder': SingleFileHTMLBuilder,
},
RemovedInSphinx40Warning)
def setup(app: Sphinx) -> Dict[str, Any]:
app.setup_extension('sphinx.builders.html')
app.add_builder(SingleFileHTMLBuilder)

View File

@ -4,12 +4,13 @@
Texinfo builder.
:copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import os
from os import path
from typing import Any, Dict, Iterable, List, Tuple, Union
from docutils import nodes
from docutils.frontend import OptionParser
@ -17,9 +18,11 @@ from docutils.io import FileOutput
from sphinx import addnodes
from sphinx import package_dir
from sphinx.application import Sphinx
from sphinx.builders import Builder
from sphinx.environment import NoUri
from sphinx.config import Config
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,15 +30,9 @@ 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:
# For type annotation
from sphinx.application import Sphinx # NOQA
from sphinx.config import Config # NOQA
from typing import Any, Dict, Iterable, List, Tuple, Union # NOQA
logger = logging.getLogger(__name__)
template_dir = os.path.join(package_dir, 'templates', 'texinfo')
@ -57,29 +54,24 @@ class TexinfoBuilder(Builder):
'image/gif']
default_translator_class = TexinfoTranslator
def init(self):
# type: () -> None
def init(self) -> None:
self.docnames = [] # type: Iterable[str]
self.document_data = [] # type: List[Tuple[str, str, str, str, str, str, str, bool]]
def get_outdated_docs(self):
# type: () -> Union[str, List[str]]
def get_outdated_docs(self) -> Union[str, List[str]]:
return 'all documents' # for now
def get_target_uri(self, docname, typ=None):
# type: (str, str) -> str
def get_target_uri(self, docname: str, typ: str = None) -> str:
if docname not in self.docnames:
raise NoUri
raise NoUri(docname, typ)
else:
return '%' + docname
def get_relative_uri(self, from_, to, typ=None):
# type: (str, str, str) -> str
def get_relative_uri(self, from_: str, to: str, typ: str = None) -> str:
# ignore source path
return self.get_target_uri(to, typ)
def init_document_data(self):
# type: () -> None
def init_document_data(self) -> None:
preliminary_document_data = [list(x) for x in self.config.texinfo_documents]
if not preliminary_document_data:
logger.warning(__('no "texinfo_documents" config value found; no documents '
@ -98,8 +90,7 @@ class TexinfoBuilder(Builder):
docname = docname[:-5]
self.titles.append((docname, entry[2]))
def write(self, *ignored):
# type: (Any) -> None
def write(self, *ignored: Any) -> None:
self.init_document_data()
for entry in self.document_data:
docname, targetname, title, author = entry[:4]
@ -134,9 +125,9 @@ 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
def assemble_doctree(self, indexfile: str, toctree_only: bool, appendices: List[str]) -> nodes.document: # NOQA
self.docnames = set([indexfile] + appendices)
logger.info(darkgreen(indexfile) + " ", nonl=True)
tree = self.env.get_doctree(indexfile)
@ -178,13 +169,10 @@ class TexinfoBuilder(Builder):
pendingnode.replace_self(newnodes)
return largetree
def finish(self):
# type: () -> None
self.copy_image_files()
def finish(self) -> None:
self.copy_support_files()
def copy_image_files(self):
# type: () -> None
def copy_image_files(self, targetname: 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,14 +180,14 @@ 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)
def copy_support_files(self):
# type: () -> None
def copy_support_files(self) -> None:
try:
with progress_message(__('copying Texinfo support files')):
logger.info('Makefile ', nonl=True)
@ -208,16 +196,14 @@ class TexinfoBuilder(Builder):
logger.warning(__("error writing file Makefile: %s"), err)
def default_texinfo_documents(config):
# type: (Config) -> List[Tuple[str, str, str, str, str, str, str]]
def default_texinfo_documents(config: Config) -> List[Tuple[str, str, str, str, str, str, str]]: # NOQA
""" Better default texinfo_documents settings. """
filename = make_filename_from_project(config.project)
return [(config.master_doc, filename, config.project, config.author, filename,
'One line description of project', 'Miscellaneous')]
def setup(app):
# type: (Sphinx) -> Dict[str, Any]
def setup(app: Sphinx) -> Dict[str, Any]:
app.add_builder(TexinfoBuilder)
app.add_config_value('texinfo_documents', default_texinfo_documents, None)

View File

@ -4,26 +4,23 @@
Plain-text Sphinx builder.
:copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from os import path
from typing import Any, Dict, Iterator, Set, Tuple
from docutils.io import StringOutput
from docutils.nodes import Node
from sphinx.application import Sphinx
from sphinx.builders import Builder
from sphinx.locale import __
from sphinx.util import logging
from sphinx.util.osutil import ensuredir, os_path
from sphinx.writers.text import TextWriter, TextTranslator
if False:
# For type annotation
from typing import Any, Dict, Iterator, Set, Tuple # NOQA
from docutils import nodes # NOQA
from sphinx.application import Sphinx # NOQA
logger = logging.getLogger(__name__)
@ -38,13 +35,11 @@ class TextBuilder(Builder):
current_docname = None # type: str
def init(self):
# type: () -> None
def init(self) -> None:
# section numbers for headings in the currently visited document
self.secnumbers = {} # type: Dict[str, Tuple[int, ...]]
def get_outdated_docs(self):
# type: () -> Iterator[str]
def get_outdated_docs(self) -> Iterator[str]:
for docname in self.env.found_docs:
if docname not in self.env.all_docs:
yield docname
@ -62,16 +57,13 @@ class TextBuilder(Builder):
# source doesn't exist anymore
pass
def get_target_uri(self, docname, typ=None):
# type: (str, str) -> str
def get_target_uri(self, docname: str, typ: str = None) -> str:
return ''
def prepare_writing(self, docnames):
# type: (Set[str]) -> None
def prepare_writing(self, docnames: Set[str]) -> None:
self.writer = TextWriter(self)
def write_doc(self, docname, doctree):
# type: (str, nodes.Node) -> None
def write_doc(self, docname: str, doctree: Node) -> None:
self.current_docname = docname
self.secnumbers = self.env.toc_secnumbers.get(docname, {})
destination = StringOutput(encoding='utf-8')
@ -84,13 +76,11 @@ class TextBuilder(Builder):
except OSError as err:
logger.warning(__("error writing file %s: %s"), outfilename, err)
def finish(self):
# type: () -> None
def finish(self) -> None:
pass
def setup(app):
# type: (Sphinx) -> Dict[str, Any]
def setup(app: Sphinx) -> Dict[str, Any]:
app.add_builder(TextBuilder)
app.add_config_value('text_sectionchars', '*=-~"+`', 'env')

View File

@ -4,16 +4,19 @@
Docutils-native XML and pseudo-XML builders.
:copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from os import path
from typing import Any, Dict, Iterator, Set, Union
from docutils import nodes
from docutils.io import StringOutput
from docutils.nodes import Node
from docutils.writers.docutils_xml import XMLTranslator
from sphinx.application import Sphinx
from sphinx.builders import Builder
from sphinx.locale import __
from sphinx.util import logging
@ -22,9 +25,8 @@ from sphinx.writers.xml import XMLWriter, PseudoXMLWriter
if False:
# For type annotation
from typing import Any, Dict, Iterator, Set, Type # NOQA
from docutils.writers.xml import BaseXMLWriter # NOQA
from sphinx.application import Sphinx # NOQA
from typing import Type # for python3.5.1
logger = logging.getLogger(__name__)
@ -40,15 +42,13 @@ class XMLBuilder(Builder):
out_suffix = '.xml'
allow_parallel = True
_writer_class = XMLWriter # type: Type[BaseXMLWriter]
_writer_class = XMLWriter # type: Union[Type[XMLWriter], Type[PseudoXMLWriter]]
default_translator_class = XMLTranslator
def init(self):
# type: () -> None
def init(self) -> None:
pass
def get_outdated_docs(self):
# type: () -> Iterator[str]
def get_outdated_docs(self) -> Iterator[str]:
for docname in self.env.found_docs:
if docname not in self.env.all_docs:
yield docname
@ -66,16 +66,13 @@ class XMLBuilder(Builder):
# source doesn't exist anymore
pass
def get_target_uri(self, docname, typ=None):
# type: (str, str) -> str
def get_target_uri(self, docname: str, typ: str = None) -> str:
return docname
def prepare_writing(self, docnames):
# type: (Set[str]) -> None
def prepare_writing(self, docnames: Set[str]) -> None:
self.writer = self._writer_class(self)
def write_doc(self, docname, doctree):
# type: (str, nodes.Node) -> None
def write_doc(self, docname: str, doctree: Node) -> None:
# work around multiple string % tuple issues in docutils;
# replace tuples in attribute values with lists
doctree = doctree.deepcopy()
@ -98,8 +95,7 @@ class XMLBuilder(Builder):
except OSError as err:
logger.warning(__("error writing file %s: %s"), outfilename, err)
def finish(self):
# type: () -> None
def finish(self) -> None:
pass
@ -116,8 +112,7 @@ class PseudoXMLBuilder(XMLBuilder):
_writer_class = PseudoXMLWriter
def setup(app):
# type: (Sphinx) -> Dict[str, Any]
def setup(app: Sphinx) -> Dict[str, Any]:
app.add_builder(XMLBuilder)
app.add_builder(PseudoXMLBuilder)

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