Merge branch 'master' into patch-1

This commit is contained in:
Takeshi KOMIYA 2021-04-10 00:32:26 +09:00 committed by GitHub
commit fe6d95e429
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
261 changed files with 6383 additions and 6976 deletions

View File

@ -4,16 +4,12 @@ on: [push, pull_request]
jobs:
ubuntu:
runs-on: ${{ matrix.os }}
runs-on: ubuntu-18.04
strategy:
fail-fast: false
matrix:
name: [py35, py36, py37, py38, py39]
os: [ubuntu-16.04]
name: [py36, py37, py38, py39, py310-dev]
include:
- name: py35
python: 3.5
docutils: du12
- name: py36
python: 3.6
docutils: du13
@ -29,8 +25,7 @@ jobs:
coverage: "--cov ./ --cov-append --cov-config setup.cfg"
- name: py310-dev
python: 3.10-dev
docutils: du17
os: ubuntu-latest # required
docutils: du16
env:
PYTEST_ADDOPTS: ${{ matrix.coverage }}

141
CHANGES
View File

@ -1,3 +1,139 @@
Release 4.0.0 (in development)
==============================
Dependencies
------------
* Drop python 3.5 support
* Drop docutils 0.12 and 0.13 support
* LaTeX: add ``tex-gyre`` font dependency
Incompatible changes
--------------------
* #8539: autodoc: info-field-list is generated into the class description when
``autodoc_typehints='description'`` and ``autoclass_content='class'`` set
* domain: The ``Index`` class becomes subclasses of ``abc.ABC`` to indicate
methods that must be overrided in the concrete classes
* #4826: py domain: The structure of python objects is changed. A boolean value
is added to indicate that the python object is canonical one
* #7425: MathJax: The MathJax was changed from 2 to 3. Users using a custom
MathJax configuration may have to set the old MathJax path or update their
configuration for version 3. See :mod:`sphinx.ext.mathjax`.
* #7784: i18n: The msgid for alt text of image is changed
* #5560: napoleon: :confval:`napoleon_use_param` also affect "other parameters"
section
* #7996: manpage: Make a section directory on build manpage by default (see
:confval:`man_make_section_directory`)
* #7849: html: Change the default setting of
:confval:`html_codeblock_linenos_style` to ``'inline'``
* #8380: html search: search results are wrapped with ``<p>`` instead of
``<div>``
* html theme: Move a script tag for documentation_options.js in
basic/layout.html to ``script_files`` variable
* html theme: Move CSS tags in basic/layout.html to ``css_files`` variable
* #8915: html theme: Emit a warning for sphinx_rtd_theme-0.2.4 or older
* #8508: LaTeX: uplatex becomes a default setting of latex_engine for Japanese
documents
* #5977: py domain: ``:var:``, ``:cvar:`` and ``:ivar:`` fields do not create
cross-references
* #4550: The ``align`` attribute of ``figure`` and ``table`` nodes becomes
``None`` by default instead of ``'default'``
* #8769: LaTeX refactoring: split sphinx.sty into multiple files and rename
some auxiliary files created in ``latex`` build output repertory
* #8937: Use explicit title instead of <no title>
* #8487: The :file: option for csv-table directive now recognizes an absolute
path as a relative path from source directory
Deprecated
----------
* :confval:`html_codeblock_linenos_style`
* ``favicon`` and ``logo`` variable in HTML templates
* ``sphinx.directives.patches.CSVTable``
* ``sphinx.directives.patches.ListTable``
* ``sphinx.directives.patches.RSTTable``
* ``sphinx.ext.autodoc.directive.DocumenterBridge.filename_set``
* ``sphinx.ext.autodoc.directive.DocumenterBridge.warn()``
* ``sphinx.registry.SphinxComponentRegistry.get_source_input()``
* ``sphinx.registry.SphinxComponentRegistry.source_inputs``
* ``sphinx.transforms.FigureAligner``
* ``sphinx.util.pycompat.convert_with_2to3()``
* ``sphinx.util.pycompat.execfile_()``
* ``sphinx.util.smartypants``
* ``sphinx.util.typing.DirectiveOption``
Features added
--------------
* #8924: autodoc: Support ``bound`` argument for TypeVar
* #7383: autodoc: Support typehints for properties
* #5603: autodoc: Allow to refer to a python class using its canonical name
when the class has two different names; a canonical name and an alias name
* #8539: autodoc: Add :confval:`autodoc_typehints_description_target` to control
the behavior of ``autodoc_typehints=description``
* #8841: autodoc: :confval:`autodoc_docstring_signature` will continue to look
for multiple signature lines without backslash character
* #7549: autosummary: Enable :confval:`autosummary_generate` by default
* #4826: py domain: Add ``:canonical:`` option to python directives to describe
the location where the object is defined
* #7199: py domain: Add :confval:`python_use_unqualified_type_names` to suppress
the module name of the python reference if it can be resolved (experimental)
* #7068: py domain: Add :rst:dir:`py:property` directive to describe a property
* #7784: i18n: The alt text for image is translated by default (without
:confval:`gettext_additional_targets` setting)
* #2018: html: :confval:`html_favicon` and :confval:`html_logo` now accept URL
for the image
* #8070: html search: Support searching for 2characters word
* #9036: html theme: Allow to inherite the search page
* #8938: imgconverter: Show the error of the command availability check
* #7830: Add debug logs for change detection of sources and templates
* #8201: Emit a warning if toctree contains duplicated entries
* #8326: ``master_doc`` is now renamed to :confval:`root_doc`
* #8942: C++, add support for the C++20 spaceship operator, ``<=>``.
* #7199: A new node, ``sphinx.addnodes.pending_xref_condition`` has been added.
It can be used to choose appropriate content of the reference by conditions.
Bugs fixed
----------
* #8917: autodoc: Raises a warning if function has wrong __globals__ value
* #8415: autodoc: a TypeVar imported from other module is not resolved (in
Python 3.7 or above)
* #8992: autodoc: Failed to resolve types.TracebackType type annotation
* #8905: html: html_add_permalinks=None and html_add_permalinks="" are ignored
* #8380: html search: Paragraphs in search results are not identified as ``<p>``
* #8915: html theme: The translation of sphinx_rtd_theme does not work
* #8342: Emit a warning if a unknown domain is given for directive or role (ex.
``:unknown:doc:``)
* #7241: LaTeX: No wrapping for ``cpp:enumerator``
* #8711: LaTeX: backticks in code-blocks trigger latexpdf build warning (and font
change) with late TeXLive 2019
* #8253: LaTeX: Figures with no size defined get overscaled (compared to images
with size explicitly set in pixels) (fixed for ``'pdflatex'/'lualatex'`` only)
* #8881: LaTeX: The depth of bookmarks panel in PDF is not enough for navigation
* #8874: LaTeX: the fix to two minor Pygments LaTeXFormatter output issues ignore
Pygments style
* #8925: LaTeX: 3.5.0 ``verbatimmaxunderfull`` setting does not work as
expected
* #8980: LaTeX: missing line break in ``\pysigline``
* #8995: LaTeX: legacy ``\pysiglinewithargsret`` does not compute correctly
available horizontal space and should use a ragged right style
* #9009: LaTeX: "release" value with underscore leads to invalid LaTeX
* #8911: C++: remove the longest matching prefix in
:confval:`cpp_index_common_prefix` instead of the first that matches.
* C, properly reject function declarations when a keyword is used
as parameter name.
* #8933: viewcode: Failed to create back-links on parallel build
* #8960: C and C++, fix rendering of (member) function pointer types in
function parameter lists.
* C++, fix linking of names in array declarators, pointer to member
(function) declarators, and in the argument to ``sizeof...``.
* C, fix linking of names in array declarators.
Testing
--------
Release 3.5.4 (in development)
==============================
@ -16,6 +152,8 @@ Features added
Bugs fixed
----------
* #8870: The style of toctree captions has been changed with docutils-0.17
Testing
--------
@ -100,6 +238,9 @@ Features added
* #8775: autodoc: Support type union operator (PEP-604) in Python 3.10 or above
* #8297: autodoc: Allow to extend :confval:`autodoc_default_options` via
directive options
* #759: autodoc: Add a new configuration :confval:`autodoc_preserve_defaults` as
an experimental feature. It preserves the default argument values of
functions in source code and keep them not evaluated for readability.
* #8619: html: kbd role generates customizable HTML tags for compound keys
* #8634: html: Allow to change the order of JS/CSS via ``priority`` parameter
for :meth:`Sphinx.add_js_file()` and :meth:`Sphinx.add_css_file()`

View File

@ -12,19 +12,20 @@ interesting examples.
Documentation using the alabaster theme
---------------------------------------
* `AIOHTTP <https://docs.aiohttp.org/>`__
* `Alabaster <https://alabaster.readthedocs.io/>`__
* `Blinker <https://pythonhosted.org/blinker/>`__
* `Calibre <https://manual.calibre-ebook.com/>`__
* `Click <http://click.pocoo.org/>`__ (customized)
* `Click <https://click.palletsprojects.com/>`__ (customized)
* `coala <https://docs.coala.io/>`__ (customized)
* `CodePy <https://documen.tician.de/codepy/>`__
* `Eve <https://docs.python-eve.org/>`__ (Python REST API framework)
* `Fabric <https://docs.fabfile.org/>`__
* `Fityk <https://fityk.nieto.pl/>`__
* `Flask <http://flask.pocoo.org/docs/>`__
* `Flask <https://flask.palletsprojects.com/>`__
* `Flask-OpenID <https://pythonhosted.org/Flask-OpenID/>`__
* `Invoke <https://docs.pyinvoke.org/>`__
* `Jinja <http://jinja.pocoo.org/docs/>`__
* `Jinja <https://jinja.palletsprojects.com/>`__
* `Lino <http://www.lino-framework.org/>`__ (customized)
* `marbl <https://getmarbl.readthedocs.io/>`__
* `MDAnalysis <https://www.mdanalysis.org/docs/>`__ (customized)
@ -41,7 +42,8 @@ Documentation using the alabaster theme
* `Spyder <https://docs.spyder-ide.org/>`__ (customized)
* `Tablib <http://docs.python-tablib.org/>`__
* `urllib3 <https://urllib3.readthedocs.io/>`__ (customized)
* `Werkzeug <http://werkzeug.pocoo.org/docs/>`__ (customized)
* `Werkzeug <https://werkzeug.palletsprojects.com/>`__
* `Write the Docs <https://writethedocs-www.readthedocs.io/>`__
Documentation using the classic theme
-------------------------------------
@ -132,7 +134,7 @@ Documentation using the sphinxdoc theme
Documentation using the nature theme
------------------------------------
* `Alembic <http://alembic.zzzcomputing.com/>`__
* `Alembic <https://alembic.sqlalchemy.org/>`__
* `Cython <http://docs.cython.org/>`__
* `easybuild <https://easybuild.readthedocs.io/>`__
* `jsFiddle <http://doc.jsfiddle.net/>`__
@ -141,6 +143,7 @@ Documentation using the nature theme
* `MapServer <https://mapserver.org/>`__ (customized)
* `Pandas <https://pandas.pydata.org/pandas-docs/stable/>`__
* `pyglet <https://pyglet.readthedocs.io/>`__ (customized)
* `PyWavelets <https://pywavelets.readthedocs.io/>`__
* `Setuptools <https://setuptools.readthedocs.io/>`__
* `Spring Python <https://docs.spring.io/spring-python/1.2.x/sphinx/html/>`__
* `StatsModels <https://www.statsmodels.org/>`__ (customized)
@ -156,6 +159,7 @@ Documentation using another builtin theme
* `PyPubSub <https://pypubsub.readthedocs.io/>`__ (bizstyle)
* `Pylons <https://docs.pylonsproject.org/projects/pylons-webframework/>`__ (pyramid)
* `Pyramid web framework <https://docs.pylonsproject.org/projects/pyramid/>`__ (pyramid)
* `RxDock <https://www.rxdock.org/documentation/html/devel/>`__
* `Sphinx <http://www.sphinx-doc.org/>`__ (sphinx13) :-)
* `Valence <https://docs.valence.desire2learn.com/>`__ (haiku, customized)
@ -219,6 +223,7 @@ Documentation using sphinx_rtd_theme
* `Mailman <http://docs.list.org/>`__
* `MathJax <https://docs.mathjax.org/>`__
* `MDTraj <http://mdtraj.org/latest/>`__ (customized)
* `Mesa 3D <https://docs.mesa3d.org/>`__
* `micca - MICrobial Community Analysis <https://micca.readthedocs.io/>`__
* `MicroPython <https://docs.micropython.org/>`__
* `Minds <https://www.minds.org/docs/>`__ (customized)
@ -227,6 +232,7 @@ Documentation using sphinx_rtd_theme
* `mod_wsgi <https://modwsgi.readthedocs.io/>`__
* `MoinMoin <https://moin-20.readthedocs.io/>`__
* `Mopidy <https://docs.mopidy.com/>`__
* `mpi4py <https://mpi4py.readthedocs.io/>`__
* `MyHDL <http://docs.myhdl.org/>`__
* `Nextflow <https://www.nextflow.io/docs/latest/index.html>`__
* `NICOS <https://forge.frm2.tum.de/nicos/doc/nicos-master/>`__ (customized)
@ -247,6 +253,7 @@ Documentation using sphinx_rtd_theme
* `PyVISA <https://pyvisa.readthedocs.io/>`__
* `pyvista <https://docs.pyvista.org/>`__
* `Read The Docs <https://docs.readthedocs.io/>`__
* `ROCm Platform <https://rocm-documentation.readthedocs.io/>`__
* `Free your information from their silos (French) <http://redaction-technique.org/>`__ (customized)
* `Releases Sphinx extension <https://releases.readthedocs.io/>`__
* `Qtile <http://docs.qtile.org/>`__
@ -254,7 +261,7 @@ Documentation using sphinx_rtd_theme
* `QuTiP <http://qutip.org/docs/latest/>`__
* `Satchmo <http://docs.satchmoproject.com/>`__
* `Scapy <https://scapy.readthedocs.io/>`__
* `SimGrid <http://simgrid.gforge.inria.fr/simgrid/latest/doc/>`__
* `SimGrid <https://simgrid.org/doc/latest/>`__
* `SimPy <https://simpy.readthedocs.io/>`__
* `six <https://six.readthedocs.io/>`__
* `SlamData <https://newdocs.slamdata.com>`__
@ -283,7 +290,6 @@ Documentation using sphinx_rtd_theme
* `Web Application Attack and Audit Framework (w3af) <http://docs.w3af.org/>`__
* `Weblate <https://docs.weblate.org/>`__
* `x265 <https://x265.readthedocs.io/>`__
* `ZeroNet <https://zeronet.readthedocs.io/>`__
* `Zulip <https://zulip.readthedocs.io/>`__
Documentation using sphinx_bootstrap_theme
@ -319,6 +325,7 @@ Documentation using a custom theme or integrated in a website
* `Doctrine <https://www.doctrine-project.org/>`__
* `Enterprise Toolkit for Acrobat products <https://www.adobe.com/devnet-docs/acrobatetk/>`__
* `FreeFEM <https://doc.freefem.org/introduction/>`__
* `fmt <https://fmt.dev/>`__
* `Gameduino <http://excamera.com/sphinx/gameduino/>`__
* `gensim <https://radimrehurek.com/gensim/>`__
* `GeoServer <http://docs.geoserver.org/>`__
@ -328,6 +335,7 @@ Documentation using a custom theme or integrated in a website
* `H2O.ai <http://docs.h2o.ai/>`__
* `Heka <https://hekad.readthedocs.io/>`__
* `Istihza (Turkish Python documentation project) <https://belgeler.yazbel.com/python-istihza/>`__
* `JupyterHub <https://jupyterhub.readthedocs.io/>`__
* `Kombu <http://docs.kombu.me/>`__
* `Lasso <http://lassoguide.com/>`__
* `Mako <http://docs.makotemplates.org/>`__

View File

@ -15,6 +15,7 @@ include sphinx-quickstart.py
include sphinx-apidoc.py
include tox.ini
include sphinx/locale/.tx/config
include sphinx/py.typed
recursive-include sphinx/templates *
recursive-include sphinx/texinputs *

View File

@ -12,10 +12,12 @@ texlive-luatex85 [platform:rpm]
texlive-anyfontsize [platform:rpm]
texlive-ctablestack [platform:rpm]
texlive-gnu-freefont [platform:rpm]
texlive-tex-gyre [platform:rpm]
latexmk [platform:rpm]
texlive-latex-recommended [platform:dpkg]
texlive-fonts-recommended [platform:dpkg]
tex-gyre [platform:dpkg]
texlive-latex-extra [platform:dpkg]
texlive-luatex [platform:dpkg]
latexmk [platform:dpkg]

View File

@ -43,7 +43,7 @@ source_suffix = '.rst'
# source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
root_doc = 'index'
# General information about the project.
project = u'test'
@ -252,7 +252,7 @@ latex_elements = {
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'test.tex', u'test Documentation',
(root_doc, 'test.tex', u'test Documentation',
u'test', 'manual'),
]
@ -283,7 +283,7 @@ latex_documents = [
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'test', u'test Documentation',
(root_doc, 'test', u'test Documentation',
[author], 1)
]
@ -298,7 +298,7 @@ man_pages = [
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'test', u'test Documentation',
(root_doc, 'test', u'test Documentation',
author, 'test', 'One line description of project.',
'Miscellaneous'),
]

View File

@ -10,7 +10,6 @@ Changelog
.. raw:: latex
\hypersetup{bookmarksdepth=1}% pdf bookmarks
\addtocontents{toc}{\protect\setcounter{tocdepth}{1}}%
.. include:: ../CHANGES

View File

@ -9,7 +9,7 @@ extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.todo',
'sphinx.ext.intersphinx',
'sphinx.ext.viewcode', 'sphinx.ext.inheritance_diagram']
master_doc = 'contents'
root_doc = 'contents'
templates_path = ['_templates']
exclude_patterns = ['_build']
@ -59,27 +59,14 @@ latex_documents = [('contents', 'sphinx.tex', 'Sphinx Documentation',
latex_logo = '_static/sphinx.png'
latex_elements = {
'fontenc': r'\usepackage[LGR,X2,T1]{fontenc}',
'fontpkg': r'''
\usepackage[sc]{mathpazo}
\usepackage[scaled]{helvet}
\usepackage{courier}
\substitutefont{LGR}{\rmdefault}{cmr}
\substitutefont{LGR}{\sfdefault}{cmss}
\substitutefont{LGR}{\ttdefault}{cmtt}
\substitutefont{X2}{\rmdefault}{cmr}
\substitutefont{X2}{\sfdefault}{cmss}
\substitutefont{X2}{\ttdefault}{cmtt}
''',
'passoptionstopackages': r'''
\PassOptionsToPackage{svgnames}{xcolor}
\PassOptionsToPackage{bookmarksdepth=3}{hyperref}% depth of pdf bookmarks
''',
'preamble': r'''
\DeclareUnicodeCharacter{229E}{\ensuremath{\boxplus}}
\setcounter{tocdepth}{3}% depth of what is kept from toc file
\setcounter{tocdepth}{3}% depth of what main TOC shows (3=subsubsection)
\setcounter{secnumdepth}{1}% depth of section numbering
''',
'fvset': '\\fvset{fontsize=auto}',
# fix missing index entry due to RTD doing only once pdflatex after makeindex
'printindex': r'''
\IfFileExists{\jobname.ind}
@ -91,6 +78,7 @@ latex_show_urls = 'footnote'
latex_use_xindy = True
autodoc_member_order = 'groupwise'
autosummary_generate = False
todo_include_todos = True
extlinks = {'duref': ('http://docutils.sourceforge.net/docs/ref/rst/'
'restructuredtext.html#%s', ''),

View File

@ -0,0 +1,142 @@
.. _autodoc_ext_tutorial:
Developing autodoc extension for IntEnum
========================================
The objective of this tutorial is to create an extension that adds
support for new type for autodoc. This autodoc extension will format
the ``IntEnum`` class from Python standard library. (module ``enum``)
Overview
--------
We want the extension that will create auto-documentation for IntEnum.
``IntEnum`` is the integer enum class from standard library ``enum`` module.
Currently this class has no special auto documentation behavior.
We want to add following to autodoc:
* A new ``autointenum`` directive that will document the ``IntEnum`` class.
* The generated documentation will have all the enum possible values
with names.
* The ``autointenum`` directive will have an option ``:hex:`` which will
cause the integers be printed in hexadecimal form.
Prerequisites
-------------
We need the same setup as in :doc:`the previous extensions <todo>`. This time,
we will be putting out extension in a file called :file:`autodoc_intenum.py`.
The :file:`my_enums.py` will contain the sample enums we will document.
Here is an example of the folder structure you might obtain:
.. code-block:: text
└── source
   ├── _ext
  └── autodoc_intenum.py
   ├── conf.py
   ├── index.rst
   └── my_enums.py
Writing the extension
---------------------
Start with ``setup`` function for the extension.
.. literalinclude:: examples/autodoc_intenum.py
:language: python
:linenos:
:pyobject: setup
The :meth:`~Sphinx.setup_extension` method will pull the autodoc extension
because our new extension depends on autodoc. :meth:`~Sphinx.add_autodocumenter`
is the method that registers our new auto documenter class.
We want to import certain objects from the autodoc extension:
.. literalinclude:: examples/autodoc_intenum.py
:language: python
:linenos:
:lines: 1-7
There are several different documenter classes such as ``MethodDocumenter``
or ``AttributeDocumenter`` available in the autodoc extension but
our new class is the subclass of ``ClassDocumenter`` which a
documenter class used by autodoc to document classes.
This is the definition of our new the auto-documenter class:
.. literalinclude:: examples/autodoc_intenum.py
:language: python
:linenos:
:pyobject: IntEnumDocumenter
Important attributes of the new class:
**objtype**
This attribute determines the ``auto`` directive name. In
this case the auto directive will be ``autointenum``.
**directivetype**
This attribute sets the generated directive name. In
this example the generated directive will be ``.. :py:class::``.
**priority**
the larger the number the higher is the priority. We want our
documenter be higher priority than the parent.
**option_spec**
option specifications. We copy the parent class options and
add a new option *hex*.
Overridden members:
**can_document_member**
This member is important to override. It should
return *True* when the passed object can be documented by this class.
**add_directive_header**
This method generates the directive header. We add
**:final:** directive option. Remember to call **super** or no directive
will be generated.
**add_content**
This method generates the body of the class documentation.
After calling the super method we generate lines for enum description.
Using the extension
-------------------
You can now use the new autodoc directive to document any ``IntEnum``.
For example, you have the following ``IntEnum``:
.. code-block:: python
:caption: my_enums.py
class Colors(IntEnum):
"""Colors enumerator"""
NONE = 0
RED = 1
GREEN = 2
BLUE = 3
This will be the documentation file with auto-documentation directive:
.. code-block:: rst
:caption: index.rst
.. autointenum:: my_enums.Colors

View File

@ -0,0 +1,52 @@
from enum import IntEnum
from typing import Any, Optional
from docutils.statemachine import StringList
from sphinx.application import Sphinx
from sphinx.ext.autodoc import ClassDocumenter, bool_option
class IntEnumDocumenter(ClassDocumenter):
objtype = 'intenum'
directivetype = 'class'
priority = 10 + ClassDocumenter.priority
option_spec = dict(ClassDocumenter.option_spec)
option_spec['hex'] = bool_option
@classmethod
def can_document_member(cls,
member: Any, membername: str,
isattr: bool, parent: Any) -> bool:
return isinstance(member, IntEnum)
def add_directive_header(self, sig: str) -> None:
super().add_directive_header(sig)
self.add_line(' :final:', self.get_sourcename())
def add_content(self,
more_content: Optional[StringList],
no_docstring: bool = False
) -> None:
super().add_content(more_content, no_docstring)
source_name = self.get_sourcename()
enum_object: IntEnum = self.object
use_hex = self.options.hex
self.add_line('', source_name)
for enum_value in enum_object:
the_value_name = enum_value.name
the_value_value = enum_value.value
if use_hex:
the_value_value = hex(the_value_value)
self.add_line(
f"**{the_value_name}**: {the_value_value}", source_name)
self.add_line('', source_name)
def setup(app: Sphinx) -> None:
app.setup_extension('sphinx.ext.autodoc') # Require autodoc extension
app.add_autodocumenter(IntEnumDocumenter)

View File

@ -13,3 +13,5 @@ Refer to the following tutorials to get started with extension development.
helloworld
todo
recipe
autodoc_ext

View File

@ -167,26 +167,33 @@ type for that event::
4. event.env-before-read-docs(app, env, docnames)
for docname in docnames:
5. event.env-purge-doc(app, env, docname)
5. event.env-purge-doc(app, env, docname)
if doc changed and not removed:
6. source-read(app, docname, source)
7. run source parsers: text -> docutils.document (parsers can be added with the app.add_source_parser() API)
8. apply transforms (by priority): docutils.document -> docutils.document
- event.doctree-read(app, doctree) is called in the middly of transforms,
7. run source parsers: text -> docutils.document
- parsers can be added with the app.add_source_parser() API
8. apply transforms based on priority: docutils.document -> docutils.document
- event.doctree-read(app, doctree) is called in the middle of transforms,
transforms come before/after this event depending on their priority.
9. (if running in parallel mode, for each process) event.env-merged-info(app, env, docnames, other)
9. event.env-merge-info(app, env, docnames, other)
- if running in parallel mode, this event will be emitted for each process
10. event.env-updated(app, env)
11. event.env-get-updated(app, env)
12. event.env-check-consistency(app, env)
# The updated-docs list can be builder dependent, but generally includes all new/changed documents,
# plus any output from `env-get-updated`, and then all "parent" documents in the ToC tree
# For builders that output a single page, they are first joined into a single doctree before post-transforms/doctree-resolved
# For builders that output a single page, they are first joined into a single doctree before post-transforms
# or the doctree-resolved event is emitted
for docname in updated-docs:
13. apply post-transforms (by priority): docutils.document -> docutils.document
14. event.doctree-resolved(app, doctree, docname)
- (for any reference node that fails to resolve) event.missing-reference(env, node, contnode)
- (for any reference node that fails to resolve) event.warn-missing-reference(domain, node)
- In the event that any reference nodes fail to resolve, the following may emit:
- event.missing-reference(env, node, contnode)
- event.warn-missing-reference(domain, node)
15. Generate output files
16. event.build-finished(app, exception)

View File

@ -22,6 +22,71 @@ The following is a list of deprecated interfaces.
- (will be) Removed
- Alternatives
* - ``favicon`` variable in HTML templates
- 4.0
- TBD
- ``favicon_url``
* - ``logo`` variable in HTML templates
- 4.0
- TBD
- ``logo_url``
* - ``sphinx.directives.patches.ListTable``
- 4.0
- 6.0
- ``docutils.parsers.rst.diretives.tables.ListSVTable``
* - ``sphinx.directives.patches.RSTTable``
- 4.0
- 6.0
- ``docutils.parsers.rst.diretives.tables.RSTTable``
* - ``sphinx.ext.autodoc.directive.DocumenterBridge.filename_set``
- 4.0
- 6.0
- ``sphinx.ext.autodoc.directive.DocumenterBridge.record_dependencies``
* - ``sphinx.ext.autodoc.directive.DocumenterBridge.warn()``
- 4.0
- 6.0
- :ref:`logging-api`
* - ``sphinx.registry.SphinxComponentRegistry.get_source_input()``
- 4.0
- 6.0
- N/A
* - ``sphinx.registry.SphinxComponentRegistry.source_inputs``
- 4.0
- 6.0
- N/A
* - ``sphinx.transforms.FigureAligner``
- 4.0
- 6.0
- N/A
* - ``sphinx.util.pycompat.convert_with_2to3()``
- 4.0
- 6.0
- N/A
* - ``sphinx.util.pycompat.execfile_()``
- 4.0
- 6.0
- N/A
* - ``sphinx.util.smartypants``
- 4.0
- 6.0
- ``docutils.utils.smartyquotes``
* - ``sphinx.util.typing.DirectiveOption``
- 4.0
- 6.0
- N/A
* - pending_xref node for viewcode extension
- 3.5
- 5.0

View File

@ -37,8 +37,8 @@ New inline nodes
.. autoclass:: index
.. autoclass:: pending_xref
.. autoclass:: pending_xref_condition
.. autoclass:: literal_emphasis
.. autoclass:: abbreviation
.. autoclass:: download_reference
Special nodes

View File

@ -139,57 +139,33 @@ Keys that you may want to override include:
``babel``, not ``polyglossia``.
``'fontpkg'``
Font package inclusion. The default of ``'\\usepackage{times}'`` uses Times
for text, Helvetica for sans serif and Courier for monospace.
Font package inclusion. The default is::
In order to support occasional Cyrillic (физика частиц) or Greek
letters (Σωματιδιακή φυσική) in a document whose language is
English or a Latin European one, the default set-up is enhanced (only for
``'pdflatex'`` engine) to do:
r"""\usepackage{tgtermes}
\usepackage{tgheros}
\renewcommand\ttdefault{txtt}
"""
.. code-block:: latex
\substitutefont{LGR}{\rmdefault}{cmr}
\substitutefont{LGR}{\sfdefault}{cmss}
\substitutefont{LGR}{\ttdefault}{cmtt}
\substitutefont{X2}{\rmdefault}{cmr}
\substitutefont{X2}{\sfdefault}{cmss}
\substitutefont{X2}{\ttdefault}{cmtt}
This is activated only under the condition that the ``'fontenc'`` key is
configured to load the ``LGR`` (Greek) and/or ``X2`` (Cyrillic)
pdflatex-font encodings (if the :confval:`language` is set to a Cyrillic
language, this ``'fontpkg'`` key must be used as "times" package has no
direct support for it; then keep only ``LGR`` lines from the above, if
support is needed for Greek in the text).
The ``\substitutefont`` command is from the eponymous LaTeX package, which
is loaded by Sphinx if needed (on Ubuntu Xenial it is part of
``texlive-latex-extra`` which is a Sphinx requirement).
Only if the document actually does contain Unicode Greek letters (in text)
or Cyrillic letters, will the above default set-up cause additional
requirements for the PDF build. On Ubuntu Xenial, these are the
``texlive-lang-greek``, ``texlive-lang-cyrillic``, and (with the above
choice of fonts) the ``cm-super`` (or ``cm-super-minimal``) packages.
For ``'xelatex'`` and ``'lualatex'``, the default is to use the FreeFont
family: this OpenType font family supports both Cyrillic and Greek scripts
and is available as separate Ubuntu Xenial package ``fonts-freefont-otf``.
It is not necessary to install the much larger ``texlive-fonts-extra``
package.
``'platex'`` (Japanese documents) engine supports individual Cyrillic and
Greek letters with no need of extra user set-up.
Default: ``'\\usepackage{times}'`` (or ``''`` when using a Cyrillic script)
For ``'xelatex'`` and ``'lualatex'`` however the default is to use
the GNU FreeFont.
.. versionchanged:: 1.2
Defaults to ``''`` when the :confval:`language` uses the Cyrillic
script.
.. versionchanged:: 2.0
Added support for individual Greek and Cyrillic letters:
Incorporates some font substitution commands to help support occasional
Greek or Cyrillic in a document using ``'pdflatex'`` engine.
.. versionchanged:: 4.0.0
- The font substitution commands added at ``2.0`` have been moved
to the ``'fontsubstitution'`` key, as their presence here made
it complicated for user to customize the value of ``'fontpkg'``.
- The default font setting has changed: it still uses Times and
Helvetica clones for serif and sans serif, but via better, more
complete TeX fonts and associated LaTeX packages. The
monospace font has been changed to better match the Times clone.
``'fncychap'``
Inclusion of the "fncychap" package (which makes fancy chapter titles),
@ -320,37 +296,28 @@ Keys that don't need to be overridden unless in special cases are:
.. versionadded:: 1.2
``'fontenc'``
"fontenc" package inclusion.
Customize this from its default ``'\\usepackage[T1]{fontenc}'`` to:
If ``'pdflatex'`` is the :confval:`latex_engine`, one can add ``LGR``
for support of Greek letters in the document, and also ``X2`` (or
``T2A``) for Cyrillic letters, like this:
- ``'\\usepackage[X2,T1]{fontenc}'`` if you need occasional
Cyrillic letters (физика частиц),
.. code-block:: latex
- ``'\\usepackage[LGR,T1]{fontenc}'`` if you need occasional
Greek letters (Σωματιδιακή φυσική).
r'\usepackage[LGR,X2,T1]{fontenc}'
Use ``[LGR,X2,T1]`` rather if both are needed.
.. 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.
- Do not use this key for a :confval:`latex_engine` other than
``'pdflatex'``.
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
Unicode Greek.
- If Greek is main language, do not use this key. Since Sphinx 2.2.1,
``xelatex`` will be used automatically as :confval:`latex_engine`.
On Ubuntu xenial, packages ``texlive-lang-greek`` and ``cm-super``
(for the latter, only if the ``'fontpkg'`` setting is left to its
default) are needed for ``LGR`` to work. In place of ``cm-super``
one can install smaller ``cm-super-minimal``, but it requires the
LaTeX document to execute ``\usepackage[10pt]{type1ec}`` before
loading ``fontenc``. Thus, use this key with this extra at its
start if needed.
Default: ``'\\usepackage[T1]{fontenc}'``
- The TeX installation may need some extra packages. For example,
on Ubuntu xenial, packages ``texlive-lang-greek`` and ``cm-super``
are needed for ``LGR`` to work. And ``texlive-lang-cyrillic`` and
``cm-super`` are needed for support of Cyrillic.
.. versionchanged:: 1.5
Defaults to ``'\\usepackage{fontspec}'`` when
@ -367,32 +334,37 @@ Keys that don't need to be overridden unless in special cases are:
.. 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'``).
occasional Greek or Cyrillic letters (``'pdflatex'``).
.. versionchanged:: 2.3.0
``'xelatex'`` also executes
``'xelatex'`` 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``.
``'fontsubstitution'``
Ignored if ``'fontenc'`` was not configured to use ``LGR`` or ``X2`` (or
``T2A``). In case ``'fontpkg'`` key is configured for usage with some
TeX fonts known to be available in the ``LGR`` or ``X2`` encodings, set
this one to be the empty string. Else leave to its default.
Ignored with :confval:`latex_engine` other than ``'pdflatex'``.
.. versionadded:: 4.0.0
``'textgreek'``
This is needed for ``pdflatex`` to support Unicode input of Greek
letters such as φύσις. Expert users may want to load the ``textalpha``
package with its option ``normalize-symbols``.
For the support of occasional Greek letters.
.. hint::
It is ignored with ``'platex'``, ``'xelatex'`` or ``'lualatex'`` as
:confval:`latex_engine` and defaults to either the empty string or
to ``'\\usepackage{textalpha}'`` for ``'pdflatex'`` depending on
whether the ``'fontenc'`` key was used with ``LGR`` or not. Only
expert LaTeX users may want to customize this key.
Unicode Greek (but no further Unicode symbols) in :rst:dir:`math`
can be supported by ``'pdflatex'`` from setting this key to
``r'\usepackage{textalpha,alphabeta}'``. Then ``:math:`α``` (U+03B1)
will render as :math:`\alpha`. For wider Unicode support in math
input, see the discussion of :confval:`latex_engine`.
With ``'platex'`` (Japanese), ``'xelatex'`` or ``'lualatex'``, this
key is ignored.
It can also be used as ``r'\usepackage{textalpha,alphabeta}'`` to let
``'pdflatex'`` support Greek Unicode input in :rst:dir:`math` context.
For example ``:math:`α``` (U+03B1) will render as :math:`\alpha`.
Default: ``'\\usepackage{textalpha}'`` or ``''`` if ``fontenc`` does not
include the ``LGR`` option.
@ -517,19 +489,25 @@ Keys that don't need to be overridden unless in special cases are:
Default: ``'\\printindex'``
``'fvset'``
Customization of ``fancyvrb`` LaTeX package. The default value of
``'\\fvset{fontsize=\\small}'`` is used to adjust for the large character
width of the monospace font, used in code-blocks. You may need to modify
this if you use custom fonts.
Customization of ``fancyvrb`` LaTeX package.
Default: ``'\\fvset{fontsize=\\small}'``
The default value is ``'\\fvset{fontsize=auto}'`` which means that the
font size will adjust correctly if a code-block ends up in a footnote.
You may need to modify this if you use custom fonts:
``'\\fvset{fontsize=\\small}'`` if the monospace font is Courier-like.
Default: ``'\\fvset{fontsize=auto}'``
.. versionadded:: 1.8
.. versionchanged:: 2.0
Due to new default font choice for ``'xelatex'`` and ``'lualatex'``
(FreeFont), Sphinx does ``\\fvset{fontsize=\\small}`` also with these
engines (and not ``\\fvset{fontsize=auto}``).
For ``'xelatex'`` and ``'lualatex'`` defaults to
``'\\fvset{fontsize=\\small}'`` as this
is adapted to the relative widths of the FreeFont family.
.. versionchanged:: 4.0.0
Changed default for ``'pdflatex'``. Previously it was using
``'\\fvset{fontsize=\\small}'``.
Keys that are set by other options and therefore should not be overridden are:
@ -600,6 +578,17 @@ e.g ``'sphinxsetup': "verbatimwrapslines=false"``. If setting the
boolean key to ``true``, ``=true`` is optional.
Spaces around the commas and equal signs are ignored, spaces inside LaTeX
macros may be significant.
Do not use quotes to enclose values, whether numerical or strings.
``bookmarksdepth``
Controls the depth of the collapsable bookmarks panel in the PDF.
May be either a number (e.g. ``3``) or a LaTeX sectioning name (e.g.
``subsubsection``, i.e. without backslash).
For details, refer to the ``hyperref`` LaTeX docs.
Default: ``5``
.. versionadded:: 4.0.0
.. _latexsphinxsetuphmargin:
@ -917,9 +906,36 @@ macros may be significant.
LaTeX macros and environments
-----------------------------
Here are some macros from the package file :file:`sphinx.sty` and class files
:file:`sphinxhowto.cls`, :file:`sphinxmanual.cls`, which have public names
thus allowing redefinitions. Check the respective files for the defaults.
The "LaTeX package" file :file:`sphinx.sty` loads various components
providing support macros (aka commands), and environments, which are used in
the mark-up produced on output from the ``latex`` builder, before conversion
to ``pdf`` via the LaTeX toolchain. Also the "LaTeX class" files
:file:`sphinxhowto.cls` and :file:`sphinxmanual.cls` define or customize some
environments. All of these files can be found in the latex build repertory.
Some of these provide facilities not available from pre-existing LaTeX
packages and work around LaTeX limitations with lists, table cells, verbatim
rendering, footnotes, etc...
Others simply define macros with public names to make overwriting their
defaults easy via user-added contents to the preamble. We will survey most of
those public names here, but defaults have to be looked at in their respective
definition files.
.. hint::
Sphinx LaTeX support code is split across multiple smaller-sized files.
Rather than adding code to the preamble via
`latex_elements <latex_elements_confval_>`_\ [``'preamble'``] it is
also possible to replace entirely one of the component files of Sphinx
LaTeX code with a custom version, simply by including a modified copy in
the project source and adding the filename to the
:confval:`latex_additional_files` list. Check the LaTeX build repertory
for the filenames and contents.
.. versionchanged:: 4.0.0
split of :file:`sphinx.sty` into multiple smaller units, to facilitate
customization of many aspects simultaneously.
.. _latex-macros:

View File

@ -145,7 +145,7 @@ These options are used when :option:`--full` is specified:
* ``module.rst_t``
* ``package.rst_t``
* ``toc.rst_t``
* ``master_doc.rst_t``
* ``root_doc.rst_t``
* ``conf.py_t``
* ``Makefile_t``
* ``Makefile.new_t``

View File

@ -19,13 +19,12 @@ files, including ``conf.py``.
:program:`sphinx-build` can create documentation in different formats. A
format is selected by specifying the builder name on the command line; it
defaults to HTML. Builders can also perform other tasks related to
documentation processing.
documentation processing. For a list of available builders, refer to
:option:`sphinx-build -b`.
By default, everything that is outdated is built. Output only for selected
files can be built by specifying individual filenames.
For a list of available options, refer to :option:`sphinx-build -b`.
Options
-------

View File

@ -72,7 +72,7 @@ Options
.. option:: --master=MASTER
Master document name. (see :confval:`master_doc`).
Master document name. (see :confval:`root_doc`).
.. rubric:: Extension Options
@ -149,7 +149,7 @@ Options
sphinx project files generated by quickstart. Following Jinja2 template
files are allowed:
* ``master_doc.rst_t``
* ``root_doc.rst_t``
* ``conf.py_t``
* ``Makefile_t``
* ``Makefile.new_t``

View File

@ -274,7 +274,19 @@ in the future.
.. data:: favicon
The path to the HTML favicon in the static path, or ``''``.
The path to the HTML favicon in the static path, or URL to the favicon, or
``''``.
.. deprecated:: 4.0
Recommend to use ``favicon_url`` instead.
.. data:: favicon_url
The relative path to the HTML favicon image from the current document, or
URL to the favicon, or ``''``.
.. versionadded:: 4.0
.. data:: file_suffix
@ -297,11 +309,35 @@ in the future.
.. data:: logo
The path to the HTML logo image in the static path, or ``''``.
The path to the HTML logo image in the static path, or URL to the logo, or
``''``.
.. deprecated:: 4.0
Recommend to use ``logo_url`` instead.
.. data:: logo_url
The relative path to the HTML logo image from the current document, or URL
to the logo, or ``''``.
.. versionadded:: 4.0
.. data:: master_doc
The value of :confval:`master_doc`, for usage with :func:`pathto`.
Same as :data:`root_doc`.
.. versionchanged:: 4.0
Renamed to ``root_doc``.
.. data:: root_doc
The value of :confval:`root_doc`, for usage with :func:`pathto`.
.. versionchanged:: 4.0
Renamed from ``master_doc``.
.. data:: pagename

View File

@ -306,7 +306,7 @@ Contributing to Sphinx reference translation
The recommended way for new contributors to translate Sphinx reference is to
join the translation team on Transifex.
There is `sphinx translation page`_ for Sphinx (master) documentation.
There is a `sphinx translation page`_ for Sphinx (master) documentation.
1. Login to transifex_ service.
2. Go to `sphinx translation page`_.
@ -314,6 +314,8 @@ There is `sphinx translation page`_ for Sphinx (master) documentation.
4. Wait acceptance by transifex sphinx translation maintainers.
5. (After acceptance) Translate on transifex.
Detail is here: https://docs.transifex.com/getting-started-1/translators
.. rubric:: Footnotes
.. [1] See the `GNU gettext utilities

View File

@ -179,6 +179,7 @@ The builder's "name" must be given to the **-b** command-line option of
* ``texlive-latex-recommended``
* ``texlive-fonts-recommended``
* ``tex-gyre`` (if :confval:`latex_engine` is ``'pdflatex'``)
* ``texlive-latex-extra``
* ``latexmk`` (this is a Sphinx requirement on GNU/Linux and MacOS X
for functioning of ``make latexpdf``)
@ -186,17 +187,14 @@ The builder's "name" must be given to the **-b** command-line option of
Additional packages are needed in some circumstances (see the discussion of
the ``'fontpkg'`` key of :confval:`latex_elements` for more information):
* to support occasional Cyrillic letters or words, and a fortiori if
:confval:`language` is set to a Cyrillic language, the package
``texlive-lang-cyrillic`` is required, and, with unmodified ``'fontpkg'``,
also ``cm-super`` or ``cm-super-minimal``,
* to support occasional Greek letters or words (in text, not in
:rst:dir:`math` directive contents), ``texlive-lang-greek`` is required,
and, with unmodified ``'fontpkg'``, also ``cm-super`` or
``cm-super-minimal``,
* for ``'xelatex'`` or ``'lualatex'`` (see :confval:`latex_engine`),
``texlive-xetex`` resp. ``texlive-luatex``, and, if leaving unchanged
``'fontpkg'``, ``fonts-freefont-otf``.
* ``texlive-lang-cyrillic`` for Cyrillic (even individual letters), and,
``cm-super`` or ``cm-super-minimal`` (if default fonts),
* ``texlive-lang-greek`` for Greek (even individual letters), and,
``cm-super`` or ``cm-super-minimal`` (if default fonts),
* ``texlive-xetex`` if :confval:`latex_engine` is ``'xelatex'``,
* ``texlive-luatex`` if :confval:`latex_engine` is ``'lualatex'``,
* ``fonts-freefont-otf`` if :confval:`latex_engine` is ``'xelatex'``
or ``'lualatex'``.
The testing of Sphinx LaTeX is done on Ubuntu xenial whose TeX distribution
is based on a TeXLive 2015 snapshot dated March 2016.
@ -207,6 +205,9 @@ The builder's "name" must be given to the **-b** command-line option of
.. versionchanged:: 2.0
Formerly, testing had been done on Ubuntu trusty (TeXLive 2013).
.. versionchanged:: 4.0.0
TeX Gyre fonts dependency for the default LaTeX font configuration.
.. note::
Since 1.6, ``make latexpdf`` uses ``latexmk`` (not on Windows). This

View File

@ -183,11 +183,20 @@ General configuration
.. confval:: master_doc
The document name of the "master" document, that is, the document that
Same as :confval:`root_doc`.
.. versionchanged:: 4.0
Renamed ``master_doc`` to ``master_doc``.
.. confval:: root_doc
The document name of the "root" document, that is, the document that
contains the root :rst:dir:`toctree` directive. Default is ``'index'``.
.. versionchanged:: 2.0
The default is changed to ``'index'`` from ``'contents'``.
.. versionchanged:: 4.0
Renamed ``master_doc`` from ``master_doc``.
.. confval:: exclude_patterns
@ -479,11 +488,10 @@ General configuration
.. confval:: smartquotes_action
This string, for use with Docutils ``0.14`` or later, customizes the Smart
Quotes transform. See the file :file:`smartquotes.py` at the `Docutils
repository`__ for details. The default ``'qDe'`` educates normal **q**\
uote characters ``"``, ``'``, em- and en-**D**\ ashes ``---``, ``--``, and
**e**\ llipses ``...``.
This string customizes the Smart Quotes transform. See the file
:file:`smartquotes.py` at the `Docutils repository`__ for details. The
default ``'qDe'`` educates normal **q**\ uote characters ``"``, ``'``,
em- and en-**D**\ ashes ``---``, ``--``, and **e**\ llipses ``...``.
.. versionadded:: 1.6.6
@ -834,13 +842,16 @@ documentation on :ref:`intl` for details.
:literal-block: literal blocks (``::`` annotation and ``code-block`` directive)
:doctest-block: doctest block
:raw: raw content
:image: image/figure uri and alt
:image: image/figure uri
For example: ``gettext_additional_targets = ['literal-block', 'image']``.
The default is ``[]``.
.. versionadded:: 1.3
.. versionchanged:: 4.0
The alt text for image is translated by default.
.. confval:: figure_language_filename
@ -970,10 +981,15 @@ that use Sphinx's HTMLWriter class.
The style of line numbers for code-blocks.
* ``'table'`` -- display line numbers using ``<table>`` tag (default)
* ``'inline'`` -- display line numbers using ``<span>`` tag
* ``'table'`` -- display line numbers using ``<table>`` tag
* ``'inline'`` -- display line numbers using ``<span>`` tag (default)
.. versionadded:: 3.2
.. versionchanged:: 4.0
It defaults to ``'inline'``.
.. deprecated:: 4.0
.. confval:: html_context
@ -986,26 +1002,32 @@ that use Sphinx's HTMLWriter class.
.. confval:: html_logo
If given, this must be the name of an image file (path relative to the
:term:`configuration directory`) that is the logo of the docs. It is placed
at the top of the sidebar; its width should therefore not exceed 200 pixels.
Default: ``None``.
:term:`configuration directory`) that is the logo of the docs, or URL that
points an image file for the logo. It is placed at the top of the sidebar;
its width should therefore not exceed 200 pixels. Default: ``None``.
.. versionadded:: 0.4.1
The image file will be copied to the ``_static`` directory of the output
HTML, but only if the file does not already exist there.
.. versionchanged:: 4.0
Also accepts the URL for the logo file.
.. confval:: html_favicon
If given, this must be the name of an image file (path relative to the
:term:`configuration directory`) that is the favicon of the docs. Modern
browsers use this as the icon for tabs, windows and bookmarks. It should
be a Windows-style icon file (``.ico``), which is 16x16 or 32x32
pixels large. Default: ``None``.
:term:`configuration directory`) that is the favicon of the docs, or URL that
points an image file for the favicon. Modern browsers use this as the icon
for tabs, windows and bookmarks. It should be a Windows-style icon file
(``.ico``), which is 16x16 or 32x32 pixels large. Default: ``None``.
.. versionadded:: 0.4
The image file will be copied to the ``_static`` directory of the output
HTML, but only if the file does not already exist there.
.. versionchanged:: 4.0
Also accepts the URL for the favicon.
.. confval:: html_css_files
A list of CSS files. The entry must be a *filename* string or a tuple
@ -1479,8 +1501,7 @@ that use Sphinx's HTMLWriter class.
.. confval:: html_experimental_html5_writer
Output is processed with HTML5 writer. This feature needs docutils 0.13 or
newer. Default is ``False``.
Output is processed with HTML5 writer. Default is ``False``.
.. versionadded:: 1.6
@ -1957,8 +1978,8 @@ These options influence LaTeX output.
* ``'pdflatex'`` -- PDFLaTeX (default)
* ``'xelatex'`` -- XeLaTeX
* ``'lualatex'`` -- LuaLaTeX
* ``'platex'`` -- pLaTeX (default if :confval:`language` is ``'ja'``)
* ``'uplatex'`` -- upLaTeX (experimental)
* ``'platex'`` -- pLaTeX
* ``'uplatex'`` -- upLaTeX (default if :confval:`language` is ``'ja'``)
``'pdflatex'``\ 's support for Unicode characters is limited.
@ -1988,6 +2009,10 @@ These options influence LaTeX output.
Add ``uplatex`` support.
.. versionchanged:: 4.0
``uplatex`` becomes the default setting of Japanese documents.
Contrarily to :ref:`MathJaX math rendering in HTML output <math-support>`,
LaTeX requires some extra configuration to support Unicode literals in
:rst:dir:`math`: the only comprehensive solution (as far as we know) is to
@ -2007,8 +2032,8 @@ These options influence LaTeX output.
*startdocname*
String that specifies the :term:`document name` of the LaTeX file's master
document. All documents referenced by the *startdoc* document in TOC trees
will be included in the LaTeX file. (If you want to use the default master
document for your LaTeX build, provide your :confval:`master_doc` here.)
will be included in the LaTeX file. (If you want to use the default root
document for your LaTeX build, provide your :confval:`root_doc` here.)
*targetname*
File name of the LaTeX file in the output directory.
@ -2277,7 +2302,7 @@ These options influence manual page output.
String that specifies the :term:`document name` of the manual page's master
document. All documents referenced by the *startdoc* document in TOC trees
will be included in the manual file. (If you want to use the default
master document for your manual pages build, use your :confval:`master_doc`
root document for your manual pages build, use your :confval:`root_doc`
here.)
*name*
@ -2307,10 +2332,12 @@ These options influence manual page output.
.. confval:: man_make_section_directory
If true, make a section directory on build man page. Default is False.
If true, make a section directory on build man page. Default is True.
.. versionadded:: 3.3
.. versionchanged:: 4.0
The default is changed to ``False`` from ``True``.
.. _texinfo-options:
@ -2331,7 +2358,7 @@ These options influence Texinfo output.
master document. All documents referenced by the *startdoc* document in
TOC trees will be included in the Texinfo file. (If you want to use the
default master document for your Texinfo build, provide your
:confval:`master_doc` here.)
:confval:`root_doc` here.)
*targetname*
File name (no extension) of the Texinfo file in the output directory.
@ -2687,6 +2714,17 @@ Options for the C++ domain
.. versionadded:: 1.5
Options for the Python domain
-----------------------------
.. confval:: python_use_unqualified_type_names
If true, suppress the module name of the python reference if it can be
resolved. The default is ``False``.
.. versionadded:: 4.0
.. note:: This configuration is still in experimental
Example of configuration file
=============================

View File

@ -89,33 +89,96 @@ inserting them into the page source under a suitable :rst:dir:`py:module`,
Boil the noodle *time* minutes.
**Options and advanced usage**
.. rubric:: Options
* If you want to automatically document members, there's a ``members``
option::
.. rst:directive:option:: members
:type: no value or comma separated list
If set, autodoc will generate document for the members of the target
module, class or exception.
For example::
.. automodule:: noodle
:members:
will document all module members (recursively), and ::
will document all module members (recursively), and ::
.. autoclass:: Noodle
:members:
will document all non-private member functions and properties (that is,
those whose name doesn't start with ``_``).
will document all class member methods and properties.
For modules, ``__all__`` will be respected when looking for members unless
you give the ``ignore-module-all`` flag option. Without
``ignore-module-all``, the order of the members will also be the order in
``__all__``.
By default, autodoc will not generate document for the members that are
private, not having docstrings, inherited from super class, or special
members.
You can also give an explicit list of members; only these will then be
documented::
For modules, ``__all__`` will be respected when looking for members unless
you give the ``ignore-module-all`` flag option. Without
``ignore-module-all``, the order of the members will also be the order in
``__all__``.
You can also give an explicit list of members; only these will then be
documented::
.. autoclass:: Noodle
:members: eat, slurp
.. rst:directive:option:: undoc-members
:type: no value
If set, autodoc will also generate document for the members not having
docstrings::
.. automodule:: noodle
:members:
:undoc-members:
.. rst:directive:option:: private-members
:type: no value or comma separated list
If set, autodoc will also generate document for the private members
(that is, those named like ``_private`` or ``__private``)::
.. automodule:: noodle
:members:
:private-members:
It can also take an explicit list of member names to be documented as
arguments::
.. automodule:: noodle
:members:
:private-members: _spicy, _garlickly
.. versionadded:: 1.1
.. versionchanged:: 3.2
The option can now take arguments.
.. rst:directive:option:: special-members
:type: no value or comma separated list
If set, autodoc will also generate document for the special members
(that is, those named like ``__special__``)::
.. autoclass:: my.Class
:members:
:special-members:
It can also take an explicit list of member names to be documented as
arguments::
.. autoclass:: my.Class
:members:
:special-members: __init__, __name__
.. versionadded:: 1.1
.. versionchanged:: 1.2
The option can now take arguments
**Options and advanced usage**
* If you want to make the ``members`` option (or other options described
below) the default, see :confval:`autodoc_default_options`.
@ -139,31 +202,6 @@ inserting them into the page source under a suitable :rst:dir:`py:module`,
.. versionchanged:: 3.5
The default options can be overridden or extended temporarily.
* Members without docstrings will be left out, unless you give the
``undoc-members`` flag option::
.. automodule:: noodle
:members:
:undoc-members:
* "Private" members (that is, those named like ``_private`` or ``__private``)
will be included if the ``private-members`` flag option is given::
.. automodule:: noodle
:members:
:private-members:
It can also take an explicit list of member names to be documented as
arguments::
.. automodule:: noodle
:members:
:private-members: _spicy, _garlickly
.. versionadded:: 1.1
.. versionchanged:: 3.2
The option can now take arguments.
* autodoc considers a member private if its docstring contains
``:meta private:`` in its :ref:`info-field-lists`.
For example:
@ -203,21 +241,6 @@ inserting them into the page source under a suitable :rst:dir:`py:module`,
.. versionadded:: 3.5
* Python "special" members (that is, those named like ``__special__``) will
be included if the ``special-members`` flag option is given::
.. autoclass:: my.Class
:members:
:private-members:
:special-members:
would document both "private" and "special" members of the class.
.. versionadded:: 1.1
.. versionchanged:: 1.2
The option can now take arguments, i.e. the special members to document.
* For classes and exceptions, members inherited from base classes will be
left out when documenting all members, unless you give the
``inherited-members`` option, in addition to ``members``::
@ -506,15 +529,19 @@ There are also config values that you can set:
looks like a signature, use the line as the signature and remove it from the
docstring content.
If the signature line ends with backslash, autodoc considers the function has
multiple signatures and look at the next line of the docstring. It is useful
for overloaded function.
autodoc will continue to look for multiple signature lines,
stopping at the first line that does not look like a signature.
This is useful for declaring overloaded function signatures.
.. versionadded:: 1.1
.. versionchanged:: 3.1
Support overloaded signatures
.. versionchanged:: 4.0
Overloaded signatures do not need to be separated by a backslash
.. confval:: autodoc_mock_imports
This value contains a list of modules to be mocked up. This is useful when
@ -548,6 +575,19 @@ There are also config values that you can set:
New option ``'description'`` is added.
.. confval:: autodoc_typehints_description_target
This value controls whether the types of undocumented parameters and return
values are documented when ``autodoc_typehints`` is set to ``description``.
The default value is ``"all"``, meaning that types are documented for all
parameters and return values, whether they are documented or not.
When set to ``"documented"``, types will only be documented for a parameter
or a return value that is already documented by the docstring.
.. versionadded:: 4.0
.. confval:: autodoc_type_aliases
A dictionary for users defined `type aliases`__ that maps a type name to the
@ -586,6 +626,16 @@ There are also config values that you can set:
.. __: https://mypy.readthedocs.io/en/latest/kinds_of_types.html#type-aliases
.. versionadded:: 3.3
.. confval:: autodoc_preserve_defaults
If True, the default argument values of functions will be not evaluated on
generating document. It preserves them as is in the source code.
.. versionadded:: 4.0
Added as an experimental feature. This will be integrated into autodoc core
in the future.
.. confval:: autodoc_warningiserror
This value controls the behavior of :option:`sphinx-build -W` during
@ -597,7 +647,7 @@ There are also config values that you can set:
This value controls the docstrings inheritance.
If set to True the docstring for classes or methods, if not explicitly set,
is inherited form parents.
is inherited from parents.
The default is ``True``.

View File

@ -19,11 +19,13 @@ The :mod:`sphinx.ext.autosummary` extension does this in two parts:
that contain links to the documented items, and short summary blurbs
extracted from their docstrings.
2. Optionally, the convenience script :program:`sphinx-autogen` or the new
:confval:`autosummary_generate` config value can be used to generate short
"stub" files for the entries listed in the :rst:dir:`autosummary` directives.
These files by default contain only the corresponding
:mod:`sphinx.ext.autodoc` directive, but can be customized with templates.
2. A :rst:dir:`autosummary` directive also generates short "stub" files for the
entries listed in its content. These files by default contain only the
corresponding :mod:`sphinx.ext.autodoc` directive, but can be customized with
templates.
The :program:`sphinx-autogen` script is also able to generate "stub" files
from command line.
.. rst:directive:: autosummary
@ -161,7 +163,7 @@ also use these config values:
.. confval:: autosummary_generate
Boolean indicating whether to scan all found documents for autosummary
directives, and to generate stub pages for each. It is disabled by default.
directives, and to generate stub pages for each. It is enabled by default.
Can also be a list of documents for which stub pages should be generated.
@ -173,6 +175,10 @@ also use these config values:
Emits :event:`autodoc-skip-member` event as :mod:`~sphinx.ext.autodoc`
does.
.. versionchanged:: 4.0
Enabled by default.
.. confval:: autosummary_generate_overwrite
If true, autosummary overwrites existing files by generated stub pages.

View File

@ -140,6 +140,12 @@ are built:
.. module:: sphinx.ext.mathjax
:synopsis: Render math using JavaScript via MathJax.
.. warning::
Version 4.0 changes the version of MathJax used to version 3. You may need to
override ``mathjax_path`` to
``https://cdn.jsdelivr.net/npm/mathjax@2/MathJax.js?config=TeX-AMS-MML_HTMLorMML``
or update your configuration options for version 3.
.. versionadded:: 1.1
This extension puts math as-is into the HTML files. The JavaScript package
@ -161,14 +167,14 @@ Sphinx but is set to automatically include it from a third-party site.
MathJax.
The default is the ``https://`` URL that loads the JS files from the
`cdnjs`__ Content Delivery Network. See the `MathJax Getting Started
`jsdelivr`__ Content Delivery Network. See the `MathJax Getting Started
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
__ https://www.jsdelivr.com/
__ https://docs.mathjax.org/en/latest/start.html
__ https://www.mathjax.org/#gettingstarted
The path can be absolute or relative; if it is relative, it is relative to
the ``_static`` directory of the built docs.

View File

@ -12,7 +12,7 @@ Installing Sphinx
Overview
--------
Sphinx is written in `Python`__ and supports Python 3.5+. It builds upon the
Sphinx is written in `Python`__ and supports Python 3.6+. It builds upon the
shoulders of many third-party libraries such as `Docutils`__ and `Jinja`__,
which are installed when Sphinx is installed.
@ -107,7 +107,30 @@ Anaconda
Windows
-------
.. todo:: Could we start packaging this?
Sphinx can be install using `Chocolatey`__ or
:ref:`installed manually <windows-other-method>`.
__ https://chocolatey.org/
Chocolatey
~~~~~~~~~~
::
$ choco install sphinx
You would need to `install Chocolatey
<https://chocolatey.org/install/>`_
before running this.
For more information, refer to the `chocolatey page`__.
__ https://chocolatey.org/packages/sphinx/
.. _windows-other-method:
Other Methods
~~~~~~~~~~~~~
Most Windows users do not have Python installed by default, so we begin with
the installation of Python itself. To check if you already have Python
@ -183,7 +206,7 @@ Please choose one for your purpose.
When using docker images, please use ``docker run`` command to invoke sphinx commands. For example,
you can use following command to create a Sphinx project::
$ docker run --rm -v /path/to/document:/docs sphinxdoc/sphinx sphinx-quickstart
$ docker run -it --rm -v /path/to/document:/docs sphinxdoc/sphinx sphinx-quickstart
And you can following command this to build HTML document::

View File

@ -13,7 +13,7 @@ Sphinx focuses on documentation, in particular handwritten documentation,
however, Sphinx can also be used to generate blogs, homepages and even books.
Much of Sphinx's power comes from the richness of its default plain-text markup
format, :doc:`reStructuredText </usage/restructuredtext/index>`, along with
it's :doc:`significant extensibility capabilities </development/index>`.
its :doc:`significant extensibility capabilities </development/index>`.
The goal of this document is to give you a quick taste of what Sphinx is and
how you might use it. When you're done here, you can check out the

View File

@ -288,7 +288,7 @@ Roles
-----
A role or "custom interpreted text role" (:duref:`ref <roles>`) is an inline
piece of explicit markup. It signifies that that the enclosed text should be
piece of explicit markup. It signifies that the enclosed text should be
interpreted in a specific way. Sphinx uses this to provide semantic markup and
cross-referencing of identifiers, as described in the appropriate section. The
general syntax is ``:rolename:`content```.

View File

@ -197,9 +197,9 @@ tables of contents. The ``toctree`` directive is the central element.
<metadata>` to let a document be built, but notify Sphinx that it is not
reachable via a toctree.
The "master document" (selected by :confval:`master_doc`) is the "root" of
the TOC tree hierarchy. It can be used as the documentation's main page, or
as a "full table of contents" if you don't give a ``maxdepth`` option.
The "root document" (selected by :confval:`root_doc`) is the "root" of the TOC
tree hierarchy. It can be used as the documentation's main page, or as a
"full table of contents" if you don't give a ``maxdepth`` option.
.. versionchanged:: 0.3
Added "globbing" option.
@ -404,10 +404,15 @@ Showing code examples
single: sourcecode
There are multiple ways to show syntax-highlighted literal code blocks in
Sphinx: using :ref:`reST doctest blocks <rst-doctest-blocks>`; using :ref:`reST
literal blocks <rst-literal-blocks>`, optionally in combination with the
:rst:dir:`highlight` directive; using the :rst:dir:`code-block` directive; and
using the :rst:dir:`literalinclude` directive. Doctest blocks can only be used
Sphinx:
* using :ref:`reST doctest blocks <rst-doctest-blocks>`;
* using :ref:`reST literal blocks <rst-literal-blocks>`, optionally in
combination with the :rst:dir:`highlight` directive;
* using the :rst:dir:`code-block` directive;
* and using the :rst:dir:`literalinclude` directive.
Doctest blocks can only be used
to show interactive Python sessions, while the remaining three can be used for
other languages. Of these three, literal blocks are useful when an entire
document, or at least large sections of it, use code blocks with the same

View File

@ -202,6 +202,14 @@ The following directives are provided for module and class contents:
.. versionadded:: 2.1
.. rst:directive:option:: canonical
:type: full qualified name including module name
Describe the location where the object is defined if the object is
imported from other modules
.. versionadded:: 4.0
.. rst:directive:: .. py:data:: name
Describes global data in a module, including both variables and values used
@ -220,6 +228,14 @@ The following directives are provided for module and class contents:
.. versionadded:: 2.4
.. rst:directive:option:: canonical
:type: full qualified name including module name
Describe the location where the object is defined if the object is
imported from other modules
.. versionadded:: 4.0
.. rst:directive:: .. py:exception:: name
Describes an exception class. The signature can, but need not include
@ -259,6 +275,14 @@ The following directives are provided for module and class contents:
.. rubric:: options
.. rst:directive:option:: canonical
:type: full qualified name including module name
Describe the location where the object is defined if the object is
imported from other modules
.. versionadded:: 4.0
.. rst:directive:option:: final
:type: no value
@ -284,6 +308,30 @@ The following directives are provided for module and class contents:
.. versionadded:: 2.4
.. rst:directive:option:: canonical
:type: full qualified name including module name
Describe the location where the object is defined if the object is
imported from other modules
.. versionadded:: 4.0
.. rst:directive:: .. py:property:: name
Describes an object property.
.. versionadded:: 4.0
.. rubric:: options
.. rst:directive:option:: abstractmethod
:type: no value
Indicate the property is abstract.
.. rst:directive:option:: type: type of the property
:type: text
.. rst:directive:: .. py:method:: name(parameters)
Describes an object method. The parameters should not include the ``self``
@ -307,6 +355,14 @@ The following directives are provided for module and class contents:
.. versionadded:: 2.1
.. rst:directive:option:: canonical
:type: full qualified name including module name
Describe the location where the object is defined if the object is
imported from other modules
.. versionadded:: 4.0
.. rst:directive:option:: classmethod
:type: no value
@ -328,6 +384,10 @@ The following directives are provided for module and class contents:
.. versionadded:: 2.1
.. deprecated:: 4.0
Use :rst:dir:`py:property` instead.
.. rst:directive:option:: staticmethod
:type: no value
@ -544,6 +604,8 @@ a matching identifier is found:
Reference a data attribute of an object.
.. note:: The role is also able to refer to property.
.. rst:role:: py:exc
Reference an exception. A dotted name may be used.
@ -1848,7 +1910,7 @@ currently Ada_, CoffeeScript_, Erlang_, HTTP_, Lasso_, MATLAB_, PHP_, and Ruby_
domains. Also available are domains for `Chapel`_, `Common Lisp`_, dqn_, Go_,
Jinja_, Operation_, and Scala_.
.. _sphinx-contrib: https://bitbucket.org/birkenfeld/sphinx-contrib/
.. _sphinx-contrib: https://github.com/sphinx-contrib
.. _Ada: https://pypi.org/project/sphinxcontrib-adadomain/
.. _Chapel: https://pypi.org/project/sphinxcontrib-chapeldomain/

View File

@ -334,38 +334,15 @@ These themes are:
Third Party Themes
~~~~~~~~~~~~~~~~~~
.. cssclass:: longtable
There are many third-party themes available for Sphinx. Some of these are for
general use, while others are specific to an individual project.
+--------------------+--------------------+
| **Theme overview** | |
+--------------------+--------------------+
| |sphinx_rtd_theme| | |
| | |
| *sphinx_rtd_theme* | |
+--------------------+--------------------+
sphinx-themes.org__ is a gallery that showcases various themes for Sphinx,
with demo documentation rendered under each theme. Themes can also be found
on PyPI__ (using the classifier ``Framework :: Sphinx :: Theme``), GitHub__
and GitLab__.
.. |sphinx_rtd_theme| image:: /_static/themes/sphinx_rtd_theme.png
There are many third-party themes available. Some of these are general use,
while others are specific to an individual project. A section of third-party
themes is listed below. Many more can be found on PyPI__, GitHub__, GitLab__ and
sphinx-themes.org__.
.. cssclass:: clear
**sphinx_rtd_theme**
`Read the Docs Sphinx Theme`_.
This is a mobile-friendly sphinx theme that was made for readthedocs.org.
View a working demo over on readthedocs.org. You can get install and options
information at `Read the Docs Sphinx Theme`_ page.
.. _Read the Docs Sphinx Theme: https://pypi.org/project/sphinx_rtd_theme/
.. versionchanged:: 1.4
**sphinx_rtd_theme** has become optional.
.. __: https://pypi.org/search/?q=&o=&c=Framework+%3A%3A+Sphinx+%3A%3A+Theme
.. __: https://github.com/search?utf8=%E2%9C%93&q=sphinx+theme&type=
.. __: https://gitlab.com/explore?name=sphinx+theme
.. __: https://sphinx-themes.org/
.. __: https://pypi.org/search/?q=&o=&c=Framework+%3A%3A+Sphinx+%3A%3A+Theme
.. __: https://github.com/search?utf8=%E2%9C%93&q=sphinx+theme
.. __: https://gitlab.com/explore?name=sphinx+theme

View File

@ -45,7 +45,7 @@ paths =
line_length = 95
[mypy]
python_version = 3.5
python_version = 3.6
disallow_incomplete_defs = True
show_column_numbers = True
show_error_context = True
@ -61,7 +61,6 @@ filterwarnings =
ignore::DeprecationWarning:docutils.io
ignore::DeprecationWarning:pyximport.pyximport
ignore::ImportWarning:importlib._bootstrap
ignore::PendingDeprecationWarning:sphinx.util.pycompat
markers =
apidoc
setup_command

View File

@ -10,8 +10,8 @@ import sphinx
with open('README.rst') as f:
long_desc = f.read()
if sys.version_info < (3, 5):
print('ERROR: Sphinx requires at least Python 3.5 to run.')
if sys.version_info < (3, 6):
print('ERROR: Sphinx requires at least Python 3.6 to run.')
sys.exit(1)
install_requires = [
@ -23,7 +23,7 @@ install_requires = [
'sphinxcontrib-qthelp',
'Jinja2>=2.3',
'Pygments>=2.0',
'docutils>=0.12',
'docutils>=0.14',
'snowballstemmer>=1.1',
'babel>=1.3',
'alabaster>=0.7,<0.8',
@ -200,7 +200,6 @@ setup(
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3 :: Only',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
@ -242,7 +241,7 @@ setup(
'build_sphinx = sphinx.setup_command:BuildDoc',
],
},
python_requires=">=3.5",
python_requires=">=3.6",
install_requires=install_requires,
extras_require=extras_require,
cmdclass=cmdclass,

View File

@ -19,11 +19,6 @@ from subprocess import PIPE
from .deprecation import RemovedInNextVersionWarning
if False:
# For type annotation
from typing import Any # NOQA
# by default, all DeprecationWarning under sphinx package will be emit.
# Users can avoid this by using environment variable: PYTHONWARNINGS=
if 'PYTHONWARNINGS' not in os.environ:
@ -32,8 +27,8 @@ if 'PYTHONWARNINGS' not in os.environ:
warnings.filterwarnings('ignore', "'U' mode is deprecated",
DeprecationWarning, module='docutils.io')
__version__ = '3.5.4+'
__released__ = '3.5.4' # used when Sphinx builds its own docs
__version__ = '4.0.0+'
__released__ = '4.0.0' # used when Sphinx builds its own docs
#: Version info for better programmatic use.
#:
@ -43,7 +38,7 @@ __released__ = '3.5.4' # used when Sphinx builds its own docs
#:
#: .. versionadded:: 1.2
#: Before version 1.2, check the string ``sphinx.__version__``.
version_info = (3, 5, 4, 'beta', 0)
version_info = (4, 0, 0, 'beta', 0)
package_dir = path.abspath(path.dirname(__file__))
@ -57,8 +52,8 @@ if __version__.endswith('+'):
try:
ret = subprocess.run(['git', 'show', '-s', '--pretty=format:%h'],
cwd=package_dir,
stdout=PIPE, stderr=PIPE)
stdout=PIPE, stderr=PIPE, encoding='ascii')
if ret.stdout:
__display_version__ += '/' + ret.stdout.decode('ascii').strip()
__display_version__ += '/' + ret.stdout.strip()
except Exception:
pass

View File

@ -8,16 +8,12 @@
:license: BSD, see LICENSE for details.
"""
import warnings
from typing import Any, Dict, List, Sequence
from typing import TYPE_CHECKING, Any, Dict, List, Sequence
from docutils import nodes
from docutils.nodes import Element, Node
from docutils.nodes import Element
from sphinx.deprecation import RemovedInSphinx40Warning
if False:
# For type annotation
if TYPE_CHECKING:
from sphinx.application import Sphinx
@ -108,7 +104,7 @@ class toctree(nodes.General, nodes.Element, translatable):
self['caption'] = translated_message
def extract_original_messages(self) -> List[str]:
messages = [] # type: List[str]
messages: List[str] = []
# toctree entries
messages.extend(self.get('rawentries', []))
@ -213,7 +209,7 @@ class desc_content(nodes.General, nodes.Element):
class desc_sig_element(nodes.inline):
"""Common parent class of nodes for inline text of a signature."""
classes = [] # type: List[str]
classes: List[str] = []
def __init__(self, rawsource: str = '', text: str = '',
*children: Element, **attributes: Any) -> None:
@ -342,6 +338,54 @@ class pending_xref(nodes.Inline, nodes.Element):
"""
class pending_xref_condition(nodes.Inline, nodes.TextElement):
"""Node for cross-references that are used to choose appropriate
content of the reference by conditions on the resolving phase.
When the :py:class:`pending_xref` node contains one or more
**pending_xref_condition** nodes, the cross-reference resolver
should choose the content of the reference using defined conditions
in ``condition`` attribute of each pending_xref_condition nodes::
<pending_xref refdomain="py" reftarget="io.StringIO ...>
<pending_xref_condition condition="resolved">
<literal>
StringIO
<pending_xref_condition condition="*">
<literal>
io.StringIO
After the processing of cross-reference resolver, one of the content node
under pending_xref_condition node is chosen by its condition and to be
removed all of pending_xref_condition nodes::
# When resolved the cross-reference successfully
<reference>
<literal>
StringIO
# When resolution is failed
<reference>
<literal>
io.StringIO
.. note:: This node is only allowed to be placed under pending_xref node.
It is not allows to place it under other nodes. In addition,
pending_xref node must contain only pending_xref_condition
nodes if it contains one or more pending_xref_condition nodes.
The pending_xref_condition node should have **condition** attribute.
Domains can be store their individual conditions into the attribute to
filter contents on resolving phase. As a reserved condition name,
``condition="*"`` is used for the fallback of resolution failure.
Additionally, as a recommended condition name, ``condition="resolved"``
is used for the representation of resolstion success in the intersphinx
module.
.. versionadded:: 4.0
"""
class number_reference(nodes.reference):
"""Node for number references, similar to pending_xref."""
@ -362,20 +406,6 @@ class literal_strong(nodes.strong, not_smartquotable):
"""
class abbreviation(nodes.abbreviation):
"""Node for abbreviations with explanations.
.. deprecated:: 2.0
"""
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)
super().__init__(rawsource, text, *children, **attributes)
class manpage(nodes.Inline, nodes.FixedTextElement):
"""Node for references to manpages."""

View File

@ -14,11 +14,10 @@ import os
import pickle
import platform
import sys
import warnings
from collections import deque
from io import StringIO
from os import path
from typing import IO, Any, Callable, Dict, List, Optional, Tuple, Union
from typing import IO, TYPE_CHECKING, Any, Callable, Dict, List, Optional, Tuple, Type, Union
from docutils import nodes
from docutils.nodes import Element, TextElement
@ -30,14 +29,13 @@ from pygments.lexer import Lexer
import sphinx
from sphinx import locale, package_dir
from sphinx.config import Config
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.highlighting import lexer_classes
from sphinx.locale import __
from sphinx.project import Project
from sphinx.registry import SphinxComponentRegistry
@ -52,10 +50,7 @@ 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 Type # for python3.5.1
if TYPE_CHECKING:
from docutils.nodes import Node # NOQA
from sphinx.builders import Builder
@ -135,6 +130,9 @@ class Sphinx:
:ivar outdir: Directory for storing build documents.
"""
warningiserror: bool
_warncount: int
def __init__(self, srcdir: str, confdir: Optional[str], outdir: str, doctreedir: str,
buildername: str, confoverrides: Dict = None,
status: IO = sys.stdout, warning: IO = sys.stderr,
@ -142,12 +140,12 @@ class Sphinx:
verbosity: int = 0, parallel: int = 0, keep_going: bool = False) -> None:
self.phase = BuildPhase.INITIALIZATION
self.verbosity = verbosity
self.extensions = {} # type: Dict[str, Extension]
self.builder = None # type: Builder
self.env = None # type: BuildEnvironment
self.project = None # type: Project
self.extensions: Dict[str, Extension] = {}
self.builder: Builder = None
self.env: BuildEnvironment = None
self.project: Project = None
self.registry = SphinxComponentRegistry()
self.html_themes = {} # type: Dict[str, str]
self.html_themes: Dict[str, str] = {}
# validate provided directories
self.srcdir = abspath(srcdir)
@ -175,14 +173,14 @@ class Sphinx:
self.parallel = parallel
if status is None:
self._status = StringIO() # type: IO
self._status: IO = StringIO()
self.quiet = True
else:
self._status = status
self.quiet = False
if warning is None:
self._warning = StringIO() # type: IO
self._warning: IO = StringIO()
else:
self._warning = warning
self._warncount = 0
@ -197,7 +195,7 @@ class Sphinx:
# keep last few messages for traceback
# This will be filled by sphinx.util.logging.LastMessagesWriter
self.messagelog = deque(maxlen=10) # type: deque
self.messagelog: deque = deque(maxlen=10)
# say hello to the world
logger.info(bold(__('Running Sphinx v%s') % sphinx.__display_version__))
@ -294,7 +292,7 @@ class Sphinx:
if catalog.domain == 'sphinx' and catalog.is_outdated():
catalog.write_mo(self.config.language)
locale_dirs = list(repo.locale_dirs) # type: List[Optional[str]]
locale_dirs: List[Optional[str]] = list(repo.locale_dirs)
locale_dirs += [None]
locale_dirs += [path.join(package_dir, 'locale')]
@ -445,7 +443,7 @@ class Sphinx:
self.events.disconnect(listener_id)
def emit(self, event: str, *args: Any,
allowed_exceptions: Tuple["Type[Exception]", ...] = ()) -> List:
allowed_exceptions: Tuple[Type[Exception], ...] = ()) -> List:
"""Emit *event* and pass *arguments* to the callback functions.
Return the return values of all callbacks as a list. Do not emit core
@ -462,7 +460,7 @@ class Sphinx:
return self.events.emit(event, *args, allowed_exceptions=allowed_exceptions)
def emit_firstresult(self, event: str, *args: Any,
allowed_exceptions: Tuple["Type[Exception]", ...] = ()) -> Any:
allowed_exceptions: Tuple[Type[Exception], ...] = ()) -> Any:
"""Emit *event* and pass *arguments* to the callback functions.
Return the result of the first callback that doesn't return ``None``.
@ -481,7 +479,7 @@ class Sphinx:
# registering addon parts
def add_builder(self, builder: "Type[Builder]", override: bool = False) -> None:
def add_builder(self, builder: Type["Builder"], override: bool = False) -> None:
"""Register a new builder.
:param builder: A builder class
@ -528,8 +526,7 @@ class Sphinx:
``'env'``) to a string. However, booleans are still accepted and
converted internally.
"""
logger.debug('[app] adding config value: %r',
(name, default, rebuild) + ((types,) if types else ()))
logger.debug('[app] adding config value: %r', (name, default, rebuild, types))
if rebuild in (False, True):
rebuild = 'env' if rebuild else ''
self.config.add(name, default, rebuild, types)
@ -544,7 +541,7 @@ class Sphinx:
logger.debug('[app] adding event: %r', name)
self.events.add(name)
def set_translator(self, name: str, translator_class: "Type[nodes.NodeVisitor]",
def set_translator(self, name: str, translator_class: Type[nodes.NodeVisitor],
override: bool = False) -> None:
"""Register or override a Docutils translator class.
@ -563,7 +560,7 @@ class Sphinx:
"""
self.registry.add_translator(name, translator_class, override=override)
def add_node(self, node: "Type[Element]", override: bool = False,
def add_node(self, node: Type[Element], override: bool = False,
**kwargs: Tuple[Callable, Optional[Callable]]) -> None:
"""Register a Docutils node class.
@ -607,7 +604,7 @@ class Sphinx:
docutils.register_node(node)
self.registry.add_translation_handlers(node, **kwargs)
def add_enumerable_node(self, node: "Type[Element]", figtype: str,
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.
@ -636,7 +633,7 @@ class Sphinx:
self.registry.add_enumerable_node(node, figtype, title_getter, override=override)
self.add_node(node, override=override, **kwargs)
def add_directive(self, name: str, cls: "Type[Directive]", override: bool = False) -> None:
def add_directive(self, name: str, cls: Type[Directive], override: bool = False) -> None:
"""Register a Docutils directive.
:param name: The name of directive
@ -726,7 +723,7 @@ class Sphinx:
role = roles.GenericRole(name, nodeclass)
docutils.register_role(name, role)
def add_domain(self, domain: "Type[Domain]", override: bool = False) -> None:
def add_domain(self, domain: Type[Domain], override: bool = False) -> None:
"""Register a domain.
:param domain: A domain class
@ -740,7 +737,7 @@ class Sphinx:
self.registry.add_domain(domain, override=override)
def add_directive_to_domain(self, domain: str, name: str,
cls: "Type[Directive]", override: bool = False) -> None:
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
@ -777,7 +774,7 @@ class Sphinx:
"""
self.registry.add_role_to_domain(domain, name, role, override=override)
def add_index_to_domain(self, domain: str, index: "Type[Index]", override: bool = False
def add_index_to_domain(self, domain: str, index: Type[Index], override: bool = False
) -> None:
"""Register a custom index for a domain.
@ -795,7 +792,7 @@ class Sphinx:
self.registry.add_index_to_domain(domain, index)
def add_object_type(self, directivename: str, rolename: str, indextemplate: str = '',
parse_node: Callable = None, ref_nodeclass: "Type[TextElement]" = None,
parse_node: Callable = None, ref_nodeclass: Type[TextElement] = None,
objname: str = '', doc_field_types: List = [], override: bool = False
) -> None:
"""Register a new object type.
@ -862,7 +859,7 @@ class Sphinx:
override=override)
def add_crossref_type(self, directivename: str, rolename: str, indextemplate: str = '',
ref_nodeclass: "Type[TextElement]" = None, objname: str = '',
ref_nodeclass: Type[TextElement] = None, objname: str = '',
override: bool = False) -> None:
"""Register a new crossref object type.
@ -900,7 +897,7 @@ class Sphinx:
indextemplate, ref_nodeclass, objname,
override=override)
def add_transform(self, transform: "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
@ -935,7 +932,7 @@ class Sphinx:
""" # NOQA
self.registry.add_transform(transform)
def add_post_transform(self, transform: "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
@ -946,13 +943,6 @@ class Sphinx:
"""
self.registry.add_post_transform(transform)
def add_javascript(self, filename: str, **kwargs: Any) -> 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: str, priority: int = 500, **kwargs: Any) -> None:
"""Register a JavaScript file to include in the HTML output.
@ -1032,6 +1022,8 @@ class Sphinx:
* - Priority
- Main purpose in Sphinx
* - 200
- default priority for built-in CSS files
* - 500
- default priority for extensions
* - 800
@ -1061,24 +1053,6 @@ class Sphinx:
if hasattr(self.builder, 'add_css_file'):
self.builder.add_css_file(filename, priority=priority, **kwargs) # type: ignore
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.',
RemovedInSphinx40Warning, stacklevel=2)
attributes = {} # type: Dict[str, Any]
if alternate:
attributes['rel'] = 'alternate stylesheet'
else:
attributes['rel'] = 'stylesheet'
if title:
attributes['title'] = title
self.add_css_file(filename, **attributes)
def add_latex_package(self, packagename: str, options: str = None,
after_hyperref: bool = False) -> None:
r"""Register a package to include in the LaTeX source code.
@ -1102,7 +1076,7 @@ class Sphinx:
"""
self.registry.add_latex_package(packagename, options, after_hyperref)
def add_lexer(self, alias: str, lexer: Union[Lexer, "Type[Lexer]"]) -> None:
def add_lexer(self, alias: str, lexer: Type[Lexer]) -> None:
"""Register a new lexer for source code.
Use *lexer* to highlight code blocks with the given language *alias*.
@ -1113,13 +1087,7 @@ class Sphinx:
still supported until Sphinx-3.x.
"""
logger.debug('[app] adding lexer: %r', (alias, lexer))
if isinstance(lexer, Lexer):
warnings.warn('app.add_lexer() API changed; '
'Please give lexer class instead of instance',
RemovedInSphinx40Warning, stacklevel=2)
lexers[alias] = lexer
else:
lexer_classes[alias] = lexer
lexer_classes[alias] = lexer
def add_autodocumenter(self, cls: Any, override: bool = False) -> None:
"""Register a new documenter class for the autodoc extension.
@ -1133,7 +1101,7 @@ class Sphinx:
If *override* is True, the given *cls* is forcedly installed even if
a documenter having the same name is already installed.
.. todo:: Add real docs for Documenter and subclassing
See :ref:`autodoc_ext_tutorial`.
.. versionadded:: 0.6
.. versionchanged:: 2.2
@ -1144,7 +1112,7 @@ class Sphinx:
self.registry.add_documenter(cls.objtype, cls)
self.add_directive('auto' + cls.objtype, AutodocDirective, override=override)
def add_autodoc_attrgetter(self, typ: "Type", getter: Callable[[Any, str, Any], Any]
def add_autodoc_attrgetter(self, typ: Type, getter: Callable[[Any, str, Any], Any]
) -> None:
"""Register a new ``getattr``-like function for the autodoc extension.
@ -1188,7 +1156,7 @@ class Sphinx:
"""
self.registry.add_source_suffix(suffix, filetype, override=override)
def add_source_parser(self, parser: "Type[Parser]", override: bool = False) -> None:
def add_source_parser(self, parser: Type[Parser], override: bool = False) -> None:
"""Register a parser class.
If *override* is True, the given *parser* is forcedly installed even if
@ -1203,7 +1171,7 @@ class Sphinx:
"""
self.registry.add_source_parser(parser, override=override)
def add_env_collector(self, collector: "Type[EnvironmentCollector]") -> None:
def add_env_collector(self, collector: Type[EnvironmentCollector]) -> None:
"""Register an environment collector class.
Refer to :ref:`collector-api`.

View File

@ -11,7 +11,7 @@
import pickle
import time
from os import path
from typing import Any, Dict, Iterable, List, Sequence, Set, Tuple, Union
from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Sequence, Set, Tuple, Type, Union
from docutils import nodes
from docutils.nodes import Node
@ -40,10 +40,7 @@ try:
except ImportError:
multiprocessing = None
if False:
# For type annotation
from typing import Type # for python3.5.1
if TYPE_CHECKING:
from sphinx.application import Sphinx
@ -66,7 +63,7 @@ class Builder:
#: default translator class for the builder. This can be overridden by
#: :py:meth:`app.set_translator()`.
default_translator_class = None # type: Type[nodes.NodeVisitor]
default_translator_class: Type[nodes.NodeVisitor] = None
# doctree versioning method
versioning_method = 'none'
versioning_compare = False
@ -77,7 +74,7 @@ class Builder:
#: The list of MIME types of image formats supported by the builder.
#: Image files are searched in the order in which they appear here.
supported_image_types = [] # type: List[str]
supported_image_types: List[str] = []
#: The builder supports remote images or not.
supported_remote_images = False
#: The builder supports data URIs or not.
@ -90,18 +87,18 @@ class Builder:
self.doctreedir = app.doctreedir
ensuredir(self.doctreedir)
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.app: Sphinx = app
self.env: BuildEnvironment = None
self.events: EventManager = app.events
self.config: Config = app.config
self.tags: Tags = app.tags
self.tags.add(self.format)
self.tags.add(self.name)
self.tags.add("format_%s" % self.format)
self.tags.add("builder_%s" % self.name)
# images that need to be copied over (source -> dest)
self.images = {} # type: Dict[str, str]
self.images: Dict[str, str] = {}
# basename of images directory
self.imagedir = ""
# relative path to image directory from current docname (used at writing docs)
@ -109,7 +106,7 @@ class Builder:
# these get set later
self.parallel_ok = False
self.finish_tasks = None # type: Any
self.finish_tasks: Any = None
def set_environment(self, env: BuildEnvironment) -> None:
"""Store BuildEnvironment object."""
@ -117,7 +114,7 @@ class Builder:
self.env.set_versioning_method(self.versioning_method,
self.versioning_compare)
def get_translator_class(self, *args: 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)
@ -264,8 +261,7 @@ class Builder:
# relative to the source directory and without source_suffix.
dirlen = len(self.srcdir) + 1
to_write = []
suffixes = None # type: Tuple[str]
suffixes = tuple(self.config.source_suffix) # type: ignore
suffixes: Tuple[str] = tuple(self.config.source_suffix) # type: ignore
for filename in filenames:
filename = path.normpath(path.abspath(filename))
if not filename.startswith(self.srcdir):
@ -416,9 +412,9 @@ class Builder:
else:
self._read_serial(docnames)
if self.config.master_doc not in self.env.all_docs:
raise SphinxError('master file %s not found' %
self.env.doc2path(self.config.master_doc))
if self.config.root_doc not in self.env.all_docs:
raise SphinxError('root file %s not found' %
self.env.doc2path(self.config.root_doc))
for retval in self.events.emit('env-updated', self.env):
if retval is not None:
@ -520,7 +516,7 @@ class Builder:
for tocdocname in self.env.files_to_rebuild.get(docname, set()):
if tocdocname in self.env.found_docs:
docnames.add(tocdocname)
docnames.add(self.config.master_doc)
docnames.add(self.config.root_doc)
with progress_message(__('preparing documents')):
self.prepare_writing(docnames)

View File

@ -11,10 +11,8 @@
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 typing import Any, Dict, List, NamedTuple, Set, Tuple
from zipfile import ZIP_DEFLATED, ZIP_STORED, ZipFile
from docutils import nodes
@ -23,7 +21,6 @@ from docutils.utils import smartquotes
from sphinx import addnodes
from sphinx.builders.html import BuildInfo, StandaloneHTMLBuilder
from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.locale import __
from sphinx.util import logging, status_iterator
from sphinx.util.fileutil import copy_asset_file
@ -84,10 +81,30 @@ VECTOR_GRAPHICS_EXTENSIONS = ('.svg',)
REFURI_RE = re.compile("([^#:]*#)(.*)")
ManifestItem = namedtuple('ManifestItem', ['href', 'id', 'media_type'])
Spine = namedtuple('Spine', ['idref', 'linear'])
Guide = namedtuple('Guide', ['type', 'title', 'uri'])
NavPoint = namedtuple('NavPoint', ['navpoint', 'playorder', 'text', 'refuri', 'children'])
class ManifestItem(NamedTuple):
href: str
id: str
media_type: str
class Spine(NamedTuple):
idref: str
linear: bool
class Guide(NamedTuple):
type: str
title: str
uri: str
class NavPoint(NamedTuple):
navpoint: str
playorder: int
text: str
refuri: str
children: List[Any] # mypy does not support recursive types
# https://github.com/python/mypy/issues/7069
def sphinx_smarty_pants(t: str, language: str = 'en') -> str:
@ -148,9 +165,9 @@ class EpubBuilder(StandaloneHTMLBuilder):
self.link_suffix = '.xhtml'
self.playorder = 0
self.tocid = 0
self.id_cache = {} # type: Dict[str, str]
self.id_cache: Dict[str, str] = {}
self.use_index = self.get_builder_config('use_index', 'epub')
self.refnodes = [] # type: List[Dict[str, Any]]
self.refnodes: List[Dict[str, Any]] = []
def create_build_info(self) -> BuildInfo:
return BuildInfo(self.config, self.tags, ['html', 'epub'])
@ -168,18 +185,6 @@ class EpubBuilder(StandaloneHTMLBuilder):
self.id_cache[name] = id
return id
def esc(self, name: str) -> str:
"""Replace all characters not allowed in text an attribute values."""
warnings.warn(
'%s.esc() is deprecated. Use html.escape() instead.' % self.__class__.__name__,
RemovedInSphinx40Warning, stacklevel=2)
name = name.replace('&', '&amp;')
name = name.replace('<', '&lt;')
name = name.replace('>', '&gt;')
name = name.replace('"', '&quot;')
name = name.replace('\'', '&#39;')
return name
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
@ -204,7 +209,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
return result
def check_refnodes(self, nodes: List[Dict[str, Any]]) -> None:
appeared = set() # type: Set[str]
appeared: Set[str] = set()
for node in nodes:
if node['refuri'] in appeared:
logger.warning(
@ -217,14 +222,14 @@ class EpubBuilder(StandaloneHTMLBuilder):
appeared.add(node['refuri'])
def get_toc(self) -> None:
"""Get the total table of contents, containing the master_doc
"""Get the total table of contents, containing the root_doc
and pre and post files not managed by sphinx.
"""
doctree = self.env.get_and_resolve_doctree(self.config.master_doc,
doctree = self.env.get_and_resolve_doctree(self.config.root_doc,
self, prune_toctrees=False,
includehidden=True)
self.refnodes = self.get_refnodes(doctree, [])
master_dir = path.dirname(self.config.master_doc)
master_dir = path.dirname(self.config.root_doc)
if master_dir:
master_dir += '/' # XXX or os.sep?
for item in self.refnodes:
@ -232,13 +237,13 @@ class EpubBuilder(StandaloneHTMLBuilder):
self.toc_add_files(self.refnodes)
def toc_add_files(self, refnodes: List[Dict[str, Any]]) -> None:
"""Add the master_doc, pre and post files to a list of refnodes.
"""Add the root_doc, pre and post files to a list of refnodes.
"""
refnodes.insert(0, {
'level': 1,
'refuri': html.escape(self.config.master_doc + self.out_suffix),
'refuri': html.escape(self.config.root_doc + self.out_suffix),
'text': ssp(html.escape(
self.env.titles[self.config.master_doc].astext()))
self.env.titles[self.config.root_doc].astext()))
})
for file, text in reversed(self.config.epub_pre_files):
refnodes.insert(0, {
@ -265,7 +270,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
"""
def update_node_id(node: Element) -> None:
"""Update IDs of given *node*."""
new_ids = []
new_ids: List[str] = []
for node_id in node['ids']:
new_id = self.fix_fragment('', node_id)
if new_id not in new_ids:
@ -283,7 +288,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
for target in tree.traverse(nodes.target):
update_node_id(target)
next_node = target.next_node(ascend=True) # type: Node
next_node: Node = target.next_node(ascend=True)
if isinstance(next_node, nodes.Element):
update_node_id(next_node)
@ -461,36 +466,23 @@ class EpubBuilder(StandaloneHTMLBuilder):
addctx['doctype'] = self.doctype
super().handle_page(pagename, addctx, templatename, outfilename, event_arg)
def build_mimetype(self, outdir: str = None, outname: str = 'mimetype') -> None:
def build_mimetype(self) -> None:
"""Write the metainfo file mimetype."""
if outdir:
warnings.warn('The arguments of EpubBuilder.build_mimetype() is deprecated.',
RemovedInSphinx40Warning, stacklevel=2)
else:
outdir = self.outdir
logger.info(__('writing mimetype file...'))
copy_asset_file(path.join(self.template_dir, 'mimetype'), self.outdir)
logger.info(__('writing %s file...'), outname)
copy_asset_file(path.join(self.template_dir, 'mimetype'),
path.join(outdir, outname))
def build_container(self, outdir: str = None, outname: str = 'META-INF/container.xml') -> None: # NOQA
def build_container(self, 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.',
RemovedInSphinx40Warning, stacklevel=2)
else:
outdir = self.outdir
logger.info(__('writing %s file...'), outname)
filename = path.join(outdir, outname)
ensuredir(path.dirname(filename))
copy_asset_file(path.join(self.template_dir, 'container.xml'), filename)
logger.info(__('writing META-INF/container.xml file...'))
outdir = path.join(self.outdir, 'META-INF')
ensuredir(outdir)
copy_asset_file(path.join(self.template_dir, 'container.xml'), outdir)
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: Dict[str, Any] = {}
metadata['title'] = html.escape(self.config.epub_title)
metadata['author'] = html.escape(self.config.epub_author)
metadata['uid'] = html.escape(self.config.epub_uid)
@ -505,24 +497,18 @@ class EpubBuilder(StandaloneHTMLBuilder):
metadata['guides'] = []
return metadata
def build_content(self, outdir: str = None, outname: str = 'content.opf') -> None:
def build_content(self) -> None:
"""Write the metainfo file content.opf It contains bibliographic data,
a file list and the spine (the reading order).
"""
if outdir:
warnings.warn('The arguments of EpubBuilder.build_content() is deprecated.',
RemovedInSphinx40Warning, stacklevel=2)
else:
outdir = self.outdir
logger.info(__('writing %s file...'), outname)
logger.info(__('writing content.opf file...'))
metadata = self.content_metadata()
# files
if not outdir.endswith(os.sep):
outdir += os.sep
olen = len(outdir)
self.files = [] # type: List[str]
if not self.outdir.endswith(os.sep):
self.outdir += os.sep
olen = len(self.outdir)
self.files: List[str] = []
self.ignored_files = ['.buildinfo', 'mimetype', 'content.opf',
'toc.ncx', 'META-INF/container.xml',
'Thumbs.db', 'ehthumbs.db', '.DS_Store',
@ -530,7 +516,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
self.config.epub_exclude_files
if not self.use_index:
self.ignored_files.append('genindex' + self.out_suffix)
for root, dirs, files in os.walk(outdir):
for root, dirs, files in os.walk(self.outdir):
dirs.sort()
for fn in sorted(files):
filename = path.join(root, fn)[olen:]
@ -620,9 +606,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
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)
copy_asset_file(path.join(self.template_dir, 'content.opf_t'), self.outdir, metadata)
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."""
@ -639,8 +623,8 @@ class EpubBuilder(StandaloneHTMLBuilder):
Subelements of a node are nested inside the navpoint. For nested nodes
the parent node is reinserted in the subnav.
"""
navstack = [] # type: List[NavPoint]
navstack.append(NavPoint('dummy', '', '', '', []))
navstack: List[NavPoint] = []
navstack.append(NavPoint('dummy', 0, '', '', []))
level = 0
lastnode = None
for node in nodes:
@ -681,25 +665,19 @@ class EpubBuilder(StandaloneHTMLBuilder):
"""Create a dictionary with all metadata for the toc.ncx file
properly escaped.
"""
metadata = {} # type: Dict[str, Any]
metadata: Dict[str, Any] = {}
metadata['uid'] = self.config.epub_uid
metadata['title'] = html.escape(self.config.epub_title)
metadata['level'] = level
metadata['navpoints'] = navpoints
return metadata
def build_toc(self, outdir: str = None, outname: str = 'toc.ncx') -> None:
def build_toc(self) -> None:
"""Write the metainfo file toc.ncx."""
if outdir:
warnings.warn('The arguments of EpubBuilder.build_toc() is deprecated.',
RemovedInSphinx40Warning, stacklevel=2)
else:
outdir = self.outdir
logger.info(__('writing %s file...'), outname)
logger.info(__('writing toc.ncx file...'))
if self.config.epub_tocscope == 'default':
doctree = self.env.get_and_resolve_doctree(self.config.master_doc,
doctree = self.env.get_and_resolve_doctree(self.config.root_doc,
self, prune_toctrees=False,
includehidden=False)
refnodes = self.get_refnodes(doctree, [])
@ -711,28 +689,21 @@ class EpubBuilder(StandaloneHTMLBuilder):
navpoints = self.build_navpoints(refnodes)
level = max(item['level'] for item in self.refnodes)
level = min(level, self.config.epub_tocdepth)
copy_asset_file(path.join(self.template_dir, 'toc.ncx_t'),
path.join(outdir, outname),
copy_asset_file(path.join(self.template_dir, 'toc.ncx_t'), self.outdir,
self.toc_metadata(level, navpoints))
def build_epub(self, outdir: str = None, outname: str = None) -> None:
def build_epub(self) -> None:
"""Write the epub file.
It is a zip file with the mimetype file stored uncompressed as the first
entry.
"""
if outdir:
warnings.warn('The arguments of EpubBuilder.build_epub() is deprecated.',
RemovedInSphinx40Warning, stacklevel=2)
else:
outdir = self.outdir
outname = self.config.epub_basename + '.epub'
outname = self.config.epub_basename + '.epub'
logger.info(__('writing %s file...'), outname)
epub_filename = path.join(outdir, outname)
epub_filename = path.join(self.outdir, outname)
with ZipFile(epub_filename, 'w', ZIP_DEFLATED) as epub:
epub.write(path.join(outdir, 'mimetype'), 'mimetype', ZIP_STORED)
epub.write(path.join(self.outdir, 'mimetype'), 'mimetype', ZIP_STORED)
for filename in ['META-INF/container.xml', 'content.opf', 'toc.ncx']:
epub.write(path.join(outdir, filename), filename, ZIP_DEFLATED)
epub.write(path.join(self.outdir, filename), filename, ZIP_DEFLATED)
for filename in self.files:
epub.write(path.join(outdir, filename), filename, ZIP_DEFLATED)
epub.write(path.join(self.outdir, filename), filename, ZIP_DEFLATED)

View File

@ -1,45 +0,0 @@
"""
sphinx.builders.applehelp
~~~~~~~~~~~~~~~~~~~~~~~~~
Build Apple help books.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import warnings
from typing import Any, Dict
from sphinxcontrib.applehelp import (AppleHelpBuilder, AppleHelpCodeSigningFailed,
AppleHelpIndexerFailed)
from sphinx.application import Sphinx
from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
deprecated_alias('sphinx.builders.applehelp',
{
'AppleHelpCodeSigningFailed': AppleHelpCodeSigningFailed,
'AppleHelpIndexerFailed': AppleHelpIndexerFailed,
'AppleHelpBuilder': AppleHelpBuilder,
},
RemovedInSphinx40Warning,
{
'AppleHelpCodeSigningFailed':
'sphinxcontrib.applehelp.AppleHelpCodeSigningFailed',
'AppleHelpIndexerFailed':
'sphinxcontrib.applehelp.AppleHelpIndexerFailed',
'AppleHelpBuilder': 'sphinxcontrib.applehelp.AppleHelpBuilder',
})
def setup(app: Sphinx) -> Dict[str, Any]:
warnings.warn('sphinx.builders.applehelp has been moved to sphinxcontrib-applehelp.',
RemovedInSphinx40Warning, stacklevel=2)
app.setup_extension('sphinxcontrib.applehelp')
return {
'version': 'builtin',
'parallel_read_safe': True,
'parallel_write_safe': True,
}

View File

@ -51,9 +51,9 @@ class ChangesBuilder(Builder):
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]]]
apichanges = [] # type: List[Tuple[str, str, int]]
otherchanges = {} # type: Dict[Tuple[str, str], List[Tuple[str, str, int]]]
libchanges: Dict[str, List[Tuple[str, str, int]]] = {}
apichanges: List[Tuple[str, str, int]] = []
otherchanges: Dict[Tuple[str, str], List[Tuple[str, str, int]]] = {}
changesets = domain.get_changesets_for(version)
if not changesets:

View File

@ -1,40 +0,0 @@
"""
sphinx.builders.devhelp
~~~~~~~~~~~~~~~~~~~~~~~
Build HTML documentation and Devhelp_ support files.
.. _Devhelp: https://wiki.gnome.org/Apps/Devhelp
:copyright: Copyright 2007-2021 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
deprecated_alias('sphinx.builders.devhelp',
{
'DevhelpBuilder': DevhelpBuilder,
},
RemovedInSphinx40Warning,
{
'DevhelpBuilder': 'sphinxcontrib.devhelp.DevhelpBuilder'
})
def setup(app: Sphinx) -> Dict[str, Any]:
warnings.warn('sphinx.builders.devhelp has been moved to sphinxcontrib-devhelp.',
RemovedInSphinx40Warning)
app.setup_extension('sphinxcontrib.devhelp')
return {
'version': 'builtin',
'parallel_read_safe': True,
'parallel_write_safe': True,
}

View File

@ -10,16 +10,13 @@
"""
import html
import warnings
from collections import namedtuple
from os import path
from typing import Any, Dict, List, Set, Tuple
from typing import Any, Dict, List, NamedTuple, Set, Tuple
from sphinx import package_dir
from sphinx.application import Sphinx
from sphinx.builders import _epub_base
from sphinx.config import ENUM, Config
from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.locale import __
from sphinx.util import logging, xmlname_checker
from sphinx.util.fileutil import copy_asset_file
@ -29,7 +26,12 @@ from sphinx.util.osutil import make_filename
logger = logging.getLogger(__name__)
NavPoint = namedtuple('NavPoint', ['text', 'refuri', 'children'])
class NavPoint(NamedTuple):
text: str
refuri: str
children: List[Any] # mypy does not support recursive types
# https://github.com/python/mypy/issues/7069
# writing modes
PAGE_PROGRESSION_DIRECTIONS = {
@ -81,10 +83,6 @@ class Epub3Builder(_epub_base.EpubBuilder):
self.build_toc()
self.build_epub()
def validate_config_value(self) -> None:
warnings.warn('Epub3Builder.validate_config_value() is deprecated.',
RemovedInSphinx40Warning, stacklevel=2)
def content_metadata(self) -> Dict:
"""Create a dictionary with all metadata for the content.opf
file properly escaped.
@ -120,7 +118,7 @@ class Epub3Builder(_epub_base.EpubBuilder):
The difference from build_navpoints method is templates which are used
when generating navigation documents.
"""
navstack = [] # type: List[NavPoint]
navstack: List[NavPoint] = []
navstack.append(NavPoint('', '', []))
level = 0
for node in navnodes:
@ -156,25 +154,19 @@ class Epub3Builder(_epub_base.EpubBuilder):
"""Create a dictionary with all metadata for the nav.xhtml file
properly escaped.
"""
metadata = {} # type: Dict
metadata: Dict = {}
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: str = None, outname: str = 'nav.xhtml') -> None:
def build_navigation_doc(self) -> None:
"""Write the metainfo file nav.xhtml."""
if outdir:
warnings.warn('The arguments of Epub3Builder.build_navigation_doc() '
'is deprecated.', RemovedInSphinx40Warning, stacklevel=2)
else:
outdir = self.outdir
logger.info(__('writing %s file...'), outname)
logger.info(__('writing nav.xhtml file...'))
if self.config.epub_tocscope == 'default':
doctree = self.env.get_and_resolve_doctree(
self.config.master_doc, self,
self.config.root_doc, self,
prune_toctrees=False, includehidden=False)
refnodes = self.get_refnodes(doctree, [])
self.toc_add_files(refnodes)
@ -182,13 +174,12 @@ class Epub3Builder(_epub_base.EpubBuilder):
# 'includehidden'
refnodes = self.refnodes
navlist = self.build_navlist(refnodes)
copy_asset_file(path.join(self.template_dir, 'nav.xhtml_t'),
path.join(outdir, outname),
copy_asset_file(path.join(self.template_dir, 'nav.xhtml_t'), self.outdir,
self.navigation_doc_metadata(navlist))
# Add nav.xhtml to epub file
if outname not in self.files:
self.files.append(outname)
if 'nav.xhtml' not in self.files:
self.files.append('nav.xhtml')
def validate_config_values(app: Sphinx) -> None:
@ -232,7 +223,7 @@ def validate_config_values(app: Sphinx) -> 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]]
epub_css_files: List[Tuple[str, Dict]] = []
for entry in config.epub_css_files:
if isinstance(entry, str):
epub_css_files.append((entry, {}))

View File

@ -13,7 +13,7 @@ from collections import OrderedDict, defaultdict
from datetime import datetime, timedelta, tzinfo
from os import getenv, path, walk
from time import time
from typing import Any, Dict, Generator, Iterable, List, Set, Tuple, Union
from typing import Any, DefaultDict, Dict, Generator, Iterable, List, Set, Tuple, Union
from uuid import uuid4
from docutils import nodes
@ -33,33 +33,8 @@ from sphinx.util.osutil import canon_path, ensuredir, relpath
from sphinx.util.tags import Tags
from sphinx.util.template import SphinxRenderer
if False:
# For type annotation
from typing import DefaultDict # for python3.5.1
logger = logging.getLogger(__name__)
POHEADER = r"""
# SOME DESCRIPTIVE TITLE.
# Copyright (C) %(copyright)s
# This file is distributed under the same license as the %(project)s package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: %(project)s %(version)s\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: %(ctime)s\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"""[1:] # RemovedInSphinx40Warning
class Message:
"""An entry of translatable message."""
@ -73,10 +48,10 @@ class Catalog:
"""Catalog of translatable messages."""
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
self.messages: List[str] = [] # retain insertion order, a la OrderedDict
# msgid -> file, line, uid
self.metadata: Dict[str, List[Tuple[str, int, str]]] = OrderedDict()
def add(self, msg: str, origin: Union[Element, "MsgOrigin"]) -> None:
if not hasattr(origin, 'uid'):
@ -146,8 +121,7 @@ class I18nBuilder(Builder):
"""
name = 'i18n'
versioning_method = 'text'
versioning_compare = None # type: bool
# be set by `gettext_uuid`
versioning_compare: bool = None # be set by `gettext_uuid`
use_message_catalog = False
def init(self) -> None:
@ -155,7 +129,7 @@ class I18nBuilder(Builder):
self.env.set_versioning_method(self.versioning_method,
self.env.config.gettext_uuid)
self.tags = I18nTags()
self.catalogs = defaultdict(Catalog) # type: DefaultDict[str, Catalog]
self.catalogs: DefaultDict[str, Catalog] = defaultdict(Catalog)
def get_target_uri(self, docname: str, typ: str = None) -> str:
return ''

View File

@ -13,9 +13,9 @@ import os
import posixpath
import re
import sys
import warnings
from datetime import datetime
from os import path
from typing import IO, Any, Dict, Iterable, Iterator, List, Set, Tuple
from typing import IO, Any, Dict, Iterable, Iterator, List, Set, Tuple, Type
from urllib.parse import quote
from docutils import nodes
@ -29,7 +29,6 @@ from sphinx import __display_version__, package_dir
from sphinx.application import Sphinx
from sphinx.builders import Builder
from sphinx.config import ENUM, 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
@ -39,7 +38,7 @@ 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, md5, progress_message, status_iterator
from sphinx.util import isurl, logging, md5, 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
@ -49,11 +48,6 @@ from sphinx.util.osutil import copyfile, ensuredir, os_path, relative_uri
from sphinx.util.tags import Tags
from sphinx.writers.html import HTMLTranslator, HTMLWriter
if False:
# For type annotation
from typing import Type # for python3.5.1
# HTML5 Writer is available or not
if is_html5_writer_available():
from sphinx.writers.html5 import HTML5Translator
@ -88,9 +82,9 @@ class Stylesheet(str):
its filename (str).
"""
attributes = None # type: Dict[str, str]
filename = None # type: str
priority = None # type: int
attributes: Dict[str, str] = None
filename: str = None
priority: int = None
def __new__(cls, filename: str, *args: str, priority: int = 500, **attributes: Any
) -> "Stylesheet":
@ -114,9 +108,9 @@ class JavaScript(str):
its filename (str).
"""
attributes = None # type: Dict[str, str]
filename = None # type: str
priority = None # type: int
attributes: Dict[str, str] = None
filename: str = None
priority: int = None
def __new__(cls, filename: str, priority: int = 500, **attributes: str) -> "JavaScript":
self = str.__new__(cls, filename)
@ -185,7 +179,7 @@ class StandaloneHTMLBuilder(Builder):
allow_parallel = True
out_suffix = '.html'
link_suffix = '.html' # defaults to matching out_suffix
indexer_format = js_index # type: Any
indexer_format: Any = js_index
indexer_dumps_unicode = True
# create links to original images from images [True/False]
html_scaled_image_link = True
@ -201,26 +195,26 @@ class StandaloneHTMLBuilder(Builder):
use_index = False
download_support = True # enable download role
imgpath = None # type: str
domain_indices = [] # type: List[Tuple[str, Type[Index], List[Tuple[str, List[IndexEntry]]], bool]] # NOQA
imgpath: str = None
domain_indices: List[Tuple[str, Type[Index], List[Tuple[str, List[IndexEntry]]], bool]] = [] # NOQA
def __init__(self, app: Sphinx) -> None:
super().__init__(app)
# CSS files
self.css_files = [] # type: List[Dict[str, str]]
self.css_files: List[Dict[str, str]] = []
# JS files
self.script_files = [] # type: List[JavaScript]
self.script_files: List[JavaScript] = []
def init(self) -> None:
self.build_info = self.create_build_info()
# basename of images directory
self.imagedir = '_images'
# section numbers for headings in the currently visited document
self.secnumbers = {} # type: Dict[str, Tuple[int, ...]]
self.secnumbers: Dict[str, Tuple[int, ...]] = {}
# currently written docname
self.current_docname = None # type: str
self.current_docname: str = None
self.init_templates()
self.init_highlighter()
@ -256,6 +250,14 @@ class StandaloneHTMLBuilder(Builder):
return jsfile
return None
def _get_style_filename(self) -> str:
if self.config.html_style is not None:
return self.config.html_style
elif self.theme:
return self.theme.get_config('theme', 'stylesheet')
else:
return 'default.css'
def get_theme_config(self) -> Tuple[str, Dict]:
return self.config.html_theme, self.config.html_theme_options
@ -291,6 +293,9 @@ class StandaloneHTMLBuilder(Builder):
self.dark_highlighter = None
def init_css_files(self) -> None:
self.add_css_file('pygments.css', priority=200)
self.add_css_file(self._get_style_filename(), priority=200)
for filename, attrs in self.app.registry.css_files:
self.add_css_file(filename, **attrs)
@ -305,6 +310,8 @@ class StandaloneHTMLBuilder(Builder):
self.css_files.append(Stylesheet(filename, **kwargs)) # type: ignore
def init_js_files(self) -> None:
self.add_js_file('documentation_options.js', id="documentation_options",
data_url_root='', priority=200)
self.add_js_file('jquery.js', priority=200)
self.add_js_file('underscore.js', priority=200)
self.add_js_file('doctools.js', priority=200)
@ -326,7 +333,7 @@ class StandaloneHTMLBuilder(Builder):
self.script_files.append(JavaScript(filename, **kwargs))
@property
def default_translator_class(self) -> "Type[nodes.NodeVisitor]": # type: ignore
def default_translator_class(self) -> Type[nodes.NodeVisitor]: # type: ignore
if not html5_ready or self.config.html4_writer:
return HTMLTranslator
else:
@ -358,6 +365,7 @@ class StandaloneHTMLBuilder(Builder):
buildinfo = BuildInfo.load(fp)
if self.build_info != buildinfo:
logger.debug('[build target] did not match: build_info ')
yield from self.env.found_docs
return
except ValueError as exc:
@ -372,6 +380,7 @@ class StandaloneHTMLBuilder(Builder):
template_mtime = 0
for docname in self.env.found_docs:
if docname not in self.env.all_docs:
logger.debug('[build target] did not in env: %r', docname)
yield docname
continue
targetname = self.get_outfilename(docname)
@ -383,6 +392,14 @@ class StandaloneHTMLBuilder(Builder):
srcmtime = max(path.getmtime(self.env.doc2path(docname)),
template_mtime)
if srcmtime > targetmtime:
logger.debug(
'[build target] targetname %r(%s), template(%s), docname %r(%s)',
targetname,
datetime.utcfromtimestamp(targetmtime),
datetime.utcfromtimestamp(template_mtime),
docname,
datetime.utcfromtimestamp(path.getmtime(self.env.doc2path(docname))),
)
yield docname
except OSError:
# source doesn't exist anymore
@ -419,10 +436,10 @@ class StandaloneHTMLBuilder(Builder):
self.load_indexer(docnames)
self.docwriter = HTMLWriter(self)
self.docsettings = OptionParser(
self.docsettings: Any = OptionParser(
defaults=self.env.settings,
components=(self.docwriter,),
read_config_files=True).get_default_values() # type: Any
read_config_files=True).get_default_values()
self.docsettings.compact_lists = bool(self.config.html_compact_lists)
# determine the additional indices to include
@ -431,8 +448,7 @@ class StandaloneHTMLBuilder(Builder):
indices_config = self.config.html_domain_indices
if indices_config:
for domain_name in sorted(self.env.domains):
domain = None # type: Domain
domain = self.env.domains[domain_name]
domain: Domain = self.env.domains[domain_name]
for indexcls in domain.indices:
indexname = '%s-%s' % (domain.name, indexcls.name)
if isinstance(indices_config, list):
@ -457,7 +473,7 @@ class StandaloneHTMLBuilder(Builder):
self.relations = self.env.collect_relations()
rellinks = [] # type: List[Tuple[str, str, str, str]]
rellinks: List[Tuple[str, str, str, str]] = []
if self.use_index:
rellinks.append(('genindex', _('General Index'), 'I', _('index')))
for indexname, indexcls, content, collapse in self.domain_indices:
@ -470,13 +486,6 @@ class StandaloneHTMLBuilder(Builder):
self._script_files = list(self.script_files)
self._css_files = list(self.css_files)
if self.config.html_style is not None:
stylename = self.config.html_style
elif self.theme:
stylename = self.theme.get_config('theme', 'stylesheet')
else:
stylename = 'default.css'
self.globalcontext = {
'embedded': self.embedded,
'project': self.config.project,
@ -484,7 +493,8 @@ class StandaloneHTMLBuilder(Builder):
'version': self.config.version,
'last_updated': self.last_updated,
'copyright': self.config.copyright,
'master_doc': self.config.master_doc,
'master_doc': self.config.root_doc,
'root_doc': self.config.root_doc,
'use_opensearch': self.config.html_use_opensearch,
'docstitle': self.config.html_title,
'shorttitle': self.config.html_short_title,
@ -499,7 +509,7 @@ class StandaloneHTMLBuilder(Builder):
'language': self.config.language,
'css_files': self.css_files,
'sphinx_version': __display_version__,
'style': stylename,
'style': self._get_style_filename(),
'rellinks': rellinks,
'builder': self.name,
'parents': [],
@ -786,12 +796,12 @@ class StandaloneHTMLBuilder(Builder):
excluded, context=context, renderer=self.templates, onerror=onerror)
def copy_html_logo(self) -> None:
if self.config.html_logo:
if self.config.html_logo and not isurl(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:
if self.config.html_favicon and not isurl(self.config.html_favicon):
copy_asset(path.join(self.confdir, self.config.html_favicon),
path.join(self.outdir, '_static'))
@ -889,21 +899,11 @@ class StandaloneHTMLBuilder(Builder):
# only index pages with title
if self.indexer is not None and title:
filename = self.env.doc2path(pagename, base=None)
try:
metadata = self.env.metadata.get(pagename, {})
if 'nosearch' in metadata:
self.indexer.feed(pagename, filename, '', new_document(''))
else:
self.indexer.feed(pagename, filename, title, doctree)
except TypeError:
# fallback for old search-adapters
self.indexer.feed(pagename, title, doctree) # type: ignore
indexer_name = self.indexer.__class__.__name__
warnings.warn(
'The %s.feed() method signature is deprecated. Update to '
'%s.feed(docname, filename, title, doctree).' % (
indexer_name, indexer_name),
RemovedInSphinx40Warning, stacklevel=2)
metadata = self.env.metadata.get(pagename, {})
if 'nosearch' in metadata:
self.indexer.feed(pagename, filename, '', new_document(''))
else:
self.indexer.feed(pagename, filename, title, doctree)
def _get_local_toctree(self, docname: str, collapse: bool = True, **kwargs: Any) -> str:
if 'includehidden' not in kwargs:
@ -1108,7 +1108,7 @@ class StandaloneHTMLBuilder(Builder):
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]]
html_css_files: List[Tuple[str, Dict]] = []
for entry in config.html_css_files:
if isinstance(entry, str):
html_css_files.append((entry, {}))
@ -1125,7 +1125,7 @@ def convert_html_css_files(app: Sphinx, config: 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]]
html_js_files: List[Tuple[str, Dict]] = []
for entry in config.html_js_files:
if isinstance(entry, str):
html_js_files.append((entry, {}))
@ -1140,7 +1140,7 @@ def convert_html_js_files(app: Sphinx, config: Config) -> None:
config.html_js_files = html_js_files # type: ignore
def setup_js_tag_helper(app: Sphinx, pagename: str, templatexname: str,
def setup_js_tag_helper(app: Sphinx, pagename: str, templatename: str,
context: Dict, doctree: Node) -> None:
"""Set up js_tag() template helper.
@ -1157,6 +1157,8 @@ def setup_js_tag_helper(app: Sphinx, pagename: str, templatexname: str,
if value is not None:
if key == 'body':
body = value
elif key == 'data_url_root':
attrs.append('data-url_root="%s"' % pathto('', resource=True))
else:
attrs.append('%s="%s"' % (key, html.escape(value, True)))
if js.filename:
@ -1169,6 +1171,26 @@ def setup_js_tag_helper(app: Sphinx, pagename: str, templatexname: str,
context['js_tag'] = js_tag
def setup_resource_paths(app: Sphinx, pagename: str, templatename: str,
context: Dict, doctree: Node) -> None:
"""Set up relative resource paths."""
pathto = context.get('pathto')
# favicon_url
favicon = context.get('favicon')
if favicon and not isurl(favicon):
context['favicon_url'] = pathto('_static/' + favicon, resource=True)
else:
context['favicon_url'] = favicon
# logo_url
logo = context.get('logo')
if logo and not isurl(logo):
context['logo_url'] = pathto('_static/' + logo, resource=True)
else:
context['logo_url'] = logo
def validate_math_renderer(app: Sphinx) -> None:
if app.builder.format != 'html':
return
@ -1209,27 +1231,47 @@ def validate_html_static_path(app: Sphinx, config: Config) -> None:
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)):
if (config.html_logo and
not path.isfile(path.join(app.confdir, config.html_logo)) and
not isurl(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)):
if (config.html_favicon and
not path.isfile(path.join(app.confdir, config.html_favicon)) and
not isurl(config.html_favicon)):
logger.warning(__('favicon file %r does not exist'), config.html_favicon)
config.html_favicon = None # type: ignore
class _stable_repr_object():
def __repr__(self):
return '<object>'
UNSET = _stable_repr_object()
def migrate_html_add_permalinks(app: Sphinx, config: Config) -> None:
"""Migrate html_add_permalinks to html_permalinks*."""
if config.html_add_permalinks:
if (isinstance(config.html_add_permalinks, bool) and
config.html_add_permalinks is False):
config.html_permalinks = False # type: ignore
else:
config.html_permalinks_icon = html.escape(config.html_add_permalinks) # type: ignore # NOQA
html_add_permalinks = config.html_add_permalinks
if html_add_permalinks is UNSET:
return
# RemovedInSphinx60Warning
logger.warning(__('html_add_permalinks has been deprecated since v3.5.0. '
'Please use html_permalinks and html_permalinks_icon instead.'))
if not html_add_permalinks:
config.html_permalinks = False # type: ignore[attr-defined]
return
config.html_permalinks_icon = html.escape( # type: ignore[attr-defined]
html_add_permalinks
)
# for compatibility
import sphinxcontrib.serializinghtml # NOQA
@ -1261,7 +1303,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
app.add_config_value('html_sidebars', {}, 'html')
app.add_config_value('html_additional_pages', {}, 'html')
app.add_config_value('html_domain_indices', True, 'html', [list])
app.add_config_value('html_add_permalinks', None, 'html')
app.add_config_value('html_add_permalinks', UNSET, 'html')
app.add_config_value('html_permalinks', True, 'html')
app.add_config_value('html_permalinks_icon', '', 'html')
app.add_config_value('html_use_index', True, 'html')
@ -1283,7 +1325,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
app.add_config_value('html_search_scorer', '', None)
app.add_config_value('html_scaled_image_link', True, 'html')
app.add_config_value('html_baseurl', '', 'html')
app.add_config_value('html_codeblock_linenos_style', 'table', 'html',
app.add_config_value('html_codeblock_linenos_style', 'inline', 'html', # RemovedInSphinx60Warning # NOQA
ENUM('table', 'inline'))
app.add_config_value('html_math_renderer', None, 'env')
app.add_config_value('html4_writer', False, 'html')
@ -1302,6 +1344,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
app.connect('config-inited', validate_html_favicon, priority=800)
app.connect('builder-inited', validate_math_renderer)
app.connect('html-page-context', setup_js_tag_helper)
app.connect('html-page-context', setup_resource_paths)
# load default math renderer
app.setup_extension('sphinx.ext.mathjax')

View File

@ -1,47 +0,0 @@
"""
sphinx.builders.htmlhelp
~~~~~~~~~~~~~~~~~~~~~~~~
Build HTML help support files.
Parts adapted from Python's Doc/tools/prechm.py.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import warnings
from typing import Any, Dict
from sphinxcontrib.htmlhelp import (HTMLHelpBuilder, chm_htmlescape, chm_locales,
default_htmlhelp_basename)
from sphinx.application import Sphinx
from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
deprecated_alias('sphinx.builders.htmlhelp',
{
'chm_locales': chm_locales,
'chm_htmlescape': chm_htmlescape,
'HTMLHelpBuilder': HTMLHelpBuilder,
'default_htmlhelp_basename': default_htmlhelp_basename,
},
RemovedInSphinx40Warning,
{
'chm_locales': 'sphinxcontrib.htmlhelp.chm_locales',
'chm_htmlescape': 'sphinxcontrib.htmlhelp.chm_htmlescape',
'HTMLHelpBuilder': 'sphinxcontrib.htmlhelp.HTMLHelpBuilder',
'default_htmlhelp_basename':
'sphinxcontrib.htmlhelp.default_htmlhelp_basename',
})
def setup(app: Sphinx) -> Dict[str, Any]:
warnings.warn('sphinx.builders.htmlhelp has been moved to sphinxcontrib-htmlhelp.',
RemovedInSphinx40Warning, stacklevel=2)
app.setup_extension('sphinxcontrib.htmlhelp')
return {
'version': 'builtin',
'parallel_read_safe': True,
'parallel_write_safe': True,
}

View File

@ -24,7 +24,7 @@ from sphinx.builders.latex.constants import ADDITIONAL_SETTINGS, DEFAULT_SETTING
from sphinx.builders.latex.theming import Theme, ThemeFactory
from sphinx.builders.latex.util import ExtBabel
from sphinx.config import ENUM, Config
from sphinx.deprecation import RemovedInSphinx40Warning, RemovedInSphinx50Warning
from sphinx.deprecation import RemovedInSphinx50Warning
from sphinx.environment.adapters.asset import ImageAdapter
from sphinx.errors import NoUri, SphinxError
from sphinx.locale import _, __
@ -122,10 +122,10 @@ class LaTeXBuilder(Builder):
default_translator_class = LaTeXTranslator
def init(self) -> None:
self.babel = None # type: ExtBabel
self.context = {} # type: Dict[str, Any]
self.docnames = [] # type: Iterable[str]
self.document_data = [] # type: List[Tuple[str, str, str, str, str, bool]]
self.babel: ExtBabel = None
self.context: Dict[str, Any] = {}
self.docnames: Iterable[str] = {}
self.document_data: List[Tuple[str, str, str, str, str, bool]] = []
self.themes = ThemeFactory(self.app)
texescape.init()
@ -153,7 +153,7 @@ class LaTeXBuilder(Builder):
'will be written'))
return
# assign subdirs to titles
self.titles = [] # type: List[Tuple[str, str]]
self.titles: List[Tuple[str, str]] = []
for entry in preliminary_document_data:
docname = entry[0]
if docname not in self.env.all_docs:
@ -216,14 +216,18 @@ class LaTeXBuilder(Builder):
if not self.babel.uses_cyrillic():
if 'X2' in self.context['fontenc']:
self.context['substitutefont'] = '\\usepackage{substitutefont}'
self.context['textcyrillic'] = '\\usepackage[Xtwo]{sphinxcyrillic}'
self.context['textcyrillic'] = ('\\usepackage[Xtwo]'
'{sphinxpackagecyrillic}')
elif 'T2A' in self.context['fontenc']:
self.context['substitutefont'] = '\\usepackage{substitutefont}'
self.context['textcyrillic'] = '\\usepackage[TtwoA]{sphinxcyrillic}'
self.context['textcyrillic'] = ('\\usepackage[TtwoA]'
'{sphinxpackagecyrillic}')
if 'LGR' in self.context['fontenc']:
self.context['substitutefont'] = '\\usepackage{substitutefont}'
else:
self.context['textgreek'] = ''
if self.context['substitutefont'] == '':
self.context['fontsubstitution'] = ''
# 'babel' key is public and user setting must be obeyed
if self.context['babel']:
@ -252,16 +256,16 @@ class LaTeXBuilder(Builder):
with open(stylesheet, 'w') as f:
f.write('\\NeedsTeXFormat{LaTeX2e}[1995/12/01]\n')
f.write('\\ProvidesPackage{sphinxhighlight}'
'[2016/05/29 stylesheet for highlighting with pygments]\n\n')
'[2016/05/29 stylesheet for highlighting with pygments]\n')
f.write('% Its contents depend on pygments_style configuration variable.\n\n')
f.write(highlighter.get_stylesheet())
def write(self, *ignored: Any) -> None:
docwriter = LaTeXWriter(self)
docsettings = OptionParser(
docsettings: Any = OptionParser(
defaults=self.env.settings,
components=(docwriter,),
read_config_files=True).get_default_values() # type: Any
patch_settings(docsettings)
read_config_files=True).get_default_values()
self.init_document_data()
self.write_stylesheet()
@ -352,7 +356,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[Node]
newnodes: List[Node] = [nodes.emphasis(sectname, sectname)]
for subdir, title in self.titles:
if docname.startswith(subdir):
newnodes.append(nodes.Text(_(' (in '), _(' (in ')))
@ -364,10 +368,6 @@ class LaTeXBuilder(Builder):
pendingnode.replace_self(newnodes)
return largetree
def apply_transforms(self, doctree: nodes.document) -> None:
warnings.warn('LaTeXBuilder.apply_transforms() is deprecated.',
RemovedInSphinx40Warning, stacklevel=2)
def finish(self) -> None:
self.copy_image_files()
self.write_message_catalog()
@ -462,44 +462,6 @@ class LaTeXBuilder(Builder):
return self.app.registry.latex_packages_after_hyperref
def patch_settings(settings: Any) -> Any:
"""Make settings object to show deprecation messages."""
class Values(type(settings)): # type: ignore
@property
def author(self) -> str:
warnings.warn('settings.author is deprecated',
RemovedInSphinx40Warning, stacklevel=2)
return self._author
@property
def title(self) -> str:
warnings.warn('settings.title is deprecated',
RemovedInSphinx40Warning, stacklevel=2)
return self._title
@property
def contentsname(self) -> str:
warnings.warn('settings.contentsname is deprecated',
RemovedInSphinx40Warning, stacklevel=2)
return self._contentsname
@property
def docname(self) -> str:
warnings.warn('settings.docname is deprecated',
RemovedInSphinx40Warning, stacklevel=2)
return self._docname
@property
def docclass(self) -> str:
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:
@ -525,7 +487,7 @@ def install_packages_for_ja(app: Sphinx) -> None:
def default_latex_engine(config: Config) -> str:
""" Better default latex_engine settings for specific languages. """
if config.language == 'ja':
return 'platex'
return 'uplatex'
elif (config.language or '').startswith('zh'):
return 'xelatex'
elif config.language == 'el':
@ -556,7 +518,7 @@ def default_latex_documents(config: Config) -> List[Tuple[str, str, str, str, st
""" 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,
return [(config.root_doc,
make_filename_from_project(config.project) + '.tex',
texescape.escape_abbr(project),
texescape.escape_abbr(author),

View File

@ -11,7 +11,12 @@
from typing import Any, Dict
PDFLATEX_DEFAULT_FONTPKG = r'''
\usepackage{times}
\usepackage{tgtermes}
\usepackage{tgheros}
\renewcommand{\ttdefault}{txtt}
'''
PDFLATEX_DEFAULT_FONTSUBSTITUTION = r'''
\expandafter\ifx\csname T@LGR\endcsname\relax
\else
% LGR was declared as font encoding
@ -66,7 +71,7 @@ XELATEX_GREEK_DEFAULT_FONTPKG = (XELATEX_DEFAULT_FONTPKG +
LUALATEX_DEFAULT_FONTPKG = XELATEX_DEFAULT_FONTPKG
DEFAULT_SETTINGS = {
DEFAULT_SETTINGS: Dict[str, Any] = {
'latex_engine': 'pdflatex',
'papersize': '',
'pointsize': '',
@ -76,7 +81,7 @@ DEFAULT_SETTINGS = {
'maxlistdepth': '',
'sphinxpkgoptions': '',
'sphinxsetup': '',
'fvset': '\\fvset{fontsize=\\small}',
'fvset': '\\fvset{fontsize=auto}',
'passoptionstopackages': '',
'geometry': '\\usepackage{geometry}',
'inputenc': '',
@ -88,6 +93,7 @@ DEFAULT_SETTINGS = {
'babel': '\\usepackage{babel}',
'polyglossia': '',
'fontpkg': PDFLATEX_DEFAULT_FONTPKG,
'fontsubstitution': PDFLATEX_DEFAULT_FONTSUBSTITUTION,
'substitutefont': '',
'textcyrillic': '',
'textgreek': '\\usepackage{textalpha}',
@ -115,9 +121,9 @@ DEFAULT_SETTINGS = {
'figure_align': 'htbp',
'tocdepth': '',
'secnumdepth': '',
} # type: Dict[str, Any]
}
ADDITIONAL_SETTINGS = {
ADDITIONAL_SETTINGS: Dict[Any, Dict[str, Any]] = {
'pdflatex': {
'inputenc': '\\usepackage[utf8]{inputenc}',
'utf8extra': ('\\ifdefined\\DeclareUnicodeCharacter\n'
@ -142,6 +148,8 @@ ADDITIONAL_SETTINGS = {
'fontenc': ('\\usepackage{fontspec}\n'
'\\defaultfontfeatures[\\rmfamily,\\sffamily,\\ttfamily]{}'),
'fontpkg': XELATEX_DEFAULT_FONTPKG,
'fvset': '\\fvset{fontsize=\\small}',
'fontsubstitution': '',
'textgreek': '',
'utf8extra': ('\\catcode`^^^^00a0\\active\\protected\\def^^^^00a0'
'{\\leavevmode\\nobreak\\ }'),
@ -153,6 +161,8 @@ ADDITIONAL_SETTINGS = {
'fontenc': ('\\usepackage{fontspec}\n'
'\\defaultfontfeatures[\\rmfamily,\\sffamily,\\ttfamily]{}'),
'fontpkg': LUALATEX_DEFAULT_FONTPKG,
'fvset': '\\fvset{fontsize=\\small}',
'fontsubstitution': '',
'textgreek': '',
'utf8extra': ('\\catcode`^^^^00a0\\active\\protected\\def^^^^00a0'
'{\\leavevmode\\nobreak\\ }'),
@ -161,7 +171,8 @@ ADDITIONAL_SETTINGS = {
'latex_engine': 'platex',
'babel': '',
'classoptions': ',dvipdfmx',
'fontpkg': '\\usepackage{times}',
'fontpkg': PDFLATEX_DEFAULT_FONTPKG,
'fontsubstitution': '',
'textgreek': '',
'fncychap': '',
'geometry': '\\usepackage[dvipdfm]{geometry}',
@ -170,7 +181,8 @@ ADDITIONAL_SETTINGS = {
'latex_engine': 'uplatex',
'babel': '',
'classoptions': ',dvipdfmx',
'fontpkg': '\\usepackage{times}',
'fontpkg': PDFLATEX_DEFAULT_FONTPKG,
'fontsubstitution': '',
'textgreek': '',
'fncychap': '',
'geometry': '\\usepackage[dvipdfm]{geometry}',
@ -190,7 +202,7 @@ ADDITIONAL_SETTINGS = {
('xelatex', 'el'): {
'fontpkg': XELATEX_GREEK_DEFAULT_FONTPKG,
},
} # type: Dict[Any, Dict[str, Any]]
}
SHORTHANDOFF = r'''

View File

@ -106,7 +106,7 @@ class ThemeFactory:
"""A factory class for LaTeX Themes."""
def __init__(self, app: Sphinx) -> None:
self.themes = {} # type: Dict[str, Theme]
self.themes: Dict[str, Theme] = {}
self.theme_paths = [path.join(app.srcdir, p) for p in app.config.latex_theme_path]
self.config = app.config
self.load_builtin_themes(app.config)

View File

@ -33,7 +33,7 @@ class FootnoteDocnameUpdater(SphinxTransform):
def apply(self, **kwargs: Any) -> None:
matcher = NodeMatcher(*self.TARGET_NODES)
for node in self.document.traverse(matcher): # type: nodes.Element
for node in self.document.traverse(matcher): # type: Element
node['docname'] = self.env.docname
@ -65,7 +65,7 @@ class ShowUrlsTransform(SphinxPostTransform):
def run(self, **kwargs: Any) -> None:
try:
# replace id_prefix temporarily
settings = self.document.settings # type: Any
settings: Any = self.document.settings
id_prefix = settings.id_prefix
settings.id_prefix = 'show_urls'
@ -157,9 +157,9 @@ class FootnoteCollector(nodes.NodeVisitor):
"""Collect footnotes and footnote references on the document"""
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]
self.auto_footnotes: List[nodes.footnote] = []
self.used_footnote_numbers: Set[str] = set()
self.footnote_refs: List[nodes.footnote_reference] = []
super().__init__(document)
def unknown_visit(self, node: Node) -> None:
@ -358,11 +358,11 @@ class LaTeXFootnoteTransform(SphinxPostTransform):
class LaTeXFootnoteVisitor(nodes.NodeVisitor):
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]
self.table_footnotes = [] # type: List[nodes.footnote]
self.restricted = None # type: nodes.Element
self.appeared: Set[Tuple[str, str]] = set()
self.footnotes: List[nodes.footnote] = footnotes
self.pendings: List[nodes.footnote] = []
self.table_footnotes: List[nodes.footnote] = []
self.restricted: Element = None
super().__init__(document)
def unknown_visit(self, node: Node) -> None:

View File

@ -9,7 +9,6 @@
"""
import json
import queue
import re
import socket
import time
@ -18,6 +17,7 @@ from datetime import datetime, timezone
from email.utils import parsedate_to_datetime
from html.parser import HTMLParser
from os import path
from queue import PriorityQueue, Queue
from threading import Thread
from typing import (Any, Dict, Generator, List, NamedTuple, Optional, Pattern, Set, Tuple,
Union, cast)
@ -120,16 +120,16 @@ class CheckExternalLinksBuilder(DummyBuilder):
'%(outdir)s/output.txt')
def init(self) -> None:
self.hyperlinks = {} # type: Dict[str, Hyperlink]
self._good = set() # type: Set[str]
self._broken = {} # type: Dict[str, str]
self._redirected = {} # type: Dict[str, Tuple[str, int]]
self.hyperlinks: Dict[str, Hyperlink] = {}
self._good: Set[str] = set()
self._broken: Dict[str, str] = {}
self._redirected: Dict[str, Tuple[str, int]] = {}
# set a timeout for non-responding servers
socket.setdefaulttimeout(5.0)
# create queues and worker threads
self._wqueue = queue.PriorityQueue() # type: queue.PriorityQueue[CheckRequestType]
self._rqueue = queue.Queue() # type: queue.Queue
self._wqueue: PriorityQueue[CheckRequestType] = PriorityQueue()
self._rqueue: Queue = Queue()
@property
def anchors_ignore(self) -> List[Pattern]:
@ -204,7 +204,7 @@ class CheckExternalLinksBuilder(DummyBuilder):
None, None, {})
return worker.limit_rate(response)
def rqueue(self, response: Response) -> queue.Queue:
def rqueue(self, response: Response) -> Queue:
warnings.warn(
"%s.%s is deprecated." % (self.__class__.__name__, "rqueue"),
RemovedInSphinx50Warning,
@ -220,7 +220,7 @@ class CheckExternalLinksBuilder(DummyBuilder):
)
return []
def wqueue(self, response: Response) -> queue.Queue:
def wqueue(self, response: Response) -> Queue:
warnings.warn(
"%s.%s is deprecated." % (self.__class__.__name__, "wqueue"),
RemovedInSphinx50Warning,
@ -313,8 +313,8 @@ class HyperlinkAvailabilityChecker:
self.builder = builder
self.config = config
self.env = env
self.rate_limits = {} # type: Dict[str, RateLimit]
self.workers = [] # type: List[Thread]
self.rate_limits: Dict[str, RateLimit] = {}
self.workers: List[Thread] = []
self.to_ignore = [re.compile(x) for x in self.config.linkcheck_ignore]
@ -322,8 +322,8 @@ class HyperlinkAvailabilityChecker:
self.rqueue = builder._rqueue
self.wqueue = builder._wqueue
else:
self.rqueue = queue.Queue()
self.wqueue = queue.PriorityQueue()
self.rqueue = Queue()
self.wqueue = PriorityQueue()
def invoke_threads(self) -> None:
for i in range(self.config.linkcheck_workers):
@ -364,8 +364,8 @@ class HyperlinkAvailabilityChecker:
class HyperlinkAvailabilityCheckWorker(Thread):
"""A worker class for checking the availability of hyperlinks."""
def __init__(self, env: BuildEnvironment, config: Config, rqueue: queue.Queue,
wqueue: queue.Queue, rate_limits: Dict[str, RateLimit],
def __init__(self, env: BuildEnvironment, config: Config, rqueue: Queue,
wqueue: Queue, rate_limits: Dict[str, RateLimit],
builder: CheckExternalLinksBuilder = None) -> None:
# Warning: builder argument will be removed in the sphinx-5.0.
# Don't use it from extensions.

View File

@ -38,7 +38,7 @@ class ManualPageBuilder(Builder):
epilog = __('The manual pages are in %(outdir)s.')
default_translator_class = ManualPageTranslator
supported_image_types = [] # type: List[str]
supported_image_types: List[str] = []
def init(self) -> None:
if not self.config.man_pages:
@ -56,10 +56,10 @@ class ManualPageBuilder(Builder):
@progress_message(__('writing'))
def write(self, *ignored: Any) -> None:
docwriter = ManualPageWriter(self)
docsettings = OptionParser(
docsettings: Any = OptionParser(
defaults=self.env.settings,
components=(docwriter,),
read_config_files=True).get_default_values() # type: Any
read_config_files=True).get_default_values()
for info in self.config.man_pages:
docname, name, description, authors, section = info
@ -90,7 +90,7 @@ class ManualPageBuilder(Builder):
encoding='utf-8')
tree = self.env.get_doctree(docname)
docnames = set() # type: Set[str]
docnames: Set[str] = set()
largetree = inline_all_toctrees(self, docnames, docname, tree,
darkgreen, [docname])
largetree.settings = docsettings
@ -109,7 +109,7 @@ class ManualPageBuilder(Builder):
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),
return [(config.root_doc, filename, '%s %s' % (config.project, config.release),
[config.author], 1)]
@ -118,7 +118,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
app.add_config_value('man_pages', default_man_pages, None)
app.add_config_value('man_show_urls', False, None)
app.add_config_value('man_make_section_directory', False, None)
app.add_config_value('man_make_section_directory', True, None)
return {
'version': 'builtin',

View File

@ -1,42 +0,0 @@
"""
sphinx.builders.qthelp
~~~~~~~~~~~~~~~~~~~~~~
Build input files for the Qt collection generator.
:copyright: Copyright 2007-2021 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
deprecated_alias('sphinx.builders.qthelp',
{
'render_file': render_file,
'QtHelpBuilder': QtHelpBuilder,
},
RemovedInSphinx40Warning,
{
'render_file': 'sphinxcontrib.qthelp.render_file',
'QtHelpBuilder': 'sphinxcontrib.qthelp.QtHelpBuilder',
})
def setup(app: Sphinx) -> Dict[str, Any]:
warnings.warn('sphinx.builders.qthelp has been moved to sphinxcontrib-qthelp.',
RemovedInSphinx40Warning)
app.setup_extension('sphinxcontrib.qthelp')
return {
'version': sphinx.__display_version__,
'parallel_read_safe': True,
'parallel_write_safe': True,
}

View File

@ -42,7 +42,7 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
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 + \
return self.config.root_doc + self.out_suffix + \
'#document-' + docname
else:
# chances are this is a html_additional_page
@ -54,7 +54,7 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
def fix_refuris(self, tree: Node) -> None:
# fix refuris with double anchor
fname = self.config.master_doc + self.out_suffix
fname = self.config.root_doc + self.out_suffix
for refnode in tree.traverse(nodes.reference):
if 'refuri' not in refnode:
continue
@ -75,7 +75,7 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
return self.render_partial(toctree)['fragment']
def assemble_doctree(self) -> nodes.document:
master = self.config.master_doc
master = self.config.root_doc
tree = self.env.get_doctree(master)
tree = inline_all_toctrees(self, set(), master, tree, darkgreen, [master])
tree['docname'] = master
@ -93,13 +93,13 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
#
# There are related codes in inline_all_toctres() and
# HTMLTranslter#add_secnumber().
new_secnumbers = {} # type: Dict[str, Tuple[int, ...]]
new_secnumbers: Dict[str, Tuple[int, ...]] = {}
for docname, secnums in self.env.toc_secnumbers.items():
for id, secnum in secnums.items():
alias = "%s/%s" % (docname, id)
new_secnumbers[alias] = secnum
return {self.config.master_doc: new_secnumbers}
return {self.config.root_doc: new_secnumbers}
def assemble_toc_fignumbers(self) -> Dict[str, Dict[str, Dict[str, Tuple[int, ...]]]]:
# Assemble toc_fignumbers to resolve figure numbers on SingleHTML.
@ -111,7 +111,7 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
#
# There are related codes in inline_all_toctres() and
# HTMLTranslter#add_fignumber().
new_fignumbers = {} # type: Dict[str, Dict[str, Tuple[int, ...]]]
new_fignumbers: Dict[str, Dict[str, Tuple[int, ...]]] = {}
# {'foo': {'figure': {'id2': (2,), 'id1': (1,)}}, 'bar': {'figure': {'id1': (3,)}}}
for docname, fignumlist in self.env.toc_fignumbers.items():
for figtype, fignums in fignumlist.items():
@ -120,11 +120,11 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
for id, fignum in fignums.items():
new_fignumbers[alias][id] = fignum
return {self.config.master_doc: new_fignumbers}
return {self.config.root_doc: new_fignumbers}
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)
toctree = TocTree(self.env).get_toctree_for(self.config.root_doc, self, False)
# if there is no toctree, toc is None
if toctree:
self.fix_refuris(toctree)
@ -160,8 +160,8 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
self.env.toc_fignumbers = self.assemble_toc_fignumbers()
with progress_message(__('writing')):
self.write_doc_serialized(self.config.master_doc, doctree)
self.write_doc(self.config.master_doc, doctree)
self.write_doc_serialized(self.config.root_doc, doctree)
self.write_doc(self.config.root_doc, doctree)
def finish(self) -> None:
self.write_additional_files()

View File

@ -15,6 +15,7 @@ from typing import Any, Dict, Iterable, List, Tuple, Union
from docutils import nodes
from docutils.frontend import OptionParser
from docutils.io import FileOutput
from docutils.nodes import Node
from sphinx import addnodes, package_dir
from sphinx.application import Sphinx
@ -52,8 +53,8 @@ class TexinfoBuilder(Builder):
default_translator_class = TexinfoTranslator
def init(self) -> None:
self.docnames = [] # type: Iterable[str]
self.document_data = [] # type: List[Tuple[str, str, str, str, str, str, str, bool]]
self.docnames: Iterable[str] = []
self.document_data: List[Tuple[str, str, str, str, str, str, str, bool]] = []
def get_outdated_docs(self) -> Union[str, List[str]]:
return 'all documents' # for now
@ -75,7 +76,7 @@ class TexinfoBuilder(Builder):
'will be written'))
return
# assign subdirs to titles
self.titles = [] # type: List[Tuple[str, str]]
self.titles: List[Tuple[str, str]] = []
for entry in preliminary_document_data:
docname = entry[0]
if docname not in self.env.all_docs:
@ -108,10 +109,10 @@ class TexinfoBuilder(Builder):
with progress_message(__("writing")):
self.post_process_images(doctree)
docwriter = TexinfoWriter(self)
settings = OptionParser(
settings: Any = OptionParser(
defaults=self.env.settings,
components=(docwriter,),
read_config_files=True).get_default_values() # type: Any
read_config_files=True).get_default_values()
settings.author = author
settings.title = title
settings.texinfo_filename = targetname[:-5] + '.info'
@ -154,7 +155,7 @@ class TexinfoBuilder(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: List[Node] = [nodes.emphasis(sectname, sectname)]
for subdir, title in self.titles:
if docname.startswith(subdir):
newnodes.append(nodes.Text(_(' (in '), _(' (in ')))
@ -197,7 +198,7 @@ class TexinfoBuilder(Builder):
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,
return [(config.root_doc, filename, config.project, config.author, filename,
'One line description of project', 'Miscellaneous')]

View File

@ -33,11 +33,11 @@ class TextBuilder(Builder):
allow_parallel = True
default_translator_class = TextTranslator
current_docname = None # type: str
current_docname: str = None
def init(self) -> None:
# section numbers for headings in the currently visited document
self.secnumbers = {} # type: Dict[str, Tuple[int, ...]]
self.secnumbers: Dict[str, Tuple[int, ...]] = {}
def get_outdated_docs(self) -> Iterator[str]:
for docname in self.env.found_docs:

View File

@ -9,7 +9,7 @@
"""
from os import path
from typing import Any, Dict, Iterator, Set, Union
from typing import Any, Dict, Iterator, Set, Type, Union
from docutils import nodes
from docutils.io import StringOutput
@ -23,11 +23,6 @@ from sphinx.util import logging
from sphinx.util.osutil import ensuredir, os_path
from sphinx.writers.xml import PseudoXMLWriter, XMLWriter
if False:
# For type annotation
from typing import Type # for python3.5.1
logger = logging.getLogger(__name__)
@ -42,7 +37,7 @@ class XMLBuilder(Builder):
out_suffix = '.xml'
allow_parallel = True
_writer_class = XMLWriter # type: Union[Type[XMLWriter], Type[PseudoXMLWriter]]
_writer_class: Union[Type[XMLWriter], Type[PseudoXMLWriter]] = XMLWriter
default_translator_class = XMLTranslator
def init(self) -> None:

View File

@ -50,6 +50,7 @@ BUILDERS = [
("", "doctest", "to run all doctests embedded in the documentation "
"(if enabled)"),
("", "coverage", "to run coverage check of the documentation (if enabled)"),
("", "clean", "to remove everything in the build directory"),
]

View File

@ -11,13 +11,11 @@
import argparse
import locale
import os
import re
import sys
import time
import warnings
from collections import OrderedDict
from os import path
from typing import Any, Callable, Dict, List, Pattern, Union
from typing import Any, Callable, Dict, List, Union
# try to import readline, unix specific enhancement
try:
@ -36,15 +34,11 @@ from docutils.utils import column_width
import sphinx.locale
from sphinx import __display_version__, package_dir
from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.locale import __
from sphinx.util.console import (bold, color_terminal, colorize, nocolor, red, # type: ignore
turquoise)
from sphinx.util.console import bold, color_terminal, colorize, nocolor, red # type: ignore
from sphinx.util.osutil import ensuredir
from sphinx.util.template import SphinxRenderer
TERM_ENCODING = getattr(sys.stdin, 'encoding', None) # RemovedInSphinx40Warning
EXTENSIONS = OrderedDict([
('autodoc', __('automatically insert docstrings from modules')),
('doctest', __('automatically test code snippets in doctest blocks')),
@ -135,30 +129,6 @@ def ok(x: str) -> str:
return x
def term_decode(text: Union[bytes, str]) -> str:
warnings.warn('term_decode() is deprecated.',
RemovedInSphinx40Warning, stacklevel=2)
if isinstance(text, str):
return text
# Use the known encoding, if possible
if TERM_ENCODING:
return text.decode(TERM_ENCODING)
# If ascii is safe, use it with no warning
if text.decode('ascii', 'replace').encode('ascii', 'replace') == text:
return text.decode('ascii')
print(turquoise(__('* Note: non-ASCII characters entered '
'and terminal encoding unknown -- assuming '
'UTF-8 or Latin-1.')))
try:
return text.decode()
except UnicodeDecodeError:
return text.decode('latin1')
def do_prompt(text: str, default: str = None, validator: Callable[[str], Any] = nonempty) -> Union[str, bool]: # NOQA
while True:
if default is not None:
@ -187,22 +157,27 @@ def do_prompt(text: str, default: str = None, validator: Callable[[str], Any] =
return x
def convert_python_source(source: str, rex: Pattern = re.compile(r"[uU]('.*?')")) -> str:
# remove Unicode literal prefixes
warnings.warn('convert_python_source() is deprecated.',
RemovedInSphinx40Warning, stacklevel=2)
return rex.sub('\\1', source)
class QuickstartRenderer(SphinxRenderer):
def __init__(self, templatedir: str) -> None:
self.templatedir = templatedir or ''
super().__init__()
def _has_custom_template(self, template_name: str) -> bool:
"""Check if custom template file exists.
Note: Please don't use this function from extensions.
It will be removed in the future without deprecation period.
"""
template = path.join(self.templatedir, path.basename(template_name))
if self.templatedir and path.exists(template):
return True
else:
return False
def render(self, template_name: str, context: Dict) -> str:
user_template = path.join(self.templatedir, path.basename(template_name))
if self.templatedir and path.exists(user_template):
return self.render_from_file(user_template, context)
if self._has_custom_template(template_name):
custom_template = path.join(self.templatedir, path.basename(template_name))
return self.render_from_file(custom_template, context)
else:
return super().render(template_name, context)
@ -355,6 +330,7 @@ def generate(d: Dict, overwrite: bool = True, silent: bool = False, templatedir:
if 'mastertocmaxdepth' not in d:
d['mastertocmaxdepth'] = 2
d['root_doc'] = d['master']
d['now'] = time.asctime()
d['project_underline'] = column_width(d['project']) * '='
d.setdefault('extensions', [])
@ -399,7 +375,13 @@ def generate(d: Dict, overwrite: bool = True, silent: bool = False, templatedir:
write_file(path.join(srcdir, 'conf.py'), template.render_string(conf_text, d))
masterfile = path.join(srcdir, d['master'] + d['suffix'])
write_file(masterfile, template.render('quickstart/master_doc.rst_t', d))
if template._has_custom_template('quickstart/master_doc.rst_t'):
msg = ('A custom template `master_doc.rst_t` found. It has been renamed to '
'`root_doc.rst_t`. Please rename it on your project too.')
print(colorize('red', msg)) # RemovedInSphinx60Warning
write_file(masterfile, template.render('quickstart/master_doc.rst_t', d))
else:
write_file(masterfile, template.render('quickstart/root_doc.rst_t', d))
if d.get('make_mode') is True:
makefile_template = 'quickstart/Makefile.new_t'

View File

@ -11,24 +11,20 @@
import re
import traceback
import types
import warnings
from collections import OrderedDict
from os import getenv, path
from typing import (Any, Callable, Dict, Generator, Iterator, List, NamedTuple, Set, Tuple,
Union)
from typing import (TYPE_CHECKING, Any, Callable, Dict, Generator, Iterator, List, NamedTuple,
Optional, Set, Tuple, Union)
from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.errors import ConfigError, ExtensionError
from sphinx.locale import _, __
from sphinx.util import logging
from sphinx.util.i18n import format_date
from sphinx.util.osutil import cd
from sphinx.util.pycompat import execfile_
from sphinx.util.osutil import cd, fs_encoding
from sphinx.util.tags import Tags
from sphinx.util.typing import NoneType
if False:
# For type annotation
if TYPE_CHECKING:
from sphinx.application import Sphinx
from sphinx.environment import BuildEnvironment
@ -38,9 +34,11 @@ CONFIG_FILENAME = 'conf.py'
UNSERIALIZABLE_TYPES = (type, types.ModuleType, types.FunctionType)
copyright_year_re = re.compile(r'^((\d{4}-)?)(\d{4})(?=[ ,])')
ConfigValue = NamedTuple('ConfigValue', [('name', str),
('value', Any),
('rebuild', Union[bool, str])])
class ConfigValue(NamedTuple):
name: str
value: Any
rebuild: Union[bool, str]
def is_serializable(obj: Any) -> bool:
@ -73,10 +71,6 @@ class ENUM:
return value in self.candidates
# RemovedInSphinx40Warning
string_classes = [str] # type: List
class Config:
"""Configuration file abstraction.
@ -94,7 +88,7 @@ class Config:
# If you add a value here, don't forget to include it in the
# quickstart.py file template as well as in the docs!
config_values = {
config_values: Dict[str, Tuple] = {
# general options
'project': ('Python', 'env', []),
'author': ('unknown', 'env', []),
@ -111,9 +105,9 @@ class Config:
'figure_language_filename': ('{root}.{language}{ext}', 'env', [str]),
'master_doc': ('index', 'env', []),
'root_doc': (lambda config: config.master_doc, 'env', []),
'source_suffix': ({'.rst': 'restructuredtext'}, 'env', Any),
'source_encoding': ('utf-8-sig', 'env', []),
'source_parsers': ({}, 'env', []),
'exclude_patterns': ([], 'env', []),
'default_role': (None, 'env', [str]),
'add_function_parentheses': (True, 'env', []),
@ -152,20 +146,20 @@ class Config:
'smartquotes_excludes': ({'languages': ['ja'],
'builders': ['man', 'text']},
'env', []),
} # type: Dict[str, Tuple]
}
def __init__(self, config: Dict[str, Any] = {}, overrides: Dict[str, Any] = {}) -> None:
self.overrides = dict(overrides)
self.values = Config.config_values.copy()
self._raw_config = config
self.setup = config.get('setup', None) # type: Callable
self.setup: Optional[Callable] = config.get('setup', None)
if 'extensions' in self.overrides:
if isinstance(self.overrides['extensions'], str):
config['extensions'] = self.overrides.pop('extensions').split(',')
else:
config['extensions'] = self.overrides.pop('extensions')
self.extensions = config.get('extensions', []) # type: List[str]
self.extensions: List[str] = config.get('extensions', [])
@classmethod
def read(cls, confdir: str, overrides: Dict = None, tags: Tags = None) -> "Config":
@ -317,14 +311,16 @@ class Config:
def eval_config_file(filename: str, tags: Tags) -> Dict[str, Any]:
"""Evaluate a config file."""
namespace = {} # type: Dict[str, Any]
namespace: Dict[str, Any] = {}
namespace['__file__'] = filename
namespace['tags'] = tags
with cd(path.dirname(filename)):
# during executing config file, current dir is changed to ``confdir``.
try:
execfile_(filename, namespace)
with open(filename, 'rb') as f:
code = compile(f.read(), filename.encode(fs_encoding), 'exec')
exec(code, namespace)
except SyntaxError as err:
msg = __("There is a syntax error in your configuration file: %s\n")
raise ConfigError(msg % err) from err
@ -459,22 +455,6 @@ def check_confval_types(app: "Sphinx", config: Config) -> None:
default=type(default)))
def check_unicode(config: Config) -> None:
"""check all string values for non-ASCII characters in bytestrings,
since that can result in UnicodeErrors all over the place
"""
warnings.warn('sphinx.config.check_unicode() is deprecated.',
RemovedInSphinx40Warning, stacklevel=2)
nonascii_re = re.compile(br'[\x80-\xff]')
for name, value in config._raw_config.items():
if isinstance(value, bytes) and nonascii_re.search(value):
logger.warning(__('the config value %r is set to a string with non-ASCII '
'characters; this can lead to Unicode errors occurring. '
'Please use Unicode strings, e.g. %r.'), name, 'Content')
def check_primary_domain(app: "Sphinx", config: Config) -> None:
primary_domain = config.primary_domain
if primary_domain and not app.registry.has_domain(primary_domain):
@ -482,17 +462,17 @@ def check_primary_domain(app: "Sphinx", config: Config) -> None:
config.primary_domain = None # type: ignore
def check_master_doc(app: "Sphinx", env: "BuildEnvironment", added: Set[str],
changed: Set[str], removed: Set[str]) -> Set[str]:
"""Adjust master_doc to 'contents' to support an old project which does not have
no master_doc setting.
def check_root_doc(app: "Sphinx", env: "BuildEnvironment", added: Set[str],
changed: Set[str], removed: Set[str]) -> Set[str]:
"""Adjust root_doc to 'contents' to support an old project which does not have
no root_doc setting.
"""
if (app.config.master_doc == 'index' and
if (app.config.root_doc == 'index' and
'index' not in app.project.docnames and
'contents' in app.project.docnames):
logger.warning(__('Since v2.0, Sphinx uses "index" as master_doc by default. '
'Please add "master_doc = \'contents\'" to your conf.py.'))
app.config.master_doc = "contents" # type: ignore
logger.warning(__('Since v2.0, Sphinx uses "index" as root_doc by default. '
'Please add "root_doc = \'contents\'" to your conf.py.'))
app.config.root_doc = "contents" # type: ignore
return changed
@ -504,7 +484,7 @@ def setup(app: "Sphinx") -> Dict[str, Any]:
app.connect('config-inited', correct_copyright_year, priority=800)
app.connect('config-inited', check_confval_types, priority=800)
app.connect('config-inited', check_primary_domain, priority=800)
app.connect('env-get-outdated', check_master_doc)
app.connect('env-get-outdated', check_root_doc)
return {
'version': 'builtin',

View File

@ -11,18 +11,14 @@
import sys
import warnings
from importlib import import_module
from typing import Any, Dict
if False:
# For type annotation
from typing import Type # for python3.5.1
from typing import Any, Dict, Type
class RemovedInSphinx40Warning(DeprecationWarning):
pass
class RemovedInSphinx50Warning(PendingDeprecationWarning):
class RemovedInSphinx50Warning(DeprecationWarning):
pass
@ -30,11 +26,11 @@ class RemovedInSphinx60Warning(PendingDeprecationWarning):
pass
RemovedInNextVersionWarning = RemovedInSphinx40Warning
RemovedInNextVersionWarning = RemovedInSphinx50Warning
def deprecated_alias(modname: str, objects: Dict[str, object],
warning: "Type[Warning]", names: Dict[str, str] = None) -> None:
warning: Type[Warning], names: Dict[str, str] = {}) -> None:
module = import_module(modname)
sys.modules[modname] = _ModuleWrapper( # type: ignore
module, modname, objects, warning, names)
@ -43,7 +39,7 @@ def deprecated_alias(modname: str, objects: Dict[str, object],
class _ModuleWrapper:
def __init__(self, module: Any, modname: str,
objects: Dict[str, object],
warning: "Type[Warning]",
warning: Type[Warning],
names: Dict[str, str]) -> None:
self._module = module
self._modname = modname
@ -71,7 +67,7 @@ class _ModuleWrapper:
class DeprecatedDict(dict):
"""A deprecated dict which warns on each access."""
def __init__(self, data: Dict, message: str, warning: "Type[Warning]") -> None:
def __init__(self, data: Dict, message: str, warning: Type[Warning]) -> None:
self.message = message
self.warning = warning
super().__init__(data)

View File

@ -9,7 +9,7 @@
"""
import re
from typing import Any, Dict, Generic, List, Tuple, TypeVar, cast
from typing import TYPE_CHECKING, Any, Dict, Generic, List, Tuple, TypeVar, cast
from docutils import nodes
from docutils.nodes import Node
@ -17,15 +17,13 @@ from docutils.parsers.rst import directives, roles
from sphinx import addnodes
from sphinx.addnodes import desc_signature
from sphinx.deprecation import (RemovedInSphinx40Warning, RemovedInSphinx50Warning,
deprecated_alias)
from sphinx.deprecation import RemovedInSphinx50Warning, deprecated_alias
from sphinx.util import docutils
from sphinx.util.docfields import DocFieldTransformer, Field, TypedField
from sphinx.util.docutils import SphinxDirective
from sphinx.util.typing import DirectiveOption
from sphinx.util.typing import OptionSpec
if False:
# For type annotation
if TYPE_CHECKING:
from sphinx.application import Sphinx
@ -60,18 +58,18 @@ class ObjectDescription(SphinxDirective, Generic[T]):
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = True
option_spec = {
option_spec: OptionSpec = {
'noindex': directives.flag,
} # type: Dict[str, DirectiveOption]
}
# types of doc fields that this directive handles, see sphinx.util.docfields
doc_field_types = [] # type: List[Field]
domain = None # type: str
objtype = None # type: str
indexnode = None # type: addnodes.index
doc_field_types: List[Field] = []
domain: str = None
objtype: str = None
indexnode: addnodes.index = None
# Warning: this might be removed in future version. Don't touch this from extensions.
_doc_field_type_map = {} # type: Dict[str, Tuple[Field, bool]]
_doc_field_type_map: Dict[str, Tuple[Field, bool]] = {}
def get_field_type_map(self) -> Dict[str, Tuple[Field, bool]]:
if self._doc_field_type_map == {}:
@ -175,7 +173,7 @@ class ObjectDescription(SphinxDirective, Generic[T]):
if self.domain:
node['classes'].append(self.domain)
self.names = [] # type: List[T]
self.names: List[T] = []
signatures = self.get_signatures()
for i, sig in enumerate(signatures):
# add a signature node for each signature in the current unit
@ -253,7 +251,7 @@ class DefaultDomain(SphinxDirective):
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = False
option_spec = {} # type: Dict
option_spec: OptionSpec = {}
def run(self) -> List[Node]:
domain_name = self.arguments[0].lower()
@ -266,52 +264,6 @@ class DefaultDomain(SphinxDirective):
self.env.temp_data['default_domain'] = self.env.domains.get(domain_name)
return []
from sphinx.directives.code import CodeBlock, Highlight, LiteralInclude # noqa
from sphinx.directives.other import (Acks, Author, Centered, Class, HList, Include, # noqa
Only, SeeAlso, TabularColumns, TocTree, VersionChange)
from sphinx.directives.patches import Figure, Meta # noqa
from sphinx.domains.index import IndexDirective # noqa
deprecated_alias('sphinx.directives',
{
'Highlight': Highlight,
'CodeBlock': CodeBlock,
'LiteralInclude': LiteralInclude,
'TocTree': TocTree,
'Author': Author,
'Index': IndexDirective,
'VersionChange': VersionChange,
'SeeAlso': SeeAlso,
'TabularColumns': TabularColumns,
'Centered': Centered,
'Acks': Acks,
'HList': HList,
'Only': Only,
'Include': Include,
'Class': Class,
'Figure': Figure,
'Meta': Meta,
},
RemovedInSphinx40Warning,
{
'Highlight': 'sphinx.directives.code.Highlight',
'CodeBlock': 'sphinx.directives.code.CodeBlock',
'LiteralInclude': 'sphinx.directives.code.LiteralInclude',
'TocTree': 'sphinx.directives.other.TocTree',
'Author': 'sphinx.directives.other.Author',
'Index': 'sphinx.directives.other.IndexDirective',
'VersionChange': 'sphinx.directives.other.VersionChange',
'SeeAlso': 'sphinx.directives.other.SeeAlso',
'TabularColumns': 'sphinx.directives.other.TabularColumns',
'Centered': 'sphinx.directives.other.Centered',
'Acks': 'sphinx.directives.other.Acks',
'HList': 'sphinx.directives.other.HList',
'Only': 'sphinx.directives.other.Only',
'Include': 'sphinx.directives.other.Include',
'Class': 'sphinx.directives.other.Class',
'Figure': 'sphinx.directives.patches.Figure',
'Meta': 'sphinx.directives.patches.Meta',
})
deprecated_alias('sphinx.directives',
{

View File

@ -8,9 +8,8 @@
import sys
import textwrap
import warnings
from difflib import unified_diff
from typing import Any, Dict, List, Tuple
from typing import TYPE_CHECKING, Any, Dict, List, Tuple
from docutils import nodes
from docutils.nodes import Element, Node
@ -19,14 +18,13 @@ from docutils.statemachine import StringList
from sphinx import addnodes
from sphinx.config import Config
from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.directives import optional_int
from sphinx.locale import __
from sphinx.util import logging, parselinenos
from sphinx.util.docutils import SphinxDirective
from sphinx.util.typing import OptionSpec
if False:
# For type annotation
if TYPE_CHECKING:
from sphinx.application import Sphinx
logger = logging.getLogger(__name__)
@ -42,7 +40,7 @@ class Highlight(SphinxDirective):
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = False
option_spec = {
option_spec: OptionSpec = {
'force': directives.flag,
'linenothreshold': directives.positive_int,
}
@ -58,16 +56,6 @@ class Highlight(SphinxDirective):
linenothreshold=linenothreshold)]
class HighlightLang(Highlight):
"""highlightlang directive (deprecated)"""
def run(self) -> List[Node]:
warnings.warn('highlightlang directive is deprecated. '
'Please use highlight directive instead.',
RemovedInSphinx40Warning, stacklevel=2)
return super().run()
def dedent_lines(lines: List[str], dedent: int, location: Tuple[str, int] = None) -> List[str]:
if not dedent:
return textwrap.dedent(''.join(lines)).splitlines(True)
@ -116,7 +104,7 @@ class CodeBlock(SphinxDirective):
required_arguments = 0
optional_arguments = 1
final_argument_whitespace = False
option_spec = {
option_spec: OptionSpec = {
'force': directives.flag,
'linenos': directives.flag,
'dedent': optional_int,
@ -154,7 +142,7 @@ class CodeBlock(SphinxDirective):
lines = dedent_lines(lines, self.options['dedent'], location=location)
code = '\n'.join(lines)
literal = nodes.literal_block(code, code) # type: Element
literal: Element = nodes.literal_block(code, code)
if 'linenos' in self.options or 'lineno-start' in self.options:
literal['linenos'] = True
literal['classes'] += self.options.get('class', [])
@ -392,7 +380,7 @@ class LiteralInclude(SphinxDirective):
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = True
option_spec = {
option_spec: OptionSpec = {
'dedent': optional_int,
'linenos': directives.flag,
'lineno-start': int,
@ -434,7 +422,7 @@ class LiteralInclude(SphinxDirective):
reader = LiteralIncludeReader(filename, self.options, self.config)
text, lines = reader.read(location=location)
retnode = nodes.literal_block(text, text, source=filename) # type: Element
retnode: Element = nodes.literal_block(text, text, source=filename)
retnode['force'] = 'force' in self.options
self.set_source_info(retnode)
if self.options.get('diff'): # if diff is set, set udiff
@ -470,7 +458,6 @@ class LiteralInclude(SphinxDirective):
def setup(app: "Sphinx") -> Dict[str, Any]:
directives.register_directive('highlight', Highlight)
directives.register_directive('highlightlang', HighlightLang)
directives.register_directive('code-block', CodeBlock)
directives.register_directive('sourcecode', CodeBlock)
directives.register_directive('literalinclude', LiteralInclude)

View File

@ -7,7 +7,7 @@
"""
import re
from typing import Any, Dict, List, cast
from typing import TYPE_CHECKING, Any, Dict, List, cast
from docutils import nodes
from docutils.nodes import Element, Node
@ -17,16 +17,15 @@ from docutils.parsers.rst.directives.misc import Class
from docutils.parsers.rst.directives.misc import Include as BaseInclude
from sphinx import addnodes
from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
from sphinx.domains.changeset import VersionChange # NOQA # for compatibility
from sphinx.locale import _
from sphinx.util import docname_join, url_re
from sphinx.util.docutils import SphinxDirective
from sphinx.util.matching import Matcher, patfilter
from sphinx.util.nodes import explicit_title_re
from sphinx.util.typing import OptionSpec
if False:
# For type annotation
if TYPE_CHECKING:
from sphinx.application import Sphinx
@ -91,7 +90,7 @@ class TocTree(SphinxDirective):
all_docnames = self.env.found_docs.copy()
all_docnames.remove(self.env.docname) # remove current document
ret = [] # type: List[Node]
ret: List[Node] = []
excluded = Matcher(self.config.exclude_patterns)
for entry in self.content:
if not entry:
@ -137,7 +136,13 @@ class TocTree(SphinxDirective):
line=self.lineno))
self.env.note_reread()
else:
all_docnames.discard(docname)
if docname in all_docnames:
all_docnames.remove(docname)
else:
message = 'duplicated entry found in toctree: %s'
ret.append(self.state.document.reporter.warning(message % docname,
line=self.lineno))
toctree['entries'].append((title, docname))
toctree['includefiles'].append(docname)
@ -158,12 +163,12 @@ class Author(SphinxDirective):
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = True
option_spec = {} # type: Dict
option_spec: OptionSpec = {}
def run(self) -> List[Node]:
if not self.config.show_authors:
return []
para = nodes.paragraph(translatable=False) # type: Element
para: Element = nodes.paragraph(translatable=False)
emph = nodes.emphasis()
para += emph
if self.name == 'sectionauthor':
@ -178,7 +183,7 @@ class Author(SphinxDirective):
inodes, messages = self.state.inline_text(self.arguments[0], self.lineno)
emph.extend(inodes)
ret = [para] # type: List[Node]
ret: List[Node] = [para]
ret += messages
return ret
@ -198,7 +203,7 @@ class TabularColumns(SphinxDirective):
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = True
option_spec = {} # type: Dict
option_spec: OptionSpec = {}
def run(self) -> List[Node]:
node = addnodes.tabular_col_spec()
@ -215,16 +220,16 @@ class Centered(SphinxDirective):
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = True
option_spec = {} # type: Dict
option_spec: OptionSpec = {}
def run(self) -> List[Node]:
if not self.arguments:
return []
subnode = addnodes.centered() # type: Element
subnode: Element = addnodes.centered()
inodes, messages = self.state.inline_text(self.arguments[0], self.lineno)
subnode.extend(inodes)
ret = [subnode] # type: List[Node]
ret: List[Node] = [subnode]
ret += messages
return ret
@ -237,7 +242,7 @@ class Acks(SphinxDirective):
required_arguments = 0
optional_arguments = 0
final_argument_whitespace = False
option_spec = {} # type: Dict
option_spec: OptionSpec = {}
def run(self) -> List[Node]:
node = addnodes.acks()
@ -258,7 +263,7 @@ class HList(SphinxDirective):
required_arguments = 0
optional_arguments = 0
final_argument_whitespace = False
option_spec = {
option_spec: OptionSpec = {
'columns': int,
}
@ -294,7 +299,7 @@ class Only(SphinxDirective):
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = True
option_spec = {} # type: Dict
option_spec: OptionSpec = {}
def run(self) -> List[Node]:
node = addnodes.only()
@ -304,7 +309,7 @@ class Only(SphinxDirective):
# Same as util.nested_parse_with_titles but try to handle nested
# sections which should be raised higher up the doctree.
memo = self.state.memo # type: Any
memo: Any = self.state.memo
surrounding_title_styles = memo.title_styles
surrounding_section_level = memo.section_level
memo.title_styles = []
@ -361,19 +366,6 @@ class Include(BaseInclude, SphinxDirective):
return super().run()
# Import old modules here for compatibility
from sphinx.domains.index import IndexDirective # NOQA
deprecated_alias('sphinx.directives.other',
{
'Index': IndexDirective,
},
RemovedInSphinx40Warning,
{
'Index': 'sphinx.domains.index.IndexDirective',
})
def setup(app: "Sphinx") -> Dict[str, Any]:
directives.register_directive('toctree', TocTree)
directives.register_directive('sectionauthor', Author)

View File

@ -6,7 +6,10 @@
:license: BSD, see LICENSE for details.
"""
from typing import Any, Dict, List, Tuple, cast
import os
import warnings
from os import path
from typing import TYPE_CHECKING, Any, Dict, List, Tuple, cast
from docutils import nodes
from docutils.nodes import Node, make_id, system_message
@ -14,16 +17,23 @@ from docutils.parsers.rst import directives
from docutils.parsers.rst.directives import html, images, tables
from sphinx import addnodes
from sphinx.deprecation import RemovedInSphinx60Warning
from sphinx.directives import optional_int
from sphinx.domains.math import MathDomain
from sphinx.locale import __
from sphinx.util import logging
from sphinx.util.docutils import SphinxDirective
from sphinx.util.nodes import set_source_info
from sphinx.util.osutil import SEP, os_path, relpath
from sphinx.util.typing import OptionSpec
if False:
# For type annotation
if TYPE_CHECKING:
from sphinx.application import Sphinx
logger = logging.getLogger(__name__)
class Figure(images.Figure):
"""The figure directive which applies `:name:` option to the figure node
instead of the image node.
@ -72,6 +82,11 @@ class RSTTable(tables.RSTTable):
Only for docutils-0.13 or older version."""
def run(self) -> List[Node]:
warnings.warn('RSTTable is deprecated.',
RemovedInSphinx60Warning)
return super().run()
def make_title(self) -> Tuple[nodes.title, List[system_message]]:
title, message = super().make_title()
if title:
@ -81,16 +96,25 @@ class RSTTable(tables.RSTTable):
class CSVTable(tables.CSVTable):
"""The csv-table directive which sets source and line information to its caption.
"""The csv-table directive which searches a CSV file from Sphinx project's source
directory when an absolute path is given via :file: option.
"""
Only for docutils-0.13 or older version."""
def run(self) -> List[Node]:
if 'file' in self.options and self.options['file'].startswith((SEP, os.sep)):
env = self.state.document.settings.env
filename = self.options['file']
if path.exists(filename):
logger.warning(__('":file:" option for csv-table directive now recognizes '
'an absolute path as a relative path from source directory. '
'Please update your document.'),
location=(env.docname, self.lineno))
else:
abspath = path.join(env.srcdir, os_path(self.options['file'][1:]))
docdir = path.dirname(env.doc2path(env.docname))
self.options['file'] = relpath(abspath, docdir)
def make_title(self) -> Tuple[nodes.title, List[system_message]]:
title, message = super().make_title()
if title:
set_source_info(self, title)
return title, message
return super().run()
class ListTable(tables.ListTable):
@ -98,6 +122,11 @@ class ListTable(tables.ListTable):
Only for docutils-0.13 or older version."""
def run(self) -> List[Node]:
warnings.warn('ListTable is deprecated.',
RemovedInSphinx60Warning)
return super().run()
def make_title(self) -> Tuple[nodes.title, List[system_message]]:
title, message = super().make_title()
if title:
@ -112,7 +141,7 @@ class Code(SphinxDirective):
This is compatible with docutils' :rst:dir:`code` directive.
"""
optional_arguments = 1
option_spec = {
option_spec: OptionSpec = {
'class': directives.class_option,
'force': directives.flag,
'name': directives.unchanged,
@ -156,7 +185,7 @@ class MathDirective(SphinxDirective):
required_arguments = 0
optional_arguments = 1
final_argument_whitespace = True
option_spec = {
option_spec: OptionSpec = {
'label': directives.unchanged,
'name': directives.unchanged,
'class': directives.class_option,
@ -177,7 +206,7 @@ class MathDirective(SphinxDirective):
self.add_name(node)
self.set_source_info(node)
ret = [node] # type: List[Node]
ret: List[Node] = [node]
self.add_target(ret)
return ret
@ -208,9 +237,7 @@ class MathDirective(SphinxDirective):
def setup(app: "Sphinx") -> Dict[str, Any]:
directives.register_directive('figure', Figure)
directives.register_directive('meta', Meta)
directives.register_directive('table', RSTTable)
directives.register_directive('csv-table', CSVTable)
directives.register_directive('list-table', ListTable)
directives.register_directive('code', Code)
directives.register_directive('math', MathDirective)

View File

@ -10,7 +10,9 @@
"""
import copy
from typing import Any, Callable, Dict, Iterable, List, NamedTuple, Tuple, Union, cast
from abc import ABC, abstractmethod
from typing import (TYPE_CHECKING, Any, Callable, Dict, Iterable, List, NamedTuple, Tuple,
Type, Union, cast)
from docutils import nodes
from docutils.nodes import Element, Node, system_message
@ -22,10 +24,7 @@ from sphinx.locale import _
from sphinx.roles import XRefRole
from sphinx.util.typing import RoleFunction
if False:
# For type annotation
from typing import Type # for python3.5.1
if TYPE_CHECKING:
from sphinx.builders import Builder
from sphinx.environment import BuildEnvironment
@ -51,21 +50,22 @@ class ObjType:
def __init__(self, lname: str, *roles: Any, **attrs: Any) -> None:
self.lname = lname
self.roles = roles # type: Tuple
self.attrs = self.known_attrs.copy() # type: Dict
self.roles: Tuple = roles
self.attrs: Dict = self.known_attrs.copy()
self.attrs.update(attrs)
IndexEntry = NamedTuple('IndexEntry', [('name', str),
('subtype', int),
('docname', str),
('anchor', str),
('extra', str),
('qualifier', str),
('descr', str)])
class IndexEntry(NamedTuple):
name: str
subtype: int
docname: str
anchor: str
extra: str
qualifier: str
descr: str
class Index:
class Index(ABC):
"""
An Index is the description for a domain-specific index. To add an index to
a domain, subclass Index, overriding the three name attributes:
@ -88,9 +88,9 @@ class Index:
:rst:role:`ref` role.
"""
name = None # type: str
localname = None # type: str
shortname = None # type: str
name: str = None
localname: str = None
shortname: str = None
def __init__(self, domain: "Domain") -> None:
if self.name is None or self.localname is None:
@ -98,6 +98,7 @@ class Index:
% self.__class__.__name__)
self.domain = domain
@abstractmethod
def generate(self, docnames: Iterable[str] = None
) -> Tuple[List[Tuple[str, List[IndexEntry]]], bool]:
"""Get entries for the index.
@ -180,31 +181,31 @@ class Domain:
#: domain label: longer, more descriptive (used in messages)
label = ''
#: type (usually directive) name -> ObjType instance
object_types = {} # type: Dict[str, ObjType]
object_types: Dict[str, ObjType] = {}
#: directive name -> directive class
directives = {} # type: Dict[str, Any]
directives: Dict[str, Any] = {}
#: role name -> role callable
roles = {} # type: Dict[str, Union[RoleFunction, XRefRole]]
roles: Dict[str, Union[RoleFunction, XRefRole]] = {}
#: a list of Index subclasses
indices = [] # type: List[Type[Index]]
indices: List[Type[Index]] = []
#: role name -> a warning message if reference is missing
dangling_warnings = {} # type: Dict[str, str]
dangling_warnings: Dict[str, str] = {}
#: node_class -> (enum_node_type, title_getter)
enumerable_nodes = {} # type: Dict[Type[Node], Tuple[str, Callable]]
enumerable_nodes: Dict[Type[Node], Tuple[str, Callable]] = {}
#: data value for a fresh environment
initial_data = {} # type: Dict
initial_data: Dict = {}
#: data value
data = None # type: Dict
data: Dict = None
#: data version, bump this when the format of `self.data` changes
data_version = 0
def __init__(self, env: "BuildEnvironment") -> None:
self.env = env # type: BuildEnvironment
self._role_cache = {} # type: Dict[str, Callable]
self._directive_cache = {} # type: Dict[str, Callable]
self._role2type = {} # type: Dict[str, List[str]]
self._type2role = {} # type: Dict[str, str]
self.env: BuildEnvironment = env
self._role_cache: Dict[str, Callable] = {}
self._directive_cache: Dict[str, Callable] = {}
self._role2type: Dict[str, List[str]] = {}
self._type2role: Dict[str, str] = {}
# convert class variables to instance one (to enhance through API)
self.object_types = dict(self.object_types)
@ -225,8 +226,8 @@ class Domain:
for rolename in obj.roles:
self._role2type.setdefault(rolename, []).append(name)
self._type2role[name] = obj.roles[0] if obj.roles else ''
self.objtypes_for_role = self._role2type.get # type: Callable[[str], List[str]]
self.role_for_objtype = self._type2role.get # type: Callable[[str], str]
self.objtypes_for_role: Callable[[str], List[str]] = self._role2type.get
self.role_for_objtype: Callable[[str], str] = self._type2role.get
def setup(self) -> None:
"""Set up domain object."""

View File

@ -39,6 +39,7 @@ from sphinx.util.cfamily import (ASTAttribute, ASTBaseBase, ASTBaseParenExprList
from sphinx.util.docfields import Field, TypedField
from sphinx.util.docutils import SphinxDirective
from sphinx.util.nodes import make_refnode
from sphinx.util.typing import OptionSpec
logger = logging.getLogger(__name__)
T = TypeVar('T')
@ -182,10 +183,19 @@ class ASTNestedName(ASTBase):
verify_description_mode(mode)
# just print the name part, with template args, not template params
if mode == 'noneIsName':
signode += nodes.Text(str(self))
if self.rooted:
signode += nodes.Text('.')
for i in range(len(self.names)):
if i != 0:
signode += nodes.Text('.')
n = self.names[i]
n.describe_signature(signode, mode, env, '', symbol)
elif mode == 'param':
name = str(self)
signode += nodes.emphasis(name, name)
assert not self.rooted, str(self)
assert len(self.names) == 1
node = nodes.emphasis()
self.names[0].describe_signature(node, 'noneIsName', env, '', symbol)
signode += node
elif mode == 'markType' or mode == 'lastIsName' or mode == 'markName':
# Each element should be a pending xref targeting the complete
# prefix.
@ -387,19 +397,6 @@ class ASTPostfixDec(ASTPostfixOp):
signode.append(nodes.Text('--'))
class ASTPostfixMember(ASTPostfixOp):
def __init__(self, name):
self.name = name
def _stringify(self, transform: StringifyTransform) -> str:
return '.' + transform(self.name)
def describe_signature(self, signode: TextElement, mode: str,
env: "BuildEnvironment", symbol: "Symbol") -> None:
signode.append(nodes.Text('.'))
self.name.describe_signature(signode, 'noneIsName', env, symbol)
class ASTPostfixMemberOfPointer(ASTPostfixOp):
def __init__(self, name):
self.name = name
@ -682,15 +679,24 @@ class ASTParameters(ASTBase):
def describe_signature(self, signode: TextElement, mode: str,
env: "BuildEnvironment", symbol: "Symbol") -> None:
verify_description_mode(mode)
paramlist = addnodes.desc_parameterlist()
for arg in self.args:
param = addnodes.desc_parameter('', '', noemph=True)
if mode == 'lastIsName': # i.e., outer-function params
# only use the desc_parameterlist for the outer list, not for inner lists
if mode == 'lastIsName':
paramlist = addnodes.desc_parameterlist()
for arg in self.args:
param = addnodes.desc_parameter('', '', noemph=True)
arg.describe_signature(param, 'param', env, symbol=symbol)
else:
arg.describe_signature(param, 'markType', env, symbol=symbol)
paramlist += param
signode += paramlist
paramlist += param
signode += paramlist
else:
signode += nodes.Text('(', '(')
first = True
for arg in self.args:
if not first:
signode += nodes.Text(', ', ', ')
first = False
arg.describe_signature(signode, 'markType', env, symbol=symbol)
signode += nodes.Text(')', ')')
for attr in self.attrs:
signode += nodes.Text(' ')
attr.describe_signature(signode)
@ -719,7 +725,7 @@ class ASTDeclSpecsSimple(ASTBaseBase):
self.attrs + other.attrs)
def _stringify(self, transform: StringifyTransform) -> str:
res = [] # type: List[str]
res: List[str] = []
res.extend(transform(attr) for attr in self.attrs)
if self.storage:
res.append(self.storage)
@ -773,7 +779,7 @@ class ASTDeclSpecs(ASTBase):
self.trailingTypeSpec = trailing
def _stringify(self, transform: StringifyTransform) -> str:
res = [] # type: List[str]
res: List[str] = []
l = transform(self.leftSpecs)
if len(l) > 0:
res.append(l)
@ -791,7 +797,7 @@ class ASTDeclSpecs(ASTBase):
def describe_signature(self, signode: TextElement, mode: str,
env: "BuildEnvironment", symbol: "Symbol") -> None:
verify_description_mode(mode)
modifiers = [] # type: List[Node]
modifiers: List[Node] = []
def _add(modifiers: List[Node], text: str) -> None:
if len(modifiers) > 0:
@ -873,7 +879,7 @@ class ASTArray(ASTBase):
elif self.size:
if addSpace:
signode += nodes.Text(' ')
self.size.describe_signature(signode, mode, env, symbol)
self.size.describe_signature(signode, 'markType', env, symbol)
signode.append(nodes.Text("]"))
@ -1363,9 +1369,9 @@ class ASTDeclaration(ASTBaseBase):
self.declaration = declaration
self.semicolon = semicolon
self.symbol = None # type: Symbol
self.symbol: Symbol = None
# set by CObject._add_enumerator_to_parent
self.enumeratorScopedSymbol = None # type: Symbol
self.enumeratorScopedSymbol: Symbol = None
def clone(self) -> "ASTDeclaration":
return ASTDeclaration(self.objectType, self.directiveType,
@ -1497,8 +1503,8 @@ class Symbol:
declaration: ASTDeclaration, docname: str, line: int) -> None:
self.parent = parent
# declarations in a single directive are linked together
self.siblingAbove = None # type: Symbol
self.siblingBelow = None # type: Symbol
self.siblingAbove: Symbol = None
self.siblingBelow: Symbol = None
self.ident = ident
self.declaration = declaration
self.docname = docname
@ -1507,8 +1513,8 @@ class Symbol:
self._assert_invariants()
# Remember to modify Symbol.remove if modifications to the parent change.
self._children = [] # type: List[Symbol]
self._anonChildren = [] # type: List[Symbol]
self._children: List[Symbol] = []
self._anonChildren: List[Symbol] = []
# note: _children includes _anonChildren
if self.parent:
self.parent._children.append(self)
@ -2189,7 +2195,7 @@ class DefinitionParser(BaseParser):
# "(" expression ")"
# id-expression -> we parse this with _parse_nested_name
self.skip_ws()
res = self._parse_literal() # type: ASTExpression
res: ASTExpression = self._parse_literal()
if res is not None:
return res
res = self._parse_paren_expression()
@ -2256,7 +2262,7 @@ class DefinitionParser(BaseParser):
# | postfix "[" expression "]"
# | postfix "[" braced-init-list [opt] "]"
# | postfix "(" expression-list [opt] ")"
# | postfix "." id-expression
# | postfix "." id-expression // taken care of in primary by nested name
# | postfix "->" id-expression
# | postfix "++"
# | postfix "--"
@ -2264,7 +2270,7 @@ class DefinitionParser(BaseParser):
prefix = self._parse_primary_expression()
# and now parse postfixes
postFixes = [] # type: List[ASTPostfixOp]
postFixes: List[ASTPostfixOp] = []
while True:
self.skip_ws()
if self.skip_string_and_ws('['):
@ -2274,17 +2280,6 @@ class DefinitionParser(BaseParser):
self.fail("Expected ']' in end of postfix expression.")
postFixes.append(ASTPostfixArray(expr))
continue
if self.skip_string('.'):
if self.skip_string('*'):
# don't steal the dot
self.pos -= 2
elif self.skip_string('..'):
# don't steal the dot
self.pos -= 3
else:
name = self._parse_nested_name()
postFixes.append(ASTPostfixMember(name))
continue
if self.skip_string('->'):
if self.skip_string('*'):
# don't steal the arrow
@ -2493,7 +2488,7 @@ class DefinitionParser(BaseParser):
else:
# TODO: add handling of more bracket-like things, and quote handling
brackets = {'(': ')', '{': '}', '[': ']'}
symbols = [] # type: List[str]
symbols: List[str] = []
while not self.eof:
if (len(symbols) == 0 and self.current_char in end):
break
@ -2509,7 +2504,7 @@ class DefinitionParser(BaseParser):
return ASTFallbackExpr(value.strip())
def _parse_nested_name(self) -> ASTNestedName:
names = [] # type: List[Any]
names: List[Any] = []
self.skip_ws()
rooted = False
@ -2693,16 +2688,13 @@ class DefinitionParser(BaseParser):
def _parse_declarator_name_suffix(
self, named: Union[bool, str], paramMode: str, typed: bool
) -> ASTDeclarator:
assert named in (True, False, 'single')
# now we should parse the name, and then suffixes
if named == 'maybe':
pos = self.pos
try:
declId = self._parse_nested_name()
except DefinitionError:
self.pos = pos
declId = None
elif named == 'single':
if named == 'single':
if self.match(identifier_re):
if self.matched_text in _keywords:
self.fail("Expected identifier, "
"got keyword: %s" % self.matched_text)
identifier = ASTIdentifier(self.matched_text)
declId = ASTNestedName([identifier], rooted=False)
else:
@ -2865,7 +2857,7 @@ class DefinitionParser(BaseParser):
return ASTInitializer(bracedInit)
if outer == 'member':
fallbackEnd = [] # type: List[str]
fallbackEnd: List[str] = []
elif outer is None: # function parameter
fallbackEnd = [',', ')']
else:
@ -2880,8 +2872,8 @@ class DefinitionParser(BaseParser):
def _parse_type(self, named: Union[bool, str], outer: str = None) -> ASTType:
"""
named=False|'maybe'|True: 'maybe' is e.g., for function objects which
doesn't need to name the arguments
named=False|'single'|True: 'single' is e.g., for function objects which
doesn't need to name the arguments, but otherwise is a single name
"""
if outer: # always named
if outer not in ('type', 'member', 'function'):
@ -3012,7 +3004,7 @@ class DefinitionParser(BaseParser):
def parse_pre_v3_type_definition(self) -> ASTDeclaration:
self.skip_ws()
declaration = None # type: DeclarationType
declaration: DeclarationType = None
if self.skip_word('struct'):
typ = 'struct'
declaration = self._parse_struct()
@ -3035,7 +3027,7 @@ class DefinitionParser(BaseParser):
'macro', 'struct', 'union', 'enum', 'enumerator', 'type'):
raise Exception('Internal error, unknown directiveType "%s".' % directiveType)
declaration = None # type: DeclarationType
declaration: DeclarationType = None
if objectType == 'member':
declaration = self._parse_type_with_init(named=True, outer='member')
elif objectType == 'function':
@ -3074,7 +3066,7 @@ class DefinitionParser(BaseParser):
def parse_expression(self) -> Union[ASTExpression, ASTType]:
pos = self.pos
res = None # type: Union[ASTExpression, ASTType]
res: Union[ASTExpression, ASTType] = None
try:
res = self._parse_expression()
self.skip_ws()
@ -3113,7 +3105,7 @@ class CObject(ObjectDescription[ASTDeclaration]):
names=('rtype',)),
]
option_spec = {
option_spec: OptionSpec = {
'noindexentry': directives.flag,
}
@ -3221,7 +3213,7 @@ class CObject(ObjectDescription[ASTDeclaration]):
return super().run()
def handle_signature(self, sig: str, signode: TextElement) -> ASTDeclaration:
parentSymbol = self.env.temp_data['c:parent_symbol'] # type: Symbol
parentSymbol: Symbol = self.env.temp_data['c:parent_symbol']
parser = DefinitionParser(sig, location=signode, config=self.env.config)
try:
@ -3285,10 +3277,10 @@ class CObject(ObjectDescription[ASTDeclaration]):
return ast
def before_content(self) -> None:
lastSymbol = self.env.temp_data['c:last_symbol'] # type: Symbol
lastSymbol: Symbol = self.env.temp_data['c:last_symbol']
assert lastSymbol
self.oldParentSymbol = self.env.temp_data['c:parent_symbol']
self.oldParentKey = self.env.ref_context['c:parent_key'] # type: LookupKey
self.oldParentKey: LookupKey = self.env.ref_context['c:parent_key']
self.env.temp_data['c:parent_symbol'] = lastSymbol
self.env.ref_context['c:parent_key'] = lastSymbol.get_lookup_key()
@ -3353,13 +3345,13 @@ class CNamespaceObject(SphinxDirective):
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = True
option_spec = {} # type: Dict
option_spec: OptionSpec = {}
def run(self) -> List[Node]:
rootSymbol = self.env.domaindata['c']['root_symbol']
if self.arguments[0].strip() in ('NULL', '0', 'nullptr'):
symbol = rootSymbol
stack = [] # type: List[Symbol]
stack: List[Symbol] = []
else:
parser = DefinitionParser(self.arguments[0],
location=self.get_source_info(),
@ -3383,7 +3375,7 @@ class CNamespacePushObject(SphinxDirective):
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = True
option_spec = {} # type: Dict
option_spec: OptionSpec = {}
def run(self) -> List[Node]:
if self.arguments[0].strip() in ('NULL', '0', 'nullptr'):
@ -3414,7 +3406,7 @@ class CNamespacePopObject(SphinxDirective):
required_arguments = 0
optional_arguments = 0
final_argument_whitespace = True
option_spec = {} # type: Dict
option_spec: OptionSpec = {}
def run(self) -> List[Node]:
stack = self.env.temp_data.get('c:namespace_stack', None)
@ -3470,7 +3462,7 @@ class AliasTransform(SphinxTransform):
maxdepth -= 1
recurse = True
nodes = [] # type: List[Node]
nodes: List[Node] = []
if not skipThis:
signode = addnodes.desc_signature('', '')
nodes.append(signode)
@ -3478,7 +3470,7 @@ class AliasTransform(SphinxTransform):
if recurse:
if skipThis:
childContainer = nodes # type: Union[List[Node], addnodes.desc]
childContainer: Union[List[Node], addnodes.desc] = nodes
else:
content = addnodes.desc_content()
desc = addnodes.desc()
@ -3524,8 +3516,8 @@ class AliasTransform(SphinxTransform):
node.replace_self(signode)
continue
rootSymbol = self.env.domains['c'].data['root_symbol'] # type: Symbol
parentSymbol = rootSymbol.direct_lookup(parentKey) # type: Symbol
rootSymbol: Symbol = self.env.domains['c'].data['root_symbol']
parentSymbol: Symbol = rootSymbol.direct_lookup(parentKey)
if not parentSymbol:
print("Target: ", sig)
print("ParentKey: ", parentKey)
@ -3568,10 +3560,10 @@ class AliasTransform(SphinxTransform):
class CAliasObject(ObjectDescription):
option_spec = {
option_spec: OptionSpec = {
'maxdepth': directives.nonnegative_int,
'noroot': directives.flag,
} # type: Dict
}
def run(self) -> List[Node]:
"""
@ -3591,7 +3583,7 @@ class CAliasObject(ObjectDescription):
node['objtype'] = node['desctype'] = self.objtype
node['noindex'] = True
self.names = [] # type: List[str]
self.names: List[str] = []
aliasOptions = {
'maxdepth': self.options.get('maxdepth', 1),
'noroot': 'noroot' in self.options,
@ -3669,7 +3661,7 @@ class CExprRole(SphinxRole):
if asCode:
# render the expression as inline code
self.class_type = 'c-expr'
self.node_type = nodes.literal # type: Type[TextElement]
self.node_type: Type[TextElement] = nodes.literal
else:
# render the expression as inline text
self.class_type = 'c-texpr'
@ -3748,10 +3740,10 @@ class CDomain(Domain):
'expr': CExprRole(asCode=True),
'texpr': CExprRole(asCode=False)
}
initial_data = {
initial_data: Dict[str, Union[Symbol, Dict[str, Tuple[str, str, str]]]] = {
'root_symbol': Symbol(None, None, None, None, None),
'objects': {}, # fullname -> docname, node_id, objtype
} # type: Dict[str, Union[Symbol, Dict[str, Tuple[str, str, str]]]]
}
def clear_doc(self, docname: str) -> None:
if Symbol.debug_show_tree:
@ -3809,10 +3801,10 @@ class CDomain(Domain):
logger.warning('Unparseable C cross-reference: %r\n%s', target, e,
location=node)
return None, None
parentKey = node.get("c:parent_key", None) # type: LookupKey
parentKey: LookupKey = node.get("c:parent_key", None)
rootSymbol = self.data['root_symbol']
if parentKey:
parentSymbol = rootSymbol.direct_lookup(parentKey) # type: Symbol
parentSymbol: Symbol = rootSymbol.direct_lookup(parentKey)
if not parentSymbol:
print("Target: ", target)
print("ParentKey: ", parentKey)

View File

@ -8,8 +8,7 @@
:license: BSD, see LICENSE for details.
"""
from collections import namedtuple
from typing import Any, Dict, List, cast
from typing import TYPE_CHECKING, Any, Dict, List, NamedTuple, cast
from docutils import nodes
from docutils.nodes import Node
@ -18,9 +17,9 @@ from sphinx import addnodes
from sphinx.domains import Domain
from sphinx.locale import _
from sphinx.util.docutils import SphinxDirective
from sphinx.util.typing import OptionSpec
if False:
# For type annotation
if TYPE_CHECKING:
from sphinx.application import Sphinx
from sphinx.environment import BuildEnvironment
@ -38,9 +37,13 @@ versionlabel_classes = {
}
# TODO: move to typing.NamedTuple after dropping py35 support (see #5958)
ChangeSet = namedtuple('ChangeSet',
['type', 'docname', 'lineno', 'module', 'descname', 'content'])
class ChangeSet(NamedTuple):
type: str
docname: str
lineno: int
module: str
descname: str
content: str
class VersionChange(SphinxDirective):
@ -51,7 +54,7 @@ class VersionChange(SphinxDirective):
required_arguments = 1
optional_arguments = 1
final_argument_whitespace = True
option_spec = {} # type: Dict
option_spec: OptionSpec = {}
def run(self) -> List[Node]:
node = addnodes.versionmodified()
@ -91,7 +94,7 @@ class VersionChange(SphinxDirective):
domain = cast(ChangeSetDomain, self.env.get_domain('changeset'))
domain.note_changeset(node)
ret = [node] # type: List[Node]
ret: List[Node] = [node]
ret += messages
return ret
@ -102,9 +105,9 @@ class ChangeSetDomain(Domain):
name = 'changeset'
label = 'changeset'
initial_data = {
initial_data: Dict = {
'changes': {}, # version -> list of ChangeSet
} # type: Dict
}
@property
def changesets(self) -> Dict[str, List[ChangeSet]]:

View File

@ -8,7 +8,7 @@
:license: BSD, see LICENSE for details.
"""
from typing import Any, Dict, List, Set, Tuple, cast
from typing import TYPE_CHECKING, Any, Dict, List, Set, Tuple, cast
from docutils import nodes
from docutils.nodes import Element
@ -20,8 +20,7 @@ from sphinx.transforms import SphinxTransform
from sphinx.util import logging
from sphinx.util.nodes import copy_source_info, make_refnode
if False:
# For type annotation
if TYPE_CHECKING:
from sphinx.application import Sphinx
from sphinx.builders import Builder
from sphinx.environment import BuildEnvironment

View File

@ -39,6 +39,7 @@ from sphinx.util.cfamily import (ASTAttribute, ASTBaseBase, ASTBaseParenExprList
from sphinx.util.docfields import Field, GroupedField
from sphinx.util.docutils import SphinxDirective
from sphinx.util.nodes import make_refnode
from sphinx.util.typing import OptionSpec
logger = logging.getLogger(__name__)
T = TypeVar('T')
@ -179,7 +180,6 @@ T = TypeVar('T')
declarator ->
ptr-declarator
| noptr-declarator parameters-and-qualifiers trailing-return-type
(TODO: for now we don't support trailing-eturn-type)
ptr-declarator ->
noptr-declarator
| ptr-operator ptr-declarator
@ -306,6 +306,7 @@ _operator_re = re.compile(r'''(?x)
| \+\+ | --
| ->\*? | \,
| (<<|>>)=? | && | \|\|
| <=>
| [!<>=/*%+|&^~-]=?
| (\b(and|and_eq|bitand|bitor|compl|not|not_eq|or|or_eq|xor|xor_eq)\b)
''')
@ -494,6 +495,7 @@ _id_operator_v2 = {
'>': 'gt',
'<=': 'le',
'>=': 'ge',
'<=>': 'ss',
'!': 'nt', 'not': 'nt',
'&&': 'aa', 'and': 'aa',
'||': 'oo', 'or': 'oo',
@ -516,10 +518,10 @@ _id_operator_unary_v2 = {
'!': 'nt', 'not': 'nt',
'~': 'co', 'compl': 'co'
}
_id_char_from_prefix = {
_id_char_from_prefix: Dict[Optional[str], str] = {
None: 'c', 'u8': 'c',
'u': 'Ds', 'U': 'Di', 'L': 'w'
} # type: Dict[Any, str]
}
# these are ordered by preceedence
_expression_bin_ops = [
['||', 'or'],
@ -528,7 +530,7 @@ _expression_bin_ops = [
['^', 'xor'],
['&', 'bitand'],
['==', '!=', 'not_eq'],
['<=', '>=', '<', '>'],
['<=>', '<=', '>=', '<', '>'],
['<<', '>>'],
['+', '-'],
['*', '/', '%'],
@ -716,8 +718,7 @@ class ASTNestedName(ASTBase):
res.append('')
for i in range(len(self.names)):
n = self.names[i]
t = self.templates[i]
if t:
if self.templates[i]:
res.append("template " + transform(n))
else:
res.append(transform(n))
@ -728,16 +729,29 @@ class ASTNestedName(ASTBase):
verify_description_mode(mode)
# just print the name part, with template args, not template params
if mode == 'noneIsName':
signode += nodes.Text(str(self))
if self.rooted:
signode += nodes.Text('::')
for i in range(len(self.names)):
if i != 0:
signode += nodes.Text('::')
n = self.names[i]
if self.templates[i]:
signode += nodes.Text("template")
signode += nodes.Text(" ")
n.describe_signature(signode, mode, env, '', symbol)
elif mode == 'param':
name = str(self)
signode += nodes.emphasis(name, name)
assert not self.rooted, str(self)
assert len(self.names) == 1
assert not self.templates[0]
node = nodes.emphasis()
self.names[0].describe_signature(node, 'noneIsName', env, '', symbol)
signode += node
elif mode == 'markType' or mode == 'lastIsName' or mode == 'markName':
# Each element should be a pending xref targeting the complete
# prefix. however, only the identifier part should be a link, such
# that template args can be a link as well.
# For 'lastIsName' we should also prepend template parameter lists.
templateParams = [] # type: List[Any]
templateParams: List[Any] = []
if mode == 'lastIsName':
assert symbol is not None
if symbol.declaration.templatePrefix is not None:
@ -1248,7 +1262,7 @@ class ASTSizeofParamPack(ASTExpression):
def describe_signature(self, signode: TextElement, mode: str,
env: "BuildEnvironment", symbol: "Symbol") -> None:
signode.append(nodes.Text('sizeof...('))
self.identifier.describe_signature(signode, mode, env,
self.identifier.describe_signature(signode, 'markType', env,
symbol=symbol, prefix="", templateArgs="")
signode.append(nodes.Text(')'))
@ -1965,15 +1979,23 @@ class ASTParametersQualifiers(ASTBase):
def describe_signature(self, signode: TextElement, mode: str,
env: "BuildEnvironment", symbol: "Symbol") -> None:
verify_description_mode(mode)
paramlist = addnodes.desc_parameterlist()
for arg in self.args:
param = addnodes.desc_parameter('', '', noemph=True)
if mode == 'lastIsName': # i.e., outer-function params
# only use the desc_parameterlist for the outer list, not for inner lists
if mode == 'lastIsName':
paramlist = addnodes.desc_parameterlist()
for arg in self.args:
param = addnodes.desc_parameter('', '', noemph=True)
arg.describe_signature(param, 'param', env, symbol=symbol)
else:
arg.describe_signature(param, 'markType', env, symbol=symbol)
paramlist += param
signode += paramlist
paramlist += param
signode += paramlist
else:
signode += nodes.Text('(', '(')
first = True
for arg in self.args:
if not first:
signode += nodes.Text(', ', ', ')
first = False
arg.describe_signature(signode, 'markType', env, symbol=symbol)
signode += nodes.Text(')', ')')
def _add_anno(signode: TextElement, text: str) -> None:
signode += nodes.Text(' ')
@ -2035,7 +2057,7 @@ class ASTDeclSpecsSimple(ASTBase):
self.attrs + other.attrs)
def _stringify(self, transform: StringifyTransform) -> str:
res = [] # type: List[str]
res: List[str] = []
res.extend(transform(attr) for attr in self.attrs)
if self.storage:
res.append(self.storage)
@ -2122,7 +2144,7 @@ class ASTDeclSpecs(ASTBase):
return ''.join(res)
def _stringify(self, transform: StringifyTransform) -> str:
res = [] # type: List[str]
res: List[str] = []
l = transform(self.leftSpecs)
if len(l) > 0:
res.append(l)
@ -2189,7 +2211,7 @@ class ASTArray(ASTBase):
verify_description_mode(mode)
signode.append(nodes.Text("["))
if self.size:
self.size.describe_signature(signode, mode, env, symbol)
self.size.describe_signature(signode, 'markType', env, symbol)
signode.append(nodes.Text("]"))
@ -2657,7 +2679,7 @@ class ASTDeclaratorMemPtr(ASTDeclarator):
def describe_signature(self, signode: TextElement, mode: str,
env: "BuildEnvironment", symbol: "Symbol") -> None:
verify_description_mode(mode)
self.className.describe_signature(signode, mode, env, symbol)
self.className.describe_signature(signode, 'markType', env, symbol)
signode += nodes.Text('::*')
def _add_anno(signode: TextElement, text: str) -> None:
@ -3613,9 +3635,9 @@ class ASTDeclaration(ASTBase):
self.trailingRequiresClause = trailingRequiresClause
self.semicolon = semicolon
self.symbol = None # type: Symbol
self.symbol: Symbol = None
# set by CPPObject._add_enumerator_to_parent
self.enumeratorScopedSymbol = None # type: Symbol
self.enumeratorScopedSymbol: Symbol = None
def clone(self) -> "ASTDeclaration":
templatePrefixClone = self.templatePrefix.clone() if self.templatePrefix else None
@ -3835,8 +3857,8 @@ class Symbol:
docname: str, line: int) -> None:
self.parent = parent
# declarations in a single directive are linked together
self.siblingAbove = None # type: Symbol
self.siblingBelow = None # type: Symbol
self.siblingAbove: Symbol = None
self.siblingBelow: Symbol = None
self.identOrOp = identOrOp
self.templateParams = templateParams # template<templateParams>
self.templateArgs = templateArgs # identifier<templateArgs>
@ -3847,8 +3869,8 @@ class Symbol:
self._assert_invariants()
# Remember to modify Symbol.remove if modifications to the parent change.
self._children = [] # type: List[Symbol]
self._anonChildren = [] # type: List[Symbol]
self._children: List[Symbol] = []
self._anonChildren: List[Symbol] = []
# note: _children includes _anonChildren
if self.parent:
self.parent._children.append(self)
@ -3918,7 +3940,7 @@ class Symbol:
self.parent = None
def clear_doc(self, docname: str) -> None:
newChildren = [] # type: List[Symbol]
newChildren: List[Symbol] = []
for sChild in self._children:
sChild.clear_doc(docname)
if sChild.declaration and sChild.docname == docname:
@ -4950,7 +4972,7 @@ class DefinitionParser(BaseParser):
# fold-expression
# id-expression -> we parse this with _parse_nested_name
self.skip_ws()
res = self._parse_literal() # type: ASTExpression
res: ASTExpression = self._parse_literal()
if res is not None:
return res
self.skip_ws()
@ -4978,7 +5000,7 @@ class DefinitionParser(BaseParser):
if self.skip_string(close):
return [], False
exprs = [] # type: List[Union[ASTExpression, ASTBracedInitList]]
exprs: List[Union[ASTExpression, ASTBracedInitList]] = []
trailingComma = False
while True:
self.skip_ws()
@ -5057,7 +5079,7 @@ class DefinitionParser(BaseParser):
# | "typeid" "(" type-id ")"
prefixType = None
prefix = None # type: Any
prefix: Any = None
self.skip_ws()
cast = None
@ -5140,7 +5162,7 @@ class DefinitionParser(BaseParser):
raise self._make_multi_error(errors, header) from eInner
# and now parse postfixes
postFixes = [] # type: List[ASTPostfixOp]
postFixes: List[ASTPostfixOp] = []
while True:
self.skip_ws()
if prefixType in ['expr', 'cast', 'typeid']:
@ -5309,7 +5331,7 @@ class DefinitionParser(BaseParser):
# exclusive-or = and ^
# and = equality &
# equality = relational ==, !=
# relational = shift <, >, <=, >=
# relational = shift <, >, <=, >=, <=>
# shift = additive <<, >>
# additive = multiplicative +, -
# multiplicative = pm *, /, %
@ -5370,7 +5392,7 @@ class DefinitionParser(BaseParser):
# logical-or-expression
# | logical-or-expression "?" expression ":" assignment-expression
# | logical-or-expression assignment-operator initializer-clause
exprs = [] # type: List[Union[ASTExpression, ASTBracedInitList]]
exprs: List[Union[ASTExpression, ASTBracedInitList]] = []
ops = []
orExpr = self._parse_logical_or_expression(inTemplate=inTemplate)
exprs.append(orExpr)
@ -5443,7 +5465,7 @@ class DefinitionParser(BaseParser):
else:
# TODO: add handling of more bracket-like things, and quote handling
brackets = {'(': ')', '{': '}', '[': ']', '<': '>'}
symbols = [] # type: List[str]
symbols: List[str] = []
while not self.eof:
if (len(symbols) == 0 and self.current_char in end):
break
@ -5506,7 +5528,7 @@ class DefinitionParser(BaseParser):
if self.skip_string('>'):
return ASTTemplateArgs([], False)
prevErrors = []
templateArgs = [] # type: List[Union[ASTType, ASTTemplateArgConstant]]
templateArgs: List[Union[ASTType, ASTTemplateArgConstant]] = []
packExpansion = False
while 1:
pos = self.pos
@ -5558,8 +5580,8 @@ class DefinitionParser(BaseParser):
return ASTTemplateArgs(templateArgs, packExpansion)
def _parse_nested_name(self, memberPointer: bool = False) -> ASTNestedName:
names = [] # type: List[ASTNestedNameElement]
templates = [] # type: List[bool]
names: List[ASTNestedNameElement] = []
templates: List[bool] = []
self.skip_ws()
rooted = False
@ -5572,7 +5594,7 @@ class DefinitionParser(BaseParser):
else:
template = False
templates.append(template)
identOrOp = None # type: Union[ASTIdentifier, ASTOperator]
identOrOp: Union[ASTIdentifier, ASTOperator] = None
if self.skip_word_and_ws('operator'):
identOrOp = self._parse_operator()
else:
@ -6075,7 +6097,7 @@ class DefinitionParser(BaseParser):
return ASTInitializer(bracedInit)
if outer == 'member':
fallbackEnd = [] # type: List[str]
fallbackEnd: List[str] = []
elif outer == 'templateParam':
fallbackEnd = [',', '>']
elif outer is None: # function parameter
@ -6161,7 +6183,7 @@ class DefinitionParser(BaseParser):
typed=typed)
else:
paramMode = 'type'
if outer == 'member': # i.e., member
if outer == 'member':
named = True
elif outer == 'operatorCast':
paramMode = 'operatorCast'
@ -6354,7 +6376,7 @@ class DefinitionParser(BaseParser):
def _parse_template_parameter_list(self) -> ASTTemplateParams:
# only: '<' parameter-list '>'
# we assume that 'template' has just been parsed
templateParams = [] # type: List[ASTTemplateParam]
templateParams: List[ASTTemplateParam] = []
self.skip_ws()
if not self.skip_string("<"):
self.fail("Expected '<' after 'template'")
@ -6477,11 +6499,11 @@ class DefinitionParser(BaseParser):
def _parse_template_declaration_prefix(self, objectType: str
) -> Optional[ASTTemplateDeclarationPrefix]:
templates = [] # type: List[Union[ASTTemplateParams, ASTTemplateIntroduction]]
templates: List[Union[ASTTemplateParams, ASTTemplateIntroduction]] = []
while 1:
self.skip_ws()
# the saved position is only used to provide a better error message
params = None # type: Union[ASTTemplateParams, ASTTemplateIntroduction]
params: Union[ASTTemplateParams, ASTTemplateIntroduction] = None
pos = self.pos
if self.skip_word("template"):
try:
@ -6537,7 +6559,7 @@ class DefinitionParser(BaseParser):
msg += str(nestedName)
self.warn(msg)
newTemplates = [] # type: List[Union[ASTTemplateParams, ASTTemplateIntroduction]]
newTemplates: List[Union[ASTTemplateParams, ASTTemplateIntroduction]] = []
for i in range(numExtra):
newTemplates.append(ASTTemplateParams([]))
if templatePrefix and not isMemberInstantiation:
@ -6557,7 +6579,7 @@ class DefinitionParser(BaseParser):
templatePrefix = None
requiresClause = None
trailingRequiresClause = None
declaration = None # type: Any
declaration: Any = None
self.skip_ws()
if self.match(_visibility_re):
@ -6697,7 +6719,7 @@ class CPPObject(ObjectDescription[ASTDeclaration]):
names=('returns', 'return')),
]
option_spec = {
option_spec: OptionSpec = {
'noindexentry': directives.flag,
'tparam-line-spec': directives.flag,
}
@ -6856,7 +6878,7 @@ class CPPObject(ObjectDescription[ASTDeclaration]):
return super().run()
def handle_signature(self, sig: str, signode: desc_signature) -> ASTDeclaration:
parentSymbol = self.env.temp_data['cpp:parent_symbol'] # type: Symbol
parentSymbol: Symbol = self.env.temp_data['cpp:parent_symbol']
parser = DefinitionParser(sig, location=signode, config=self.env.config)
try:
@ -6903,10 +6925,10 @@ class CPPObject(ObjectDescription[ASTDeclaration]):
return ast
def before_content(self) -> None:
lastSymbol = self.env.temp_data['cpp:last_symbol'] # type: Symbol
lastSymbol: Symbol = self.env.temp_data['cpp:last_symbol']
assert lastSymbol
self.oldParentSymbol = self.env.temp_data['cpp:parent_symbol']
self.oldParentKey = self.env.ref_context['cpp:parent_key'] # type: LookupKey
self.oldParentKey: LookupKey = self.env.ref_context['cpp:parent_key']
self.env.temp_data['cpp:parent_symbol'] = lastSymbol
self.env.ref_context['cpp:parent_key'] = lastSymbol.get_lookup_key()
@ -6963,13 +6985,13 @@ class CPPNamespaceObject(SphinxDirective):
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = True
option_spec = {} # type: Dict
option_spec: OptionSpec = {}
def run(self) -> List[Node]:
rootSymbol = self.env.domaindata['cpp']['root_symbol']
if self.arguments[0].strip() in ('NULL', '0', 'nullptr'):
symbol = rootSymbol
stack = [] # type: List[Symbol]
stack: List[Symbol] = []
else:
parser = DefinitionParser(self.arguments[0],
location=self.get_source_info(),
@ -6994,7 +7016,7 @@ class CPPNamespacePushObject(SphinxDirective):
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = True
option_spec = {} # type: Dict
option_spec: OptionSpec = {}
def run(self) -> List[Node]:
if self.arguments[0].strip() in ('NULL', '0', 'nullptr'):
@ -7026,7 +7048,7 @@ class CPPNamespacePopObject(SphinxDirective):
required_arguments = 0
optional_arguments = 0
final_argument_whitespace = True
option_spec = {} # type: Dict
option_spec: OptionSpec = {}
def run(self) -> List[Node]:
stack = self.env.temp_data.get('cpp:namespace_stack', None)
@ -7081,7 +7103,7 @@ class AliasTransform(SphinxTransform):
maxdepth -= 1
recurse = True
nodes = [] # type: List[Node]
nodes: List[Node] = []
if not skipThis:
signode = addnodes.desc_signature('', '')
nodes.append(signode)
@ -7089,7 +7111,7 @@ class AliasTransform(SphinxTransform):
if recurse:
if skipThis:
childContainer = nodes # type: Union[List[Node], addnodes.desc]
childContainer: Union[List[Node], addnodes.desc] = nodes
else:
content = addnodes.desc_content()
desc = addnodes.desc()
@ -7138,15 +7160,15 @@ class AliasTransform(SphinxTransform):
node.replace_self(signode)
continue
rootSymbol = self.env.domains['cpp'].data['root_symbol'] # type: Symbol
parentSymbol = rootSymbol.direct_lookup(parentKey) # type: Symbol
rootSymbol: Symbol = self.env.domains['cpp'].data['root_symbol']
parentSymbol: Symbol = rootSymbol.direct_lookup(parentKey)
if not parentSymbol:
print("Target: ", sig)
print("ParentKey: ", parentKey)
print(rootSymbol.dump(1))
assert parentSymbol # should be there
symbols = [] # type: List[Symbol]
symbols: List[Symbol] = []
if isShorthand:
assert isinstance(ast, ASTNamespace)
ns = ast
@ -7203,10 +7225,10 @@ class AliasTransform(SphinxTransform):
class CPPAliasObject(ObjectDescription):
option_spec = {
option_spec: OptionSpec = {
'maxdepth': directives.nonnegative_int,
'noroot': directives.flag,
} # type: Dict
}
def run(self) -> List[Node]:
"""
@ -7225,7 +7247,7 @@ class CPPAliasObject(ObjectDescription):
# 'desctype' is a backwards compatible attribute
node['objtype'] = node['desctype'] = self.objtype
self.names = [] # type: List[str]
self.names: List[str] = []
aliasOptions = {
'maxdepth': self.options.get('maxdepth', 1),
'noroot': 'noroot' in self.options,
@ -7285,7 +7307,7 @@ class CPPExprRole(SphinxRole):
if asCode:
# render the expression as inline code
self.class_type = 'cpp-expr'
self.node_type = nodes.literal # type: Type[TextElement]
self.node_type: Type[TextElement] = nodes.literal
else:
# render the expression as inline text
self.class_type = 'cpp-texpr'
@ -7466,10 +7488,10 @@ class CPPDomain(Domain):
logger.warning('Unparseable C++ cross-reference: %r\n%s', t, ex,
location=node)
return None, None
parentKey = node.get("cpp:parent_key", None) # type: LookupKey
parentKey: LookupKey = node.get("cpp:parent_key", None)
rootSymbol = self.data['root_symbol']
if parentKey:
parentSymbol = rootSymbol.direct_lookup(parentKey) # type: Symbol
parentSymbol: Symbol = rootSymbol.direct_lookup(parentKey)
if not parentSymbol:
print("Target: ", target)
print("ParentKey: ", parentKey.data)
@ -7623,7 +7645,7 @@ class CPPDomain(Domain):
target = node.get('reftarget', None)
if target is None:
return None
parentKey = node.get("cpp:parent_key", None) # type: LookupKey
parentKey: LookupKey = node.get("cpp:parent_key", None)
if parentKey is None or len(parentKey.data) <= 0:
return None
@ -7644,10 +7666,11 @@ def setup(app: Sphinx) -> Dict[str, Any]:
app.add_config_value("cpp_debug_lookup", False, '')
app.add_config_value("cpp_debug_show_tree", False, '')
def setDebugFlags(app):
def initStuff(app):
Symbol.debug_lookup = app.config.cpp_debug_lookup
Symbol.debug_show_tree = app.config.cpp_debug_show_tree
app.connect("builder-inited", setDebugFlags)
app.config.cpp_index_common_prefix.sort(reverse=True)
app.connect("builder-inited", initStuff)
return {
'version': 'builtin',

View File

@ -8,7 +8,7 @@
:license: BSD, see LICENSE for details.
"""
from typing import Any, Dict, Iterable, List, Tuple
from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Tuple
from docutils import nodes
from docutils.nodes import Node, system_message
@ -20,9 +20,9 @@ from sphinx.environment import BuildEnvironment
from sphinx.util import logging, split_index_msg
from sphinx.util.docutils import ReferenceRole, SphinxDirective
from sphinx.util.nodes import process_index_entry
from sphinx.util.typing import OptionSpec
if False:
# For type annotation
if TYPE_CHECKING:
from sphinx.application import Sphinx
@ -68,7 +68,7 @@ class IndexDirective(SphinxDirective):
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = True
option_spec = {
option_spec: OptionSpec = {
'name': directives.unchanged,
}

View File

@ -28,6 +28,7 @@ from sphinx.util import logging
from sphinx.util.docfields import Field, GroupedField, TypedField
from sphinx.util.docutils import SphinxDirective
from sphinx.util.nodes import make_id, make_refnode
from sphinx.util.typing import OptionSpec
logger = logging.getLogger(__name__)
@ -41,13 +42,13 @@ class JSObject(ObjectDescription[Tuple[str, str]]):
has_arguments = False
#: what is displayed right before the documentation entry
display_prefix = None # type: str
display_prefix: str = None
#: If ``allow_nesting`` is ``True``, the object prefixes will be accumulated
#: based on directive nesting
allow_nesting = False
option_spec = {
option_spec: OptionSpec = {
'noindex': directives.flag,
'noindexentry': directives.flag,
}
@ -253,7 +254,7 @@ class JSModule(SphinxDirective):
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = False
option_spec = {
option_spec: OptionSpec = {
'noindex': directives.flag
}
@ -261,7 +262,7 @@ class JSModule(SphinxDirective):
mod_name = self.arguments[0].strip()
self.env.ref_context['js:module'] = mod_name
noindex = 'noindex' in self.options
ret = [] # type: List[Node]
ret: List[Node] = []
if not noindex:
domain = cast(JavaScriptDomain, self.env.get_domain('js'))
@ -345,10 +346,10 @@ class JavaScriptDomain(Domain):
'attr': JSXRefRole(),
'mod': JSXRefRole(),
}
initial_data = {
initial_data: Dict[str, Dict[str, Tuple[str, str]]] = {
'objects': {}, # fullname -> docname, node_id, objtype
'modules': {}, # modname -> docname, node_id
} # type: Dict[str, Dict[str, Tuple[str, str]]]
}
@property
def objects(self) -> Dict[str, Tuple[str, str, str]]:

View File

@ -8,14 +8,12 @@
:license: BSD, see LICENSE for details.
"""
import warnings
from typing import Any, Dict, Iterable, List, Tuple
from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Tuple
from docutils import nodes
from docutils.nodes import Element, Node, make_id, system_message
from sphinx.addnodes import pending_xref
from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.domains import Domain
from sphinx.environment import BuildEnvironment
from sphinx.locale import __
@ -23,8 +21,7 @@ from sphinx.roles import XRefRole
from sphinx.util import logging
from sphinx.util.nodes import make_refnode
if False:
# For type annotation
if TYPE_CHECKING:
from sphinx.application import Sphinx
from sphinx.builders import Builder
@ -44,10 +41,10 @@ class MathDomain(Domain):
name = 'math'
label = 'mathematics'
initial_data = {
initial_data: Dict = {
'objects': {}, # labelid -> (docname, eqno)
'has_equations': {}, # docname -> bool
} # type: Dict
}
dangling_warnings = {
'eq': 'equation not found: %(target)s',
}
@ -139,24 +136,6 @@ class MathDomain(Domain):
def get_objects(self) -> List:
return []
def add_equation(self, env: BuildEnvironment, docname: str, labelid: str) -> int:
warnings.warn('MathDomain.add_equation() is deprecated.',
RemovedInSphinx40Warning, stacklevel=2)
if labelid in self.equations:
path = env.doc2path(self.equations[labelid][0])
msg = __('duplicate label of equation %s, other instance in %s') % (labelid, path)
raise UserWarning(msg)
else:
eqno = self.get_next_equation_number(docname)
self.equations[labelid] = (docname, eqno)
return eqno
def get_next_equation_number(self, docname: str) -> int:
warnings.warn('MathDomain.get_next_equation_number() is deprecated.',
RemovedInSphinx40Warning, stacklevel=2)
targets = [eq for eq in self.equations.values() if eq[0] == docname]
return len(targets) + 1
def has_equations(self, docname: str = None) -> bool:
if docname:
return self.data['has_equations'].get(docname, False)

View File

@ -15,17 +15,17 @@ import sys
import typing
import warnings
from inspect import Parameter
from typing import Any, Dict, Iterable, Iterator, List, NamedTuple, Tuple, cast
from typing import Any, Dict, Iterable, Iterator, List, NamedTuple, Tuple, Type, cast
from docutils import nodes
from docutils.nodes import Element, Node
from docutils.parsers.rst import directives
from sphinx import addnodes
from sphinx.addnodes import desc_signature, pending_xref
from sphinx.addnodes import desc_signature, pending_xref, pending_xref_condition
from sphinx.application import Sphinx
from sphinx.builders import Builder
from sphinx.deprecation import RemovedInSphinx40Warning, RemovedInSphinx50Warning
from sphinx.deprecation import RemovedInSphinx50Warning
from sphinx.directives import ObjectDescription
from sphinx.domains import Domain, Index, IndexEntry, ObjType
from sphinx.environment import BuildEnvironment
@ -37,13 +37,8 @@ from sphinx.util import logging
from sphinx.util.docfields import Field, GroupedField, TypedField
from sphinx.util.docutils import SphinxDirective
from sphinx.util.inspect import signature_from_str
from sphinx.util.nodes import make_id, make_refnode
from sphinx.util.typing import TextlikeNode
if False:
# For type annotation
from typing import Type # for python3.5.1
from sphinx.util.nodes import find_pending_xref_condition, make_id, make_refnode
from sphinx.util.typing import OptionSpec, TextlikeNode
logger = logging.getLogger(__name__)
@ -68,14 +63,20 @@ pairindextypes = {
'builtin': _('built-in function'),
}
ObjectEntry = NamedTuple('ObjectEntry', [('docname', str),
('node_id', str),
('objtype', str)])
ModuleEntry = NamedTuple('ModuleEntry', [('docname', str),
('node_id', str),
('synopsis', str),
('platform', str),
('deprecated', bool)])
class ObjectEntry(NamedTuple):
docname: str
node_id: str
objtype: str
canonical: bool
class ModuleEntry(NamedTuple):
docname: str
node_id: str
synopsis: str
platform: str
deprecated: bool
def type_to_xref(text: str, env: BuildEnvironment = None) -> addnodes.pending_xref:
@ -91,7 +92,17 @@ def type_to_xref(text: str, env: BuildEnvironment = None) -> addnodes.pending_xr
else:
kwargs = {}
return pending_xref('', nodes.Text(text),
if env.config.python_use_unqualified_type_names:
# Note: It would be better to use qualname to describe the object to support support
# nested classes. But python domain can't access the real python object because this
# module should work not-dynamically.
shortname = text.split('.')[-1]
contnodes: List[Node] = [pending_xref_condition('', shortname, condition='resolved'),
pending_xref_condition('', text, condition='*')]
else:
contnodes = [nodes.Text(text)]
return pending_xref('', *contnodes,
refdomain='py', reftype=reftype, reftarget=text, **kwargs)
@ -101,12 +112,17 @@ def _parse_annotation(annotation: str, env: BuildEnvironment = None) -> List[Nod
if isinstance(node, ast.Attribute):
return [nodes.Text("%s.%s" % (unparse(node.value)[0], node.attr))]
elif isinstance(node, ast.BinOp):
result = unparse(node.left) # type: List[Node]
result: List[Node] = unparse(node.left)
result.extend(unparse(node.op))
result.extend(unparse(node.right))
return result
elif isinstance(node, ast.BitOr):
return [nodes.Text(' '), addnodes.desc_sig_punctuation('', '|'), nodes.Text(' ')]
elif isinstance(node, ast.Constant): # type: ignore
if node.value is Ellipsis:
return [addnodes.desc_sig_punctuation('', "...")]
else:
return [nodes.Text(node.value)]
elif isinstance(node, ast.Expr):
return unparse(node.value)
elif isinstance(node, ast.Index):
@ -142,13 +158,6 @@ def _parse_annotation(annotation: str, env: BuildEnvironment = None) -> List[Nod
return result
else:
if sys.version_info >= (3, 6):
if isinstance(node, ast.Constant):
if node.value is Ellipsis:
return [addnodes.desc_sig_punctuation('', "...")]
else:
return [nodes.Text(node.value)]
if sys.version_info < (3, 8):
if isinstance(node, ast.Ellipsis):
return [addnodes.desc_sig_punctuation('', "...")]
@ -230,7 +239,7 @@ def _pseudo_parse_arglist(signode: desc_signature, arglist: str) -> None:
string literal (e.g. default argument value).
"""
paramlist = addnodes.desc_parameterlist()
stack = [paramlist] # type: List[Element]
stack: List[Element] = [paramlist]
try:
for argument in arglist.split(','):
argument = argument.strip()
@ -274,7 +283,7 @@ def _pseudo_parse_arglist(signode: desc_signature, arglist: str) -> None:
# when it comes to handling "." and "~" prefixes.
class PyXrefMixin:
def make_xref(self, rolename: str, domain: str, target: str,
innernode: "Type[TextlikeNode]" = nodes.emphasis,
innernode: Type[TextlikeNode] = nodes.emphasis,
contnode: Node = None, env: BuildEnvironment = None) -> Node:
result = super().make_xref(rolename, domain, target, # type: ignore
innernode, contnode, env)
@ -293,7 +302,7 @@ class PyXrefMixin:
return result
def make_xrefs(self, rolename: str, domain: str, target: str,
innernode: "Type[TextlikeNode]" = nodes.emphasis,
innernode: Type[TextlikeNode] = nodes.emphasis,
contnode: Node = None, env: BuildEnvironment = None) -> List[Node]:
delims = r'(\s*[\[\]\(\),](?:\s*or\s)?\s*|\s+or\s+)'
delims_re = re.compile(delims)
@ -317,7 +326,7 @@ class PyXrefMixin:
class PyField(PyXrefMixin, Field):
def make_xref(self, rolename: str, domain: str, target: str,
innernode: "Type[TextlikeNode]" = nodes.emphasis,
innernode: Type[TextlikeNode] = nodes.emphasis,
contnode: Node = None, env: BuildEnvironment = None) -> Node:
if rolename == 'class' and target == 'None':
# None is not a type, so use obj role instead.
@ -332,7 +341,7 @@ class PyGroupedField(PyXrefMixin, GroupedField):
class PyTypedField(PyXrefMixin, TypedField):
def make_xref(self, rolename: str, domain: str, target: str,
innernode: "Type[TextlikeNode]" = nodes.emphasis,
innernode: Type[TextlikeNode] = nodes.emphasis,
contnode: Node = None, env: BuildEnvironment = None) -> Node:
if rolename == 'class' and target == 'None':
# None is not a type, so use obj role instead.
@ -348,10 +357,11 @@ class PyObject(ObjectDescription[Tuple[str, str]]):
:cvar allow_nesting: Class is an object that allows for nested namespaces
:vartype allow_nesting: bool
"""
option_spec = {
option_spec: OptionSpec = {
'noindex': directives.flag,
'noindexentry': directives.flag,
'module': directives.unchanged,
'canonical': directives.unchanged,
'annotation': directives.unchanged,
}
@ -361,7 +371,7 @@ class PyObject(ObjectDescription[Tuple[str, str]]):
'keyword', 'kwarg', 'kwparam'),
typerolename='class', typenames=('paramtype', 'type'),
can_collapse=True),
PyTypedField('variable', label=_('Variables'), rolename='obj',
PyTypedField('variable', label=_('Variables'),
names=('var', 'ivar', 'cvar'),
typerolename='class', typenames=('vartype',),
can_collapse=True),
@ -493,6 +503,11 @@ class PyObject(ObjectDescription[Tuple[str, str]]):
domain = cast(PythonDomain, self.env.get_domain('py'))
domain.note_object(fullname, self.objtype, node_id, location=signode)
canonical_name = self.options.get('canonical')
if canonical_name:
domain.note_object(canonical_name, self.objtype, node_id, canonical=True,
location=signode)
if 'noindexentry' not in self.options:
indextext = self.get_index_text(modname, name_cls)
if indextext:
@ -557,44 +572,10 @@ class PyObject(ObjectDescription[Tuple[str, str]]):
self.env.ref_context.pop('py:module')
class PyModulelevel(PyObject):
"""
Description of an object on module level (functions, data).
"""
def run(self) -> List[Node]:
for cls in self.__class__.__mro__:
if cls.__name__ != 'DirectiveAdapter':
warnings.warn('PyModulelevel is deprecated. '
'Please check the implementation of %s' % cls,
RemovedInSphinx40Warning, stacklevel=2)
break
else:
warnings.warn('PyModulelevel is deprecated',
RemovedInSphinx40Warning, stacklevel=2)
return super().run()
def needs_arglist(self) -> bool:
return self.objtype == 'function'
def get_index_text(self, modname: str, name_cls: Tuple[str, str]) -> str:
if self.objtype == 'function':
if not modname:
return _('%s() (built-in function)') % name_cls[0]
return _('%s() (in module %s)') % (name_cls[0], modname)
elif self.objtype == 'data':
if not modname:
return _('%s (built-in variable)') % name_cls[0]
return _('%s (in module %s)') % (name_cls[0], modname)
else:
return ''
class PyFunction(PyObject):
"""Description of a function."""
option_spec = PyObject.option_spec.copy()
option_spec: OptionSpec = PyObject.option_spec.copy()
option_spec.update({
'async': directives.flag,
})
@ -648,7 +629,7 @@ class PyDecoratorFunction(PyFunction):
class PyVariable(PyObject):
"""Description of a variable."""
option_spec = PyObject.option_spec.copy()
option_spec: OptionSpec = PyObject.option_spec.copy()
option_spec.update({
'type': directives.unchanged,
'value': directives.unchanged,
@ -681,7 +662,7 @@ class PyClasslike(PyObject):
Description of a class-like object (classes, interfaces, exceptions).
"""
option_spec = PyObject.option_spec.copy()
option_spec: OptionSpec = PyObject.option_spec.copy()
option_spec.update({
'final': directives.flag,
})
@ -705,95 +686,10 @@ class PyClasslike(PyObject):
return ''
class PyClassmember(PyObject):
"""
Description of a class member (methods, attributes).
"""
def run(self) -> List[Node]:
for cls in self.__class__.__mro__:
if cls.__name__ != 'DirectiveAdapter':
warnings.warn('PyClassmember is deprecated. '
'Please check the implementation of %s' % cls,
RemovedInSphinx40Warning, stacklevel=2)
break
else:
warnings.warn('PyClassmember is deprecated',
RemovedInSphinx40Warning, stacklevel=2)
return super().run()
def needs_arglist(self) -> bool:
return self.objtype.endswith('method')
def get_signature_prefix(self, sig: str) -> str:
if self.objtype == 'staticmethod':
return 'static '
elif self.objtype == 'classmethod':
return 'classmethod '
return ''
def get_index_text(self, modname: str, name_cls: Tuple[str, str]) -> str:
name, cls = name_cls
add_modules = self.env.config.add_module_names
if self.objtype == 'method':
try:
clsname, methname = name.rsplit('.', 1)
except ValueError:
if modname:
return _('%s() (in module %s)') % (name, modname)
else:
return '%s()' % name
if modname and add_modules:
return _('%s() (%s.%s method)') % (methname, modname, clsname)
else:
return _('%s() (%s method)') % (methname, clsname)
elif self.objtype == 'staticmethod':
try:
clsname, methname = name.rsplit('.', 1)
except ValueError:
if modname:
return _('%s() (in module %s)') % (name, modname)
else:
return '%s()' % name
if modname and add_modules:
return _('%s() (%s.%s static method)') % (methname, modname,
clsname)
else:
return _('%s() (%s static method)') % (methname, clsname)
elif self.objtype == 'classmethod':
try:
clsname, methname = name.rsplit('.', 1)
except ValueError:
if modname:
return _('%s() (in module %s)') % (name, modname)
else:
return '%s()' % name
if modname:
return _('%s() (%s.%s class method)') % (methname, modname,
clsname)
else:
return _('%s() (%s class method)') % (methname, clsname)
elif self.objtype == 'attribute':
try:
clsname, attrname = name.rsplit('.', 1)
except ValueError:
if modname:
return _('%s (in module %s)') % (name, modname)
else:
return name
if modname and add_modules:
return _('%s (%s.%s attribute)') % (attrname, modname, clsname)
else:
return _('%s (%s attribute)') % (attrname, clsname)
else:
return ''
class PyMethod(PyObject):
"""Description of a method."""
option_spec = PyObject.option_spec.copy()
option_spec: OptionSpec = PyObject.option_spec.copy()
option_spec.update({
'abstractmethod': directives.flag,
'async': directives.flag,
@ -854,7 +750,7 @@ class PyMethod(PyObject):
class PyClassMethod(PyMethod):
"""Description of a classmethod."""
option_spec = PyObject.option_spec.copy()
option_spec: OptionSpec = PyObject.option_spec.copy()
def run(self) -> List[Node]:
self.name = 'py:method'
@ -866,7 +762,7 @@ class PyClassMethod(PyMethod):
class PyStaticMethod(PyMethod):
"""Description of a staticmethod."""
option_spec = PyObject.option_spec.copy()
option_spec: OptionSpec = PyObject.option_spec.copy()
def run(self) -> List[Node]:
self.name = 'py:method'
@ -894,7 +790,7 @@ class PyDecoratorMethod(PyMethod):
class PyAttribute(PyObject):
"""Description of an attribute."""
option_spec = PyObject.option_spec.copy()
option_spec: OptionSpec = PyObject.option_spec.copy()
option_spec.update({
'type': directives.unchanged,
'value': directives.unchanged,
@ -929,6 +825,46 @@ class PyAttribute(PyObject):
return _('%s (%s attribute)') % (attrname, clsname)
class PyProperty(PyObject):
"""Description of an attribute."""
option_spec = PyObject.option_spec.copy()
option_spec.update({
'abstractmethod': directives.flag,
'type': directives.unchanged,
})
def handle_signature(self, sig: str, signode: desc_signature) -> Tuple[str, str]:
fullname, prefix = super().handle_signature(sig, signode)
typ = self.options.get('type')
if typ:
signode += addnodes.desc_annotation(typ, ': ' + typ)
return fullname, prefix
def get_signature_prefix(self, sig: str) -> str:
prefix = ['property']
if 'abstractmethod' in self.options:
prefix.insert(0, 'abstract')
return ' '.join(prefix) + ' '
def get_index_text(self, modname: str, name_cls: Tuple[str, str]) -> str:
name, cls = name_cls
try:
clsname, attrname = name.rsplit('.', 1)
if modname and self.env.config.add_module_names:
clsname = '.'.join([modname, clsname])
except ValueError:
if modname:
return _('%s (in module %s)') % (name, modname)
else:
return name
return _('%s (%s property)') % (attrname, clsname)
class PyDecoratorMixin:
"""
Mixin for decorator directives.
@ -961,7 +897,7 @@ class PyModule(SphinxDirective):
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = False
option_spec = {
option_spec: OptionSpec = {
'platform': lambda x: x,
'synopsis': lambda x: x,
'noindex': directives.flag,
@ -974,7 +910,7 @@ class PyModule(SphinxDirective):
modname = self.arguments[0].strip()
noindex = 'noindex' in self.options
self.env.ref_context['py:module'] = modname
ret = [] # type: List[Node]
ret: List[Node] = []
if not noindex:
# note module to the domain
node_id = make_id(self.env, self.state.document, 'module', modname)
@ -1025,7 +961,7 @@ class PyCurrentModule(SphinxDirective):
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = False
option_spec = {} # type: Dict
option_spec: OptionSpec = {}
def run(self) -> List[Node]:
modname = self.arguments[0].strip()
@ -1085,10 +1021,9 @@ class PythonModuleIndex(Index):
def generate(self, docnames: Iterable[str] = None
) -> Tuple[List[Tuple[str, List[IndexEntry]]], bool]:
content = {} # type: Dict[str, List[IndexEntry]]
content: Dict[str, List[IndexEntry]] = {}
# list of prefixes to ignore
ignores = None # type: List[str]
ignores = self.domain.env.config['modindex_common_prefix'] # type: ignore
ignores: List[str] = self.domain.env.config['modindex_common_prefix'] # type: ignore
ignores = sorted(ignores, key=len, reverse=True)
# list of all modules, sorted by module name
modules = sorted(self.domain.data['modules'].items(),
@ -1151,7 +1086,7 @@ class PythonDomain(Domain):
"""Python language domain."""
name = 'py'
label = 'Python'
object_types = {
object_types: Dict[str, ObjType] = {
'function': ObjType(_('function'), 'func', 'obj'),
'data': ObjType(_('data'), 'data', 'obj'),
'class': ObjType(_('class'), 'class', 'exc', 'obj'),
@ -1160,8 +1095,9 @@ class PythonDomain(Domain):
'classmethod': ObjType(_('class method'), 'meth', 'obj'),
'staticmethod': ObjType(_('static method'), 'meth', 'obj'),
'attribute': ObjType(_('attribute'), 'attr', 'obj'),
'property': ObjType(_('property'), 'attr', '_prop', 'obj'),
'module': ObjType(_('module'), 'mod', 'obj'),
} # type: Dict[str, ObjType]
}
directives = {
'function': PyFunction,
@ -1172,6 +1108,7 @@ class PythonDomain(Domain):
'classmethod': PyClassMethod,
'staticmethod': PyStaticMethod,
'attribute': PyAttribute,
'property': PyProperty,
'module': PyModule,
'currentmodule': PyCurrentModule,
'decorator': PyDecoratorFunction,
@ -1188,10 +1125,10 @@ class PythonDomain(Domain):
'mod': PyXRefRole(),
'obj': PyXRefRole(),
}
initial_data = {
initial_data: Dict[str, Dict[str, Tuple[Any]]] = {
'objects': {}, # fullname -> docname, objtype
'modules': {}, # modname -> docname, synopsis, platform, deprecated
} # type: Dict[str, Dict[str, Tuple[Any]]]
}
indices = [
PythonModuleIndex,
]
@ -1200,7 +1137,8 @@ class PythonDomain(Domain):
def objects(self) -> Dict[str, ObjectEntry]:
return self.data.setdefault('objects', {}) # fullname -> ObjectEntry
def note_object(self, name: str, objtype: str, node_id: str, location: Any = None) -> None:
def note_object(self, name: str, objtype: str, node_id: str,
canonical: bool = False, location: Any = None) -> None:
"""Note a python object for cross reference.
.. versionadded:: 2.1
@ -1210,7 +1148,7 @@ class PythonDomain(Domain):
logger.warning(__('duplicate object description of %s, '
'other instance in %s, use :noindex: for one of them'),
name, other.docname, location=location)
self.objects[name] = ObjectEntry(self.env.docname, node_id, objtype)
self.objects[name] = ObjectEntry(self.env.docname, node_id, objtype, canonical)
@property
def modules(self) -> Dict[str, ModuleEntry]:
@ -1255,7 +1193,7 @@ class PythonDomain(Domain):
if not name:
return []
matches = [] # type: List[Tuple[str, ObjectEntry]]
matches: List[Tuple[str, ObjectEntry]] = []
newname = None
if searchmode == 1:
@ -1308,8 +1246,17 @@ class PythonDomain(Domain):
type, searchmode)
if not matches and type == 'attr':
# fallback to meth (for property)
# fallback to meth (for property; Sphinx-2.4.x)
# this ensures that `:attr:` role continues to refer to the old property entry
# that defined by ``method`` directive in old reST files.
matches = self.find_obj(env, modname, clsname, target, 'meth', searchmode)
if not matches and type == 'meth':
# fallback to attr (for property)
# this ensures that `:meth:` in the old reST files can refer to the property
# entry that defined by ``property`` directive.
#
# Note: _prop is a secret role only for internal look-up.
matches = self.find_obj(env, modname, clsname, target, '_prop', searchmode)
if not matches:
return None
@ -1322,14 +1269,22 @@ class PythonDomain(Domain):
if obj[2] == 'module':
return self._make_module_refnode(builder, fromdocname, name, contnode)
else:
return make_refnode(builder, fromdocname, obj[0], obj[1], contnode, name)
# determine the content of the reference by conditions
content = find_pending_xref_condition(node, 'resolved')
if content:
children = content.children
else:
# if not found, use contnode
children = [contnode]
return make_refnode(builder, fromdocname, obj[0], obj[1], children, name)
def resolve_any_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
target: str, node: pending_xref, contnode: Element
) -> List[Tuple[str, Element]]:
modname = node.get('py:module')
clsname = node.get('py:class')
results = [] # type: List[Tuple[str, Element]]
results: List[Tuple[str, Element]] = []
# always search in "refspecific" mode with the :any: role
matches = self.find_obj(env, modname, clsname, target, None, 1)
@ -1339,9 +1294,17 @@ class PythonDomain(Domain):
self._make_module_refnode(builder, fromdocname,
name, contnode)))
else:
# determine the content of the reference by conditions
content = find_pending_xref_condition(node, 'resolved')
if content:
children = content.children
else:
# if not found, use contnode
children = [contnode]
results.append(('py:' + self.role_for_objtype(obj[2]),
make_refnode(builder, fromdocname, obj[0], obj[1],
contnode, name)))
children, name)))
return results
def _make_module_refnode(self, builder: Builder, fromdocname: str, name: str,
@ -1363,7 +1326,11 @@ class PythonDomain(Domain):
yield (modname, modname, 'module', mod.docname, mod.node_id, 0)
for refname, obj in self.objects.items():
if obj.objtype != 'module': # modules are already handled
yield (refname, refname, obj.objtype, obj.docname, obj.node_id, 1)
if obj.canonical:
# canonical names are not full-text searchable.
yield (refname, refname, obj.objtype, obj.docname, obj.node_id, -1)
else:
yield (refname, refname, obj.objtype, obj.docname, obj.node_id, 1)
def get_full_qualified_name(self, node: Element) -> str:
modname = node.get('py:module')
@ -1384,6 +1351,10 @@ def builtin_resolver(app: Sphinx, env: BuildEnvironment,
return s in typing.__all__ # type: ignore
content = find_pending_xref_condition(node, 'resolved')
if content:
contnode = content.children[0] # type: ignore
if node.get('refdomain') != 'py':
return None
elif node.get('reftype') in ('class', 'obj') and node.get('reftarget') == 'None':
@ -1404,12 +1375,13 @@ def setup(app: Sphinx) -> Dict[str, Any]:
app.setup_extension('sphinx.directives')
app.add_domain(PythonDomain)
app.add_config_value('python_use_unqualified_type_names', False, 'env')
app.connect('object-description-transform', filter_meta_fields)
app.connect('missing-reference', builtin_resolver, priority=900)
return {
'version': 'builtin',
'env_version': 2,
'env_version': 3,
'parallel_read_safe': True,
'parallel_write_safe': True,
}

View File

@ -25,6 +25,7 @@ from sphinx.locale import _, __
from sphinx.roles import XRefRole
from sphinx.util import logging
from sphinx.util.nodes import make_id, make_refnode
from sphinx.util.typing import OptionSpec
logger = logging.getLogger(__name__)
@ -117,7 +118,7 @@ class ReSTDirectiveOption(ReSTMarkup):
"""
Description of an option for reST directive.
"""
option_spec = ReSTMarkup.option_spec.copy()
option_spec: OptionSpec = ReSTMarkup.option_spec.copy()
option_spec.update({
'type': directives.unchanged,
})
@ -217,9 +218,9 @@ class ReSTDomain(Domain):
'dir': XRefRole(),
'role': XRefRole(),
}
initial_data = {
initial_data: Dict[str, Dict[Tuple[str, str], str]] = {
'objects': {}, # fullname -> docname, objtype
} # type: Dict[str, Dict[Tuple[str, str], str]]
}
@property
def objects(self) -> Dict[Tuple[str, str], Tuple[str, str]]:
@ -258,7 +259,7 @@ class ReSTDomain(Domain):
def resolve_any_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
target: str, node: pending_xref, contnode: Element
) -> List[Tuple[str, Element]]:
results = [] # type: List[Tuple[str, Element]]
results: List[Tuple[str, Element]] = []
for objtype in self.object_types:
todocname, node_id = self.objects.get((objtype, target), (None, None))
if todocname:

View File

@ -12,7 +12,8 @@ import re
import unicodedata
import warnings
from copy import copy
from typing import Any, Callable, Dict, Iterable, Iterator, List, Optional, Tuple, Union, cast
from typing import (TYPE_CHECKING, Any, Callable, Dict, Iterable, Iterator, List, Optional,
Tuple, Type, Union, cast)
from docutils import nodes
from docutils.nodes import Element, Node, system_message
@ -21,7 +22,7 @@ from docutils.statemachine import StringList
from sphinx import addnodes
from sphinx.addnodes import desc_signature, pending_xref
from sphinx.deprecation import RemovedInSphinx40Warning, RemovedInSphinx50Warning
from sphinx.deprecation import RemovedInSphinx50Warning
from sphinx.directives import ObjectDescription
from sphinx.domains import Domain, ObjType
from sphinx.locale import _, __
@ -29,12 +30,9 @@ from sphinx.roles import XRefRole
from sphinx.util import docname_join, logging, ws_re
from sphinx.util.docutils import SphinxDirective
from sphinx.util.nodes import clean_astext, make_id, make_refnode
from sphinx.util.typing import RoleFunction
if False:
# For type annotation
from typing import Type # for python3.5.1
from sphinx.util.typing import OptionSpec, RoleFunction
if TYPE_CHECKING:
from sphinx.application import Sphinx
from sphinx.builders import Builder
from sphinx.environment import BuildEnvironment
@ -52,8 +50,8 @@ class GenericObject(ObjectDescription[str]):
"""
A generic x-ref directive registered with Sphinx.add_object_type().
"""
indextemplate = ''
parse_node = None # type: Callable[[GenericObject, BuildEnvironment, str, desc_signature], str] # NOQA
indextemplate: str = ''
parse_node: Callable[["GenericObject", "BuildEnvironment", str, desc_signature], str] = None # NOQA
def handle_signature(self, sig: str, signode: desc_signature) -> str:
if self.parse_node:
@ -134,7 +132,7 @@ class Target(SphinxDirective):
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = True
option_spec = {} # type: Dict
option_spec: OptionSpec = {}
def run(self) -> List[Node]:
# normalize whitespace in fullname like XRefRole does
@ -150,7 +148,7 @@ class Target(SphinxDirective):
node['ids'].append(old_node_id)
self.state.document.note_explicit_target(node)
ret = [node] # type: List[Node]
ret: List[Node] = [node]
if self.indextemplate:
indexentry = self.indextemplate % (fullname,)
indextype = 'single'
@ -267,7 +265,7 @@ class Program(SphinxDirective):
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = True
option_spec = {} # type: Dict
option_spec: OptionSpec = {}
def run(self) -> List[Node]:
program = ws_re.sub('-', self.arguments[0].strip())
@ -292,8 +290,8 @@ def split_term_classifiers(line: str) -> List[Optional[str]]:
def make_glossary_term(env: "BuildEnvironment", textnodes: Iterable[Node], index_key: str,
source: str, lineno: int, node_id: str = None,
document: nodes.document = None) -> nodes.term:
source: str, lineno: int, node_id: str, document: nodes.document
) -> nodes.term:
# get a text-only representation of the term and register it
# as a cross-reference target
term = nodes.term('', '', *textnodes)
@ -304,23 +302,10 @@ def make_glossary_term(env: "BuildEnvironment", textnodes: Iterable[Node], index
if node_id:
# node_id is given from outside (mainly i18n module), use it forcedly
term['ids'].append(node_id)
elif document:
else:
node_id = make_id(env, document, 'term', termtext)
term['ids'].append(node_id)
document.note_explicit_target(term)
else:
warnings.warn('make_glossary_term() expects document is passed as an argument.',
RemovedInSphinx40Warning, stacklevel=2)
gloss_entries = env.temp_data.setdefault('gloss_entries', set())
node_id = nodes.make_id('term-' + termtext)
if node_id == 'term':
# "term" is not good for node_id. Generate it by sequence number instead.
node_id = 'term-%d' % env.new_serialno('glossary')
while node_id in gloss_entries:
node_id = 'term-%d' % env.new_serialno('glossary')
gloss_entries.add(node_id)
term['ids'].append(node_id)
std = cast(StandardDomain, env.get_domain('std'))
std._note_term(termtext, node_id, location=term)
@ -344,7 +329,7 @@ class Glossary(SphinxDirective):
required_arguments = 0
optional_arguments = 0
final_argument_whitespace = False
option_spec = {
option_spec: OptionSpec = {
'sorted': directives.flag,
}
@ -358,11 +343,11 @@ class Glossary(SphinxDirective):
# be* a definition list.
# first, collect single entries
entries = [] # type: List[Tuple[List[Tuple[str, str, int]], StringList]]
entries: List[Tuple[List[Tuple[str, str, int]], StringList]] = []
in_definition = True
in_comment = False
was_empty = True
messages = [] # type: List[Node]
messages: List[Node] = []
for line, (source, lineno) in zip(self.content, self.content.items):
# empty line -> add to last definition
if not line:
@ -417,9 +402,9 @@ class Glossary(SphinxDirective):
# now, parse all the entries into a big definition list
items = []
for terms, definition in entries:
termtexts = [] # type: List[str]
termnodes = [] # type: List[Node]
system_messages = [] # type: List[Node]
termtexts: List[str] = []
termnodes: List[Node] = []
system_messages: List[Node] = []
for line, source, lineno in terms:
parts = split_term_classifiers(line)
# parse the term with inline markup
@ -428,7 +413,7 @@ class Glossary(SphinxDirective):
# use first classifier as a index key
term = make_glossary_term(self.env, textnodes, parts[1], source, lineno,
document=self.state.document)
node_id=None, document=self.state.document)
term.rawsource = line
system_messages.extend(sysmsg)
termtexts.append(term.astext())
@ -458,7 +443,7 @@ class Glossary(SphinxDirective):
def token_xrefs(text: str, productionGroup: str = '') -> List[Node]:
if len(productionGroup) != 0:
productionGroup += ':'
retnodes = [] # type: List[Node]
retnodes: List[Node] = []
pos = 0
for m in token_re.finditer(text):
if m.start() > pos:
@ -497,11 +482,11 @@ class ProductionList(SphinxDirective):
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = True
option_spec = {} # type: Dict
option_spec: OptionSpec = {}
def run(self) -> List[Node]:
domain = cast(StandardDomain, self.env.get_domain('std'))
node = addnodes.productionlist() # type: Element
node: Element = addnodes.productionlist()
self.set_source_info(node)
# The backslash handling is from ObjectDescription.get_signatures
nl_escape_re = re.compile(r'\\\n')
@ -574,7 +559,7 @@ class StandardDomain(Domain):
name = 'std'
label = 'Default'
object_types = {
object_types: Dict[str, ObjType] = {
'term': ObjType(_('glossary term'), 'term', searchprio=-1),
'token': ObjType(_('grammar token'), 'token', searchprio=-1),
'label': ObjType(_('reference label'), 'ref', 'keyword',
@ -582,17 +567,17 @@ class StandardDomain(Domain):
'envvar': ObjType(_('environment variable'), 'envvar'),
'cmdoption': ObjType(_('program option'), 'option'),
'doc': ObjType(_('document'), 'doc', searchprio=-1)
} # type: Dict[str, ObjType]
}
directives = {
directives: Dict[str, Type[Directive]] = {
'program': Program,
'cmdoption': Cmdoption, # old name for backwards compatibility
'option': Cmdoption,
'envvar': EnvVar,
'glossary': Glossary,
'productionlist': ProductionList,
} # type: Dict[str, Type[Directive]]
roles = {
}
roles: Dict[str, Union[RoleFunction, XRefRole]] = {
'option': OptionXRefRole(warn_dangling=True),
'envvar': EnvVarXRefRole(),
# links to tokens in grammar productions
@ -610,7 +595,7 @@ class StandardDomain(Domain):
'keyword': XRefRole(warn_dangling=True),
# links to documents
'doc': XRefRole(warn_dangling=True, innernodeclass=nodes.inline),
} # type: Dict[str, Union[RoleFunction, XRefRole]]
}
initial_data = {
'progoptions': {}, # (program, name) -> docname, labelid
@ -635,11 +620,12 @@ class StandardDomain(Domain):
'option': 'unknown option: %(target)s',
}
enumerable_nodes = { # node_class -> (figtype, title_getter)
# node_class -> (figtype, title_getter)
enumerable_nodes: Dict[Type[Node], Tuple[str, Callable]] = {
nodes.figure: ('figure', None),
nodes.table: ('table', None),
nodes.container: ('code-block', None),
} # type: Dict[Type[Node], Tuple[str, Callable]]
}
def __init__(self, env: "BuildEnvironment") -> None:
super().__init__(env)
@ -721,7 +707,7 @@ class StandardDomain(Domain):
return self.data.setdefault('anonlabels', {}) # labelname -> docname, labelid
def clear_doc(self, docname: str) -> None:
key = None # type: Any
key: Any = None
for key, (fn, _l) in list(self.progoptions.items()):
if fn == docname:
del self.progoptions[key]
@ -837,11 +823,6 @@ class StandardDomain(Domain):
resolver = self._resolve_doc_xref
elif typ == 'option':
resolver = self._resolve_option_xref
elif typ == 'citation':
warnings.warn('pending_xref(domain=std, type=citation) is deprecated: %r' % node,
RemovedInSphinx40Warning, stacklevel=2)
domain = env.get_domain('citation')
return domain.resolve_xref(env, fromdocname, builder, typ, target, node, contnode)
elif typ == 'term':
resolver = self._resolve_term_xref
else:
@ -1012,7 +993,7 @@ class StandardDomain(Domain):
def resolve_any_xref(self, env: "BuildEnvironment", fromdocname: str,
builder: "Builder", target: str, node: pending_xref,
contnode: Element) -> List[Tuple[str, Element]]:
results = [] # type: List[Tuple[str, Element]]
results: List[Tuple[str, Element]] = []
ltarget = target.lower() # :ref: lowercases its target automatically
for role in ('ref', 'option'): # do not try "keyword"
res = self.resolve_xref(env, fromdocname, builder, role,
@ -1076,7 +1057,7 @@ class StandardDomain(Domain):
def get_enumerable_node_type(self, node: Node) -> str:
"""Get type of enumerable nodes."""
def has_child(node: Element, cls: "Type") -> bool:
def has_child(node: Element, cls: Type) -> bool:
return any(isinstance(child, cls) for child in node)
if isinstance(node, nodes.section):
@ -1127,18 +1108,6 @@ class StandardDomain(Domain):
else:
return None
def note_citations(self, env: "BuildEnvironment", docname: str, document: nodes.document) -> None: # NOQA
warnings.warn('StandardDomain.note_citations() is deprecated.',
RemovedInSphinx40Warning, stacklevel=2)
def note_citation_refs(self, env: "BuildEnvironment", docname: str, document: nodes.document) -> None: # NOQA
warnings.warn('StandardDomain.note_citation_refs() is deprecated.',
RemovedInSphinx40Warning, stacklevel=2)
def note_labels(self, env: "BuildEnvironment", docname: str, document: nodes.document) -> None: # NOQA
warnings.warn('StandardDomain.note_labels() is deprecated.',
RemovedInSphinx40Warning, stacklevel=2)
def warn_missing_reference(app: "Sphinx", domain: Domain, node: pending_xref) -> bool:
if (domain and domain.name != 'std') or node['reftype'] != 'ref':

View File

@ -10,18 +10,18 @@
import os
import pickle
import warnings
from collections import defaultdict
from copy import copy
from datetime import datetime
from os import path
from typing import Any, Callable, Dict, Generator, Iterator, List, Set, Tuple, Union, cast
from typing import (TYPE_CHECKING, Any, Callable, Dict, Generator, Iterator, List, Set, Tuple,
Union)
from docutils import nodes
from docutils.nodes import Node
from sphinx import addnodes
from sphinx.config import Config
from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.domains import Domain
from sphinx.environment.adapters.toctree import TocTree
from sphinx.errors import BuildEnvironmentError, DocumentError, ExtensionError, SphinxError
@ -35,15 +35,14 @@ from sphinx.util.i18n import CatalogRepository, docname_to_domain
from sphinx.util.nodes import is_translatable
from sphinx.util.osutil import canon_path, os_path
if False:
# For type annotation
if TYPE_CHECKING:
from sphinx.application import Sphinx
from sphinx.builders import Builder
logger = logging.getLogger(__name__)
default_settings = {
default_settings: Dict[str, Any] = {
'embed_stylesheet': False,
'cloak_email_addresses': True,
'pep_base_url': 'https://www.python.org/dev/peps/',
@ -56,7 +55,7 @@ default_settings = {
'halt_level': 5,
'file_insertion_enabled': True,
'smartquotes_locales': [],
} # type: Dict[str, Any]
}
# This is increased every time an environment attribute is added
# or changed to properly invalidate pickle files.
@ -75,10 +74,10 @@ CONFIG_CHANGED_REASON = {
}
versioning_conditions = {
versioning_conditions: Dict[str, Union[bool, Callable]] = {
'none': False,
'text': is_translatable,
} # type: Dict[str, Union[bool, Callable]]
}
class BuildEnvironment:
@ -88,24 +87,24 @@ class BuildEnvironment:
transformations to resolve links to them.
"""
domains = None # type: Dict[str, Domain]
domains: Dict[str, Domain] = None
# --------- ENVIRONMENT INITIALIZATION -------------------------------------
def __init__(self, app: "Sphinx" = None):
self.app = None # type: Sphinx
self.doctreedir = None # type: str
self.srcdir = None # type: str
self.config = None # type: Config
self.config_status = None # type: int
self.config_status_extra = None # type: str
self.events = None # type: EventManager
self.project = None # type: Project
self.version = None # type: Dict[str, str]
self.app: Sphinx = None
self.doctreedir: str = None
self.srcdir: str = None
self.config: Config = None
self.config_status: int = None
self.config_status_extra: str = None
self.events: EventManager = None
self.project: Project = None
self.version: Dict[str, str] = None
# the method of doctree versioning; see set_versioning_method
self.versioning_condition = None # type: Union[bool, Callable]
self.versioning_compare = None # type: bool
self.versioning_condition: Union[bool, Callable] = None
self.versioning_compare: bool = None
# all the registered domains, set by the application
self.domains = {}
@ -117,70 +116,67 @@ class BuildEnvironment:
# All "docnames" here are /-separated and relative and exclude
# the source suffix.
self.all_docs = {} # type: Dict[str, float]
# docname -> mtime at the time of reading
# contains all read docnames
self.dependencies = defaultdict(set) # type: Dict[str, Set[str]]
# docname -> set of dependent file
# names, relative to documentation root
self.included = defaultdict(set) # type: Dict[str, Set[str]]
# docname -> set of included file
# docnames included from other documents
self.reread_always = set() # type: Set[str]
# docnames to re-read unconditionally on
# next build
# docname -> mtime at the time of reading
# contains all read docnames
self.all_docs: Dict[str, float] = {}
# docname -> set of dependent file
# names, relative to documentation root
self.dependencies: Dict[str, Set[str]] = defaultdict(set)
# docname -> set of included file
# docnames included from other documents
self.included: Dict[str, Set[str]] = defaultdict(set)
# docnames to re-read unconditionally on next build
self.reread_always: Set[str] = set()
# File metadata
self.metadata = defaultdict(dict) # type: Dict[str, Dict[str, Any]]
# docname -> dict of metadata items
# docname -> dict of metadata items
self.metadata: Dict[str, Dict[str, Any]] = defaultdict(dict)
# TOC inventory
self.titles = {} # type: Dict[str, nodes.title]
# docname -> title node
self.longtitles = {} # type: Dict[str, nodes.title]
# docname -> title node; only different if
# set differently with title directive
self.tocs = {} # type: Dict[str, nodes.bullet_list]
# docname -> table of contents nodetree
self.toc_num_entries = {} # type: Dict[str, int]
# docname -> number of real entries
# docname -> title node
self.titles: Dict[str, nodes.title] = {}
# docname -> title node; only different if
# set differently with title directive
self.longtitles: Dict[str, nodes.title] = {}
# docname -> table of contents nodetree
self.tocs: Dict[str, nodes.bullet_list] = {}
# docname -> number of real entries
self.toc_num_entries: Dict[str, int] = {}
# used to determine when to show the TOC
# in a sidebar (don't show if it's only one item)
self.toc_secnumbers = {} # type: Dict[str, Dict[str, Tuple[int, ...]]]
# docname -> dict of sectionid -> number
self.toc_fignumbers = {} # type: Dict[str, Dict[str, Dict[str, Tuple[int, ...]]]]
# docname -> dict of figtype ->
# dict of figureid -> number
# docname -> dict of sectionid -> number
self.toc_secnumbers: Dict[str, Dict[str, Tuple[int, ...]]] = {}
# docname -> dict of figtype -> dict of figureid -> number
self.toc_fignumbers: Dict[str, Dict[str, Dict[str, Tuple[int, ...]]]] = {}
self.toctree_includes = {} # type: Dict[str, List[str]]
# docname -> list of toctree includefiles
self.files_to_rebuild = {} # type: Dict[str, Set[str]]
# docname -> set of files
# (containing its TOCs) to rebuild too
self.glob_toctrees = set() # type: Set[str]
# docnames that have :glob: toctrees
self.numbered_toctrees = set() # type: Set[str]
# docnames that have :numbered: toctrees
# docname -> list of toctree includefiles
self.toctree_includes: Dict[str, List[str]] = {}
# docname -> set of files (containing its TOCs) to rebuild too
self.files_to_rebuild: Dict[str, Set[str]] = {}
# docnames that have :glob: toctrees
self.glob_toctrees: Set[str] = set()
# docnames that have :numbered: toctrees
self.numbered_toctrees: Set[str] = set()
# domain-specific inventories, here to be pickled
self.domaindata = {} # type: Dict[str, Dict]
# domainname -> domain-specific dict
# domainname -> domain-specific dict
self.domaindata: Dict[str, Dict] = {}
# these map absolute path -> (docnames, unique filename)
self.images = FilenameUniqDict() # type: FilenameUniqDict
self.dlfiles = DownloadFiles() # type: DownloadFiles
# filename -> (set of docnames, destination)
self.images: FilenameUniqDict = FilenameUniqDict()
# filename -> (set of docnames, destination)
self.dlfiles: DownloadFiles = DownloadFiles()
# the original URI for images
self.original_image_uri = {} # type: Dict[str, str]
self.original_image_uri: Dict[str, str] = {}
# temporary data storage while reading a document
self.temp_data = {} # type: Dict[str, Any]
self.temp_data: Dict[str, Any] = {}
# context for cross-references (e.g. current module or class)
# this is similar to temp_data, but will for example be copied to
# attributes of "any" cross references
self.ref_context = {} # type: Dict[str, Any]
self.ref_context: Dict[str, Any] = {}
# set up environment
if app:
@ -270,7 +266,7 @@ class BuildEnvironment:
raise an exception if the user tries to use an environment with an
incompatible versioning method.
"""
condition = None # type: Union[bool, Callable]
condition: Union[bool, Callable] = None
if callable(method):
condition = method
else:
@ -320,28 +316,13 @@ class BuildEnvironment:
"""
return self.project.path2doc(filename)
def doc2path(self, docname: str, base: Union[bool, str] = True, suffix: str = None) -> str:
def doc2path(self, docname: str, base: bool = True) -> str:
"""Return the filename for the document name.
If *base* is True, return absolute path under self.srcdir.
If *base* is None, return relative path to self.srcdir.
If *base* is a path string, return absolute path under that.
If *suffix* is not None, add it instead of config.source_suffix.
If *base* is False, return relative path to self.srcdir.
"""
if suffix:
warnings.warn('The suffix argument for doc2path() is deprecated.',
RemovedInSphinx40Warning, stacklevel=2)
if base not in (True, False, None):
warnings.warn('The string style base argument for doc2path() is deprecated.',
RemovedInSphinx40Warning, stacklevel=2)
pathname = self.project.doc2path(docname, base is True)
if suffix:
filename, _ = path.splitext(pathname)
pathname = filename + suffix
if base and base is not True:
pathname = path.join(base, pathname) # type: ignore
return pathname
return self.project.doc2path(docname, base)
def relfn2path(self, filename: str, docname: str = None) -> Tuple[str, str]:
"""Return paths to a file referenced from a document, relative to
@ -401,8 +382,8 @@ class BuildEnvironment:
# clear all files no longer present
removed = set(self.all_docs) - self.found_docs
added = set() # type: Set[str]
changed = set() # type: Set[str]
added: Set[str] = set()
changed: Set[str] = set()
if config_changed:
# config values affect e.g. substitutions
@ -410,21 +391,28 @@ class BuildEnvironment:
else:
for docname in self.found_docs:
if docname not in self.all_docs:
logger.debug('[build target] added %r', docname)
added.add(docname)
continue
# if the doctree file is not there, rebuild
filename = path.join(self.doctreedir, docname + '.doctree')
if not path.isfile(filename):
logger.debug('[build target] changed %r', docname)
changed.add(docname)
continue
# check the "reread always" list
if docname in self.reread_always:
logger.debug('[build target] changed %r', docname)
changed.add(docname)
continue
# check the mtime of the document
mtime = self.all_docs[docname]
newmtime = path.getmtime(self.doc2path(docname))
if newmtime > mtime:
logger.debug('[build target] outdated %r: %s -> %s',
docname,
datetime.utcfromtimestamp(mtime),
datetime.utcfromtimestamp(newmtime))
changed.add(docname)
continue
# finally, check the mtime of dependencies
@ -447,7 +435,7 @@ class BuildEnvironment:
return added, changed, removed
def check_dependents(self, app: "Sphinx", already: Set[str]) -> Generator[str, None, None]:
to_rewrite = [] # type: List[str]
to_rewrite: List[str] = []
for docnames in self.events.emit('env-get-updated', self):
to_rewrite.extend(docnames)
for docname in set(to_rewrite):
@ -610,7 +598,7 @@ class BuildEnvironment:
traversed.add(subdocname)
relations = {}
docnames = traverse_toctree(None, self.config.master_doc)
docnames = traverse_toctree(None, self.config.root_doc)
prevdoc = None
parent, docname = next(docnames)
for nextparent, nextdoc in docnames:
@ -628,7 +616,7 @@ class BuildEnvironment:
included = set().union(*self.included.values()) # type: ignore
for docname in sorted(self.all_docs):
if docname not in self.files_to_rebuild:
if docname == self.config.master_doc:
if docname == self.config.root_doc:
# the master file is not included anywhere ;)
continue
if docname in included:
@ -643,19 +631,3 @@ class BuildEnvironment:
for domain in self.domains.values():
domain.check_consistency()
self.events.emit('env-check-consistency', self)
@property
def indexentries(self) -> Dict[str, List[Tuple[str, str, str, str, str]]]:
warnings.warn('env.indexentries() is deprecated. Please use IndexDomain instead.',
RemovedInSphinx40Warning, stacklevel=2)
from sphinx.domains.index import IndexDomain
domain = cast(IndexDomain, self.get_domain('index'))
return domain.entries
@indexentries.setter
def indexentries(self, entries: Dict[str, List[Tuple[str, str, str, str, str]]]) -> None:
warnings.warn('env.indexentries() is deprecated. Please use IndexDomain instead.',
RemovedInSphinx40Warning, stacklevel=2)
from sphinx.domains.index import IndexDomain
domain = cast(IndexDomain, self.get_domain('index'))
domain.data['entries'] = entries

View File

@ -31,7 +31,7 @@ class IndexEntries:
_fixre: Pattern = re.compile(r'(.*) ([(][^()]*[)])')
) -> List[Tuple[str, List[Tuple[str, Any]]]]:
"""Create the real index from the collected index entries."""
new = {} # type: Dict[str, List]
new: Dict[str, List] = {}
def add_entry(word: str, subword: str, main: str, link: bool = True,
dic: Dict = new, key: str = None) -> None:
@ -126,7 +126,7 @@ class IndexEntries:
# (in module foo)
# (in module bar)
oldkey = ''
oldsubitems = None # type: Dict[str, List]
oldsubitems: Dict[str, List] = None
i = 0
while i < len(newlist):
key, (targets, subitems, _key) = newlist[i]

View File

@ -8,7 +8,7 @@
:license: BSD, see LICENSE for details.
"""
from typing import Any, Iterable, List, cast
from typing import TYPE_CHECKING, Any, Iterable, List, cast
from docutils import nodes
from docutils.nodes import Element, Node
@ -19,8 +19,7 @@ from sphinx.util import logging, url_re
from sphinx.util.matching import Matcher
from sphinx.util.nodes import clean_astext, process_only_nodes
if False:
# For type annotation
if TYPE_CHECKING:
from sphinx.builders import Builder
from sphinx.environment import BuildEnvironment
@ -103,7 +102,7 @@ class TocTree:
if not subnode['anchorname']:
# give the whole branch a 'current' class
# (useful for styling it differently)
branchnode = subnode # type: Element
branchnode: Element = subnode
while branchnode:
branchnode['classes'].append('current')
branchnode = branchnode.parent
@ -120,7 +119,7 @@ class TocTree:
) -> List[Element]:
"""Return TOC entries for a toctree node."""
refs = [(e[0], e[1]) for e in toctreenode['entries']]
entries = [] # type: List[Element]
entries: List[Element] = []
for (title, ref) in refs:
try:
refdoc = None
@ -237,7 +236,7 @@ class TocTree:
newnode = addnodes.compact_paragraph('', '')
caption = toctree.attributes.get('caption')
if caption:
caption_node = nodes.caption(caption, '', *[nodes.Text(caption)])
caption_node = nodes.title(caption, '', *[nodes.Text(caption)])
caption_node.line = toctree.line
caption_node.source = toctree.source
caption_node.rawsource = toctree['rawcaption']
@ -269,7 +268,7 @@ class TocTree:
for p, children in self.env.toctree_includes.items():
for child in children:
parent[child] = p
ancestors = [] # type: List[str]
ancestors: List[str] = []
d = docname
while d in parent and d not in ancestors:
ancestors.append(d)
@ -316,8 +315,8 @@ class TocTree:
def get_toctree_for(self, docname: str, builder: "Builder", collapse: bool,
**kwargs: Any) -> Element:
"""Return the global TOC nodetree."""
doctree = self.env.get_doctree(self.env.config.master_doc)
toctrees = [] # type: List[Element]
doctree = self.env.get_doctree(self.env.config.root_doc)
toctrees: List[Element] = []
if 'includehidden' not in kwargs:
kwargs['includehidden'] = True
if 'maxdepth' not in kwargs or not kwargs['maxdepth']:

View File

@ -8,14 +8,13 @@
:license: BSD, see LICENSE for details.
"""
from typing import Dict, List, Set
from typing import TYPE_CHECKING, Dict, List, Set
from docutils import nodes
from sphinx.environment import BuildEnvironment
if False:
# For type annotation
if TYPE_CHECKING:
from sphinx.application import Sphinx
@ -28,7 +27,7 @@ class EnvironmentCollector:
entries and toctrees, etc.
"""
listener_ids = None # type: Dict[str, int]
listener_ids: Dict[str, int] = None
def enable(self, app: "Sphinx") -> None:
assert self.listener_ids is None

View File

@ -48,7 +48,7 @@ class ImageCollector(EnvironmentCollector):
# choose the best image from these candidates. The special key * is
# set if there is only single candidate to be used by a writer.
# The special key ? is set for nonlocal URIs.
candidates = {} # type: Dict[str, str]
candidates: Dict[str, str] = {}
node['candidates'] = candidates
imguri = node['uri']
if imguri.startswith('data:'):
@ -94,7 +94,7 @@ class ImageCollector(EnvironmentCollector):
def collect_candidates(self, env: BuildEnvironment, imgpath: str,
candidates: Dict[str, str], node: Node) -> None:
globbed = {} # type: Dict[str, List[str]]
globbed: Dict[str, List[str]] = {}
for filename in glob(imgpath):
new_imgpath = relative_path(path.join(env.srcdir, 'dummy'),
filename)

View File

@ -1,63 +0,0 @@
"""
sphinx.environment.collectors.indexentries
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Index entries collector for sphinx.environment.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import warnings
from typing import Any, Dict, Set
from docutils import nodes
from sphinx import addnodes
from sphinx.application import Sphinx
from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.environment import BuildEnvironment
from sphinx.environment.collectors import EnvironmentCollector
from sphinx.util import logging, split_index_msg
logger = logging.getLogger(__name__)
class IndexEntriesCollector(EnvironmentCollector):
name = 'indices'
def __init__(self) -> None:
warnings.warn('IndexEntriesCollector is deprecated.',
RemovedInSphinx40Warning, stacklevel=2)
def clear_doc(self, app: Sphinx, env: BuildEnvironment, docname: str) -> None:
env.indexentries.pop(docname, None)
def merge_other(self, app: Sphinx, env: BuildEnvironment,
docnames: Set[str], other: BuildEnvironment) -> None:
for docname in docnames:
env.indexentries[docname] = other.indexentries[docname]
def process_doc(self, app: Sphinx, doctree: nodes.document) -> None:
docname = app.env.docname
entries = app.env.indexentries[docname] = []
for node in doctree.traverse(addnodes.index):
try:
for entry in node['entries']:
split_index_msg(entry[0], entry[1])
except ValueError as exc:
logger.warning(str(exc), location=node)
node.parent.remove(node)
else:
for entry in node['entries']:
entries.append(entry)
def setup(app: Sphinx) -> Dict[str, Any]:
app.add_env_collector(IndexEntriesCollector)
return {
'version': 'builtin',
'parallel_read_safe': True,
'parallel_write_safe': True,
}

View File

@ -50,7 +50,7 @@ class TitleCollector(EnvironmentCollector):
break
else:
# document has no title
titlenode += nodes.Text('<no title>')
titlenode += nodes.Text(doctree.get('title', '<no title>'))
app.env.titles[app.env.docname] = titlenode
app.env.longtitles[app.env.docname] = longtitlenode

View File

@ -8,7 +8,7 @@
:license: BSD, see LICENSE for details.
"""
from typing import Any, Dict, List, Set, Tuple, TypeVar, cast
from typing import Any, Dict, List, Set, Tuple, Type, TypeVar, cast
from docutils import nodes
from docutils.nodes import Element, Node
@ -22,11 +22,6 @@ from sphinx.locale import __
from sphinx.transforms import SphinxContentsFilter
from sphinx.util import logging, url_re
if False:
# For type annotation
from typing import Type # for python3.5.1
N = TypeVar('N')
logger = logging.getLogger(__name__)
@ -67,9 +62,9 @@ class TocTreeCollector(EnvironmentCollector):
docname = app.env.docname
numentries = [0] # nonlocal again...
def traverse_in_section(node: Element, cls: "Type[N]") -> List[N]:
def traverse_in_section(node: Element, cls: Type[N]) -> List[N]:
"""Like traverse(), but stay within the same section."""
result = [] # type: List[N]
result: List[N] = []
if isinstance(node, cls):
result.append(node)
for child in node.children:
@ -80,7 +75,7 @@ class TocTreeCollector(EnvironmentCollector):
return result
def build_toc(node: Element, depth: int = 1) -> nodes.bullet_list:
entries = [] # type: List[Element]
entries: List[Element] = []
for sectionnode in node:
# find all toctree nodes in this section and add them
# to the toc (just copying the toctree node which is then
@ -105,7 +100,7 @@ class TocTreeCollector(EnvironmentCollector):
'', '', internal=True, refuri=docname,
anchorname=anchorname, *nodetext)
para = addnodes.compact_paragraph('', '', reference)
item = nodes.list_item('', para) # type: Element
item: Element = nodes.list_item('', para)
sub_item = build_toc(sectionnode, depth + 1)
if sub_item:
item += sub_item
@ -141,7 +136,7 @@ class TocTreeCollector(EnvironmentCollector):
# a list of all docnames whose section numbers changed
rewrite_needed = []
assigned = set() # type: Set[str]
assigned: Set[str] = set()
old_secnumbers = env.toc_secnumbers
env.toc_secnumbers = {}
@ -191,7 +186,7 @@ class TocTreeCollector(EnvironmentCollector):
'(nested numbered toctree?)'), ref,
location=toctreenode, type='toc', subtype='secnum')
elif ref in env.tocs:
secnums = {} # type: Dict[str, Tuple[int, ...]]
secnums: Dict[str, Tuple[int, ...]] = {}
env.toc_secnumbers[ref] = secnums
assigned.add(ref)
_walk_toc(env.tocs[ref], secnums, depth, env.titles.get(ref))
@ -215,10 +210,10 @@ class TocTreeCollector(EnvironmentCollector):
rewrite_needed = []
assigned = set() # type: Set[str]
assigned: Set[str] = set()
old_fignumbers = env.toc_fignumbers
env.toc_fignumbers = {}
fignum_counter = {} # type: Dict[str, Dict[Tuple[int, ...], int]]
fignum_counter: Dict[str, Dict[Tuple[int, ...], int]] = {}
def get_figtype(node: Node) -> str:
for domain in env.domains.values():
@ -286,7 +281,7 @@ class TocTreeCollector(EnvironmentCollector):
_walk_doctree(docname, doctree, secnum)
if env.config.numfig:
_walk_doc(env.config.master_doc, tuple())
_walk_doc(env.config.root_doc, tuple())
for docname, fignums in env.toc_fignumbers.items():
if fignums != old_fignumbers.get(docname):
rewrite_needed.append(docname)

View File

@ -10,29 +10,26 @@
:license: BSD, see LICENSE for details.
"""
import warnings
from collections import defaultdict
from operator import attrgetter
from typing import Any, Callable, Dict, List, NamedTuple, Tuple
from typing import TYPE_CHECKING, Any, Callable, Dict, List, NamedTuple, Tuple, Type
from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.errors import ExtensionError, SphinxError
from sphinx.locale import __
from sphinx.util import logging
from sphinx.util.inspect import safe_getattr
if False:
# For type annotation
from typing import Type # for python3.5.1
if TYPE_CHECKING:
from sphinx.application import Sphinx
logger = logging.getLogger(__name__)
EventListener = NamedTuple('EventListener', [('id', int),
('handler', Callable),
('priority', int)])
class EventListener(NamedTuple):
id: int
handler: Callable
priority: int
# List of all known core events. Maps name to arguments description.
@ -58,13 +55,10 @@ core_events = {
class EventManager:
"""Event manager for Sphinx."""
def __init__(self, app: "Sphinx" = None) -> None:
if app is None:
warnings.warn('app argument is required for EventManager.',
RemovedInSphinx40Warning)
def __init__(self, app: "Sphinx") -> None:
self.app = app
self.events = core_events.copy()
self.listeners = defaultdict(list) # type: Dict[str, List[EventListener]]
self.listeners: Dict[str, List[EventListener]] = defaultdict(list)
self.next_listener_id = 0
def add(self, name: str) -> None:
@ -91,7 +85,7 @@ class EventManager:
listeners.remove(listener)
def emit(self, name: str, *args: Any,
allowed_exceptions: Tuple["Type[Exception]", ...] = ()) -> List:
allowed_exceptions: Tuple[Type[Exception], ...] = ()) -> List:
"""Emit a Sphinx event."""
try:
logger.debug('[app] emitting event: %r%s', name, repr(args)[:100])
@ -104,11 +98,7 @@ class EventManager:
listeners = sorted(self.listeners[name], key=attrgetter("priority"))
for listener in listeners:
try:
if self.app is None:
# for compatibility; RemovedInSphinx40Warning
results.append(listener.handler(*args))
else:
results.append(listener.handler(self.app, *args))
results.append(listener.handler(self.app, *args))
except allowed_exceptions:
# pass through the errors specified as *allowed_exceptions*
raise
@ -121,7 +111,7 @@ class EventManager:
return results
def emit_firstresult(self, name: str, *args: Any,
allowed_exceptions: Tuple["Type[Exception]", ...] = ()) -> Any:
allowed_exceptions: Tuple[Type[Exception], ...] = ()) -> Any:
"""Emit a Sphinx event and returns first result.
This returns the result of the first handler that doesn't return ``None``.

View File

@ -19,7 +19,6 @@ import glob
import locale
import os
import sys
import warnings
from copy import copy
from fnmatch import fnmatch
from importlib.machinery import EXTENSION_SUFFIXES
@ -29,9 +28,7 @@ from typing import Any, Generator, List, Tuple
import sphinx.locale
from sphinx import __display_version__, package_dir
from sphinx.cmd.quickstart import EXTENSIONS
from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
from sphinx.locale import __
from sphinx.util import rst
from sphinx.util.osutil import FileAvoidWrite, ensuredir
from sphinx.util.template import ReSTRenderer
@ -51,20 +48,6 @@ PY_SUFFIXES = ('.py', '.pyx') + tuple(EXTENSION_SUFFIXES)
template_dir = path.join(package_dir, 'templates', 'apidoc')
def makename(package: str, module: str) -> str:
"""Join package and module with a dot."""
warnings.warn('makename() is deprecated.',
RemovedInSphinx40Warning, stacklevel=2)
# Both package and module can be None/empty.
if package:
name = package
if module:
name += '.' + module
else:
name = module
return name
def is_initpy(filename: str) -> bool:
"""Check *filename* is __init__ file or not."""
basename = path.basename(filename)
@ -109,26 +92,6 @@ def write_file(name: str, text: str, opts: Any) -> None:
f.write(text)
def format_heading(level: int, text: str, escape: bool = True) -> str:
"""Create a heading of <level> [1, 2 or 3 supported]."""
warnings.warn('format_warning() is deprecated.',
RemovedInSphinx40Warning, stacklevel=2)
if escape:
text = rst.escape(text)
underlining = ['=', '-', '~', ][level - 1] * len(text)
return '%s\n%s\n\n' % (text, underlining)
def format_directive(module: str, package: str = None) -> str:
"""Create the automodule directive and add the options."""
warnings.warn('format_directive() is deprecated.',
RemovedInSphinx40Warning, stacklevel=2)
directive = '.. automodule:: %s\n' % module_join(package, module)
for option in OPTIONS:
directive += ' :%s:\n' % option
return directive
def create_module_file(package: str, basename: str, opts: Any,
user_template_dir: str = None) -> None:
"""Build the text of the file and write the file."""
@ -206,33 +169,6 @@ def create_modules_toc_file(modules: List[str], opts: Any, name: str = 'modules'
write_file(name, text, opts)
def shall_skip(module: str, opts: Any, excludes: List[str] = []) -> bool:
"""Check if we want to skip this module."""
warnings.warn('shall_skip() is deprecated.',
RemovedInSphinx40Warning, stacklevel=2)
# skip if the file doesn't exist and not using implicit namespaces
if not opts.implicit_namespaces and not path.exists(module):
return True
# Are we a package (here defined as __init__.py, not the folder in itself)
if is_initpy(module):
# Yes, check if we have any non-excluded modules at all here
all_skipped = True
basemodule = path.dirname(module)
for submodule in glob.glob(path.join(basemodule, '*.py')):
if not is_excluded(path.join(basemodule, submodule), excludes):
# There's a non-excluded module here, we won't skip
all_skipped = False
if all_skipped:
return True
# skip if it has a "private" name and this is selected
filename = path.basename(module)
if is_initpy(filename) and filename.startswith('_') and not opts.includeprivate:
return True
return False
def is_skipped_package(dirname: str, opts: Any, excludes: List[str] = []) -> bool:
"""Check if we want to skip this module."""
if not path.isdir(dirname):
@ -279,7 +215,7 @@ def walk(rootpath: str, excludes: List[str], opts: Any
# remove hidden ('.') and private ('_') directories, as well as
# excluded dirs
if includeprivate:
exclude_prefixes = ('.',) # type: Tuple[str, ...]
exclude_prefixes: Tuple[str, ...] = ('.',)
else:
exclude_prefixes = ('.', '_')
@ -536,13 +472,6 @@ def main(argv: List[str] = sys.argv[1:]) -> int:
return 0
deprecated_alias('sphinx.ext.apidoc',
{
'INITPY': '__init__.py',
},
RemovedInSphinx40Warning)
# So program can be started with "python -m sphinx.apidoc ..."
if __name__ == "__main__":
main()

View File

@ -14,16 +14,15 @@ import re
import warnings
from inspect import Parameter, Signature
from types import ModuleType
from typing import (Any, Callable, Dict, Iterator, List, Optional, Sequence, Set, Tuple, Type,
TypeVar, Union)
from typing import (TYPE_CHECKING, Any, Callable, Dict, Iterator, List, Optional, Sequence,
Set, Tuple, Type, TypeVar, Union)
from docutils.statemachine import StringList
import sphinx
from sphinx.application import Sphinx
from sphinx.config import ENUM, Config
from sphinx.deprecation import (RemovedInSphinx40Warning, RemovedInSphinx50Warning,
RemovedInSphinx60Warning)
from sphinx.deprecation import RemovedInSphinx50Warning, RemovedInSphinx60Warning
from sphinx.environment import BuildEnvironment
from sphinx.ext.autodoc.importer import (get_class_members, get_object_members, import_module,
import_object)
@ -34,13 +33,10 @@ from sphinx.util import inspect, logging
from sphinx.util.docstrings import extract_metadata, prepare_docstring
from sphinx.util.inspect import (evaluate_signature, getdoc, object_description, safe_getattr,
stringify_signature)
from sphinx.util.typing import get_type_hints, restify
from sphinx.util.typing import OptionSpec, get_type_hints, restify
from sphinx.util.typing import stringify as stringify_typehint
if False:
# For type annotation
from typing import Type # NOQA # for python3.5.1
if TYPE_CHECKING:
from sphinx.ext.autodoc.directive import DocumenterBridge
@ -313,7 +309,9 @@ class Documenter:
#: true if the generated content may contain titles
titles_allowed = False
option_spec = {'noindex': bool_option} # type: Dict[str, Callable]
option_spec: OptionSpec = {
'noindex': bool_option
}
def get_attr(self, obj: Any, name: str, *defargs: Any) -> Any:
"""getattr() override for types such as Zope interfaces."""
@ -327,31 +325,31 @@ class Documenter:
def __init__(self, directive: "DocumenterBridge", name: str, indent: str = '') -> None:
self.directive = directive
self.config = directive.env.config
self.env = directive.env # type: BuildEnvironment
self.config: Config = directive.env.config
self.env: BuildEnvironment = directive.env
self.options = directive.genopt
self.name = name
self.indent = indent
# the module and object path within the module, and the fully
# qualified name (all set after resolve_name succeeds)
self.modname = None # type: str
self.module = None # type: ModuleType
self.objpath = None # type: List[str]
self.fullname = None # type: str
self.modname: str = None
self.module: ModuleType = None
self.objpath: List[str] = None
self.fullname: str = None
# extra signature items (arguments and return annotation,
# also set after resolve_name succeeds)
self.args = None # type: str
self.retann = None # type: str
self.args: str = None
self.retann: str = None
# the object to document (set after import_object succeeds)
self.object = None # type: Any
self.object_name = None # type: str
self.object: Any = None
self.object_name: str = None
# the parent/owner of the object to document
self.parent = None # type: Any
self.parent: Any = None
# the module analyzer to get at attribute docs, or None
self.analyzer = None # type: ModuleAnalyzer
self.analyzer: ModuleAnalyzer = None
@property
def documenters(self) -> Dict[str, "Type[Documenter]"]:
def documenters(self) -> Dict[str, Type["Documenter"]]:
"""Returns registered Documenter classes"""
return self.env.app.registry.documenters
@ -540,16 +538,12 @@ class Documenter:
# etc. don't support a prepended module name
self.add_line(' :module: %s' % self.modname, sourcename)
def get_doc(self, encoding: str = None, ignore: int = None) -> Optional[List[List[str]]]:
def get_doc(self, ignore: int = None) -> Optional[List[List[str]]]:
"""Decode and return lines of the docstring(s) for the object.
When it returns None value, autodoc-process-docstring will not be called for this
object.
"""
if encoding is not None:
warnings.warn("The 'encoding' argument to autodoc.%s.get_doc() is deprecated."
% self.__class__.__name__,
RemovedInSphinx40Warning, stacklevel=2)
if ignore is not None:
warnings.warn("The 'ignore' argument to autodoc.%s.get_doc() is deprecated."
% self.__class__.__name__,
@ -828,7 +822,7 @@ class Documenter:
members_check_module, members = self.get_object_members(want_all)
# document non-skipped members
memberdocumenters = [] # type: List[Tuple[Documenter, bool]]
memberdocumenters: List[Tuple[Documenter, bool]] = []
for (mname, member, isattr) in self.filter_members(members, want_all):
classes = [cls for cls in self.documenters.values()
if cls.can_document_member(member, mname, isattr, self)]
@ -910,7 +904,7 @@ class Documenter:
# This is used for situations where you have a module that collects the
# functions and classes of internal submodules.
guess_modname = self.get_real_modname()
self.real_modname = real_modname or guess_modname
self.real_modname: str = real_modname or guess_modname
# try to also get a source code analyzer for attribute docs
try:
@ -924,15 +918,15 @@ class Documenter:
self.analyzer = None
# at least add the module.__file__ as a dependency
if hasattr(self.module, '__file__') and self.module.__file__:
self.directive.filename_set.add(self.module.__file__)
self.directive.record_dependencies.add(self.module.__file__)
else:
self.directive.filename_set.add(self.analyzer.srcname)
self.directive.record_dependencies.add(self.analyzer.srcname)
if self.real_modname != guess_modname:
# Add module to dependency list if target object is defined in other module.
try:
analyzer = ModuleAnalyzer.for_module(guess_modname)
self.directive.filename_set.add(analyzer.srcname)
self.directive.record_dependencies.add(analyzer.srcname)
except PycodeError:
pass
@ -978,7 +972,7 @@ class ModuleDocumenter(Documenter):
content_indent = ''
titles_allowed = True
option_spec = {
option_spec: OptionSpec = {
'members': members_option, 'undoc-members': bool_option,
'noindex': bool_option, 'inherited-members': inherited_members_option,
'show-inheritance': bool_option, 'synopsis': identity,
@ -986,12 +980,12 @@ class ModuleDocumenter(Documenter):
'member-order': member_order_option, 'exclude-members': exclude_members_option,
'private-members': members_option, 'special-members': members_option,
'imported-members': bool_option, 'ignore-module-all': bool_option
} # type: Dict[str, Callable]
}
def __init__(self, *args: Any) -> None:
super().__init__(*args)
merge_members_option(self.options)
self.__all__ = None # type: Optional[Sequence[str]]
self.__all__: Optional[Sequence[str]] = None
@classmethod
def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any
@ -1048,7 +1042,7 @@ class ModuleDocumenter(Documenter):
else:
attr_docs = {}
members = {} # type: Dict[str, ObjectMember]
members: Dict[str, ObjectMember] = {}
for name in dir(self.object):
try:
value = safe_getattr(self.object, name, None)
@ -1103,7 +1097,7 @@ class ModuleDocumenter(Documenter):
# Sort by __all__
def keyfunc(entry: Tuple[Documenter, bool]) -> int:
name = entry[0].name.split('::')[1]
if name in self.__all__:
if self.__all__ and name in self.__all__:
return self.__all__.index(name)
else:
return len(self.__all__)
@ -1173,15 +1167,10 @@ class DocstringSignatureMixin:
Mixin for FunctionDocumenter and MethodDocumenter to provide the
feature of reading the signature from the docstring.
"""
_new_docstrings = None # type: List[List[str]]
_signatures = None # type: List[str]
def _find_signature(self, encoding: str = None) -> Tuple[str, str]:
if encoding is not None:
warnings.warn("The 'encoding' argument to autodoc.%s._find_signature() is "
"deprecated." % self.__class__.__name__,
RemovedInSphinx40Warning, stacklevel=2)
_new_docstrings: List[List[str]] = None
_signatures: List[str] = None
def _find_signature(self) -> Tuple[str, str]:
# candidates of the object name
valid_names = [self.objpath[-1]] # type: ignore
if isinstance(self, ClassDocumenter):
@ -1202,20 +1191,17 @@ class DocstringSignatureMixin:
break
if line.endswith('\\'):
multiline = True
line = line.rstrip('\\').rstrip()
else:
multiline = False
# match first line of docstring against signature RE
match = py_ext_sig_re.match(line)
if not match:
continue
break
exmod, path, base, args, retann = match.groups()
# the base name must match ours
if base not in valid_names:
continue
break
# re-prepare docstring to ignore more leading indentation
tab_width = self.directive.state.document.settings.tab_width # type: ignore
@ -1229,27 +1215,16 @@ class DocstringSignatureMixin:
# subsequent signatures
self._signatures.append("(%s) -> %s" % (args, retann))
if multiline:
# the signature have multiple signatures on docstring
continue
else:
# don't look any further
break
if result:
# finish the loop when signature found
break
return result
def get_doc(self, encoding: str = None, ignore: int = None) -> Optional[List[List[str]]]:
if encoding is not None:
warnings.warn("The 'encoding' argument to autodoc.%s.get_doc() is deprecated."
% self.__class__.__name__,
RemovedInSphinx40Warning, stacklevel=2)
def get_doc(self, ignore: int = None) -> List[List[str]]:
if self._new_docstrings is not None:
return self._new_docstrings
return super().get_doc(None, ignore) # type: ignore
return super().get_doc(ignore) # type: ignore
def format_signature(self, **kwargs: Any) -> str:
if self.args is None and self.config.autodoc_docstring_signature: # type: ignore
@ -1436,16 +1411,16 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
"""
objtype = 'class'
member_order = 20
option_spec = {
option_spec: OptionSpec = {
'members': members_option, 'undoc-members': bool_option,
'noindex': bool_option, 'inherited-members': inherited_members_option,
'show-inheritance': bool_option, 'member-order': member_order_option,
'exclude-members': exclude_members_option,
'private-members': members_option, 'special-members': members_option,
} # type: Dict[str, Callable]
}
_signature_class = None # type: Any
_signature_method_name = None # type: str
_signature_class: Any = None
_signature_method_name: str = None
def __init__(self, *args: Any) -> None:
super().__init__(*args)
@ -1605,6 +1580,20 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
return []
def get_canonical_fullname(self) -> Optional[str]:
__modname__ = safe_getattr(self.object, '__module__', self.modname)
__qualname__ = safe_getattr(self.object, '__qualname__', None)
if __qualname__ is None:
__qualname__ = safe_getattr(self.object, '__name__', None)
if __qualname__ and '<locals>' in __qualname__:
# No valid qualname found if the object is defined as locals
__qualname__ = None
if __modname__ and __qualname__:
return '.'.join([__modname__, __qualname__])
else:
return None
def add_directive_header(self, sig: str) -> None:
sourcename = self.get_sourcename()
@ -1615,6 +1604,10 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
if self.analyzer and '.'.join(self.objpath) in self.analyzer.finals:
self.add_line(' :final:', sourcename)
canonical_fullname = self.get_canonical_fullname()
if not self.doc_as_attr and canonical_fullname and self.fullname != canonical_fullname:
self.add_line(' :canonical: %s' % canonical_fullname, sourcename)
# add inheritance info, if wanted
if not self.doc_as_attr and self.options.show_inheritance:
sourcename = self.get_sourcename()
@ -1649,11 +1642,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
else:
return False, [m for m in members.values() if m.class_ == self.object]
def get_doc(self, encoding: str = None, ignore: int = None) -> Optional[List[List[str]]]:
if encoding is not None:
warnings.warn("The 'encoding' argument to autodoc.%s.get_doc() is deprecated."
% self.__class__.__name__,
RemovedInSphinx40Warning, stacklevel=2)
def get_doc(self, ignore: int = None) -> Optional[List[List[str]]]:
if self.doc_as_attr:
# Don't show the docstring of the class when it is an alias.
return None
@ -1746,12 +1735,12 @@ class ExceptionDocumenter(ClassDocumenter):
class DataDocumenterMixinBase:
# define types of instance variables
config = None # type: Config
env = None # type: BuildEnvironment
modname = None # type: str
parent = None # type: Any
object = None # type: Any
objpath = None # type: List[str]
config: Config = None
env: BuildEnvironment = None
modname: str = None
parent: Any = None
object: Any = None
objpath: List[str] = None
def should_suppress_directive_header(self) -> bool:
"""Check directive header should be suppressed."""
@ -1814,7 +1803,7 @@ class TypeVarMixin(DataDocumenterMixinBase):
return (isinstance(self.object, TypeVar) or
super().should_suppress_directive_header())
def get_doc(self, encoding: str = None, ignore: int = None) -> Optional[List[List[str]]]:
def get_doc(self, ignore: int = None) -> Optional[List[List[str]]]:
if ignore is not None:
warnings.warn("The 'ignore' argument to autodoc.%s.get_doc() is deprecated."
% self.__class__.__name__,
@ -1833,6 +1822,8 @@ class TypeVarMixin(DataDocumenterMixinBase):
attrs = [repr(self.object.__name__)]
for constraint in self.object.__constraints__:
attrs.append(stringify_typehint(constraint))
if self.object.__bound__:
attrs.append(r"bound=\ " + restify(self.object.__bound__))
if self.object.__covariant__:
attrs.append("covariant=True")
if self.object.__contravariant__:
@ -1878,11 +1869,11 @@ class UninitializedGlobalVariableMixin(DataDocumenterMixinBase):
return (self.object is UNINITIALIZED_ATTR or
super().should_suppress_value_header())
def get_doc(self, encoding: str = None, ignore: int = None) -> Optional[List[List[str]]]:
def get_doc(self, ignore: int = None) -> Optional[List[List[str]]]:
if self.object is UNINITIALIZED_ATTR:
return []
else:
return super().get_doc(encoding, ignore) # type: ignore
return super().get_doc(ignore) # type: ignore
class DataDocumenter(GenericAliasMixin, NewTypeMixin, TypeVarMixin,
@ -1893,7 +1884,7 @@ class DataDocumenter(GenericAliasMixin, NewTypeMixin, TypeVarMixin,
objtype = 'data'
member_order = 40
priority = -10
option_spec = dict(ModuleLevelDocumenter.option_spec)
option_spec: OptionSpec = dict(ModuleLevelDocumenter.option_spec)
option_spec["annotation"] = annotation_option
option_spec["no-value"] = bool_option
@ -1977,13 +1968,13 @@ class DataDocumenter(GenericAliasMixin, NewTypeMixin, TypeVarMixin,
return None
def get_doc(self, encoding: str = None, ignore: int = None) -> List[List[str]]:
def get_doc(self, ignore: int = None) -> Optional[List[List[str]]]:
# Check the variable has a docstring-comment
comment = self.get_module_comment(self.objpath[-1])
if comment:
return [comment]
else:
return super().get_doc(encoding, ignore)
return super().get_doc(ignore)
def add_content(self, more_content: Optional[StringList], no_docstring: bool = False
) -> None:
@ -2204,13 +2195,13 @@ class NonDataDescriptorMixin(DataDocumenterMixinBase):
return (not getattr(self, 'non_data_descriptor', False) or
super().should_suppress_directive_header())
def get_doc(self, encoding: str = None, ignore: int = None) -> Optional[List[List[str]]]:
def get_doc(self, ignore: int = None) -> Optional[List[List[str]]]:
if getattr(self, 'non_data_descriptor', False):
# the docstring of non datadescriptor is very probably the wrong thing
# to display
return None
else:
return super().get_doc(encoding, ignore) # type: ignore
return super().get_doc(ignore) # type: ignore
class SlotsMixin(DataDocumenterMixinBase):
@ -2243,7 +2234,7 @@ class SlotsMixin(DataDocumenterMixinBase):
else:
return super().should_suppress_directive_header()
def get_doc(self, encoding: str = None, ignore: int = None) -> Optional[List[List[str]]]:
def get_doc(self, ignore: int = None) -> Optional[List[List[str]]]:
if self.object is SLOTSATTR:
try:
__slots__ = inspect.getslots(self.parent)
@ -2257,7 +2248,7 @@ class SlotsMixin(DataDocumenterMixinBase):
(self.parent.__qualname__, exc), type='autodoc')
return []
else:
return super().get_doc(encoding, ignore) # type: ignore
return super().get_doc(ignore) # type: ignore
class RuntimeInstanceAttributeMixin(DataDocumenterMixinBase):
@ -2361,11 +2352,11 @@ class UninitializedInstanceAttributeMixin(DataDocumenterMixinBase):
return (self.object is UNINITIALIZED_ATTR or
super().should_suppress_value_header())
def get_doc(self, encoding: str = None, ignore: int = None) -> Optional[List[List[str]]]:
def get_doc(self, ignore: int = None) -> Optional[List[List[str]]]:
if self.object is UNINITIALIZED_ATTR:
return None
else:
return super().get_doc(encoding, ignore) # type: ignore
return super().get_doc(ignore) # type: ignore
class AttributeDocumenter(GenericAliasMixin, NewTypeMixin, SlotsMixin, # type: ignore
@ -2377,7 +2368,7 @@ class AttributeDocumenter(GenericAliasMixin, NewTypeMixin, SlotsMixin, # type:
"""
objtype = 'attribute'
member_order = 60
option_spec = dict(ModuleLevelDocumenter.option_spec)
option_spec: OptionSpec = dict(ModuleLevelDocumenter.option_spec)
option_spec["annotation"] = annotation_option
option_spec["no-value"] = bool_option
@ -2512,7 +2503,7 @@ class AttributeDocumenter(GenericAliasMixin, NewTypeMixin, SlotsMixin, # type:
return None
def get_doc(self, encoding: str = None, ignore: int = None) -> Optional[List[List[str]]]:
def get_doc(self, ignore: int = None) -> Optional[List[List[str]]]:
# Check the attribute has a docstring-comment
comment = self.get_attribute_comment(self.parent, self.objpath[-1])
if comment:
@ -2524,7 +2515,7 @@ class AttributeDocumenter(GenericAliasMixin, NewTypeMixin, SlotsMixin, # type:
# ref: https://github.com/sphinx-doc/sphinx/issues/7805
orig = self.config.autodoc_inherit_docstrings
self.config.autodoc_inherit_docstrings = False # type: ignore
return super().get_doc(encoding, ignore)
return super().get_doc(ignore)
finally:
self.config.autodoc_inherit_docstrings = orig # type: ignore
@ -2545,7 +2536,6 @@ class PropertyDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter): #
Specialized Documenter subclass for properties.
"""
objtype = 'property'
directivetype = 'method'
member_order = 60
# before AttributeDocumenter
@ -2568,7 +2558,20 @@ class PropertyDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter): #
sourcename = self.get_sourcename()
if inspect.isabstractmethod(self.object):
self.add_line(' :abstractmethod:', sourcename)
self.add_line(' :property:', sourcename)
if safe_getattr(self.object, 'fget', None):
try:
signature = inspect.signature(self.object.fget,
type_aliases=self.config.autodoc_type_aliases)
if signature.return_annotation is not Parameter.empty:
objrepr = stringify_typehint(signature.return_annotation)
self.add_line(' :type: ' + objrepr, sourcename)
except TypeError as exc:
logger.warning(__("Failed to get a function signature for %s: %s"),
self.fullname, exc)
return None
except ValueError:
raise
class NewTypeAttributeDocumenter(AttributeDocumenter):
@ -2589,7 +2592,7 @@ class NewTypeAttributeDocumenter(AttributeDocumenter):
return not isinstance(parent, ModuleDocumenter) and inspect.isNewType(member)
def get_documenters(app: Sphinx) -> Dict[str, "Type[Documenter]"]:
def get_documenters(app: Sphinx) -> Dict[str, Type[Documenter]]:
"""Returns registered Documenter classes"""
warnings.warn("get_documenters() is deprecated.", RemovedInSphinx50Warning, stacklevel=2)
return app.registry.documenters
@ -2643,6 +2646,8 @@ def setup(app: Sphinx) -> Dict[str, Any]:
app.add_config_value('autodoc_mock_imports', [], True)
app.add_config_value('autodoc_typehints', "signature", True,
ENUM("signature", "description", "none"))
app.add_config_value('autodoc_typehints_description_target', 'all', True,
ENUM('all', 'documented'))
app.add_config_value('autodoc_type_aliases', {}, True)
app.add_config_value('autodoc_warningiserror', True, True)
app.add_config_value('autodoc_inherit_docstrings', True, True)
@ -2653,6 +2658,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
app.connect('config-inited', migrate_autodoc_member_order, priority=800)
app.setup_extension('sphinx.ext.autodoc.preserve_defaults')
app.setup_extension('sphinx.ext.autodoc.type_comment')
app.setup_extension('sphinx.ext.autodoc.typehints')

View File

@ -7,27 +7,22 @@
"""
import warnings
from typing import Any, Callable, Dict, List, Set
from typing import Any, Callable, Dict, List, Set, Type
from docutils import nodes
from docutils.nodes import Element, Node
from docutils.parsers.rst.states import RSTState, Struct
from docutils.parsers.rst.states import RSTState
from docutils.statemachine import StringList
from docutils.utils import Reporter, assemble_option_dict
from sphinx.config import Config
from sphinx.deprecation import RemovedInSphinx40Warning, RemovedInSphinx50Warning
from sphinx.deprecation import RemovedInSphinx50Warning, RemovedInSphinx60Warning
from sphinx.environment import BuildEnvironment
from sphinx.ext.autodoc import Documenter, Options
from sphinx.util import logging
from sphinx.util.docutils import SphinxDirective, switch_source_input
from sphinx.util.nodes import nested_parse_with_titles
if False:
# For type annotation
from typing import Type # for python3.5.1
logger = logging.getLogger(__name__)
@ -56,27 +51,27 @@ class DocumenterBridge:
"""A parameters container for Documenters."""
def __init__(self, env: BuildEnvironment, reporter: Reporter, options: Options,
lineno: int, state: Any = None) -> None:
lineno: int, state: Any) -> None:
self.env = env
self._reporter = reporter
self.genopt = options
self.lineno = lineno
self.filename_set = set() # type: Set[str]
self.record_dependencies: Set[str] = set()
self.result = StringList()
if state:
self.state = state
else:
# create fake object for self.state.document.settings.tab_width
warnings.warn('DocumenterBridge requires a state object on instantiation.',
RemovedInSphinx40Warning, stacklevel=2)
settings = Struct(tab_width=8)
document = Struct(settings=settings)
self.state = Struct(document=document)
self.state = state
def warn(self, msg: str) -> None:
warnings.warn('DocumenterBridge.warn is deprecated. Plase use sphinx.util.logging '
'module instead.',
RemovedInSphinx60Warning, stacklevel=2)
logger.warning(msg, location=(self.env.docname, self.lineno))
@property
def filename_set(self) -> Set:
warnings.warn('DocumenterBridge.filename_set is deprecated.',
RemovedInSphinx60Warning, stacklevel=2)
return self.record_dependencies
@property
def reporter(self) -> Reporter:
warnings.warn('DocumenterBridge.reporter is deprecated.',
@ -84,7 +79,7 @@ class DocumenterBridge:
return self._reporter
def process_documenter_options(documenter: "Type[Documenter]", config: Config, options: Dict
def process_documenter_options(documenter: Type[Documenter], config: Config, options: Dict
) -> Options:
"""Recognize options of Documenter from user input."""
for name in AUTODOC_DEFAULT_OPTIONS:
@ -115,7 +110,7 @@ def parse_generated_content(state: RSTState, content: StringList, documenter: Do
"""Parse a generated content by Documenter."""
with switch_source_input(state, content):
if documenter.titles_allowed:
node = nodes.section() # type: Element
node: Element = nodes.section()
# necessary so that the child nodes get the right source/line set
node.document = state.document
nested_parse_with_titles(state, content, node)
@ -172,7 +167,7 @@ class AutodocDirective(SphinxDirective):
# record all filenames as dependencies -- this will at least
# partially make automatic invalidation possible
for fn in params.filename_set:
for fn in params.record_dependencies:
self.state.document.settings.record_dependencies.add(fn)
result = parse_generated_content(self.state, params.result, documenter)

View File

@ -11,10 +11,9 @@
import importlib
import traceback
import warnings
from typing import Any, Callable, Dict, List, Mapping, NamedTuple, Optional, Tuple
from typing import Any, Callable, Dict, List, NamedTuple, Optional, Tuple
from sphinx.deprecation import (RemovedInSphinx40Warning, RemovedInSphinx50Warning,
deprecated_alias)
from sphinx.deprecation import RemovedInSphinx50Warning
from sphinx.ext.autodoc.mock import ismock, undecorate
from sphinx.pycode import ModuleAnalyzer, PycodeError
from sphinx.util import logging
@ -148,7 +147,7 @@ def get_module_members(module: Any) -> List[Tuple[str, Any]]:
warnings.warn('sphinx.ext.autodoc.importer.get_module_members() is deprecated.',
RemovedInSphinx50Warning)
members = {} # type: Dict[str, Tuple[str, Any]]
members: Dict[str, Tuple[str, Any]] = {}
for name in dir(module):
try:
value = safe_getattr(module, name, None)
@ -164,21 +163,10 @@ def get_module_members(module: Any) -> List[Tuple[str, Any]]:
return sorted(list(members.values()))
Attribute = NamedTuple('Attribute', [('name', str),
('directly_defined', bool),
('value', Any)])
def _getmro(obj: Any) -> Tuple["Type", ...]:
warnings.warn('sphinx.ext.autodoc.importer._getmro() is deprecated.',
RemovedInSphinx40Warning)
return getmro(obj)
def _getannotations(obj: Any) -> Mapping[str, Any]:
warnings.warn('sphinx.ext.autodoc.importer._getannotations() is deprecated.',
RemovedInSphinx40Warning)
return getannotations(obj)
class Attribute(NamedTuple):
name: str
directly_defined: bool
value: Any
def get_object_members(subject: Any, objpath: List[str], attrgetter: Callable,
@ -189,7 +177,7 @@ def get_object_members(subject: Any, objpath: List[str], attrgetter: Callable,
# the members directly defined in the class
obj_dict = attrgetter(subject, '__dict__', {})
members = {} # type: Dict[str, Attribute]
members: Dict[str, Attribute] = {}
# enum members
if isenumclass(subject):
@ -250,7 +238,7 @@ def get_class_members(subject: Any, objpath: List[str], attrgetter: Callable
# the members directly defined in the class
obj_dict = attrgetter(subject, '__dict__', {})
members = {} # type: Dict[str, ObjectMember]
members: Dict[str, ObjectMember] = {}
# enum members
if isenumclass(subject):
@ -327,24 +315,3 @@ def get_class_members(subject: Any, objpath: List[str], attrgetter: Callable
pass
return members
from sphinx.ext.autodoc.mock import (MockFinder, MockLoader, _MockModule, _MockObject, # NOQA
mock)
deprecated_alias('sphinx.ext.autodoc.importer',
{
'_MockModule': _MockModule,
'_MockObject': _MockObject,
'MockFinder': MockFinder,
'MockLoader': MockLoader,
'mock': mock,
},
RemovedInSphinx40Warning,
{
'_MockModule': 'sphinx.ext.autodoc.mock._MockModule',
'_MockObject': 'sphinx.ext.autodoc.mock._MockObject',
'MockFinder': 'sphinx.ext.autodoc.mock.MockFinder',
'MockLoader': 'sphinx.ext.autodoc.mock.MockLoader',
'mock': 'sphinx.ext.autodoc.mock.mock',
})

View File

@ -14,7 +14,7 @@ import sys
from importlib.abc import Loader, MetaPathFinder
from importlib.machinery import ModuleSpec
from types import ModuleType
from typing import Any, Generator, Iterator, List, Sequence, Tuple, Union
from typing import Any, Generator, Iterator, List, Optional, Sequence, Tuple, Union
from sphinx.util import logging
from sphinx.util.inspect import safe_getattr
@ -27,7 +27,7 @@ class _MockObject:
__display_name__ = '_MockObject'
__sphinx_mock__ = True
__sphinx_decorator_args__ = () # type: Tuple[Any, ...]
__sphinx_decorator_args__: Tuple[Any, ...] = ()
def __new__(cls, *args: Any, **kwargs: Any) -> Any:
if len(args) == 3 and isinstance(args[1], tuple):
@ -86,8 +86,8 @@ class _MockModule(ModuleType):
def __init__(self, name: str) -> None:
super().__init__(name)
self.__all__ = [] # type: List[str]
self.__path__ = [] # type: List[str]
self.__all__: List[str] = []
self.__path__: List[str] = []
def __getattr__(self, name: str) -> _MockObject:
return _make_subclass(name, self.__name__)()
@ -118,10 +118,10 @@ class MockFinder(MetaPathFinder):
super().__init__()
self.modnames = modnames
self.loader = MockLoader(self)
self.mocked_modules = [] # type: List[str]
self.mocked_modules: List[str] = []
def find_spec(self, fullname: str, path: Sequence[Union[bytes, str]],
target: ModuleType = None) -> ModuleSpec:
def find_spec(self, fullname: str, path: Optional[Sequence[Union[bytes, str]]],
target: ModuleType = None) -> Optional[ModuleSpec]:
for modname in self.modnames:
# check if fullname is (or is a descendant of) one of our targets
if modname == fullname or fullname.startswith(modname + '.'):

View File

@ -0,0 +1,88 @@
"""
sphinx.ext.autodoc.preserve_defaults
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Preserve the default argument values of function signatures in source code
and keep them not evaluated for readability.
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import ast
import inspect
from typing import Any, Dict
from sphinx.application import Sphinx
from sphinx.locale import __
from sphinx.pycode.ast import parse as ast_parse
from sphinx.pycode.ast import unparse as ast_unparse
from sphinx.util import logging
logger = logging.getLogger(__name__)
class DefaultValue:
def __init__(self, name: str) -> None:
self.name = name
def __repr__(self) -> str:
return self.name
def get_function_def(obj: Any) -> ast.FunctionDef:
"""Get FunctionDef object from living object.
This tries to parse original code for living object and returns
AST node for given *obj*.
"""
try:
source = inspect.getsource(obj)
if source.startswith((' ', r'\t')):
# subject is placed inside class or block. To read its docstring,
# this adds if-block before the declaration.
module = ast_parse('if True:\n' + source)
return module.body[0].body[0] # type: ignore
else:
module = ast_parse(source)
return module.body[0] # type: ignore
except (OSError, TypeError): # failed to load source code
return None
def update_defvalue(app: Sphinx, obj: Any, bound_method: bool) -> None:
"""Update defvalue info of *obj* using type_comments."""
if not app.config.autodoc_preserve_defaults:
return
try:
function = get_function_def(obj)
if function.args.defaults or function.args.kw_defaults:
sig = inspect.signature(obj)
defaults = list(function.args.defaults)
kw_defaults = list(function.args.kw_defaults)
parameters = list(sig.parameters.values())
for i, param in enumerate(parameters):
if param.default is not param.empty:
if param.kind in (param.POSITIONAL_ONLY, param.POSITIONAL_OR_KEYWORD):
value = DefaultValue(ast_unparse(defaults.pop(0))) # type: ignore
parameters[i] = param.replace(default=value)
else:
value = DefaultValue(ast_unparse(kw_defaults.pop(0))) # type: ignore
parameters[i] = param.replace(default=value)
sig = sig.replace(parameters=parameters)
obj.__signature__ = sig
except (AttributeError, TypeError):
# failed to update signature (ex. built-in or extension types)
pass
except NotImplementedError as exc: # failed to ast.unparse()
logger.warning(__("Failed to parse a default argument value for %r: %s"), obj, exc)
def setup(app: Sphinx) -> Dict[str, Any]:
app.add_config_value('autodoc_preserve_defaults', False, True)
app.connect('autodoc-before-process-signature', update_defvalue)
return {
'version': '1.0',
'parallel_read_safe': True
}

View File

@ -10,7 +10,7 @@
import re
from collections import OrderedDict
from typing import Any, Dict, Iterable, cast
from typing import Any, Dict, Iterable, Set, cast
from docutils import nodes
from docutils.nodes import Element
@ -42,8 +42,6 @@ def merge_typehints(app: Sphinx, domain: str, objtype: str, contentnode: Element
return
if app.config.autodoc_typehints != 'description':
return
if objtype == 'class' and app.config.autoclass_content not in ('init', 'both'):
return
try:
signature = cast(addnodes.desc_signature, contentnode.parent[0])
@ -63,7 +61,10 @@ def merge_typehints(app: Sphinx, domain: str, objtype: str, contentnode: Element
field_lists.append(field_list)
for field_list in field_lists:
modify_field_list(field_list, annotations[fullname])
if app.config.autodoc_typehints_description_target == "all":
modify_field_list(field_list, annotations[fullname])
else:
augment_descriptions_with_types(field_list, annotations[fullname])
def insert_field_list(node: Element) -> nodes.field_list:
@ -80,7 +81,7 @@ def insert_field_list(node: Element) -> nodes.field_list:
def modify_field_list(node: nodes.field_list, annotations: Dict[str, str]) -> None:
arguments = {} # type: Dict[str, Dict[str, bool]]
arguments: Dict[str, Dict[str, bool]] = {}
fields = cast(Iterable[nodes.field], node)
for field in fields:
field_name = field[0].astext()
@ -126,6 +127,52 @@ def modify_field_list(node: nodes.field_list, annotations: Dict[str, str]) -> No
node += field
def augment_descriptions_with_types(
node: nodes.field_list,
annotations: Dict[str, str],
) -> None:
fields = cast(Iterable[nodes.field], node)
has_description = set() # type: Set[str]
has_type = set() # type: Set[str]
for field in fields:
field_name = field[0].astext()
parts = re.split(' +', field_name)
if parts[0] == 'param':
if len(parts) == 2:
# :param xxx:
has_description.add(parts[1])
elif len(parts) > 2:
# :param xxx yyy:
name = ' '.join(parts[2:])
has_description.add(name)
has_type.add(name)
elif parts[0] == 'type':
name = ' '.join(parts[1:])
has_type.add(name)
elif parts[0] == 'return':
has_description.add('return')
elif parts[0] == 'rtype':
has_type.add('return')
# Add 'type' for parameters with a description but no declared type.
for name in annotations:
if name == 'return':
continue
if name in has_description and name not in has_type:
field = nodes.field()
field += nodes.field_name('', 'type ' + name)
field += nodes.field_body('', nodes.paragraph('', annotations[name]))
node += field
# Add 'rtype' if 'return' is present and 'rtype' isn't.
if 'return' in annotations:
if 'return' in has_description and 'return' not in has_type:
field = nodes.field()
field += nodes.field_name('', 'rtype')
field += nodes.field_body('', nodes.paragraph('', annotations['return']))
node += field
def setup(app: Sphinx) -> Dict[str, Any]:
app.connect('autodoc-process-signature', record_typehints)
app.connect('object-description-transform', merge_typehints)

View File

@ -60,19 +60,19 @@ import sys
import warnings
from os import path
from types import ModuleType
from typing import Any, Dict, List, Tuple, cast
from typing import Any, Dict, List, Optional, Tuple, Type, cast
from docutils import nodes
from docutils.nodes import Element, Node, system_message
from docutils.parsers.rst import directives
from docutils.parsers.rst.states import Inliner, RSTStateMachine, Struct, state_classes
from docutils.parsers.rst.states import RSTStateMachine, Struct, state_classes
from docutils.statemachine import StringList
import sphinx
from sphinx import addnodes
from sphinx.application import Sphinx
from sphinx.config import Config
from sphinx.deprecation import RemovedInSphinx40Warning, RemovedInSphinx50Warning
from sphinx.deprecation import RemovedInSphinx50Warning
from sphinx.environment import BuildEnvironment
from sphinx.environment.adapters.toctree import TocTree
from sphinx.ext.autodoc import INSTANCEATTR, Documenter
@ -85,13 +85,9 @@ from sphinx.util import logging, rst
from sphinx.util.docutils import (NullReporter, SphinxDirective, SphinxRole, new_document,
switch_source_input)
from sphinx.util.matching import Matcher
from sphinx.util.typing import OptionSpec
from sphinx.writers.html import HTMLTranslator
if False:
# For type annotation
from typing import Type # for python3.5.1
logger = logging.getLogger(__name__)
@ -169,7 +165,7 @@ def autosummary_table_visit_html(self: HTMLTranslator, node: autosummary_table)
# -- autodoc integration -------------------------------------------------------
# current application object (used in `get_documenter()`).
_app = None # type: Sphinx
_app: Sphinx = None
class FakeDirective(DocumenterBridge):
@ -182,7 +178,7 @@ class FakeDirective(DocumenterBridge):
super().__init__(env, None, Options(), 0, state)
def get_documenter(app: Sphinx, obj: Any, parent: Any) -> "Type[Documenter]":
def get_documenter(app: Sphinx, obj: Any, parent: Any) -> Type[Documenter]:
"""Get an autodoc.Documenter class suitable for documenting the given
object.
@ -230,7 +226,7 @@ class Autosummary(SphinxDirective):
optional_arguments = 0
final_argument_whitespace = False
has_content = True
option_spec = {
option_spec: OptionSpec = {
'caption': directives.unchanged_required,
'toctree': directives.unchanged,
'nosignatures': directives.flag,
@ -315,7 +311,7 @@ class Autosummary(SphinxDirective):
"""
prefixes = get_import_prefixes_from_env(self.env)
items = [] # type: List[Tuple[str, str, str, str]]
items: List[Tuple[str, str, str, str]] = []
max_item_chars = 50
@ -435,29 +431,6 @@ class Autosummary(SphinxDirective):
return [table_spec, table]
def warn(self, msg: str) -> None:
warnings.warn('Autosummary.warn() is deprecated',
RemovedInSphinx40Warning, stacklevel=2)
logger.warning(msg)
@property
def genopt(self) -> Options:
warnings.warn('Autosummary.genopt is deprecated',
RemovedInSphinx40Warning, stacklevel=2)
return self.bridge.genopt
@property
def warnings(self) -> List[Node]:
warnings.warn('Autosummary.warnings is deprecated',
RemovedInSphinx40Warning, stacklevel=2)
return []
@property
def result(self) -> StringList:
warnings.warn('Autosummary.result is deprecated',
RemovedInSphinx40Warning, stacklevel=2)
return self.bridge.result
def strip_arg_typehint(s: str) -> str:
"""Strip a type hint from argument definition."""
@ -488,8 +461,8 @@ def mangle_signature(sig: str, max_chars: int = 30) -> str:
s = re.sub(r'{[^}]*}', '', s)
# Parse the signature to arguments + options
args = [] # type: List[str]
opts = [] # type: List[str]
args: List[str] = []
opts: List[str] = []
opt_re = re.compile(r"^(.*, |)([a-zA-Z0-9_*]+)\s*=\s*")
while s:
@ -606,7 +579,7 @@ def get_import_prefixes_from_env(env: BuildEnvironment) -> List[str]:
Obtain current Python import prefixes (for `import_by_name`)
from ``document.env``
"""
prefixes = [None] # type: List[str]
prefixes: List[Optional[str]] = [None]
currmodule = env.ref_context.get('py:module')
if currmodule:
@ -700,33 +673,6 @@ def import_ivar_by_name(name: str, prefixes: List[str] = [None]) -> Tuple[str, A
# -- :autolink: (smart default role) -------------------------------------------
def autolink_role(typ: str, rawtext: str, etext: str, lineno: int, inliner: Inliner,
options: Dict = {}, content: List[str] = []
) -> Tuple[List[Node], List[system_message]]:
"""Smart linking role.
Expands to ':obj:`text`' if `text` is an object that can be imported;
otherwise expands to '*text*'.
"""
warnings.warn('autolink_role() is deprecated.', RemovedInSphinx40Warning, stacklevel=2)
env = inliner.document.settings.env
pyobj_role = env.get_domain('py').role('obj')
objects, msg = pyobj_role('obj', rawtext, etext, lineno, inliner, options, content)
if msg != []:
return objects, msg
assert len(objects) == 1
pending_xref = cast(addnodes.pending_xref, objects[0])
prefixes = get_import_prefixes_from_env(env)
try:
name, obj, parent, modname = import_by_name(pending_xref['reftarget'], prefixes)
except ImportError:
literal = cast(nodes.literal, pending_xref[0])
objects[0] = nodes.emphasis(rawtext, literal.astext(), classes=literal['classes'])
return objects, msg
class AutoLink(SphinxRole):
"""Smart linking role.
@ -761,7 +707,7 @@ def get_rst_suffix(app: Sphinx) -> str:
return ('restructuredtext',)
return parser_class.supported
suffix = None # type: str
suffix: str = None
for suffix in app.config.source_suffix:
if 'restructuredtext' in get_supported_format(suffix):
return suffix
@ -827,7 +773,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
app.connect('builder-inited', process_generate_options)
app.add_config_value('autosummary_context', {}, True)
app.add_config_value('autosummary_filename_map', {}, 'html')
app.add_config_value('autosummary_generate', [], True, [bool])
app.add_config_value('autosummary_generate', True, True, [bool])
app.add_config_value('autosummary_generate_overwrite', True, False)
app.add_config_value('autosummary_mock_imports',
lambda config: config.autodoc_mock_imports, 'env')

View File

@ -28,7 +28,7 @@ import sys
import warnings
from gettext import NullTranslations
from os import path
from typing import Any, Callable, Dict, List, NamedTuple, Set, Tuple, Union
from typing import Any, Dict, List, NamedTuple, Set, Tuple, Type, Union
from jinja2 import TemplateNotFound
from jinja2.sandbox import SandboxedEnvironment
@ -38,7 +38,7 @@ from sphinx import __display_version__, package_dir
from sphinx.application import Sphinx
from sphinx.builders import Builder
from sphinx.config import Config
from sphinx.deprecation import RemovedInSphinx40Warning, RemovedInSphinx50Warning
from sphinx.deprecation import RemovedInSphinx50Warning
from sphinx.ext.autodoc import Documenter
from sphinx.ext.autodoc.importer import import_module
from sphinx.ext.autosummary import get_documenter, import_by_name, import_ivar_by_name
@ -50,11 +50,6 @@ from sphinx.util.inspect import safe_getattr
from sphinx.util.osutil import ensuredir
from sphinx.util.template import SphinxTemplateLoader
if False:
# For type annotation
from typing import Type # for python3.5.1
logger = logging.getLogger(__name__)
@ -64,7 +59,7 @@ class DummyApplication:
def __init__(self, translator: NullTranslations) -> None:
self.config = Config()
self.registry = SphinxComponentRegistry()
self.messagelog = [] # type: List[str]
self.messagelog: List[str] = []
self.srcdir = "/"
self.translator = translator
self.verbosity = 0
@ -79,10 +74,11 @@ class DummyApplication:
pass
AutosummaryEntry = NamedTuple('AutosummaryEntry', [('name', str),
('path', str),
('template', str),
('recursive', bool)])
class AutosummaryEntry(NamedTuple):
name: str
path: str
template: str
recursive: bool
def setup_documenters(app: Any) -> None:
@ -91,11 +87,11 @@ def setup_documenters(app: Any) -> None:
FunctionDocumenter, MethodDocumenter, ModuleDocumenter,
NewTypeAttributeDocumenter, NewTypeDataDocumenter,
PropertyDocumenter)
documenters = [
documenters: List[Type[Documenter]] = [
ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter,
FunctionDocumenter, MethodDocumenter, NewTypeAttributeDocumenter,
NewTypeDataDocumenter, AttributeDocumenter, DecoratorDocumenter, PropertyDocumenter,
] # type: List[Type[Documenter]]
]
for documenter in documenters:
app.registry.add_documenter(documenter.objtype, documenter)
@ -245,8 +241,8 @@ def generate_autosummary_content(name: str, obj: Any, parent: Any,
def get_members(obj: Any, types: Set[str], include_public: List[str] = [],
imported: bool = True) -> Tuple[List[str], List[str]]:
items = [] # type: List[str]
public = [] # type: List[str]
items: List[str] = []
public: List[str] = []
for name in dir(obj):
try:
value = safe_getattr(obj, name)
@ -286,7 +282,7 @@ def generate_autosummary_content(name: str, obj: Any, parent: Any,
return public, attrs
def get_modules(obj: Any) -> Tuple[List[str], List[str]]:
items = [] # type: List[str]
items: List[str] = []
for _, modname, ispkg in pkgutil.iter_modules(obj.__path__):
fullname = name + '.' + modname
try:
@ -300,7 +296,7 @@ def generate_autosummary_content(name: str, obj: Any, parent: Any,
public = [x for x in items if not x.split('.')[-1].startswith('_')]
return public, items
ns = {} # type: Dict[str, Any]
ns: Dict[str, Any] = {}
ns.update(context)
if doc.objtype == 'module':
@ -352,25 +348,10 @@ def generate_autosummary_content(name: str, obj: Any, parent: Any,
def generate_autosummary_docs(sources: List[str], output_dir: str = None,
suffix: str = '.rst', warn: Callable = None,
info: Callable = None, base_path: str = None,
suffix: str = '.rst', base_path: str = None,
builder: Builder = None, template_dir: str = None,
imported_members: bool = False, app: Any = None,
overwrite: bool = True, encoding: str = 'utf-8') -> None:
if info:
warnings.warn('info argument for generate_autosummary_docs() is deprecated.',
RemovedInSphinx40Warning, stacklevel=2)
_info = info
else:
_info = logger.info
if warn:
warnings.warn('warn argument for generate_autosummary_docs() is deprecated.',
RemovedInSphinx40Warning, stacklevel=2)
_warn = warn
else:
_warn = logger.warning
if builder:
warnings.warn('builder argument for generate_autosummary_docs() is deprecated.',
RemovedInSphinx50Warning, stacklevel=2)
@ -382,11 +363,11 @@ def generate_autosummary_docs(sources: List[str], output_dir: str = None,
showed_sources = list(sorted(sources))
if len(showed_sources) > 20:
showed_sources = showed_sources[:10] + ['...'] + showed_sources[-10:]
_info(__('[autosummary] generating autosummary for: %s') %
', '.join(showed_sources))
logger.info(__('[autosummary] generating autosummary for: %s') %
', '.join(showed_sources))
if output_dir:
_info(__('[autosummary] writing to %s') % output_dir)
logger.info(__('[autosummary] writing to %s') % output_dir)
if base_path is not None:
sources = [os.path.join(base_path, filename) for filename in sources]
@ -423,10 +404,10 @@ def generate_autosummary_docs(sources: List[str], output_dir: str = None,
name, obj, parent, modname = import_ivar_by_name(entry.name)
qualname = name.replace(modname + ".", "")
except ImportError:
_warn(__('[autosummary] failed to import %r: %s') % (entry.name, e))
logger.warning(__('[autosummary] failed to import %r: %s') % (entry.name, e))
continue
context = {}
context: Dict[str, Any] = {}
if app:
context.update(app.config.autosummary_context)
@ -453,8 +434,8 @@ def generate_autosummary_docs(sources: List[str], output_dir: str = None,
# descend recursively to new files
if new_files:
generate_autosummary_docs(new_files, output_dir=output_dir,
suffix=suffix, warn=warn, info=info,
base_path=base_path,
suffix=suffix, base_path=base_path,
builder=builder, template_dir=template_dir,
imported_members=imported_members, app=app,
overwrite=overwrite)
@ -466,7 +447,7 @@ def find_autosummary_in_files(filenames: List[str]) -> List[AutosummaryEntry]:
See `find_autosummary_in_lines`.
"""
documented = [] # type: List[AutosummaryEntry]
documented: List[AutosummaryEntry] = []
for filename in filenames:
with open(filename, encoding='utf-8', errors='ignore') as f:
lines = f.read().splitlines()
@ -520,10 +501,10 @@ def find_autosummary_in_lines(lines: List[str], module: str = None, filename: st
toctree_arg_re = re.compile(r'^\s+:toctree:\s*(.*?)\s*$')
template_arg_re = re.compile(r'^\s+:template:\s*(.*?)\s*$')
documented = [] # type: List[AutosummaryEntry]
documented: List[AutosummaryEntry] = []
recursive = False
toctree = None # type: str
toctree: str = None
template = None
current_module = module
in_autosummary = False

View File

@ -53,19 +53,19 @@ class CoverageBuilder(Builder):
'results in %(outdir)s' + path.sep + 'python.txt.')
def init(self) -> None:
self.c_sourcefiles = [] # type: List[str]
self.c_sourcefiles: List[str] = []
for pattern in self.config.coverage_c_path:
pattern = path.join(self.srcdir, pattern)
self.c_sourcefiles.extend(glob.glob(pattern))
self.c_regexes = [] # type: List[Tuple[str, Pattern]]
self.c_regexes: List[Tuple[str, Pattern]] = []
for (name, exp) in self.config.coverage_c_regexes.items():
try:
self.c_regexes.append((name, re.compile(exp)))
except Exception:
logger.warning(__('invalid regex %r in coverage_c_regexes'), exp)
self.c_ignorexps = {} # type: Dict[str, List[Pattern]]
self.c_ignorexps: Dict[str, List[Pattern]] = {}
for (name, exps) in self.config.coverage_ignore_c_items.items():
self.c_ignorexps[name] = compile_regex_list('coverage_ignore_c_items',
exps)
@ -82,11 +82,11 @@ class CoverageBuilder(Builder):
return 'coverage overview'
def write(self, *ignored: Any) -> None:
self.py_undoc = {} # type: Dict[str, Dict[str, Any]]
self.py_undoc: Dict[str, Dict[str, Any]] = {}
self.build_py_coverage()
self.write_py_coverage()
self.c_undoc = {} # type: Dict[str, Set[Tuple[str, str]]]
self.c_undoc: Dict[str, Set[Tuple[str, str]]] = {}
self.build_c_coverage()
self.write_c_coverage()
@ -94,7 +94,7 @@ class CoverageBuilder(Builder):
# Fetch all the info from the header files
c_objects = self.env.domaindata['c']['objects']
for filename in self.c_sourcefiles:
undoc = set() # type: Set[Tuple[str, str]]
undoc: Set[Tuple[str, str]] = set()
with open(filename) as f:
for line in f:
for key, regex in self.c_regexes:
@ -161,7 +161,7 @@ class CoverageBuilder(Builder):
continue
funcs = []
classes = {} # type: Dict[str, List[str]]
classes: Dict[str, List[str]] = {}
for name, obj in inspect.getmembers(mod):
# diverse module attributes are ignored:
@ -200,7 +200,7 @@ class CoverageBuilder(Builder):
classes[name] = []
continue
attrs = [] # type: List[str]
attrs: List[str] = []
for attr_name in dir(obj):
if attr_name not in obj.__dict__:

View File

@ -13,10 +13,10 @@ import doctest
import re
import sys
import time
import warnings
from io import StringIO
from os import path
from typing import Any, Callable, Dict, Iterable, List, Sequence, Set, Tuple
from typing import (TYPE_CHECKING, Any, Callable, Dict, Iterable, List, Sequence, Set, Tuple,
Type)
from docutils import nodes
from docutils.nodes import Element, Node, TextElement
@ -26,17 +26,14 @@ from packaging.version import Version
import sphinx
from sphinx.builders import Builder
from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.locale import __
from sphinx.util import logging
from sphinx.util.console import bold # type: ignore
from sphinx.util.docutils import SphinxDirective
from sphinx.util.osutil import relpath
from sphinx.util.typing import OptionSpec
if False:
# For type annotation
from typing import Type # for python3.5.1
if TYPE_CHECKING:
from sphinx.application import Sphinx
@ -46,12 +43,6 @@ blankline_re = re.compile(r'^\s*<BLANKLINE>', re.MULTILINE)
doctestopt_re = re.compile(r'#\s*doctest:.+$', re.MULTILINE)
def doctest_encode(text: str, encoding: str) -> str:
warnings.warn('doctest_encode() is deprecated.',
RemovedInSphinx40Warning, stacklevel=2)
return text
def is_allowed_version(spec: str, version: str) -> bool:
"""Check `spec` satisfies `version` or not.
@ -96,7 +87,7 @@ class TestDirective(SphinxDirective):
if not test:
test = code
code = doctestopt_re.sub('', code)
nodetype = nodes.literal_block # type: Type[TextElement]
nodetype: Type[TextElement] = nodes.literal_block
if self.name in ('testsetup', 'testcleanup') or 'hide' in self.options:
nodetype = nodes.comment
if self.arguments:
@ -160,15 +151,19 @@ class TestDirective(SphinxDirective):
class TestsetupDirective(TestDirective):
option_spec = {'skipif': directives.unchanged_required} # type: Dict
option_spec: OptionSpec = {
'skipif': directives.unchanged_required
}
class TestcleanupDirective(TestDirective):
option_spec = {'skipif': directives.unchanged_required} # type: Dict
option_spec: OptionSpec = {
'skipif': directives.unchanged_required
}
class DoctestDirective(TestDirective):
option_spec = {
option_spec: OptionSpec = {
'hide': directives.flag,
'no-trim-doctest-flags': directives.flag,
'options': directives.unchanged,
@ -179,7 +174,7 @@ class DoctestDirective(TestDirective):
class TestcodeDirective(TestDirective):
option_spec = {
option_spec: OptionSpec = {
'hide': directives.flag,
'no-trim-doctest-flags': directives.flag,
'pyversion': directives.unchanged_required,
@ -189,7 +184,7 @@ class TestcodeDirective(TestDirective):
class TestoutputDirective(TestDirective):
option_spec = {
option_spec: OptionSpec = {
'hide': directives.flag,
'no-trim-doctest-flags': directives.flag,
'options': directives.unchanged,
@ -207,9 +202,9 @@ parser = doctest.DocTestParser()
class TestGroup:
def __init__(self, name: str) -> None:
self.name = name
self.setup = [] # type: List[TestCode]
self.tests = [] # type: List[List[TestCode]]
self.cleanup = [] # type: List[TestCode]
self.setup: List[TestCode] = []
self.tests: List[List[TestCode]] = []
self.cleanup: List[TestCode] = []
def add_code(self, code: "TestCode", prepend: bool = False) -> None:
if code.type == 'testsetup':
@ -397,7 +392,7 @@ Doctest summary
return False
else:
condition = node['skipif']
context = {} # type: Dict[str, Any]
context: Dict[str, Any] = {}
if self.config.doctest_global_setup:
exec(self.config.doctest_global_setup, context)
should_skip = eval(condition, context)
@ -406,7 +401,7 @@ Doctest summary
return should_skip
def test_doc(self, docname: str, doctree: Node) -> None:
groups = {} # type: Dict[str, TestGroup]
groups: Dict[str, TestGroup] = {}
add_to_all_groups = []
self.setup_runner = SphinxDocTestRunner(verbose=False,
optionflags=self.opt)
@ -487,7 +482,7 @@ Doctest summary
return compile(code, name, self.type, flags, dont_inherit)
def test_group(self, group: TestGroup) -> None:
ns = {} # type: Dict
ns: Dict = {}
def run_setup_cleanup(runner: Any, testcodes: List[TestCode], what: Any) -> bool:
examples = []

View File

@ -30,6 +30,7 @@ from sphinx.util.fileutil import copy_asset
from sphinx.util.i18n import search_image_for_language
from sphinx.util.nodes import set_source_info
from sphinx.util.osutil import ensuredir
from sphinx.util.typing import OptionSpec
from sphinx.writers.html import HTMLTranslator
from sphinx.writers.latex import LaTeXTranslator
from sphinx.writers.manpage import ManualPageTranslator
@ -49,10 +50,10 @@ class ClickableMapDefinition:
href_re = re.compile('href=".*?"')
def __init__(self, filename: str, content: str, dot: str = '') -> None:
self.id = None # type: str
self.id: str = None
self.filename = filename
self.content = content.splitlines()
self.clickable = [] # type: List[str]
self.clickable: List[str] = []
self.parse(dot=dot)
@ -113,7 +114,7 @@ class Graphviz(SphinxDirective):
required_arguments = 0
optional_arguments = 1
final_argument_whitespace = False
option_spec = {
option_spec: OptionSpec = {
'alt': directives.unchanged,
'align': align_spec,
'caption': directives.unchanged,
@ -181,7 +182,7 @@ class GraphvizSimple(SphinxDirective):
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = False
option_spec = {
option_spec: OptionSpec = {
'alt': directives.unchanged,
'align': align_spec,
'caption': directives.unchanged,

View File

@ -28,6 +28,7 @@ import sphinx
from sphinx.application import Sphinx
from sphinx.util.docutils import SphinxDirective
from sphinx.util.nodes import nested_parse_with_titles
from sphinx.util.typing import OptionSpec
class ifconfig(nodes.Element):
@ -40,7 +41,7 @@ class IfConfig(SphinxDirective):
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = True
option_spec = {} # type: Dict
option_spec: OptionSpec = {}
def run(self) -> List[Node]:
node = ifconfig()

View File

@ -37,10 +37,10 @@ class ImagemagickConverter(ImageConverter):
logger.debug('Invoking %r ...', args)
subprocess.run(args, stdout=PIPE, stderr=PIPE, check=True)
return True
except OSError:
except OSError as exc:
logger.warning(__('convert command %r cannot be run, '
'check the image_converter setting'),
self.config.image_converter)
'check the image_converter setting: %s'),
self.config.image_converter, exc)
return False
except CalledProcessError as exc:
logger.warning(__('convert exited with error:\n'

View File

@ -26,7 +26,6 @@ from sphinx import package_dir
from sphinx.application import Sphinx
from sphinx.builders import Builder
from sphinx.config import Config
from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
from sphinx.errors import SphinxError
from sphinx.locale import _, __
from sphinx.util import logging, sha1
@ -58,35 +57,8 @@ class InvokeError(SphinxError):
SUPPORT_FORMAT = ('png', 'svg')
DOC_HEAD = r'''
\documentclass[12pt]{article}
\usepackage[utf8x]{inputenc}
\usepackage{amsmath}
\usepackage{amsthm}
\usepackage{amssymb}
\usepackage{amsfonts}
\usepackage{anyfontsize}
\usepackage{bm}
\pagestyle{empty}
'''
DOC_BODY = r'''
\begin{document}
\fontsize{%d}{%d}\selectfont %s
\end{document}
'''
DOC_BODY_PREVIEW = r'''
\usepackage[active]{preview}
\begin{document}
\begin{preview}
\fontsize{%s}{%s}\selectfont %s
\end{preview}
\end{document}
'''
depth_re = re.compile(br'\[\d+ depth=(-?\d+)\]')
depthsvg_re = re.compile(br'.*, depth=(.*)pt')
depth_re = re.compile(r'\[\d+ depth=(-?\d+)\]')
depthsvg_re = re.compile(r'.*, depth=(.*)pt')
depthsvgcomment_re = re.compile(r'<!-- DEPTH=(-?\d+) -->')
@ -174,10 +146,10 @@ def compile_math(latex: str, builder: Builder) -> str:
raise MathExtError('latex exited with error', exc.stderr, exc.stdout) from exc
def convert_dvi_to_image(command: List[str], name: str) -> Tuple[bytes, bytes]:
def convert_dvi_to_image(command: List[str], name: str) -> Tuple[str, str]:
"""Convert DVI file to specific image format."""
try:
ret = subprocess.run(command, stdout=PIPE, stderr=PIPE, check=True)
ret = subprocess.run(command, stdout=PIPE, stderr=PIPE, check=True, encoding='ascii')
return ret.stdout, ret.stderr
except OSError as exc:
logger.warning(__('%s command %r cannot be run (needed for math '
@ -370,15 +342,6 @@ def html_visit_displaymath(self: HTMLTranslator, node: nodes.math_block) -> None
raise nodes.SkipNode
deprecated_alias('sphinx.ext.imgmath',
{
'DOC_BODY': DOC_BODY,
'DOC_BODY_PREVIEW': DOC_BODY_PREVIEW,
'DOC_HEAD': DOC_HEAD,
},
RemovedInSphinx40Warning)
def setup(app: Sphinx) -> Dict[str, Any]:
app.add_html_math_renderer('imgmath',
(html_visit_math, None),

View File

@ -53,6 +53,7 @@ from sphinx.ext.graphviz import (figure_wrapper, graphviz, render_dot_html, rend
render_dot_texinfo)
from sphinx.util import md5
from sphinx.util.docutils import SphinxDirective
from sphinx.util.typing import OptionSpec
from sphinx.writers.html import HTMLTranslator
from sphinx.writers.latex import LaTeXTranslator
from sphinx.writers.texinfo import TexinfoTranslator
@ -154,7 +155,7 @@ class InheritanceGraph:
def _import_classes(self, class_names: List[str], currmodule: str) -> List[Any]:
"""Import a list of classes."""
classes = [] # type: List[Any]
classes: List[Any] = []
for name in class_names:
classes.extend(import_classes(name, currmodule))
return classes
@ -198,7 +199,7 @@ class InheritanceGraph:
except Exception: # might raise AttributeError for strange classes
pass
baselist = [] # type: List[str]
baselist: List[str] = []
all_classes[cls] = (nodename, fullname, baselist, tooltip)
if fullname in top_classes:
@ -292,7 +293,7 @@ class InheritanceGraph:
n_attrs.update(env.config.inheritance_node_attrs)
e_attrs.update(env.config.inheritance_edge_attrs)
res = [] # type: List[str]
res: List[str] = []
res.append('digraph %s {\n' % name)
res.append(self._format_graph_attrs(g_attrs))
@ -331,7 +332,7 @@ class InheritanceDiagram(SphinxDirective):
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = True
option_spec = {
option_spec: OptionSpec = {
'parts': int,
'private-bases': directives.flag,
'caption': directives.unchanged,

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