summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/main.yml5
-rw-r--r--CHANGES51
-rw-r--r--EXAMPLES22
-rw-r--r--doc/extdev/deprecated.rst29
-rw-r--r--doc/extdev/nodes.rst1
-rw-r--r--doc/usage/advanced/intl.rst4
-rw-r--r--doc/usage/configuration.rst21
-rw-r--r--doc/usage/extensions/autodoc.rst2
-rw-r--r--doc/usage/extensions/math.rst12
-rw-r--r--doc/usage/installation.rst4
-rw-r--r--doc/usage/restructuredtext/basics.rst2
-rw-r--r--doc/usage/restructuredtext/domains.rst42
-rw-r--r--doc/usage/theming.rst39
-rw-r--r--setup.cfg3
-rw-r--r--setup.py11
-rw-r--r--sphinx/__init__.py14
-rw-r--r--sphinx/addnodes.py23
-rw-r--r--sphinx/application.py47
-rw-r--r--sphinx/builders/__init__.py7
-rw-r--r--sphinx/builders/_epub_base.py129
-rw-r--r--sphinx/builders/applehelp.py49
-rw-r--r--sphinx/builders/devhelp.py41
-rw-r--r--sphinx/builders/epub3.py33
-rw-r--r--sphinx/builders/gettext.py27
-rw-r--r--sphinx/builders/html/__init__.py41
-rw-r--r--sphinx/builders/htmlhelp.py49
-rw-r--r--sphinx/builders/latex/__init__.py45
-rw-r--r--sphinx/builders/manpage.py2
-rw-r--r--sphinx/builders/qthelp.py43
-rw-r--r--sphinx/builders/xml.py6
-rw-r--r--sphinx/cmd/make_mode.py1
-rw-r--r--sphinx/cmd/quickstart.py42
-rw-r--r--sphinx/config.py41
-rw-r--r--sphinx/deprecation.py14
-rw-r--r--sphinx/directives/__init__.py61
-rw-r--r--sphinx/directives/code.py17
-rw-r--r--sphinx/directives/other.py27
-rw-r--r--sphinx/directives/patches.py25
-rw-r--r--sphinx/domains/__init__.py23
-rw-r--r--sphinx/domains/changeset.py18
-rw-r--r--sphinx/domains/citation.py5
-rw-r--r--sphinx/domains/index.py4
-rw-r--r--sphinx/domains/math.py24
-rw-r--r--sphinx/domains/python.py188
-rw-r--r--sphinx/domains/std.py48
-rw-r--r--sphinx/environment/__init__.py52
-rw-r--r--sphinx/environment/adapters/toctree.py5
-rw-r--r--sphinx/environment/collectors/__init__.py4
-rw-r--r--sphinx/environment/collectors/indexentries.py64
-rw-r--r--sphinx/environment/collectors/toctree.py6
-rw-r--r--sphinx/events.py32
-rw-r--r--sphinx/ext/apidoc.py71
-rw-r--r--sphinx/ext/autodoc/__init__.py57
-rw-r--r--sphinx/ext/autodoc/directive.py23
-rw-r--r--sphinx/ext/autodoc/importer.py30
-rw-r--r--sphinx/ext/autodoc/mock.py6
-rw-r--r--sphinx/ext/autosummary/__init__.py60
-rw-r--r--sphinx/ext/autosummary/generate.py46
-rw-r--r--sphinx/ext/doctest.py15
-rw-r--r--sphinx/ext/imgmath.py45
-rw-r--r--sphinx/ext/jsmath.py36
-rw-r--r--sphinx/ext/mathjax.py4
-rw-r--r--sphinx/ext/napoleon/docstring.py6
-rw-r--r--sphinx/ext/todo.py106
-rw-r--r--sphinx/extension.py4
-rw-r--r--sphinx/io.py34
-rw-r--r--sphinx/jinja2glue.py4
-rw-r--r--sphinx/parsers.py9
-rw-r--r--sphinx/project.py6
-rw-r--r--sphinx/pycode/__init__.py27
-rw-r--r--sphinx/pycode/ast.py34
-rw-r--r--sphinx/pycode/parser.py17
-rw-r--r--sphinx/registry.py7
-rw-r--r--sphinx/roles.py261
-rw-r--r--sphinx/search/__init__.py15
-rw-r--r--sphinx/setup_command.py6
-rw-r--r--sphinx/templates/quickstart/Makefile_t1
-rw-r--r--sphinx/templates/quickstart/make.bat_t1
-rw-r--r--sphinx/testing/fixtures.py7
-rw-r--r--sphinx/testing/util.py11
-rw-r--r--sphinx/theming.py4
-rw-r--r--sphinx/transforms/__init__.py23
-rw-r--r--sphinx/transforms/i18n.py16
-rw-r--r--sphinx/transforms/post_transforms/code.py7
-rw-r--r--sphinx/transforms/references.py4
-rw-r--r--sphinx/util/__init__.py202
-rw-r--r--sphinx/util/cfamily.py23
-rw-r--r--sphinx/util/compat.py26
-rw-r--r--sphinx/util/docfields.py31
-rw-r--r--sphinx/util/docutils.py41
-rw-r--r--sphinx/util/fileutil.py4
-rw-r--r--sphinx/util/i18n.py92
-rw-r--r--sphinx/util/images.py8
-rw-r--r--sphinx/util/inspect.py173
-rw-r--r--sphinx/util/inventory.py4
-rw-r--r--sphinx/util/jsonimpl.py46
-rw-r--r--sphinx/util/logging.py7
-rw-r--r--sphinx/util/nodes.py40
-rw-r--r--sphinx/util/osutil.py29
-rw-r--r--sphinx/util/pycompat.py65
-rw-r--r--sphinx/util/smartypants.py6
-rw-r--r--sphinx/util/texescape.py10
-rw-r--r--sphinx/util/typing.py127
-rw-r--r--sphinx/versioning.py4
-rw-r--r--sphinx/writers/html.py18
-rw-r--r--sphinx/writers/html5.py48
-rw-r--r--sphinx/writers/latex.py126
-rw-r--r--sphinx/writers/manpage.py11
-rw-r--r--sphinx/writers/texinfo.py5
-rw-r--r--sphinx/writers/text.py5
-rw-r--r--tests/roots/test-intl/xx/LC_MESSAGES/figure.po22
-rw-r--r--tests/test_build_epub.py4
-rw-r--r--tests/test_build_html.py26
-rw-r--r--tests/test_build_latex.py7
-rw-r--r--tests/test_build_manpage.py12
-rw-r--r--tests/test_domain_cpp.py5
-rw-r--r--tests/test_domain_py.py63
-rw-r--r--tests/test_domain_std.py3
-rw-r--r--tests/test_environment.py2
-rw-r--r--tests/test_ext_autodoc.py37
-rw-r--r--tests/test_ext_autodoc_autofunction.py27
-rw-r--r--tests/test_ext_autosectionlabel.py8
-rw-r--r--tests/test_ext_math.py4
-rw-r--r--tests/test_ext_todo.py4
-rw-r--r--tests/test_intl.py12
-rw-r--r--tests/test_pycode_parser.py3
-rw-r--r--tests/test_search.py10
-rw-r--r--tests/test_smartquotes.py8
-rw-r--r--tests/test_util_inspect.py5
-rw-r--r--tests/test_util_pycompat.py38
-rw-r--r--tox.ini6
131 files changed, 830 insertions, 3135 deletions
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 06309f991..9e4dd2ade 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -8,12 +8,9 @@ jobs:
strategy:
fail-fast: false
matrix:
- name: [py35, py36, py37, py38, py39]
+ name: [py36, py37, py38, py39]
os: [ubuntu-16.04]
include:
- - name: py35
- python: 3.5
- docutils: du12
- name: py36
python: 3.6
docutils: du13
diff --git a/CHANGES b/CHANGES
index 2b2eb21df..b674c91dc 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,54 @@
+Release 4.0.0 (in development)
+==============================
+
+Dependencies
+------------
+
+* Drop python 3.5 support
+* Drop docutils 0.12 and 0.13 support
+
+Incompatible changes
+--------------------
+
+* #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
+* #7996: manpage: Make a section directory on build manpage by default (see
+ :confval:`man_make_section_directory`)
+
+Deprecated
+----------
+
+* ``sphinx.directives.patches.CSVTable``
+* ``sphinx.directives.patches.ListTable``
+* ``sphinx.directives.patches.RSTTable``
+* ``sphinx.util.pycompat.convert_with_2to3()``
+* ``sphinx.util.pycompat.execfile_()``
+* ``sphinx.util.smartypants``
+
+Features added
+--------------
+
+* #4826: py domain: Add ``:canonical:`` option to python directives to describe
+ the location where the object is defined
+* #7784: i18n: The alt text for image is translated by default (without
+ :confval:`gettext_additional_targets` setting)
+* #8070: html search: Support searching for 2characters word
+* #7830: Add debug logs for change detection of sources and templates
+* #8201: Emit a warning if toctree contains duplicated entries
+
+Bugs fixed
+----------
+
+* #8342: Emit a warning if a unknown domain is given for directive or role (ex.
+ ``:unknown:doc:``)
+
+Testing
+--------
+
Release 3.4.0 (in development)
==============================
diff --git a/EXAMPLES b/EXAMPLES
index 74fa36510..e5162d66f 100644
--- a/EXAMPLES
+++ b/EXAMPLES
@@ -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
@@ -318,6 +324,7 @@ Documentation using a custom theme or integrated in a website
* `Django <https://docs.djangoproject.com/>`__
* `Doctrine <https://www.doctrine-project.org/>`__
* `Enterprise Toolkit for Acrobat products <https://www.adobe.com/devnet-docs/acrobatetk/>`__
+* `fmt <https://fmt.dev/>`__
* `Gameduino <http://excamera.com/sphinx/gameduino/>`__
* `gensim <https://radimrehurek.com/gensim/>`__
* `GeoServer <http://docs.geoserver.org/>`__
@@ -327,6 +334,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/>`__
diff --git a/doc/extdev/deprecated.rst b/doc/extdev/deprecated.rst
index 797648a1a..a206bc09b 100644
--- a/doc/extdev/deprecated.rst
+++ b/doc/extdev/deprecated.rst
@@ -26,6 +26,35 @@ The following is a list of deprecated interfaces.
- (will be) Removed
- Alternatives
+ * - ``sphinx.directives.patches.CSVTable``
+ - 4.0
+ - 6.0
+ - ``docutils.parsers.rst.diretives.tables.CSVTable``
+
+ * - ``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.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``
* - The ``follow_wrapped`` argument of ``sphinx.util.inspect.signature()``
- 3.4
diff --git a/doc/extdev/nodes.rst b/doc/extdev/nodes.rst
index 5d8272eae..e38393a78 100644
--- a/doc/extdev/nodes.rst
+++ b/doc/extdev/nodes.rst
@@ -38,7 +38,6 @@ New inline nodes
.. autoclass:: index
.. autoclass:: pending_xref
.. autoclass:: literal_emphasis
-.. autoclass:: abbreviation
.. autoclass:: download_reference
Special nodes
diff --git a/doc/usage/advanced/intl.rst b/doc/usage/advanced/intl.rst
index 67d5e10e5..03b77fb6b 100644
--- a/doc/usage/advanced/intl.rst
+++ b/doc/usage/advanced/intl.rst
@@ -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
diff --git a/doc/usage/configuration.rst b/doc/usage/configuration.rst
index 4881b8629..13dd6623c 100644
--- a/doc/usage/configuration.rst
+++ b/doc/usage/configuration.rst
@@ -473,11 +473,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
@@ -813,13 +812,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
@@ -1424,8 +1426,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
@@ -2252,10 +2253,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:
diff --git a/doc/usage/extensions/autodoc.rst b/doc/usage/extensions/autodoc.rst
index 2d8a216c0..86df8a79f 100644
--- a/doc/usage/extensions/autodoc.rst
+++ b/doc/usage/extensions/autodoc.rst
@@ -564,7 +564,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``.
diff --git a/doc/usage/extensions/math.rst b/doc/usage/extensions/math.rst
index 780e57ee2..655364767 100644
--- a/doc/usage/extensions/math.rst
+++ b/doc/usage/extensions/math.rst
@@ -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.
diff --git a/doc/usage/installation.rst b/doc/usage/installation.rst
index 46ef6a51e..0ea54b220 100644
--- a/doc/usage/installation.rst
+++ b/doc/usage/installation.rst
@@ -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.
@@ -183,7 +183,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::
diff --git a/doc/usage/restructuredtext/basics.rst b/doc/usage/restructuredtext/basics.rst
index 8f596ed9a..03b690f44 100644
--- a/doc/usage/restructuredtext/basics.rst
+++ b/doc/usage/restructuredtext/basics.rst
@@ -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```.
diff --git a/doc/usage/restructuredtext/domains.rst b/doc/usage/restructuredtext/domains.rst
index f3754ab7c..b659ba8f3 100644
--- a/doc/usage/restructuredtext/domains.rst
+++ b/doc/usage/restructuredtext/domains.rst
@@ -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,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:method:: name(parameters)
Describes an object method. The parameters should not include the ``self``
@@ -307,6 +339,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
@@ -1823,7 +1863,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/
diff --git a/doc/usage/theming.rst b/doc/usage/theming.rst
index fb06e8741..fc135a095 100644
--- a/doc/usage/theming.rst
+++ b/doc/usage/theming.rst
@@ -334,38 +334,15 @@ These themes are:
Third Party Themes
~~~~~~~~~~~~~~~~~~
-.. cssclass:: longtable
-
-+--------------------+--------------------+
-| **Theme overview** | |
-+--------------------+--------------------+
-| |sphinx_rtd_theme| | |
-| | |
-| *sphinx_rtd_theme* | |
-+--------------------+--------------------+
-
-.. |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.
+There are many third-party themes available for Sphinx. Some of these are for
+general use, while others are specific to an individual project.
+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__.
+.. __: 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&type=
+.. __: https://github.com/search?utf8=%E2%9C%93&q=sphinx+theme
.. __: https://gitlab.com/explore?name=sphinx+theme
-.. __: https://sphinx-themes.org/
diff --git a/setup.cfg b/setup.cfg
index abda98124..2730c6610 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -40,7 +40,7 @@ paths =
.
[mypy]
-python_version = 3.5
+python_version = 3.6
disallow_incomplete_defs = True
show_column_numbers = True
show_error_context = True
@@ -55,7 +55,6 @@ filterwarnings =
all
ignore::DeprecationWarning:docutils.io
ignore::DeprecationWarning:pyximport.pyximport
- ignore::PendingDeprecationWarning:sphinx.util.pycompat
markers =
apidoc
setup_command
diff --git a/setup.py b/setup.py
index 27bbb3ae0..dcd79630d 100644
--- a/setup.py
+++ b/setup.py
@@ -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',
@@ -51,7 +51,7 @@ extras_require = {
'pytest',
'pytest-cov',
'html5lib',
- 'typed_ast', # for py35-37
+ 'typed_ast', # for py36-37
'cython',
],
}
@@ -199,7 +199,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',
@@ -241,7 +240,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,
diff --git a/sphinx/__init__.py b/sphinx/__init__.py
index 2b5d853e5..574ede346 100644
--- a/sphinx/__init__.py
+++ b/sphinx/__init__.py
@@ -19,10 +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=
@@ -32,8 +28,8 @@ if 'PYTHONWARNINGS' not in os.environ:
warnings.filterwarnings('ignore', "'U' mode is deprecated",
DeprecationWarning, module='docutils.io')
-__version__ = '3.4.0+'
-__released__ = '3.4.0' # 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 +39,7 @@ __released__ = '3.4.0' # used when Sphinx builds its own docs
#:
#: .. versionadded:: 1.2
#: Before version 1.2, check the string ``sphinx.__version__``.
-version_info = (3, 4, 0, 'beta', 0)
+version_info = (4, 0, 0, 'beta', 0)
package_dir = path.abspath(path.dirname(__file__))
@@ -57,8 +53,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
diff --git a/sphinx/addnodes.py b/sphinx/addnodes.py
index 33503bb08..5b3dc08c5 100644
--- a/sphinx/addnodes.py
+++ b/sphinx/addnodes.py
@@ -8,16 +8,13 @@
:license: BSD, see LICENSE for details.
"""
-import warnings
from typing import Any, Dict, List, Sequence
+from typing import TYPE_CHECKING
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
@@ -362,20 +359,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."""
diff --git a/sphinx/application.py b/sphinx/application.py
index 59ac04b20..e432db245 100644
--- a/sphinx/application.py
+++ b/sphinx/application.py
@@ -14,11 +14,11 @@ 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 Any, Callable, Dict, IO, List, Optional, Tuple, Union
+from typing import Any, Callable, Dict, IO, List, Optional, Tuple, Type, Union
+from typing import TYPE_CHECKING
from docutils import nodes
from docutils.nodes import Element, TextElement
@@ -30,14 +30,13 @@ from pygments.lexer import Lexer
import sphinx
from sphinx import package_dir, locale
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
@@ -54,10 +53,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 docutils.nodes import Node # NOQA
- from typing import Type # for python3.5.1
+if TYPE_CHECKING:
from sphinx.builders import Builder
@@ -916,13 +912,6 @@ class Sphinx:
"""
self.registry.add_post_transform(transform)
- def add_javascript(self, filename: str, **kwargs: str) -> None:
- """An alias of :meth:`add_js_file`."""
- warnings.warn('The app.add_javascript() is deprecated. '
- 'Please use app.add_js_file() instead.',
- RemovedInSphinx40Warning, stacklevel=2)
- self.add_js_file(filename, **kwargs)
-
def add_js_file(self, filename: str, **kwargs: str) -> None:
"""Register a JavaScript file to include in the HTML output.
@@ -993,24 +982,6 @@ class Sphinx:
if hasattr(self.builder, 'add_css_file'):
self.builder.add_css_file(filename, **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, str]
- 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.
@@ -1034,7 +1005,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*.
@@ -1045,13 +1016,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.
diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py
index 93c246c69..f7da7d762 100644
--- a/sphinx/builders/__init__.py
+++ b/sphinx/builders/__init__.py
@@ -11,7 +11,8 @@
import pickle
import time
from os import path
-from typing import Any, Dict, Iterable, List, Sequence, Set, Tuple, Union
+from typing import Any, Dict, Iterable, List, Sequence, Set, Tuple, Type, Union
+from typing import TYPE_CHECKING
from docutils import nodes
from docutils.nodes import Node
@@ -42,9 +43,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
diff --git a/sphinx/builders/_epub_base.py b/sphinx/builders/_epub_base.py
index b126182e9..6a2b312d9 100644
--- a/sphinx/builders/_epub_base.py
+++ b/sphinx/builders/_epub_base.py
@@ -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
from sphinx.util import status_iterator
@@ -85,10 +82,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:
@@ -169,18 +186,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
@@ -462,30 +467,17 @@ 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
@@ -506,23 +498,17 @@ 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)
+ if not self.outdir.endswith(os.sep):
+ self.outdir += os.sep
+ olen = len(self.outdir)
self.files = [] # type: List[str]
self.ignored_files = ['.buildinfo', 'mimetype', 'content.opf',
'toc.ncx', 'META-INF/container.xml',
@@ -531,7 +517,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:]
@@ -621,9 +607,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."""
@@ -641,7 +625,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
the parent node is reinserted in the subnav.
"""
navstack = [] # type: List[NavPoint]
- navstack.append(NavPoint('dummy', '', '', '', []))
+ navstack.append(NavPoint('dummy', 0, '', '', []))
level = 0
lastnode = None
for node in nodes:
@@ -689,15 +673,9 @@ class EpubBuilder(StandaloneHTMLBuilder):
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,
@@ -712,28 +690,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)
diff --git a/sphinx/builders/applehelp.py b/sphinx/builders/applehelp.py
deleted file mode 100644
index 82a74f4b6..000000000
--- a/sphinx/builders/applehelp.py
+++ /dev/null
@@ -1,49 +0,0 @@
-"""
- sphinx.builders.applehelp
- ~~~~~~~~~~~~~~~~~~~~~~~~~
-
- Build Apple help books.
-
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
- :license: BSD, see LICENSE for details.
-"""
-
-import warnings
-from typing import Any, Dict
-
-from sphinxcontrib.applehelp import (
- AppleHelpCodeSigningFailed,
- AppleHelpIndexerFailed,
- AppleHelpBuilder,
-)
-
-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,
- }
diff --git a/sphinx/builders/devhelp.py b/sphinx/builders/devhelp.py
deleted file mode 100644
index 3e402690a..000000000
--- a/sphinx/builders/devhelp.py
+++ /dev/null
@@ -1,41 +0,0 @@
-"""
- sphinx.builders.devhelp
- ~~~~~~~~~~~~~~~~~~~~~~~
-
- Build HTML documentation and Devhelp_ support files.
-
- .. _Devhelp: https://wiki.gnome.org/Apps/Devhelp
-
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
- :license: BSD, see LICENSE for details.
-"""
-
-import warnings
-from typing import Any, Dict
-
-from sphinxcontrib.devhelp import DevhelpBuilder
-
-from sphinx.application import Sphinx
-from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
-
-
-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,
- }
diff --git a/sphinx/builders/epub3.py b/sphinx/builders/epub3.py
index cf795f3ba..1bef3c5bb 100644
--- a/sphinx/builders/epub3.py
+++ b/sphinx/builders/epub3.py
@@ -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 Config, ENUM
-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.
@@ -162,15 +160,9 @@ class Epub3Builder(_epub_base.EpubBuilder):
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(
@@ -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:
diff --git a/sphinx/builders/gettext.py b/sphinx/builders/gettext.py
index f8a19c57a..04e7382ca 100644
--- a/sphinx/builders/gettext.py
+++ b/sphinx/builders/gettext.py
@@ -13,7 +13,7 @@ from collections import defaultdict, OrderedDict
from datetime import datetime, tzinfo, timedelta
from os import path, walk, getenv
from time import time
-from typing import Any, Dict, Iterable, Generator, List, Set, Tuple, Union
+from typing import Any, DefaultDict, Dict, Iterable, Generator, List, Set, Tuple, Union
from uuid import uuid4
from docutils import nodes
@@ -34,33 +34,8 @@ from sphinx.util.osutil import ensuredir, canon_path, relpath
from sphinx.util.tags import Tags
from sphinx.util.template import SphinxRenderer
-if False:
- # For type annotation
- from typing import 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."""
diff --git a/sphinx/builders/html/__init__.py b/sphinx/builders/html/__init__.py
index beb650991..0e132e4b4 100644
--- a/sphinx/builders/html/__init__.py
+++ b/sphinx/builders/html/__init__.py
@@ -12,9 +12,9 @@ import html
import posixpath
import re
import sys
-import warnings
+from datetime import datetime
from os import path
-from typing import Any, Dict, IO, Iterable, Iterator, List, Set, Tuple
+from typing import Any, Dict, IO, Iterable, Iterator, List, Set, Tuple, Type
from urllib.parse import quote
from docutils import nodes
@@ -28,7 +28,6 @@ from sphinx import package_dir, __display_version__
from sphinx.application import Sphinx
from sphinx.builders import Builder
from sphinx.config import Config, ENUM
-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
@@ -48,10 +47,6 @@ from sphinx.util.osutil import os_path, relative_uri, ensuredir, movefile, copyf
from sphinx.util.tags import Tags
from sphinx.writers.html import HTMLWriter, HTMLTranslator
-if False:
- # For type annotation
- from typing import Type # for python3.5.1
-
# HTML5 Writer is available or not
if is_html5_writer_available():
@@ -351,6 +346,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:
@@ -365,6 +361,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)
@@ -376,6 +373,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
@@ -877,21 +882,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:
@@ -1189,7 +1184,7 @@ def validate_html_favicon(app: Sphinx, config: Config) -> None:
config.html_favicon = None # type: ignore
-# for compatibility
+# for compatibility; RemovedInSphinx40Warning
import sphinx.builders.dirhtml # NOQA
import sphinx.builders.singlehtml # NOQA
import sphinxcontrib.serializinghtml # NOQA
diff --git a/sphinx/builders/htmlhelp.py b/sphinx/builders/htmlhelp.py
deleted file mode 100644
index d30f9805a..000000000
--- a/sphinx/builders/htmlhelp.py
+++ /dev/null
@@ -1,49 +0,0 @@
-"""
- sphinx.builders.htmlhelp
- ~~~~~~~~~~~~~~~~~~~~~~~~
-
- Build HTML help support files.
- Parts adapted from Python's Doc/tools/prechm.py.
-
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
- :license: BSD, see LICENSE for details.
-"""
-
-import warnings
-from typing import Any, Dict
-
-from sphinxcontrib.htmlhelp import (
- chm_locales, chm_htmlescape, HTMLHelpBuilder, default_htmlhelp_basename
-)
-
-from sphinx.application import Sphinx
-from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
-
-
-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,
- }
diff --git a/sphinx/builders/latex/__init__.py b/sphinx/builders/latex/__init__.py
index 8fd1c077d..6a876862d 100644
--- a/sphinx/builders/latex/__init__.py
+++ b/sphinx/builders/latex/__init__.py
@@ -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 Config, ENUM
-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 _, __
@@ -262,7 +262,6 @@ class LaTeXBuilder(Builder):
defaults=self.env.settings,
components=(docwriter,),
read_config_files=True).get_default_values() # type: Any
- patch_settings(docsettings)
self.init_document_data()
self.write_stylesheet()
@@ -365,10 +364,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()
@@ -463,44 +458,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:
diff --git a/sphinx/builders/manpage.py b/sphinx/builders/manpage.py
index 2a10ba62f..442abb915 100644
--- a/sphinx/builders/manpage.py
+++ b/sphinx/builders/manpage.py
@@ -120,7 +120,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',
diff --git a/sphinx/builders/qthelp.py b/sphinx/builders/qthelp.py
deleted file mode 100644
index cec4d338e..000000000
--- a/sphinx/builders/qthelp.py
+++ /dev/null
@@ -1,43 +0,0 @@
-"""
- sphinx.builders.qthelp
- ~~~~~~~~~~~~~~~~~~~~~~
-
- Build input files for the Qt collection generator.
-
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
- :license: BSD, see LICENSE for details.
-"""
-
-import warnings
-from typing import Any, Dict
-
-from sphinxcontrib.qthelp import QtHelpBuilder, render_file
-
-import sphinx
-from sphinx.application import Sphinx
-from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
-
-
-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,
- }
diff --git a/sphinx/builders/xml.py b/sphinx/builders/xml.py
index 81d729def..d5fd6b40a 100644
--- a/sphinx/builders/xml.py
+++ b/sphinx/builders/xml.py
@@ -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,10 +23,6 @@ from sphinx.util import logging
from sphinx.util.osutil import ensuredir, os_path
from sphinx.writers.xml import XMLWriter, PseudoXMLWriter
-if False:
- # For type annotation
- from typing import Type # for python3.5.1
-
logger = logging.getLogger(__name__)
diff --git a/sphinx/cmd/make_mode.py b/sphinx/cmd/make_mode.py
index aff2ea7f5..1961d37d5 100644
--- a/sphinx/cmd/make_mode.py
+++ b/sphinx/cmd/make_mode.py
@@ -51,6 +51,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"),
]
diff --git a/sphinx/cmd/quickstart.py b/sphinx/cmd/quickstart.py
index 2363f9fe4..dc587600c 100644
--- a/sphinx/cmd/quickstart.py
+++ b/sphinx/cmd/quickstart.py
@@ -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:
@@ -35,16 +33,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 ( # type: ignore
- colorize, bold, red, turquoise, nocolor, color_terminal
-)
+from sphinx.util.console import colorize, bold, red, nocolor, color_terminal # 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 +128,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:
@@ -184,13 +153,6 @@ 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 ''
diff --git a/sphinx/config.py b/sphinx/config.py
index 353268e38..a6d070e3f 100644
--- a/sphinx/config.py
+++ b/sphinx/config.py
@@ -11,25 +11,22 @@
import re
import traceback
import types
-import warnings
from collections import OrderedDict
from os import path, getenv
from typing import (
Any, Callable, Dict, Generator, Iterator, List, NamedTuple, Set, Tuple, Union
)
+from typing import TYPE_CHECKING
-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
@@ -39,9 +36,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:
@@ -74,10 +73,6 @@ class ENUM:
return value in self.candidates
-# RemovedInSphinx40Warning
-string_classes = [str] # type: List
-
-
class Config:
"""Configuration file abstraction.
@@ -316,7 +311,9 @@ def eval_config_file(filename: str, tags: Tags) -> Dict[str, Any]:
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
@@ -439,22 +436,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):
diff --git a/sphinx/deprecation.py b/sphinx/deprecation.py
index 48db97760..aeefc4f9f 100644
--- a/sphinx/deprecation.py
+++ b/sphinx/deprecation.py
@@ -11,22 +11,22 @@
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
+
+
+class RemovedInSphinx60Warning(PendingDeprecationWarning):
pass
-RemovedInNextVersionWarning = RemovedInSphinx40Warning
+RemovedInNextVersionWarning = RemovedInSphinx50Warning
def deprecated_alias(modname: str, objects: Dict[str, object],
diff --git a/sphinx/directives/__init__.py b/sphinx/directives/__init__.py
index e6313ddab..e2b0d3342 100644
--- a/sphinx/directives/__init__.py
+++ b/sphinx/directives/__init__.py
@@ -10,7 +10,7 @@
import re
from typing import Any, Dict, List, Tuple
-from typing import cast
+from typing import TYPE_CHECKING, cast
from docutils import nodes
from docutils.nodes import Node
@@ -18,16 +18,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
-if False:
- # For type annotation
+if TYPE_CHECKING:
from sphinx.application import Sphinx
@@ -266,58 +263,6 @@ class DefaultDomain(SphinxDirective):
self.env.temp_data['default_domain'] = self.env.domains.get(domain_name)
return []
-from sphinx.directives.code import ( # noqa
- Highlight, CodeBlock, LiteralInclude
-)
-from sphinx.directives.other import ( # noqa
- TocTree, Author, VersionChange, SeeAlso,
- TabularColumns, Centered, Acks, HList, Only, Include, Class
-)
-from sphinx.directives.patches import ( # noqa
- Figure, Meta
-)
-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',
{
diff --git a/sphinx/directives/code.py b/sphinx/directives/code.py
index 4ca849cf0..4447b746a 100644
--- a/sphinx/directives/code.py
+++ b/sphinx/directives/code.py
@@ -7,9 +7,9 @@
"""
import sys
-import warnings
from difflib import unified_diff
from typing import Any, Dict, List, Tuple
+from typing import TYPE_CHECKING
from docutils import nodes
from docutils.nodes import Element, Node
@@ -18,14 +18,12 @@ from docutils.statemachine import StringList
from sphinx import addnodes
from sphinx.config import Config
-from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.locale import __
from sphinx.util import logging
from sphinx.util import parselinenos
from sphinx.util.docutils import SphinxDirective
-if False:
- # For type annotation
+if TYPE_CHECKING:
from sphinx.application import Sphinx
logger = logging.getLogger(__name__)
@@ -57,16 +55,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 lines
@@ -469,7 +457,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)
diff --git a/sphinx/directives/other.py b/sphinx/directives/other.py
index f55f95219..ec7f84661 100644
--- a/sphinx/directives/other.py
+++ b/sphinx/directives/other.py
@@ -8,7 +8,7 @@
import re
from typing import Any, Dict, List
-from typing import cast
+from typing import TYPE_CHECKING, cast
from docutils import nodes
from docutils.nodes import Element, Node
@@ -18,7 +18,6 @@ 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 url_re, docname_join
@@ -26,8 +25,7 @@ from sphinx.util.docutils import SphinxDirective
from sphinx.util.matching import Matcher, patfilter
from sphinx.util.nodes import explicit_title_re
-if False:
- # For type annotation
+if TYPE_CHECKING:
from sphinx.application import Sphinx
@@ -138,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)
@@ -361,19 +365,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)
diff --git a/sphinx/directives/patches.py b/sphinx/directives/patches.py
index 4b73a7955..c4535e119 100644
--- a/sphinx/directives/patches.py
+++ b/sphinx/directives/patches.py
@@ -6,8 +6,9 @@
:license: BSD, see LICENSE for details.
"""
+import warnings
from typing import Any, Dict, List, Tuple
-from typing import cast
+from typing import TYPE_CHECKING, cast
from docutils import nodes
from docutils.nodes import Node, make_id, system_message
@@ -15,13 +16,13 @@ from docutils.parsers.rst import directives
from docutils.parsers.rst.directives import images, html, tables
from sphinx import addnodes
+from sphinx.deprecation import RemovedInSphinx60Warning
from sphinx.directives import optional_int
from sphinx.domains.math import MathDomain
from sphinx.util.docutils import SphinxDirective
from sphinx.util.nodes import set_source_info
-if False:
- # For type annotation
+if TYPE_CHECKING:
from sphinx.application import Sphinx
@@ -73,6 +74,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:
@@ -86,6 +92,11 @@ class CSVTable(tables.CSVTable):
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:
@@ -99,6 +110,11 @@ class ListTable(tables.ListTable):
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:
@@ -209,9 +225,6 @@ 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)
diff --git a/sphinx/domains/__init__.py b/sphinx/domains/__init__.py
index 11b3a4604..e333af8ed 100644
--- a/sphinx/domains/__init__.py
+++ b/sphinx/domains/__init__.py
@@ -10,8 +10,8 @@
"""
import copy
-from typing import Any, Callable, Dict, Iterable, List, NamedTuple, Tuple, Union
-from typing import cast
+from typing import Any, Callable, Dict, Iterable, List, NamedTuple, Tuple, Type, Union
+from typing import TYPE_CHECKING, cast
from docutils import nodes
from docutils.nodes import Element, Node, system_message
@@ -23,9 +23,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
@@ -56,13 +54,14 @@ class ObjType:
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:
diff --git a/sphinx/domains/changeset.py b/sphinx/domains/changeset.py
index a07944db8..a5a6e51e5 100644
--- a/sphinx/domains/changeset.py
+++ b/sphinx/domains/changeset.py
@@ -8,9 +8,8 @@
:license: BSD, see LICENSE for details.
"""
-from collections import namedtuple
-from typing import Any, Dict, List
-from typing import cast
+from typing import Any, Dict, List, NamedTuple
+from typing import TYPE_CHECKING, cast
from docutils import nodes
from docutils.nodes import Node
@@ -21,8 +20,7 @@ from sphinx.locale import _
from sphinx.util.docutils import SphinxDirective
-if False:
- # For type annotation
+if TYPE_CHECKING:
from sphinx.application import Sphinx
from sphinx.environment import BuildEnvironment
@@ -40,9 +38,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):
diff --git a/sphinx/domains/citation.py b/sphinx/domains/citation.py
index 38901867a..f097ecf97 100644
--- a/sphinx/domains/citation.py
+++ b/sphinx/domains/citation.py
@@ -9,7 +9,7 @@
"""
from typing import Any, Dict, List, Set, Tuple
-from typing import cast
+from typing import TYPE_CHECKING, cast
from docutils import nodes
from docutils.nodes import Element
@@ -21,8 +21,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
diff --git a/sphinx/domains/index.py b/sphinx/domains/index.py
index 18a256bac..25d681f5e 100644
--- a/sphinx/domains/index.py
+++ b/sphinx/domains/index.py
@@ -9,6 +9,7 @@
"""
from typing import Any, Dict, Iterable, List, Tuple
+from typing import TYPE_CHECKING
from docutils import nodes
from docutils.nodes import Node, system_message
@@ -22,8 +23,7 @@ from sphinx.util import split_index_msg
from sphinx.util.docutils import ReferenceRole, SphinxDirective
from sphinx.util.nodes import process_index_entry
-if False:
- # For type annotation
+if TYPE_CHECKING:
from sphinx.application import Sphinx
diff --git a/sphinx/domains/math.py b/sphinx/domains/math.py
index e77bd0ed7..02c93433a 100644
--- a/sphinx/domains/math.py
+++ b/sphinx/domains/math.py
@@ -8,15 +8,14 @@
:license: BSD, see LICENSE for details.
"""
-import warnings
from typing import Any, Dict, Iterable, List, Tuple
+from typing import TYPE_CHECKING
from docutils import nodes
from docutils.nodes import Element, Node, system_message
from docutils.nodes import make_id
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 __
@@ -24,8 +23,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
@@ -140,24 +138,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) -> bool:
return any(self.data['has_equations'].values())
diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py
index f4bc58b69..fb00b8dad 100644
--- a/sphinx/domains/python.py
+++ b/sphinx/domains/python.py
@@ -15,7 +15,7 @@ import sys
import typing
import warnings
from inspect import Parameter
-from typing import Any, Dict, Iterable, Iterator, List, NamedTuple, Tuple
+from typing import Any, Dict, Iterable, Iterator, List, NamedTuple, Tuple, Type
from typing import cast
from docutils import nodes
@@ -26,7 +26,7 @@ from sphinx import addnodes
from sphinx.addnodes import pending_xref, desc_signature
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, ObjType, Index, IndexEntry
from sphinx.environment import BuildEnvironment
@@ -40,10 +40,6 @@ 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
-
logger = logging.getLogger(__name__)
@@ -68,14 +64,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:
@@ -100,6 +102,11 @@ def _parse_annotation(annotation: str, env: BuildEnvironment = None) -> List[Nod
def unparse(node: ast.AST) -> List[Node]:
if isinstance(node, ast.Attribute):
return [nodes.Text("%s.%s" % (unparse(node.value)[0], node.attr))]
+ 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):
@@ -135,13 +142,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('', "...")]
@@ -267,7 +267,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)
@@ -284,7 +284,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)
@@ -308,7 +308,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.
@@ -323,7 +323,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.
@@ -343,6 +343,7 @@ class PyObject(ObjectDescription):
'noindex': directives.flag,
'noindexentry': directives.flag,
'module': directives.unchanged,
+ 'canonical': directives.unchanged,
'annotation': directives.unchanged,
}
@@ -484,6 +485,11 @@ class PyObject(ObjectDescription):
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:
@@ -548,40 +554,6 @@ class PyObject(ObjectDescription):
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."""
@@ -696,91 +668,6 @@ 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."""
@@ -1191,7 +1078,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
@@ -1201,7 +1089,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]:
@@ -1354,7 +1242,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')
@@ -1400,7 +1292,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
return {
'version': 'builtin',
- 'env_version': 2,
+ 'env_version': 3,
'parallel_read_safe': True,
'parallel_write_safe': True,
}
diff --git a/sphinx/domains/std.py b/sphinx/domains/std.py
index 7ea468404..aa4793b66 100644
--- a/sphinx/domains/std.py
+++ b/sphinx/domains/std.py
@@ -12,8 +12,8 @@ import re
import unicodedata
import warnings
from copy import copy
-from typing import Any, Callable, Dict, Iterable, Iterator, List, Optional, Tuple, Union
-from typing import cast
+from typing import Any, Callable, Dict, Iterable, Iterator, List, Optional, Tuple, Type, Union
+from typing import TYPE_CHECKING, cast
from docutils import nodes
from docutils.nodes import Element, Node, system_message
@@ -22,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 _, __
@@ -32,9 +32,7 @@ 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
+if TYPE_CHECKING:
from sphinx.application import Sphinx
from sphinx.builders import Builder
from sphinx.environment import BuildEnvironment
@@ -287,8 +285,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)
@@ -299,23 +297,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_object('term', termtext, node_id, location=term)
@@ -423,7 +408,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())
@@ -796,11 +781,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:
@@ -1092,18 +1072,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.name != 'std' or node['reftype'] != 'ref':
diff --git a/sphinx/environment/__init__.py b/sphinx/environment/__init__.py
index 8ce9fa365..628c9aaf6 100644
--- a/sphinx/environment/__init__.py
+++ b/sphinx/environment/__init__.py
@@ -10,19 +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
-from typing import cast
+from typing import TYPE_CHECKING
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 SphinxError, BuildEnvironmentError, DocumentError, ExtensionError
@@ -36,8 +35,7 @@ from sphinx.util.docutils import LoggingReporter
from sphinx.util.i18n import CatalogRepository, docname_to_domain
from sphinx.util.nodes import is_translatable
-if False:
- # For type annotation
+if TYPE_CHECKING:
from sphinx.application import Sphinx
from sphinx.builders import Builder
@@ -321,28 +319,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
@@ -410,21 +393,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
@@ -643,19 +633,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
diff --git a/sphinx/environment/adapters/toctree.py b/sphinx/environment/adapters/toctree.py
index 9a1ef73d4..1d3320af2 100644
--- a/sphinx/environment/adapters/toctree.py
+++ b/sphinx/environment/adapters/toctree.py
@@ -9,7 +9,7 @@
"""
from typing import Any, Iterable, List
-from typing import cast
+from typing import TYPE_CHECKING, cast
from docutils import nodes
from docutils.nodes import Element, Node
@@ -20,8 +20,7 @@ from sphinx.util import url_re, logging
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
diff --git a/sphinx/environment/collectors/__init__.py b/sphinx/environment/collectors/__init__.py
index 53e3a1c72..2dd33c767 100644
--- a/sphinx/environment/collectors/__init__.py
+++ b/sphinx/environment/collectors/__init__.py
@@ -9,13 +9,13 @@
"""
from typing import Dict, List, Set
+from typing import TYPE_CHECKING
from docutils import nodes
from sphinx.environment import BuildEnvironment
-if False:
- # For type annotation
+if TYPE_CHECKING:
from sphinx.application import Sphinx
diff --git a/sphinx/environment/collectors/indexentries.py b/sphinx/environment/collectors/indexentries.py
deleted file mode 100644
index a4c0450d2..000000000
--- a/sphinx/environment/collectors/indexentries.py
+++ /dev/null
@@ -1,64 +0,0 @@
-"""
- sphinx.environment.collectors.indexentries
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
- Index entries collector for sphinx.environment.
-
- :copyright: Copyright 2007-2020 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 split_index_msg, logging
-
-
-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,
- }
diff --git a/sphinx/environment/collectors/toctree.py b/sphinx/environment/collectors/toctree.py
index d6cdc8354..d17f7090e 100644
--- a/sphinx/environment/collectors/toctree.py
+++ b/sphinx/environment/collectors/toctree.py
@@ -8,7 +8,7 @@
:license: BSD, see LICENSE for details.
"""
-from typing import Any, Dict, List, Set, Tuple, TypeVar
+from typing import Any, Dict, List, Set, Tuple, Type, TypeVar
from typing import cast
from docutils import nodes
@@ -23,10 +23,6 @@ from sphinx.locale import __
from sphinx.transforms import SphinxContentsFilter
from sphinx.util import url_re, logging
-if False:
- # For type annotation
- from typing import Type # for python3.5.1
-
N = TypeVar('N')
diff --git a/sphinx/events.py b/sphinx/events.py
index 214654706..59d38daf0 100644
--- a/sphinx/events.py
+++ b/sphinx/events.py
@@ -10,27 +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 Any, Callable, Dict, List, NamedTuple, Tuple, Type
+from typing import TYPE_CHECKING
-from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.errors import ExtensionError, SphinxError
from sphinx.locale import __
from sphinx.util import logging
-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,10 +57,7 @@ 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]]
@@ -91,7 +87,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 +100,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
@@ -120,7 +112,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``.
diff --git a/sphinx/ext/apidoc.py b/sphinx/ext/apidoc.py
index b01604617..8e3fbd7d0 100644
--- a/sphinx/ext/apidoc.py
+++ b/sphinx/ext/apidoc.py
@@ -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, 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):
@@ -516,13 +452,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()
diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py
index 80e525e1c..cf92b0137 100644
--- a/sphinx/ext/autodoc/__init__.py
+++ b/sphinx/ext/autodoc/__init__.py
@@ -18,14 +18,14 @@ from types import ModuleType
from typing import (
Any, Callable, Dict, Iterator, List, Optional, Sequence, Set, Tuple, Type, TypeVar, Union
)
-from typing import get_type_hints
+from typing import TYPE_CHECKING, get_type_hints
from docutils.statemachine import StringList
import sphinx
from sphinx.application import Sphinx
from sphinx.config import Config, ENUM
-from sphinx.deprecation import RemovedInSphinx40Warning, RemovedInSphinx50Warning
+from sphinx.deprecation import RemovedInSphinx50Warning
from sphinx.environment import BuildEnvironment
from sphinx.ext.autodoc.importer import import_object, get_module_members, get_object_members
from sphinx.ext.autodoc.mock import mock
@@ -39,9 +39,7 @@ from sphinx.util.inspect import (
)
from sphinx.util.typing import restify, 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
@@ -349,7 +347,7 @@ class Documenter:
self.analyzer = None # type: ModuleAnalyzer
@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
@@ -536,12 +534,8 @@ 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) -> List[List[str]]:
+ def get_doc(self, ignore: int = None) -> List[List[str]]:
"""Decode and return lines of the docstring(s) for the 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__,
@@ -882,7 +876,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 = real_modname or guess_modname # type: str
# try to also get a source code analyzer for attribute docs
try:
@@ -1062,7 +1056,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__)
@@ -1135,12 +1129,7 @@ class DocstringSignatureMixin:
_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)
-
+ def _find_signature(self) -> Tuple[str, str]:
# candidates of the object name
valid_names = [self.objpath[-1]] # type: ignore
if isinstance(self, ClassDocumenter):
@@ -1199,14 +1188,10 @@ class DocstringSignatureMixin:
return result
- def get_doc(self, encoding: str = None, ignore: int = None) -> 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
@@ -1587,11 +1572,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
bases = [restify(cls) for cls in self.object.__bases__]
self.add_line(' ' + _('Bases: %s') % ', '.join(bases), sourcename)
- def get_doc(self, encoding: str = None, ignore: int = None) -> 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]]:
lines = getattr(self, '_new_docstrings', None)
if lines is not None:
return lines
@@ -1713,9 +1694,6 @@ class DataDocumenter(ModuleLevelDocumenter):
except KeyError:
# a broken class found (refs: https://github.com/sphinx-doc/sphinx/issues/8084)
annotations = {}
- except AttributeError:
- # AttributeError is raised on 3.5.2 (fixed by 3.5.3)
- annotations = {}
if self.objpath[-1] in annotations:
objrepr = stringify_typehint(annotations.get(self.objpath[-1]))
@@ -1830,7 +1808,7 @@ class TypeVarDocumenter(DataDocumenter):
self.options['annotation'] = SUPPRESS
super().add_directive_header(sig)
- def get_doc(self, encoding: str = None, ignore: int = None) -> List[List[str]]:
+ def get_doc(self, ignore: int = None) -> List[List[str]]:
if ignore is not None:
warnings.warn("The 'ignore' argument to autodoc.%s.get_doc() is deprecated."
% self.__class__.__name__,
@@ -2104,9 +2082,6 @@ class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter):
except KeyError:
# a broken class found (refs: https://github.com/sphinx-doc/sphinx/issues/8084)
annotations = {}
- except AttributeError:
- # AttributeError is raised on 3.5.2 (fixed by 3.5.3)
- annotations = {}
if self.objpath[-1] in annotations:
objrepr = stringify_typehint(annotations.get(self.objpath[-1]))
@@ -2132,14 +2107,14 @@ class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter):
else:
self.add_line(' :annotation: %s' % self.options.annotation, sourcename)
- def get_doc(self, encoding: str = None, ignore: int = None) -> List[List[str]]:
+ def get_doc(self, ignore: int = None) -> List[List[str]]:
try:
# Disable `autodoc_inherit_docstring` temporarily to avoid to obtain
# a docstring from the value which descriptor returns unexpectedly.
# 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
@@ -2265,7 +2240,7 @@ class SlotsAttributeDocumenter(AttributeDocumenter):
self.env.note_reread()
return False
- def get_doc(self, encoding: str = None, ignore: int = None) -> List[List[str]]:
+ def get_doc(self, ignore: int = None) -> List[List[str]]:
"""Decode and return lines of the docstring(s) for the object."""
if ignore is not None:
warnings.warn("The 'ignore' argument to autodoc.%s.get_doc() is deprecated."
@@ -2280,7 +2255,7 @@ class SlotsAttributeDocumenter(AttributeDocumenter):
return []
-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
diff --git a/sphinx/ext/autodoc/directive.py b/sphinx/ext/autodoc/directive.py
index 9a3428f5d..5543059cb 100644
--- a/sphinx/ext/autodoc/directive.py
+++ b/sphinx/ext/autodoc/directive.py
@@ -6,27 +6,21 @@
:license: BSD, see LICENSE for details.
"""
-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
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__)
@@ -53,23 +47,14 @@ 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.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:
logger.warning(msg, location=(self.env.docname, self.lineno))
diff --git a/sphinx/ext/autodoc/importer.py b/sphinx/ext/autodoc/importer.py
index 52b07639a..df5614f99 100644
--- a/sphinx/ext/autodoc/importer.py
+++ b/sphinx/ext/autodoc/importer.py
@@ -13,7 +13,6 @@ import traceback
import warnings
from typing import Any, Callable, Dict, List, Mapping, NamedTuple, Optional, Tuple
-from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
from sphinx.pycode import ModuleAnalyzer
from sphinx.util import logging
from sphinx.util.inspect import isclass, isenumclass, safe_getattr
@@ -157,9 +156,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)])
+class Attribute(NamedTuple):
+ name: str
+ directly_defined: bool
+ value: Any
def _getmro(obj: Any) -> Tuple["Type", ...]:
@@ -238,25 +238,3 @@ def get_object_members(subject: Any, objpath: List[str], attrgetter: Callable,
members[name] = Attribute(name, True, INSTANCEATTR)
return members
-
-
-from sphinx.ext.autodoc.mock import ( # NOQA
- _MockModule, _MockObject, MockFinder, MockLoader, mock
-)
-
-deprecated_alias('sphinx.ext.autodoc.importer',
- {
- '_MockModule': _MockModule,
- '_MockObject': _MockObject,
- 'MockFinder': MockFinder,
- 'MockLoader': MockLoader,
- 'mock': mock,
- },
- RemovedInSphinx40Warning,
- {
- '_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',
- })
diff --git a/sphinx/ext/autodoc/mock.py b/sphinx/ext/autodoc/mock.py
index 40258a135..bf4ac2979 100644
--- a/sphinx/ext/autodoc/mock.py
+++ b/sphinx/ext/autodoc/mock.py
@@ -14,7 +14,7 @@ import sys
from importlib.abc import Loader, MetaPathFinder
from importlib.machinery import ModuleSpec
from types import FunctionType, MethodType, 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
@@ -117,8 +117,8 @@ class MockFinder(MetaPathFinder):
self.loader = MockLoader(self)
self.mocked_modules = [] # type: 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 + '.'):
diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py
index faee7716a..ee1785251 100644
--- a/sphinx/ext/autosummary/__init__.py
+++ b/sphinx/ext/autosummary/__init__.py
@@ -60,20 +60,20 @@ import sys
import warnings
from os import path
from types import ModuleType
-from typing import Any, Dict, List, Tuple
+from typing import Any, Dict, List, Tuple, Type
from typing import 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 Documenter, INSTANCEATTR
@@ -89,10 +89,6 @@ from sphinx.util.docutils import (
from sphinx.util.matching import Matcher
from sphinx.writers.html import HTMLTranslator
-if False:
- # For type annotation
- from typing import Type # for python3.5.1
-
logger = logging.getLogger(__name__)
@@ -437,29 +433,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."""
@@ -702,33 +675,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.
diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py
index 7580285aa..148b7282e 100644
--- a/sphinx/ext/autosummary/generate.py
+++ b/sphinx/ext/autosummary/generate.py
@@ -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
@@ -39,7 +39,7 @@ 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, RemovedInSphinx50Warning
+from sphinx.deprecation import RemovedInSphinx50Warning
from sphinx.ext.autodoc import Documenter
from sphinx.ext.autosummary import import_by_name, import_ivar_by_name, get_documenter
from sphinx.locale import __
@@ -52,10 +52,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__)
@@ -81,10 +77,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:
@@ -347,25 +344,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)
@@ -377,11 +359,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]
@@ -418,7 +400,7 @@ 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 = {}
@@ -448,8 +430,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)
diff --git a/sphinx/ext/doctest.py b/sphinx/ext/doctest.py
index 7cd89ad32..2485b89f0 100644
--- a/sphinx/ext/doctest.py
+++ b/sphinx/ext/doctest.py
@@ -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 Any, Callable, Dict, Iterable, List, Sequence, Set, Tuple, Type
+from typing import TYPE_CHECKING
from docutils import nodes
from docutils.nodes import Element, Node, TextElement
@@ -26,16 +26,13 @@ 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
-if False:
- # For type annotation
- from typing import Type # for python3.5.1
+if TYPE_CHECKING:
from sphinx.application import Sphinx
@@ -45,12 +42,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.
diff --git a/sphinx/ext/imgmath.py b/sphinx/ext/imgmath.py
index d11c5d7c5..a8da39709 100644
--- a/sphinx/ext/imgmath.py
+++ b/sphinx/ext/imgmath.py
@@ -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),
diff --git a/sphinx/ext/jsmath.py b/sphinx/ext/jsmath.py
deleted file mode 100644
index 75369a5ca..000000000
--- a/sphinx/ext/jsmath.py
+++ /dev/null
@@ -1,36 +0,0 @@
-"""
- sphinx.ext.jsmath
- ~~~~~~~~~~~~~~~~~
-
- Set up everything for use of JSMath to display math in HTML
- via JavaScript.
-
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
- :license: BSD, see LICENSE for details.
-"""
-
-import warnings
-from typing import Any, Dict
-
-from sphinxcontrib.jsmath import ( # NOQA
- html_visit_math,
- html_visit_displaymath,
- install_jsmath,
-)
-
-import sphinx
-from sphinx.application import Sphinx
-from sphinx.deprecation import RemovedInSphinx40Warning
-
-
-def setup(app: Sphinx) -> Dict[str, Any]:
- warnings.warn('sphinx.ext.jsmath has been moved to sphinxcontrib-jsmath.',
- RemovedInSphinx40Warning)
-
- app.setup_extension('sphinxcontrib.jsmath')
-
- return {
- 'version': sphinx.__display_version__,
- 'parallel_read_safe': True,
- 'parallel_write_safe': True,
- }
diff --git a/sphinx/ext/mathjax.py b/sphinx/ext/mathjax.py
index cc3cd4ba3..59eb16047 100644
--- a/sphinx/ext/mathjax.py
+++ b/sphinx/ext/mathjax.py
@@ -96,8 +96,8 @@ def setup(app: Sphinx) -> Dict[str, Any]:
# more information for mathjax secure url is here:
# https://docs.mathjax.org/en/latest/start.html#secure-access-to-the-cdn
app.add_config_value('mathjax_path',
- 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/latest.js?'
- 'config=TeX-AMS-MML_HTMLorMML', 'html')
+ 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js',
+ 'html')
app.add_config_value('mathjax_options', {}, 'html')
app.add_config_value('mathjax_inline', [r'\(', r'\)'], 'html')
app.add_config_value('mathjax_display', [r'\[', r'\]'], 'html')
diff --git a/sphinx/ext/napoleon/docstring.py b/sphinx/ext/napoleon/docstring.py
index ddcf3f01b..ca50635a9 100644
--- a/sphinx/ext/napoleon/docstring.py
+++ b/sphinx/ext/napoleon/docstring.py
@@ -14,7 +14,7 @@ import collections
import inspect
import re
from functools import partial
-from typing import Any, Callable, Dict, List, Tuple, Union
+from typing import Any, Callable, Dict, List, Tuple, Type, Union
from sphinx.application import Sphinx
from sphinx.config import Config as SphinxConfig
@@ -22,10 +22,6 @@ from sphinx.ext.napoleon.iterators import modify_iter
from sphinx.locale import _, __
from sphinx.util import logging
-if False:
- # For type annotation
- from typing import Type # for python3.5.1
-
logger = logging.getLogger(__name__)
diff --git a/sphinx/ext/todo.py b/sphinx/ext/todo.py
index bfb46903f..44b6acabc 100644
--- a/sphinx/ext/todo.py
+++ b/sphinx/ext/todo.py
@@ -11,8 +11,7 @@
:license: BSD, see LICENSE for details.
"""
-import warnings
-from typing import Any, Dict, Iterable, List, Tuple
+from typing import Any, Dict, List, Tuple
from typing import cast
from docutils import nodes
@@ -22,14 +21,12 @@ from docutils.parsers.rst.directives.admonitions import BaseAdmonition
import sphinx
from sphinx.application import Sphinx
-from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.domains import Domain
from sphinx.environment import BuildEnvironment
from sphinx.errors import NoUri
from sphinx.locale import _, __
from sphinx.util import logging, texescape
from sphinx.util.docutils import SphinxDirective, new_document
-from sphinx.util.nodes import make_refnode
from sphinx.writers.html import HTMLTranslator
from sphinx.writers.latex import LaTeXTranslator
@@ -104,33 +101,6 @@ class TodoDomain(Domain):
location=todo)
-def process_todos(app: Sphinx, doctree: nodes.document) -> None:
- warnings.warn('process_todos() is deprecated.', RemovedInSphinx40Warning, stacklevel=2)
- # collect all todos in the environment
- # this is not done in the directive itself because it some transformations
- # must have already been run, e.g. substitutions
- env = app.builder.env
- if not hasattr(env, 'todo_all_todos'):
- env.todo_all_todos = [] # type: ignore
- for node in doctree.traverse(todo_node):
- app.events.emit('todo-defined', node)
-
- newnode = node.deepcopy()
- newnode['ids'] = []
- env.todo_all_todos.append({ # type: ignore
- 'docname': env.docname,
- 'source': node.source or env.doc2path(env.docname),
- 'lineno': node.line,
- 'todo': newnode,
- 'target': node['ids'][0],
- })
-
- if env.config.todo_emit_warnings:
- label = cast(nodes.Element, node[1])
- logger.warning(__("TODO entry found: %s"), label.astext(),
- location=node)
-
-
class TodoList(SphinxDirective):
"""
A list of all todo entries.
@@ -217,80 +187,6 @@ class TodoListProcessor:
return para
-def process_todo_nodes(app: Sphinx, doctree: nodes.document, fromdocname: str) -> None:
- """Replace all todolist nodes with a list of the collected todos.
- Augment each todo with a backlink to the original location.
- """
- warnings.warn('process_todo_nodes() is deprecated.',
- RemovedInSphinx40Warning, stacklevel=2)
-
- domain = cast(TodoDomain, app.env.get_domain('todo'))
- todos = sum(domain.todos.values(), []) # type: List[todo_node]
-
- for node in doctree.traverse(todolist):
- if node.get('ids'):
- content = [nodes.target()] # type: List[Element]
- else:
- content = []
-
- if not app.config['todo_include_todos']:
- node.replace_self(content)
- continue
-
- for todo_info in todos:
- para = nodes.paragraph(classes=['todo-source'])
- if app.config['todo_link_only']:
- description = _('<<original entry>>')
- else:
- description = (
- _('(The <<original entry>> is located in %s, line %d.)') %
- (todo_info.source, todo_info.line)
- )
- desc1 = description[:description.find('<<')]
- desc2 = description[description.find('>>') + 2:]
- para += nodes.Text(desc1, desc1)
-
- # Create a reference
- innernode = nodes.emphasis(_('original entry'), _('original entry'))
- try:
- para += make_refnode(app.builder, fromdocname, todo_info['docname'],
- todo_info['ids'][0], innernode)
- except NoUri:
- # ignore if no URI can be determined, e.g. for LaTeX output
- pass
- para += nodes.Text(desc2, desc2)
-
- todo_entry = todo_info.deepcopy()
- todo_entry['ids'].clear()
-
- # (Recursively) resolve references in the todo content
- app.env.resolve_references(todo_entry, todo_info['docname'], app.builder) # type: ignore # NOQA
-
- # Insert into the todolist
- content.append(todo_entry)
- content.append(para)
-
- node.replace_self(content)
-
-
-def purge_todos(app: Sphinx, env: BuildEnvironment, docname: str) -> None:
- warnings.warn('purge_todos() is deprecated.', RemovedInSphinx40Warning, stacklevel=2)
- if not hasattr(env, 'todo_all_todos'):
- return
- env.todo_all_todos = [todo for todo in env.todo_all_todos # type: ignore
- if todo['docname'] != docname]
-
-
-def merge_info(app: Sphinx, env: BuildEnvironment, docnames: Iterable[str],
- other: BuildEnvironment) -> None:
- warnings.warn('merge_info() is deprecated.', RemovedInSphinx40Warning, stacklevel=2)
- if not hasattr(other, 'todo_all_todos'):
- return
- if not hasattr(env, 'todo_all_todos'):
- env.todo_all_todos = [] # type: ignore
- env.todo_all_todos.extend(other.todo_all_todos) # type: ignore
-
-
def visit_todo_node(self: HTMLTranslator, node: todo_node) -> None:
if self.config.todo_include_todos:
self.visit_admonition(node)
diff --git a/sphinx/extension.py b/sphinx/extension.py
index fa7a0d490..98efc48fe 100644
--- a/sphinx/extension.py
+++ b/sphinx/extension.py
@@ -9,14 +9,14 @@
"""
from typing import Any, Dict
+from typing import TYPE_CHECKING
from sphinx.config import Config
from sphinx.errors import VersionRequirementError
from sphinx.locale import __
from sphinx.util import logging
-if False:
- # For type annotation
+if TYPE_CHECKING:
from sphinx.application import Sphinx
logger = logging.getLogger(__name__)
diff --git a/sphinx/io.py b/sphinx/io.py
index fd30b86a9..8f10bbfa2 100644
--- a/sphinx/io.py
+++ b/sphinx/io.py
@@ -8,8 +8,8 @@
:license: BSD, see LICENSE for details.
"""
import codecs
-import warnings
-from typing import Any, List
+from typing import Any, List, Type
+from typing import TYPE_CHECKING
from docutils import nodes
from docutils.core import Publisher
@@ -23,9 +23,7 @@ from docutils.transforms.references import DanglingReferences
from docutils.writers import UnfilteredWriter
from sphinx import addnodes
-from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
from sphinx.environment import BuildEnvironment
-from sphinx.errors import FiletypeNotFoundError
from sphinx.transforms import (
AutoIndexUpgrader, DoctreeReadEvent, FigureAligner, SphinxTransformer
)
@@ -38,9 +36,7 @@ from sphinx.util import UnicodeDecodeErrorHandler
from sphinx.util.docutils import LoggingReporter
from sphinx.versioning import UIDTransform
-if False:
- # For type annotation
- from typing import Type # for python3.5.1
+if TYPE_CHECKING:
from sphinx.application import Sphinx
@@ -65,18 +61,6 @@ class SphinxBaseReader(standalone.Reader):
super().__init__(*args, **kwargs)
- @property
- def app(self) -> "Sphinx":
- warnings.warn('SphinxBaseReader.app is deprecated.',
- RemovedInSphinx40Warning, stacklevel=2)
- return self._app
-
- @property
- def env(self) -> BuildEnvironment:
- warnings.warn('SphinxBaseReader.env is deprecated.',
- RemovedInSphinx40Warning, stacklevel=2)
- return self._env
-
def setup(self, app: "Sphinx") -> None:
self._app = app # hold application object only for compatibility
self._env = app.env
@@ -222,15 +206,3 @@ def read_doc(app: "Sphinx", env: BuildEnvironment, filename: str) -> nodes.docum
pub.publish()
return pub.document
-
-
-deprecated_alias('sphinx.io',
- {
- 'FiletypeNotFoundError': FiletypeNotFoundError,
- 'get_filetype': get_filetype,
- },
- RemovedInSphinx40Warning,
- {
- 'FiletypeNotFoundError': 'sphinx.errors.FiletypeNotFoundError',
- 'get_filetype': 'sphinx.util.get_filetype',
- })
diff --git a/sphinx/jinja2glue.py b/sphinx/jinja2glue.py
index 52d0257e5..5829c2250 100644
--- a/sphinx/jinja2glue.py
+++ b/sphinx/jinja2glue.py
@@ -11,6 +11,7 @@
from os import path
from pprint import pformat
from typing import Any, Callable, Dict, Iterator, List, Tuple, Union
+from typing import TYPE_CHECKING
from jinja2 import FileSystemLoader, BaseLoader, TemplateNotFound, contextfunction
from jinja2.environment import Environment
@@ -22,8 +23,7 @@ from sphinx.theming import Theme
from sphinx.util import logging
from sphinx.util.osutil import mtimes_of_files
-if False:
- # For type annotation
+if TYPE_CHECKING:
from sphinx.builders import Builder
diff --git a/sphinx/parsers.py b/sphinx/parsers.py
index 6a07d1801..94c17dc0d 100644
--- a/sphinx/parsers.py
+++ b/sphinx/parsers.py
@@ -9,7 +9,8 @@
"""
import warnings
-from typing import Any, Dict, List, Union
+from typing import Any, Dict, List, Type, Union
+from typing import TYPE_CHECKING
import docutils.parsers
import docutils.parsers.rst
@@ -21,10 +22,8 @@ from docutils.transforms.universal import SmartQuotes
from sphinx.deprecation import RemovedInSphinx50Warning
from sphinx.util.rst import append_epilog, prepend_prolog
-if False:
- # For type annotation
+if TYPE_CHECKING:
from docutils.transforms import Transform # NOQA
- from typing import Type # NOQA # for python3.5.1
from sphinx.application import Sphinx
@@ -71,7 +70,7 @@ class Parser(docutils.parsers.Parser):
class RSTParser(docutils.parsers.rst.Parser, Parser):
"""A reST parser for Sphinx."""
- def get_transforms(self) -> List["Type[Transform]"]:
+ def get_transforms(self) -> List[Type["Transform"]]:
"""Sphinx's reST parser replaces a transform class for smart-quotes by own's
refs: sphinx.io.SphinxStandaloneReader
diff --git a/sphinx/project.py b/sphinx/project.py
index f4afdadad..1909659b1 100644
--- a/sphinx/project.py
+++ b/sphinx/project.py
@@ -10,6 +10,7 @@
import os
from glob import glob
+from typing import TYPE_CHECKING
from sphinx.locale import __
from sphinx.util import get_matching_files
@@ -18,9 +19,8 @@ from sphinx.util import path_stabilize
from sphinx.util.matching import compile_matchers
from sphinx.util.osutil import SEP, relpath
-if False:
- # For type annotation
- from typing import Dict, List, Set # NOQA
+if TYPE_CHECKING:
+ from typing import Dict, List, Set
logger = logging.getLogger(__name__)
diff --git a/sphinx/pycode/__init__.py b/sphinx/pycode/__init__.py
index b7163072f..8bc1ba6c1 100644
--- a/sphinx/pycode/__init__.py
+++ b/sphinx/pycode/__init__.py
@@ -10,7 +10,6 @@
import re
import tokenize
-import warnings
from collections import OrderedDict
from importlib import import_module
from inspect import Signature
@@ -19,7 +18,6 @@ from os import path
from typing import Any, Dict, IO, List, Tuple, Optional
from zipfile import ZipFile
-from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.errors import PycodeError
from sphinx.pycode.parser import Parser
@@ -79,7 +77,7 @@ class ModuleAnalyzer:
@classmethod
def for_string(cls, string: str, modname: str, srcname: str = '<string>'
) -> "ModuleAnalyzer":
- return cls(StringIO(string), modname, srcname, decoded=True)
+ return cls(StringIO(string), modname, srcname)
@classmethod
def for_file(cls, filename: str, modname: str) -> "ModuleAnalyzer":
@@ -87,7 +85,7 @@ class ModuleAnalyzer:
return cls.cache['file', filename]
try:
with tokenize.open(filename) as f:
- obj = cls(f, modname, filename, decoded=True)
+ obj = cls(f, modname, filename)
cls.cache['file', filename] = obj
except Exception as err:
if '.egg' + path.sep in filename:
@@ -127,21 +125,12 @@ class ModuleAnalyzer:
cls.cache['module', modname] = obj
return obj
- def __init__(self, source: IO, modname: str, srcname: str, decoded: bool = False) -> None:
+ def __init__(self, source: IO, modname: str, srcname: str) -> None:
self.modname = modname # name of the module
self.srcname = srcname # name of the source file
# cache the source code as well
- pos = source.tell()
- if not decoded:
- warnings.warn('decode option for ModuleAnalyzer is deprecated.',
- RemovedInSphinx40Warning, stacklevel=2)
- self._encoding, _ = tokenize.detect_encoding(source.readline)
- source.seek(pos)
- self.code = source.read().decode(self._encoding)
- else:
- self._encoding = None
- self.code = source.read()
+ self.code = source.read()
# will be filled by parse()
self.annotations = None # type: Dict[Tuple[str, str], str]
@@ -158,7 +147,7 @@ class ModuleAnalyzer:
return None
try:
- parser = Parser(self.code, self._encoding)
+ parser = Parser(self.code)
parser.parse()
self.attr_docs = OrderedDict()
@@ -186,9 +175,3 @@ class ModuleAnalyzer:
"""Find class, function and method definitions and their location."""
self.parse()
return self.tags
-
- @property
- def encoding(self) -> str:
- warnings.warn('ModuleAnalyzer.encoding is deprecated.',
- RemovedInSphinx40Warning, stacklevel=2)
- return self._encoding
diff --git a/sphinx/pycode/ast.py b/sphinx/pycode/ast.py
index 17d78f4eb..e5b84600b 100644
--- a/sphinx/pycode/ast.py
+++ b/sphinx/pycode/ast.py
@@ -10,6 +10,7 @@
import sys
from typing import Dict, List, Type, Optional
+from typing import overload
if sys.version_info > (3, 8):
import ast
@@ -58,6 +59,16 @@ def parse(code: str, mode: str = 'exec') -> "ast.AST":
return ast.parse(code, mode=mode)
+@overload
+def unparse(node: None, code: str = '') -> None:
+ ...
+
+
+@overload
+def unparse(node: ast.AST, code: str = '') -> str:
+ ...
+
+
def unparse(node: Optional[ast.AST], code: str = '') -> Optional[str]:
"""Unparse an AST to string."""
if node is None:
@@ -146,6 +157,17 @@ class _UnparseVisitor(ast.NodeVisitor):
["%s=%s" % (k.arg, self.visit(k.value)) for k in node.keywords])
return "%s(%s)" % (self.visit(node.func), ", ".join(args))
+ def visit_Constant(self, node: ast.Constant) -> str: # type: ignore
+ if node.value is Ellipsis:
+ return "..."
+ elif isinstance(node.value, (int, float, complex)):
+ if self.code and sys.version_info > (3, 8):
+ return ast.get_source_segment(self.code, node) # type: ignore
+ else:
+ return repr(node.value)
+ else:
+ return repr(node.value)
+
def visit_Dict(self, node: ast.Dict) -> str:
keys = (self.visit(k) for k in node.keys)
values = (self.visit(v) for v in node.values)
@@ -193,18 +215,6 @@ class _UnparseVisitor(ast.NodeVisitor):
else:
return "()"
- if sys.version_info >= (3, 6):
- def visit_Constant(self, node: ast.Constant) -> str:
- if node.value is Ellipsis:
- return "..."
- elif isinstance(node.value, (int, float, complex)):
- if self.code and sys.version_info > (3, 8):
- return ast.get_source_segment(self.code, node)
- else:
- return repr(node.value)
- else:
- return repr(node.value)
-
if sys.version_info < (3, 8):
# these ast nodes were deprecated in python 3.8
def visit_Bytes(self, node: ast.Bytes) -> str:
diff --git a/sphinx/pycode/parser.py b/sphinx/pycode/parser.py
index be9bfc96d..2d454cc96 100644
--- a/sphinx/pycode/parser.py
+++ b/sphinx/pycode/parser.py
@@ -10,7 +10,6 @@
import inspect
import itertools
import re
-import sys
import tokenize
from collections import OrderedDict
from inspect import Signature
@@ -27,12 +26,6 @@ indent_re = re.compile('^\\s*$')
emptyline_re = re.compile('^\\s*(#.*)?$')
-if sys.version_info >= (3, 6):
- ASSIGN_NODES = (ast.Assign, ast.AnnAssign)
-else:
- ASSIGN_NODES = (ast.Assign)
-
-
def filter_whitespace(code: str) -> str:
return code.replace('\f', ' ') # replace FF (form feed) with whitespace
@@ -95,7 +88,10 @@ def dedent_docstring(s: str) -> str:
dummy.__doc__ = s
docstring = inspect.getdoc(dummy)
- return docstring.lstrip("\r\n").rstrip("\r\n")
+ if docstring:
+ return docstring.lstrip("\r\n").rstrip("\r\n")
+ else:
+ return ""
class Token:
@@ -399,13 +395,14 @@ class VariableCommentPicker(ast.NodeVisitor):
for varname in varnames:
self.add_entry(varname)
- def visit_AnnAssign(self, node: ast.AST) -> None: # Note: ast.AnnAssign not found in py35
+ def visit_AnnAssign(self, node: ast.AnnAssign) -> None:
"""Handles AnnAssign node and pick up a variable comment."""
self.visit_Assign(node) # type: ignore
def visit_Expr(self, node: ast.Expr) -> None:
"""Handles Expr node and pick up a comment if string."""
- if (isinstance(self.previous, ASSIGN_NODES) and isinstance(node.value, ast.Str)):
+ if (isinstance(self.previous, (ast.Assign, ast.AnnAssign)) and
+ isinstance(node.value, ast.Str)):
try:
targets = get_assign_targets(self.previous)
varnames = get_lvar_names(targets[0], self.get_self())
diff --git a/sphinx/registry.py b/sphinx/registry.py
index d0c00b85f..541fef1b3 100644
--- a/sphinx/registry.py
+++ b/sphinx/registry.py
@@ -11,7 +11,8 @@
import traceback
from importlib import import_module
from types import MethodType
-from typing import Any, Callable, Dict, Iterator, List, Tuple, Union
+from typing import Any, Callable, Dict, Iterator, List, Tuple, Type, Union
+from typing import TYPE_CHECKING
from docutils import nodes
from docutils.io import Input
@@ -35,9 +36,7 @@ from sphinx.util import logging
from sphinx.util.logging import prefixed_warnings
from sphinx.util.typing import RoleFunction, TitleGetter
-if False:
- # For type annotation
- from typing import Type # for python3.5.1
+if TYPE_CHECKING:
from sphinx.application import Sphinx
from sphinx.ext.autodoc import Documenter
diff --git a/sphinx/roles.py b/sphinx/roles.py
index 2ff66d07b..426a62e90 100644
--- a/sphinx/roles.py
+++ b/sphinx/roles.py
@@ -9,26 +9,19 @@
"""
import re
-import warnings
-from typing import Any, Dict, List, Tuple
+from typing import Any, Dict, List, Tuple, Type
+from typing import TYPE_CHECKING
from docutils import nodes, utils
from docutils.nodes import Element, Node, TextElement, system_message
-from docutils.parsers.rst.states import Inliner
from sphinx import addnodes
-from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.locale import _
from sphinx.util import ws_re
from sphinx.util.docutils import ReferenceRole, SphinxRole
-from sphinx.util.nodes import (
- split_explicit_title, process_index_entry, set_role_source_info
-)
from sphinx.util.typing import RoleFunction
-if False:
- # For type annotation
- from typing import Type # for python3.5.1
+if TYPE_CHECKING:
from sphinx.application import Sphinx
from sphinx.environment import BuildEnvironment
@@ -89,22 +82,6 @@ class XRefRole(ReferenceRole):
super().__init__()
- def _fix_parens(self, env: "BuildEnvironment", has_explicit_title: bool, title: str,
- target: str) -> Tuple[str, str]:
- warnings.warn('XRefRole._fix_parens() is deprecated.',
- RemovedInSphinx40Warning, stacklevel=2)
- if not has_explicit_title:
- if title.endswith('()'):
- # remove parentheses
- title = title[:-2]
- if env.config.add_function_parentheses:
- # add them back to all occurrences if configured
- title += '()'
- # remove parentheses from the target too
- if target.endswith('()'):
- target = target[:-2]
- return title, target
-
def update_title_and_target(self, title: str, target: str) -> Tuple[str, str]:
if not self.has_explicit_title:
if title.endswith('()'):
@@ -195,75 +172,6 @@ class AnyXRefRole(XRefRole):
return result
-def indexmarkup_role(typ: str, rawtext: str, text: str, lineno: int, inliner: Inliner,
- options: Dict = {}, content: List[str] = []
- ) -> Tuple[List[Node], List[system_message]]:
- """Role for PEP/RFC references that generate an index entry."""
- warnings.warn('indexmarkup_role() is deprecated. Please use PEP or RFC class instead.',
- RemovedInSphinx40Warning, stacklevel=2)
- env = inliner.document.settings.env
- if not typ:
- assert env.temp_data['default_role']
- typ = env.temp_data['default_role'].lower()
- else:
- typ = typ.lower()
-
- has_explicit_title, title, target = split_explicit_title(text)
- title = utils.unescape(title)
- target = utils.unescape(target)
- targetid = 'index-%s' % env.new_serialno('index')
- indexnode = addnodes.index()
- targetnode = nodes.target('', '', ids=[targetid])
- inliner.document.note_explicit_target(targetnode)
- if typ == 'pep':
- indexnode['entries'] = [
- ('single', _('Python Enhancement Proposals; PEP %s') % target,
- targetid, '', None)]
- anchor = ''
- anchorindex = target.find('#')
- if anchorindex > 0:
- target, anchor = target[:anchorindex], target[anchorindex:]
- if not has_explicit_title:
- title = "PEP " + utils.unescape(title)
- try:
- pepnum = int(target)
- except ValueError:
- msg = inliner.reporter.error('invalid PEP number %s' % target,
- line=lineno)
- prb = inliner.problematic(rawtext, rawtext, msg)
- return [prb], [msg]
- ref = inliner.document.settings.pep_base_url + 'pep-%04d' % pepnum
- sn = nodes.strong(title, title)
- rn = nodes.reference('', '', internal=False, refuri=ref + anchor,
- classes=[typ])
- rn += sn
- return [indexnode, targetnode, rn], []
- elif typ == 'rfc':
- indexnode['entries'] = [
- ('single', 'RFC; RFC %s' % target, targetid, '', None)]
- anchor = ''
- anchorindex = target.find('#')
- if anchorindex > 0:
- target, anchor = target[:anchorindex], target[anchorindex:]
- if not has_explicit_title:
- title = "RFC " + utils.unescape(title)
- try:
- rfcnum = int(target)
- except ValueError:
- msg = inliner.reporter.error('invalid RFC number %s' % target,
- line=lineno)
- prb = inliner.problematic(rawtext, rawtext, msg)
- return [prb], [msg]
- ref = inliner.document.settings.rfc_base_url + inliner.rfc_url % rfcnum
- sn = nodes.strong(title, title)
- rn = nodes.reference('', '', internal=False, refuri=ref + anchor,
- classes=[typ])
- rn += sn
- return [indexnode, targetnode, rn], []
- else:
- raise ValueError('unknown role type: %s' % typ)
-
-
class PEP(ReferenceRole):
def run(self) -> Tuple[List[Node], List[system_message]]:
target_id = 'index-%s' % self.env.new_serialno('index')
@@ -336,44 +244,6 @@ class RFC(ReferenceRole):
_amp_re = re.compile(r'(?<!&)&(?![&\s])')
-def menusel_role(typ: str, rawtext: str, text: str, lineno: int, inliner: Inliner,
- options: Dict = {}, content: List[str] = []
- ) -> Tuple[List[Node], List[system_message]]:
- warnings.warn('menusel_role() is deprecated. '
- 'Please use MenuSelection or GUILabel class instead.',
- RemovedInSphinx40Warning, stacklevel=2)
- env = inliner.document.settings.env
- if not typ:
- assert env.temp_data['default_role']
- typ = env.temp_data['default_role'].lower()
- else:
- typ = typ.lower()
-
- text = utils.unescape(text)
- if typ == 'menuselection':
- text = text.replace('-->', '\N{TRIANGULAR BULLET}')
- spans = _amp_re.split(text)
-
- node = nodes.inline(rawtext=rawtext)
- for i, span in enumerate(spans):
- span = span.replace('&&', '&')
- if i == 0:
- if len(span) > 0:
- textnode = nodes.Text(span)
- node += textnode
- continue
- accel_node = nodes.inline()
- letter_node = nodes.Text(span[0])
- accel_node += letter_node
- accel_node['classes'].append('accelerator')
- node += accel_node
- textnode = nodes.Text(span[1:])
- node += textnode
-
- node['classes'].append(typ)
- return [node], []
-
-
class GUILabel(SphinxRole):
amp_re = re.compile(r'(?<!&)&(?![&\s])')
@@ -404,59 +274,6 @@ _litvar_re = re.compile('{([^}]+)}')
parens_re = re.compile(r'(\\*{|\\*})')
-def emph_literal_role(typ: str, rawtext: str, text: str, lineno: int, inliner: Inliner,
- options: Dict = {}, content: List[str] = []
- ) -> Tuple[List[Node], List[system_message]]:
- warnings.warn('emph_literal_role() is deprecated. '
- 'Please use EmphasizedLiteral class instead.',
- RemovedInSphinx40Warning, stacklevel=2)
- env = inliner.document.settings.env
- if not typ:
- assert env.temp_data['default_role']
- typ = env.temp_data['default_role'].lower()
- else:
- typ = typ.lower()
-
- retnode = nodes.literal(role=typ.lower(), classes=[typ])
- parts = list(parens_re.split(utils.unescape(text)))
- stack = ['']
- for part in parts:
- matched = parens_re.match(part)
- if matched:
- backslashes = len(part) - 1
- if backslashes % 2 == 1: # escaped
- stack[-1] += "\\" * int((backslashes - 1) / 2) + part[-1]
- elif part[-1] == '{': # rparen
- stack[-1] += "\\" * int(backslashes / 2)
- if len(stack) >= 2 and stack[-2] == "{":
- # nested
- stack[-1] += "{"
- else:
- # start emphasis
- stack.append('{')
- stack.append('')
- else: # lparen
- stack[-1] += "\\" * int(backslashes / 2)
- if len(stack) == 3 and stack[1] == "{" and len(stack[2]) > 0:
- # emphasized word found
- if stack[0]:
- retnode += nodes.Text(stack[0], stack[0])
- retnode += nodes.emphasis(stack[2], stack[2])
- stack = ['']
- else:
- # emphasized word not found; the rparen is not a special symbol
- stack.append('}')
- stack = [''.join(stack)]
- else:
- stack[-1] += part
- if ''.join(stack):
- # remaining is treated as Text
- text = ''.join(stack)
- retnode += nodes.Text(text, text)
-
- return [retnode], []
-
-
class EmphasizedLiteral(SphinxRole):
parens_re = re.compile(r'(\\\\|\\{|\\}|{|})')
@@ -510,22 +327,6 @@ class EmphasizedLiteral(SphinxRole):
_abbr_re = re.compile(r'\((.*)\)$', re.S)
-def abbr_role(typ: str, rawtext: str, text: str, lineno: int, inliner: Inliner,
- options: Dict = {}, content: List[str] = []
- ) -> Tuple[List[Node], List[system_message]]:
- warnings.warn('abbr_role() is deprecated. Please use Abbrevation class instead.',
- RemovedInSphinx40Warning, stacklevel=2)
- text = utils.unescape(text)
- m = _abbr_re.search(text)
- if m is None:
- return [nodes.abbreviation(text, text, **options)], []
- abbr = text[:m.start()].strip()
- expl = m.group(1)
- options = options.copy()
- options['explanation'] = expl
- return [nodes.abbreviation(abbr, abbr, **options)], []
-
-
class Abbreviation(SphinxRole):
abbr_re = re.compile(r'\((.*)\)$', re.S)
@@ -541,62 +342,6 @@ class Abbreviation(SphinxRole):
return [nodes.abbreviation(self.rawtext, text, **options)], []
-def index_role(typ: str, rawtext: str, text: str, lineno: int, inliner: Inliner,
- options: Dict = {}, content: List[str] = []
- ) -> Tuple[List[Node], List[system_message]]:
- warnings.warn('index_role() is deprecated. Please use Index class instead.',
- RemovedInSphinx40Warning, stacklevel=2)
- # create new reference target
- env = inliner.document.settings.env
- targetid = 'index-%s' % env.new_serialno('index')
- targetnode = nodes.target('', '', ids=[targetid])
- # split text and target in role content
- has_explicit_title, title, target = split_explicit_title(text)
- title = utils.unescape(title)
- target = utils.unescape(target)
- # if an explicit target is given, we can process it as a full entry
- if has_explicit_title:
- entries = process_index_entry(target, targetid)
- # otherwise we just create a "single" entry
- else:
- # but allow giving main entry
- main = ''
- if target.startswith('!'):
- target = target[1:]
- title = title[1:]
- main = 'main'
- entries = [('single', target, targetid, main, None)]
- indexnode = addnodes.index()
- indexnode['entries'] = entries
- set_role_source_info(inliner, lineno, indexnode)
- textnode = nodes.Text(title, title)
- return [indexnode, targetnode, textnode], []
-
-
-class Index(ReferenceRole):
- def run(self) -> Tuple[List[Node], List[system_message]]:
- warnings.warn('Index role is deprecated.', RemovedInSphinx40Warning, stacklevel=2)
- target_id = 'index-%s' % self.env.new_serialno('index')
- if self.has_explicit_title:
- # if an explicit target is given, process it as a full entry
- title = self.title
- entries = process_index_entry(self.target, target_id)
- else:
- # otherwise we just create a single entry
- if self.target.startswith('!'):
- title = self.title[1:]
- entries = [('single', self.target[1:], target_id, 'main', None)]
- else:
- title = self.title
- entries = [('single', self.target, target_id, '', None)]
-
- index = addnodes.index(entries=entries)
- target = nodes.target('', '', ids=[target_id])
- text = nodes.Text(title, title)
- self.set_source_info(index)
- return [index, target, text], []
-
-
specific_docroles = {
# links to download references
'download': XRefRole(nodeclass=addnodes.download_reference),
diff --git a/sphinx/search/__init__.py b/sphinx/search/__init__.py
index 048b333d5..5850b86e2 100644
--- a/sphinx/search/__init__.py
+++ b/sphinx/search/__init__.py
@@ -13,22 +13,17 @@ import re
import warnings
from importlib import import_module
from os import path
-from typing import Any, Dict, IO, Iterable, List, Tuple, Set
+from typing import Any, Dict, IO, Iterable, List, Tuple, Set, Type
from docutils import nodes
from docutils.nodes import Node
from sphinx import addnodes
from sphinx import package_dir
-from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.environment import BuildEnvironment
from sphinx.search.jssplitter import splitter_code
from sphinx.util import jsdump
-if False:
- # For type annotation
- from typing import Type # for python3.5.1
-
class SearchLanguage:
"""
@@ -117,7 +112,7 @@ var Stemmer = function() {
len(word) == 0 or not (
((len(word) < 3) and (12353 < ord(word[0]) < 12436)) or
(ord(word[0]) < 256 and (
- len(word) < 3 or word in self.stopwords
+ word in self.stopwords
))))
@@ -200,11 +195,7 @@ class WordCollector(nodes.NodeVisitor):
self.found_title_words = [] # type: List[str]
self.lang = lang
- def is_meta_keywords(self, node: addnodes.meta, nodetype: Any = None) -> bool:
- if nodetype is not None:
- warnings.warn('"nodetype" argument for WordCollector.is_meta_keywords() '
- 'is deprecated.', RemovedInSphinx40Warning, stacklevel=2)
-
+ def is_meta_keywords(self, node: addnodes.meta) -> bool:
if isinstance(node, addnodes.meta) and node.get('name') == 'keywords':
meta_lang = node.get('lang')
if meta_lang is None: # lang not specified
diff --git a/sphinx/setup_command.py b/sphinx/setup_command.py
index 29a9dace7..9965aa15a 100644
--- a/sphinx/setup_command.py
+++ b/sphinx/setup_command.py
@@ -16,6 +16,7 @@ import sys
from distutils.cmd import Command
from distutils.errors import DistutilsOptionError, DistutilsExecError
from io import StringIO
+from typing import TYPE_CHECKING
from sphinx.application import Sphinx
from sphinx.cmd.build import handle_exception
@@ -23,9 +24,8 @@ from sphinx.util.console import nocolor, color_terminal
from sphinx.util.docutils import docutils_namespace, patch_docutils
from sphinx.util.osutil import abspath
-if False:
- # For type annotation
- from typing import Any, Dict # NOQA
+if TYPE_CHECKING:
+ from typing import Any, Dict
class BuildDoc(Command):
diff --git a/sphinx/templates/quickstart/Makefile_t b/sphinx/templates/quickstart/Makefile_t
index 7b656dff0..8a71a928a 100644
--- a/sphinx/templates/quickstart/Makefile_t
+++ b/sphinx/templates/quickstart/Makefile_t
@@ -46,6 +46,7 @@ help:
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
@echo " coverage to run coverage check of the documentation (if enabled)"
@echo " dummy to check syntax errors of document sources"
+ @echo " clean to remove everything in the build directory"
.PHONY: clean
clean:
diff --git a/sphinx/templates/quickstart/make.bat_t b/sphinx/templates/quickstart/make.bat_t
index 830cf656e..e5d93d1ae 100644
--- a/sphinx/templates/quickstart/make.bat_t
+++ b/sphinx/templates/quickstart/make.bat_t
@@ -43,6 +43,7 @@ if "%1" == "help" (
echo. doctest to run all doctests embedded in the documentation if enabled
echo. coverage to run coverage check of the documentation if enabled
echo. dummy to check syntax errors of document sources
+ echo. clean to remove everything in the build directory
goto end
)
diff --git a/sphinx/testing/fixtures.py b/sphinx/testing/fixtures.py
index f457e3745..55e639df6 100644
--- a/sphinx/testing/fixtures.py
+++ b/sphinx/testing/fixtures.py
@@ -8,7 +8,6 @@
:license: BSD, see LICENSE for details.
"""
-import os
import subprocess
import sys
from collections import namedtuple
@@ -91,7 +90,6 @@ def app_params(request: Any, test_params: Dict, shared_result: SharedResult,
args = [pargs[i] for i in sorted(pargs.keys())]
# ##### process pytest.mark.test_params
-
if test_params['shared_result']:
if 'srcdir' in kwargs:
raise pytest.Exception('You can not specify shared_result and '
@@ -237,10 +235,7 @@ def sphinx_test_tempdir(tmpdir_factory: Any) -> "util.path":
"""
temporary directory that wrapped with `path` class.
"""
- tmpdir = os.environ.get('SPHINX_TEST_TEMPDIR') # RemovedInSphinx40Warning
- if tmpdir is None:
- tmpdir = tmpdir_factory.getbasetemp()
-
+ tmpdir = tmpdir_factory.getbasetemp()
return util.path(tmpdir).abspath()
diff --git a/sphinx/testing/util.py b/sphinx/testing/util.py
index c0dd1feac..db1ae22b6 100644
--- a/sphinx/testing/util.py
+++ b/sphinx/testing/util.py
@@ -21,16 +21,13 @@ from docutils.nodes import Node
from docutils.parsers.rst import directives, roles
from sphinx import application, locale
-from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.pycode import ModuleAnalyzer
from sphinx.testing.path import path
from sphinx.util.osutil import relpath
__all__ = [
- 'Struct',
- 'SphinxTestApp', 'SphinxTestAppWrapperForSkipBuilding',
- 'remove_unicode_literals',
+ 'Struct', 'SphinxTestApp', 'SphinxTestAppWrapperForSkipBuilding',
]
@@ -178,12 +175,6 @@ class SphinxTestAppWrapperForSkipBuilding:
_unicode_literals_re = re.compile(r'u(".*?")|u(\'.*?\')')
-def remove_unicode_literals(s: str) -> str:
- warnings.warn('remove_unicode_literals() is deprecated.',
- RemovedInSphinx40Warning, stacklevel=2)
- return _unicode_literals_re.sub(lambda x: x.group(1) or x.group(2), s)
-
-
def find_files(root: str, suffix: bool = None) -> Generator[str, None, None]:
for dirpath, dirs, files in os.walk(root, followlinks=True):
dirpath = path(dirpath)
diff --git a/sphinx/theming.py b/sphinx/theming.py
index 087ee7f24..c05d87407 100644
--- a/sphinx/theming.py
+++ b/sphinx/theming.py
@@ -14,6 +14,7 @@ import shutil
import tempfile
from os import path
from typing import Any, Dict, List
+from typing import TYPE_CHECKING
from zipfile import ZipFile
import pkg_resources
@@ -24,8 +25,7 @@ from sphinx.locale import __
from sphinx.util import logging
from sphinx.util.osutil import ensuredir
-if False:
- # For type annotation
+if TYPE_CHECKING:
from sphinx.application import Sphinx
diff --git a/sphinx/transforms/__init__.py b/sphinx/transforms/__init__.py
index beb983f2a..43ea64de8 100644
--- a/sphinx/transforms/__init__.py
+++ b/sphinx/transforms/__init__.py
@@ -10,6 +10,7 @@
import re
from typing import Any, Dict, Generator, List, Tuple
+from typing import TYPE_CHECKING
from docutils import nodes
from docutils.nodes import Element, Node, Text
@@ -21,7 +22,6 @@ from docutils.utils.smartquotes import smartchars
from sphinx import addnodes
from sphinx.config import Config
-from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
from sphinx.locale import _, __
from sphinx.util import docutils
from sphinx.util import logging
@@ -29,8 +29,7 @@ from sphinx.util.docutils import new_document
from sphinx.util.i18n import format_date
from sphinx.util.nodes import NodeMatcher, apply_source_workaround, is_smartquotable
-if False:
- # For type annotation
+if TYPE_CHECKING:
from sphinx.application import Sphinx
from sphinx.domain.std import StandardDomain
from sphinx.environment import BuildEnvironment
@@ -401,24 +400,6 @@ class ManpageLink(SphinxTransform):
node.attributes.update(info)
-from sphinx.domains.citation import ( # NOQA
- CitationDefinitionTransform, CitationReferenceTransform
-)
-
-deprecated_alias('sphinx.transforms',
- {
- 'CitationReferences': CitationReferenceTransform,
- 'SmartQuotesSkipper': CitationDefinitionTransform,
- },
- RemovedInSphinx40Warning,
- {
- 'CitationReferences':
- 'sphinx.domains.citation.CitationReferenceTransform',
- 'SmartQuotesSkipper':
- 'sphinx.domains.citation.CitationDefinitionTransform',
- })
-
-
def setup(app: "Sphinx") -> Dict[str, Any]:
app.add_transform(ApplySourceWorkaround)
app.add_transform(ExtraTranslatableNodes)
diff --git a/sphinx/transforms/i18n.py b/sphinx/transforms/i18n.py
index c935ab195..b016ba418 100644
--- a/sphinx/transforms/i18n.py
+++ b/sphinx/transforms/i18n.py
@@ -10,7 +10,8 @@
from os import path
from textwrap import indent
-from typing import Any, Dict, List, Tuple, TypeVar
+from typing import Any, Dict, List, Tuple, Type, TypeVar
+from typing import TYPE_CHECKING
from docutils import nodes
from docutils.io import StringInput
@@ -29,9 +30,7 @@ from sphinx.util.nodes import (
extract_messages, is_pending_meta, traverse_translatable_index,
)
-if False:
- # For type annotation
- from typing import Type # for python3.5.1
+if TYPE_CHECKING:
from sphinx.application import Sphinx
@@ -245,6 +244,10 @@ class Locale(SphinxTransform):
node.details['nodes'][0]['content'] = msgstr
continue
+ if isinstance(node, nodes.image) and node.get('alt') == msg:
+ node['alt'] = msgstr
+ continue
+
# Avoid "Literal block expected; none found." warnings.
# If msgstr ends with '::' then it cause warning message at
# parser.parse() processing.
@@ -445,8 +448,9 @@ class Locale(SphinxTransform):
if isinstance(node, LITERAL_TYPE_NODES):
node.rawsource = node.astext()
- if isinstance(node, IMAGE_TYPE_NODES):
- node.update_all_atts(patch)
+ if isinstance(node, nodes.image) and node.get('alt') != msg:
+ node['uri'] = patch['uri']
+ continue # do not mark translated
node['translated'] = True # to avoid double translation
diff --git a/sphinx/transforms/post_transforms/code.py b/sphinx/transforms/post_transforms/code.py
index 2012d6e11..b5b2a95de 100644
--- a/sphinx/transforms/post_transforms/code.py
+++ b/sphinx/transforms/post_transforms/code.py
@@ -21,9 +21,10 @@ from sphinx.ext import doctest
from sphinx.transforms import SphinxTransform
-HighlightSetting = NamedTuple('HighlightSetting', [('language', str),
- ('force', bool),
- ('lineno_threshold', int)])
+class HighlightSetting(NamedTuple):
+ language: str
+ force: bool
+ lineno_threshold: int
class HighlightLanguageTransform(SphinxTransform):
diff --git a/sphinx/transforms/references.py b/sphinx/transforms/references.py
index b6129b0bc..372d06470 100644
--- a/sphinx/transforms/references.py
+++ b/sphinx/transforms/references.py
@@ -9,13 +9,13 @@
"""
from typing import Any, Dict
+from typing import TYPE_CHECKING
from docutils.transforms.references import DanglingReferences
from sphinx.transforms import SphinxTransform
-if False:
- # For type annotation
+if TYPE_CHECKING:
from sphinx.application import Sphinx
diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py
index 082c5caa3..ab9c1c20e 100644
--- a/sphinx/util/__init__.py
+++ b/sphinx/util/__init__.py
@@ -8,7 +8,6 @@
:license: BSD, see LICENSE for details.
"""
-import fnmatch
import functools
import hashlib
import os
@@ -19,19 +18,16 @@ import tempfile
import traceback
import unicodedata
import warnings
-from codecs import BOM_UTF8
-from collections import deque
from datetime import datetime
from importlib import import_module
from os import path
from time import mktime, strptime
-from typing import Any, Callable, Dict, IO, Iterable, Iterator, List, Pattern, Set, Tuple
+from typing import Any, Callable, Dict, IO, Iterable, Iterator, List, Pattern, Set, Tuple, Type
+from typing import TYPE_CHECKING
from urllib.parse import urlsplit, urlunsplit, quote_plus, parse_qsl, urlencode
-from sphinx.deprecation import RemovedInSphinx40Warning, RemovedInSphinx50Warning
-from sphinx.errors import (
- PycodeError, SphinxParallelError, ExtensionError, FiletypeNotFoundError
-)
+from sphinx.deprecation import RemovedInSphinx50Warning
+from sphinx.errors import SphinxParallelError, ExtensionError, FiletypeNotFoundError
from sphinx.locale import __
from sphinx.util import logging
from sphinx.util.console import strip_colors, colorize, bold, term_width_line # type: ignore
@@ -41,7 +37,7 @@ from sphinx.util import smartypants # noqa
# import other utilities; partly for backwards compatibility, so don't
# prune unused ones indiscriminately
from sphinx.util.osutil import ( # noqa
- SEP, os_path, relative_uri, ensuredir, walk, mtimes_of_files, movefile,
+ SEP, os_path, relative_uri, ensuredir, mtimes_of_files, movefile,
copyfile, copytimes, make_filename)
from sphinx.util.nodes import ( # noqa
nested_parse_with_titles, split_explicit_title, explicit_title_re,
@@ -49,9 +45,7 @@ from sphinx.util.nodes import ( # noqa
from sphinx.util.matching import patfilter # noqa
-if False:
- # For type annotation
- from typing import Type # for python3.5.1
+if TYPE_CHECKING:
from sphinx.application import Sphinx
@@ -102,23 +96,6 @@ def get_matching_files(dirname: str,
yield filename
-def get_matching_docs(dirname: str, suffixes: List[str],
- exclude_matchers: Tuple[PathMatcher, ...] = ()) -> Iterable[str]:
- """Get all file names (without suffixes) matching a suffix in a directory,
- recursively.
-
- Exclude files and dirs matching a pattern in *exclude_patterns*.
- """
- warnings.warn('get_matching_docs() is now deprecated. Use get_matching_files() instead.',
- RemovedInSphinx40Warning, stacklevel=2)
- suffixpatterns = ['*' + s for s in suffixes]
- for filename in get_matching_files(dirname, exclude_matchers):
- for suffixpattern in suffixpatterns:
- if fnmatch.fnmatch(filename, suffixpattern):
- yield filename[:-len(suffixpattern) + 1]
- break
-
-
def get_filetype(source_suffix: Dict[str, str], filename: str) -> str:
for suffix, filetype in source_suffix.items():
if filename.endswith(suffix):
@@ -274,53 +251,6 @@ def save_traceback(app: "Sphinx") -> str:
return path
-def get_module_source(modname: str) -> Tuple[str, str]:
- """Try to find the source code for a module.
-
- Can return ('file', 'filename') in which case the source is in the given
- file, or ('string', 'source') which which case the source is the string.
- """
- warnings.warn('get_module_source() is deprecated.',
- RemovedInSphinx40Warning, stacklevel=2)
- try:
- mod = import_module(modname)
- except Exception as err:
- raise PycodeError('error importing %r' % modname, err) from err
- filename = getattr(mod, '__file__', None)
- loader = getattr(mod, '__loader__', None)
- if loader and getattr(loader, 'get_filename', None):
- try:
- filename = loader.get_filename(modname)
- except Exception as err:
- raise PycodeError('error getting filename for %r' % filename, err) from err
- if filename is None and loader:
- try:
- filename = loader.get_source(modname)
- if filename:
- return 'string', filename
- except Exception as err:
- raise PycodeError('error getting source for %r' % modname, err) from err
- if filename is None:
- raise PycodeError('no source found for module %r' % modname)
- filename = path.normpath(path.abspath(filename))
- lfilename = filename.lower()
- if lfilename.endswith('.pyo') or lfilename.endswith('.pyc'):
- filename = filename[:-1]
- if not path.isfile(filename) and path.isfile(filename + 'w'):
- filename += 'w'
- elif not (lfilename.endswith('.py') or lfilename.endswith('.pyw')):
- raise PycodeError('source is not a .py file: %r' % filename)
- elif ('.egg' + os.path.sep) in filename:
- pat = '(?<=\\.egg)' + re.escape(os.path.sep)
- eggpath, _ = re.split(pat, filename, 1)
- if path.isfile(eggpath):
- return 'file', filename
-
- if not path.isfile(filename):
- raise PycodeError('source file is not present: %r' % filename)
- return 'file', filename
-
-
def get_full_modname(modname: str, attribute: str) -> str:
if modname is None:
# Prevents a TypeError: if the last getattr() call will return None
@@ -342,58 +272,6 @@ def get_full_modname(modname: str, attribute: str) -> str:
_coding_re = re.compile(r'coding[:=]\s*([-\w.]+)')
-def detect_encoding(readline: Callable[[], bytes]) -> str:
- """Like tokenize.detect_encoding() from Py3k, but a bit simplified."""
- warnings.warn('sphinx.util.detect_encoding() is deprecated',
- RemovedInSphinx40Warning, stacklevel=2)
-
- def read_or_stop() -> bytes:
- try:
- return readline()
- except StopIteration:
- return None
-
- def get_normal_name(orig_enc: str) -> str:
- """Imitates get_normal_name in tokenizer.c."""
- # Only care about the first 12 characters.
- enc = orig_enc[:12].lower().replace('_', '-')
- if enc == 'utf-8' or enc.startswith('utf-8-'):
- return 'utf-8'
- if enc in ('latin-1', 'iso-8859-1', 'iso-latin-1') or \
- enc.startswith(('latin-1-', 'iso-8859-1-', 'iso-latin-1-')):
- return 'iso-8859-1'
- return orig_enc
-
- def find_cookie(line: bytes) -> str:
- try:
- line_string = line.decode('ascii')
- except UnicodeDecodeError:
- return None
-
- matches = _coding_re.findall(line_string)
- if not matches:
- return None
- return get_normal_name(matches[0])
-
- default = sys.getdefaultencoding()
- first = read_or_stop()
- if first and first.startswith(BOM_UTF8):
- first = first[3:]
- default = 'utf-8-sig'
- if not first:
- return default
- encoding = find_cookie(first)
- if encoding:
- return encoding
- second = read_or_stop()
- if not second:
- return default
- encoding = find_cookie(second)
- if encoding:
- return encoding
- return default
-
-
class UnicodeDecodeErrorHandler:
"""Custom error handler for open() that warns and replaces."""
@@ -462,39 +340,6 @@ def parselinenos(spec: str, total: int) -> List[int]:
return items
-def force_decode(string: str, encoding: str) -> str:
- """Forcibly get a unicode string out of a bytestring."""
- warnings.warn('force_decode() is deprecated.',
- RemovedInSphinx40Warning, stacklevel=2)
- if isinstance(string, bytes):
- try:
- if encoding:
- string = string.decode(encoding)
- else:
- # try decoding with utf-8, should only work for real UTF-8
- string = string.decode()
- except UnicodeError:
- # last resort -- can't fail
- string = string.decode('latin1')
- return string
-
-
-class attrdict(dict):
- def __init__(self, *args: Any, **kwargs: Any) -> None:
- super().__init__(*args, **kwargs)
- warnings.warn('The attrdict class is deprecated.',
- RemovedInSphinx40Warning, stacklevel=2)
-
- def __getattr__(self, key: str) -> str:
- return self[key]
-
- def __setattr__(self, key: str, val: str) -> None:
- self[key] = val
-
- def __delattr__(self, key: str) -> None:
- del self[key]
-
-
def rpartition(s: str, t: str) -> Tuple[str, str]:
"""Similar to str.rpartition from 2.5, but doesn't return the separator."""
warnings.warn('rpartition() is now deprecated.', RemovedInSphinx50Warning, stacklevel=2)
@@ -544,41 +389,6 @@ def format_exception_cut_frames(x: int = 1) -> str:
return ''.join(res)
-class PeekableIterator:
- """
- An iterator which wraps any iterable and makes it possible to peek to see
- what's the next item.
- """
- def __init__(self, iterable: Iterable) -> None:
- self.remaining = deque() # type: deque
- self._iterator = iter(iterable)
- warnings.warn('PeekableIterator is deprecated.',
- RemovedInSphinx40Warning, stacklevel=2)
-
- def __iter__(self) -> "PeekableIterator":
- return self
-
- def __next__(self) -> Any:
- """Return the next item from the iterator."""
- if self.remaining:
- return self.remaining.popleft()
- return next(self._iterator)
-
- next = __next__ # Python 2 compatibility
-
- def push(self, item: Any) -> None:
- """Push the `item` on the internal stack, it will be returned on the
- next :meth:`next` call.
- """
- self.remaining.append(item)
-
- def peek(self) -> Any:
- """Return the next item without changing the state of the iterator."""
- item = next(self)
- self.push(item)
- return item
-
-
def import_object(objname: str, source: str = None) -> Any:
"""Import python object by qualname."""
try:
diff --git a/sphinx/util/cfamily.py b/sphinx/util/cfamily.py
index 0edea128c..66ee96374 100644
--- a/sphinx/util/cfamily.py
+++ b/sphinx/util/cfamily.py
@@ -9,7 +9,6 @@
"""
import re
-import warnings
from copy import deepcopy
from typing import (
Any, Callable, List, Match, Optional, Pattern, Tuple, Union
@@ -19,7 +18,6 @@ from docutils import nodes
from docutils.nodes import TextElement
from sphinx.config import Config
-from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.util import logging
logger = logging.getLogger(__name__)
@@ -87,12 +85,7 @@ def verify_description_mode(mode: str) -> None:
class NoOldIdError(Exception):
# Used to avoid implementing unneeded id generation for old id schemes.
- @property
- def description(self) -> str:
- warnings.warn('%s.description is deprecated. '
- 'Coerce the instance to a string instead.' % self.__class__.__name__,
- RemovedInSphinx40Warning, stacklevel=2)
- return str(self)
+ pass
class ASTBaseBase:
@@ -215,21 +208,11 @@ class ASTBaseParenExprList(ASTBaseBase):
################################################################################
class UnsupportedMultiCharacterCharLiteral(Exception):
- @property
- def decoded(self) -> str:
- warnings.warn('%s.decoded is deprecated. '
- 'Coerce the instance to a string instead.' % self.__class__.__name__,
- RemovedInSphinx40Warning, stacklevel=2)
- return str(self)
+ pass
class DefinitionError(Exception):
- @property
- def description(self) -> str:
- warnings.warn('%s.description is deprecated. '
- 'Coerce the instance to a string instead.' % self.__class__.__name__,
- RemovedInSphinx40Warning, stacklevel=2)
- return str(self)
+ pass
class BaseParser:
diff --git a/sphinx/util/compat.py b/sphinx/util/compat.py
index 7c55c4ec7..6893efaf9 100644
--- a/sphinx/util/compat.py
+++ b/sphinx/util/compat.py
@@ -9,17 +9,10 @@
"""
import sys
-import warnings
from typing import Any, Dict
+from typing import TYPE_CHECKING
-from docutils.utils import get_source_line
-
-from sphinx import addnodes
-from sphinx.deprecation import RemovedInSphinx40Warning
-from sphinx.transforms import SphinxTransform
-
-if False:
- # For type annotation
+if TYPE_CHECKING:
from sphinx.application import Sphinx
@@ -36,22 +29,7 @@ def register_application_for_autosummary(app: "Sphinx") -> None:
autosummary._app = app
-class IndexEntriesMigrator(SphinxTransform):
- """Migrating indexentries from old style (4columns) to new style (5columns)."""
- default_priority = 700
-
- def apply(self, **kwargs: Any) -> None:
- for node in self.document.traverse(addnodes.index):
- for i, entries in enumerate(node['entries']):
- if len(entries) == 4:
- source, line = get_source_line(node)
- warnings.warn('An old styled index node found: %r at (%s:%s)' %
- (node, source, line), RemovedInSphinx40Warning, stacklevel=2)
- node['entries'][i] = entries + (None,)
-
-
def setup(app: "Sphinx") -> Dict[str, Any]:
- app.add_transform(IndexEntriesMigrator)
app.connect('builder-inited', register_application_for_autosummary)
return {
diff --git a/sphinx/util/docfields.py b/sphinx/util/docfields.py
index c07bc7f66..cb11a799d 100644
--- a/sphinx/util/docfields.py
+++ b/sphinx/util/docfields.py
@@ -9,20 +9,16 @@
:license: BSD, see LICENSE for details.
"""
-import warnings
-from typing import Any, Dict, List, Tuple, Union
-from typing import cast
+from typing import Any, Dict, List, Tuple, Type, Union
+from typing import TYPE_CHECKING, cast
from docutils import nodes
from docutils.nodes import Node
from sphinx import addnodes
-from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.util.typing import TextlikeNode
-if False:
- # For type annotation
- from typing import Type # for python3.5.1
+if TYPE_CHECKING:
from sphinx.environment import BuildEnvironment
from sphinx.directive import ObjectDescription
@@ -219,26 +215,7 @@ class DocFieldTransformer:
def __init__(self, directive: "ObjectDescription") -> None:
self.directive = directive
- try:
- self.typemap = directive.get_field_type_map()
- except Exception:
- # for 3rd party extensions directly calls this transformer.
- warnings.warn('DocFieldTransformer expects given directive object is a subclass '
- 'of ObjectDescription.', RemovedInSphinx40Warning, stacklevel=2)
- self.typemap = self.preprocess_fieldtypes(directive.__class__.doc_field_types)
-
- def preprocess_fieldtypes(self, types: List[Field]) -> Dict[str, Tuple[Field, bool]]:
- warnings.warn('DocFieldTransformer.preprocess_fieldtypes() is deprecated.',
- RemovedInSphinx40Warning, stacklevel=2)
- typemap = {}
- for fieldtype in types:
- for name in fieldtype.names:
- typemap[name] = fieldtype, False
- if fieldtype.is_typed:
- typed_field = cast(TypedField, fieldtype)
- for name in typed_field.typenames:
- typemap[name] = typed_field, True
- return typemap
+ self.typemap = directive.get_field_type_map()
def transform_all(self, node: addnodes.desc_content) -> None:
"""Transform all field list children of a node."""
diff --git a/sphinx/util/docutils.py b/sphinx/util/docutils.py
index 3ba7813b6..8ca38bac2 100644
--- a/sphinx/util/docutils.py
+++ b/sphinx/util/docutils.py
@@ -15,8 +15,8 @@ from copy import copy
from distutils.version import LooseVersion
from os import path
from types import ModuleType
-from typing import Any, Callable, Dict, Generator, IO, List, Optional, Set, Tuple
-from typing import cast
+from typing import Any, Callable, Dict, Generator, IO, List, Optional, Set, Tuple, Type
+from typing import TYPE_CHECKING, cast
import docutils
from docutils import nodes
@@ -28,15 +28,14 @@ from docutils.statemachine import StateMachine, State, StringList
from docutils.utils import Reporter, unescape
from sphinx.errors import SphinxError
+from sphinx.locale import _
from sphinx.util import logging
from sphinx.util.typing import RoleFunction
logger = logging.getLogger(__name__)
report_re = re.compile('^(.+?:(?:\\d+)?): \\((DEBUG|INFO|WARNING|ERROR|SEVERE)/(\\d+)?\\) ')
-if False:
- # For type annotation
- from typing import Type # for python3.5.1
+if TYPE_CHECKING:
from sphinx.builders import Builder
from sphinx.config import Config
from sphinx.environment import BuildEnvironment
@@ -145,7 +144,7 @@ def patched_get_language() -> Generator[None, None, None]:
@contextmanager
-def using_user_docutils_conf(confdir: str) -> Generator[None, None, None]:
+def using_user_docutils_conf(confdir: Optional[str]) -> Generator[None, None, None]:
"""Let docutils know the location of ``docutils.conf`` for Sphinx."""
try:
docutilsconfig = os.environ.get('DOCUTILSCONFIG', None)
@@ -161,7 +160,7 @@ def using_user_docutils_conf(confdir: str) -> Generator[None, None, None]:
@contextmanager
-def patch_docutils(confdir: str = None) -> Generator[None, None, None]:
+def patch_docutils(confdir: Optional[str] = None) -> Generator[None, None, None]:
"""Patch to docutils temporarily."""
with patched_get_language(), using_user_docutils_conf(confdir):
yield
@@ -210,6 +209,8 @@ class sphinx_domains:
element = getattr(domain, type)(name)
if element is not None:
return element, []
+ else:
+ logger.warning(_('unknown directive or role name: %s:%s'), domain_name, name)
# else look in the default domain
else:
def_domain = self.env.temp_data.get('default_domain')
@@ -347,15 +348,15 @@ class SphinxRole:
.. note:: The subclasses of this class might not work with docutils.
This class is strongly coupled with Sphinx.
"""
- name = None #: The role name actually used in the document.
- rawtext = None #: A string containing the entire interpreted text input.
- text = None #: The interpreted text content.
- lineno = None #: The line number where the interpreted text begins.
- inliner = None #: The ``docutils.parsers.rst.states.Inliner`` object.
- options = None #: A dictionary of directive options for customization
- #: (from the "role" directive).
- content = None #: A list of strings, the directive content for customization
- #: (from the "role" directive).
+ name: str #: The role name actually used in the document.
+ rawtext: str #: A string containing the entire interpreted text input.
+ text: str #: The interpreted text content.
+ lineno: int #: The line number where the interpreted text begins.
+ inliner: Inliner #: The ``docutils.parsers.rst.states.Inliner`` object.
+ options: Dict #: A dictionary of directive options for customization
+ #: (from the "role" directive).
+ content: List[str] #: A list of strings, the directive content for customization
+ #: (from the "role" directive).
def __call__(self, name: str, rawtext: str, text: str, lineno: int,
inliner: Inliner, options: Dict = {}, content: List[str] = []
@@ -408,10 +409,10 @@ class ReferenceRole(SphinxRole):
the role. The parsed result; link title and target will be stored to
``self.title`` and ``self.target``.
"""
- has_explicit_title = None #: A boolean indicates the role has explicit title or not.
- disabled = False #: A boolean indicates the reference is disabled.
- title = None #: The link title for the interpreted text.
- target = None #: The link target for the interpreted text.
+ has_explicit_title: bool #: A boolean indicates the role has explicit title or not.
+ disabled: bool #: A boolean indicates the reference is disabled.
+ title: str #: The link title for the interpreted text.
+ target: str #: The link target for the interpreted text.
# \x00 means the "<" was backslash-escaped
explicit_title_re = re.compile(r'^(.+?)\s*(?<!\x00)<(.*?)>$', re.DOTALL)
diff --git a/sphinx/util/fileutil.py b/sphinx/util/fileutil.py
index eec1ae463..2f878a578 100644
--- a/sphinx/util/fileutil.py
+++ b/sphinx/util/fileutil.py
@@ -11,14 +11,14 @@
import os
import posixpath
from typing import Callable, Dict
+from typing import TYPE_CHECKING
from docutils.utils import relative_path
from sphinx.util.osutil import copyfile, ensuredir
from sphinx.util.typing import PathMatcher
-if False:
- # For type annotation
+if TYPE_CHECKING:
from sphinx.util.template import BaseRenderer
diff --git a/sphinx/util/i18n.py b/sphinx/util/i18n.py
index 41407f4e1..e5d4f112e 100644
--- a/sphinx/util/i18n.py
+++ b/sphinx/util/i18n.py
@@ -7,34 +7,34 @@
:copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
-import gettext
+
import os
import re
-import warnings
-from collections import namedtuple
from datetime import datetime, timezone
from os import path
-from typing import Callable, Generator, List, Set, Tuple, Union
+from typing import Callable, Generator, List, NamedTuple, Tuple, Union
+from typing import TYPE_CHECKING
import babel.dates
from babel.messages.mofile import write_mo
from babel.messages.pofile import read_po
-from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.errors import SphinxError
from sphinx.locale import __
from sphinx.util import logging
-from sphinx.util.matching import Matcher
from sphinx.util.osutil import SEP, canon_path, relpath
-if False:
- # For type annotation
+if TYPE_CHECKING:
from sphinx.environment import BuildEnvironment
logger = logging.getLogger(__name__)
-LocaleFileInfoBase = namedtuple('CatalogInfo', 'base_dir,domain,charset')
+
+class LocaleFileInfoBase(NamedTuple):
+ base_dir: str
+ domain: str
+ charset: str
class CatalogInfo(LocaleFileInfoBase):
@@ -117,17 +117,6 @@ class CatalogRepository:
yield CatalogInfo(basedir, domain, self.encoding)
-def find_catalog(docname: str, compaction: bool) -> str:
- warnings.warn('find_catalog() is deprecated.',
- RemovedInSphinx40Warning, stacklevel=2)
- if compaction:
- ret = docname.split(SEP, 1)[0]
- else:
- ret = docname
-
- return ret
-
-
def docname_to_domain(docname: str, compation: Union[bool, str]) -> str:
"""Convert docname to domain for catalogs."""
if isinstance(compation, str):
@@ -138,69 +127,6 @@ def docname_to_domain(docname: str, compation: Union[bool, str]) -> str:
return docname
-def find_catalog_files(docname: str, srcdir: str, locale_dirs: List[str],
- lang: str, compaction: bool) -> List[str]:
- warnings.warn('find_catalog_files() is deprecated.',
- RemovedInSphinx40Warning, stacklevel=2)
- if not(lang and locale_dirs):
- return []
-
- domain = find_catalog(docname, compaction)
- files = [gettext.find(domain, path.join(srcdir, dir_), [lang])
- for dir_ in locale_dirs]
- files = [relpath(f, srcdir) for f in files if f]
- return files
-
-
-def find_catalog_source_files(locale_dirs: List[str], locale: str, domains: List[str] = None,
- charset: str = 'utf-8', force_all: bool = False,
- excluded: Matcher = Matcher([])) -> Set[CatalogInfo]:
- """
- :param list locale_dirs:
- list of path as `['locale_dir1', 'locale_dir2', ...]` to find
- translation catalogs. Each path contains a structure such as
- `<locale>/LC_MESSAGES/domain.po`.
- :param str locale: a language as `'en'`
- :param list domains: list of domain names to get. If empty list or None
- is specified, get all domain names. default is None.
- :param boolean force_all:
- Set True if you want to get all catalogs rather than updated catalogs.
- default is False.
- :return: [CatalogInfo(), ...]
- """
- warnings.warn('find_catalog_source_files() is deprecated.',
- RemovedInSphinx40Warning, stacklevel=2)
-
- catalogs = set() # type: Set[CatalogInfo]
-
- if not locale:
- return catalogs # locale is not specified
-
- for locale_dir in locale_dirs:
- if not locale_dir:
- continue # skip system locale directory
-
- base_dir = path.join(locale_dir, locale, 'LC_MESSAGES')
-
- if not path.exists(base_dir):
- continue # locale path is not found
-
- for dirpath, dirnames, filenames in os.walk(base_dir, followlinks=True):
- filenames = [f for f in filenames if f.endswith('.po')]
- for filename in filenames:
- if excluded(path.join(relpath(dirpath, base_dir), filename)):
- continue
- base = path.splitext(filename)[0]
- domain = relpath(path.join(dirpath, base), base_dir).replace(path.sep, SEP)
- if domains and domain not in domains:
- continue
- cat = CatalogInfo(base_dir, domain, charset)
- if force_all or cat.is_outdated():
- catalogs.add(cat)
-
- return catalogs
-
-
# date_format mappings: ustrftime() to bable.dates.format_datetime()
date_format_mappings = {
'%a': 'EEE', # Weekday as locale’s abbreviated name.
diff --git a/sphinx/util/images.py b/sphinx/util/images.py
index 0ddf64908..69bccb351 100644
--- a/sphinx/util/images.py
+++ b/sphinx/util/images.py
@@ -31,9 +31,11 @@ mime_suffixes = OrderedDict([
('.ai', 'application/illustrator'),
])
-DataURI = NamedTuple('DataURI', [('mimetype', str),
- ('charset', str),
- ('data', bytes)])
+
+class DataURI(NamedTuple):
+ mimetype: str
+ charset: str
+ data: bytes
def get_image_size(filename: str) -> Optional[Tuple[int, int]]:
diff --git a/sphinx/util/inspect.py b/sphinx/util/inspect.py
index 9896ec884..b8a149d05 100644
--- a/sphinx/util/inspect.py
+++ b/sphinx/util/inspect.py
@@ -22,11 +22,11 @@ from inspect import ( # NOQA
Parameter, isclass, ismethod, ismethoddescriptor, ismodule
)
from io import StringIO
-from typing import Any, Callable, Dict, Mapping, List, Optional, Tuple
+from typing import Any, Callable, Dict
from typing import cast
-from sphinx.deprecation import RemovedInSphinx40Warning, RemovedInSphinx50Warning
-from sphinx.pycode.ast import ast # for py35-37
+from sphinx.deprecation import RemovedInSphinx50Warning
+from sphinx.pycode.ast import ast # for py36-37
from sphinx.pycode.ast import unparse as ast_unparse
from sphinx.util import logging
from sphinx.util.typing import ForwardRef
@@ -263,7 +263,7 @@ def is_singledispatch_method(obj: Any) -> bool:
try:
from functools import singledispatchmethod # type: ignore
return isinstance(obj, singledispatchmethod)
- except ImportError: # py35-37
+ except ImportError: # py36-37
return False
@@ -341,23 +341,6 @@ def safe_getattr(obj: Any, name: str, *defargs: Any) -> Any:
raise AttributeError(name) from exc
-def safe_getmembers(object: Any, predicate: Callable[[str], bool] = None,
- attr_getter: Callable = safe_getattr) -> List[Tuple[str, Any]]:
- """A version of inspect.getmembers() that uses safe_getattr()."""
- warnings.warn('safe_getmembers() is deprecated', RemovedInSphinx40Warning, stacklevel=2)
-
- results = [] # type: List[Tuple[str, Any]]
- for key in dir(object):
- try:
- value = attr_getter(object, key, None)
- except AttributeError:
- continue
- if not predicate or predicate(value):
- results.append((key, value))
- results.sort()
- return results
-
-
def object_description(object: Any) -> str:
"""A repr() implementation that returns text safe to use in reST context."""
if isinstance(object, dict):
@@ -665,154 +648,6 @@ def signature_from_ast(node: ast.FunctionDef, code: str = '') -> inspect.Signatu
return inspect.Signature(params, return_annotation=return_annotation)
-class Signature:
- """The Signature object represents the call signature of a callable object and
- its return annotation.
- """
-
- empty = inspect.Signature.empty
-
- def __init__(self, subject: Callable, bound_method: bool = False,
- has_retval: bool = True) -> None:
- warnings.warn('sphinx.util.inspect.Signature() is deprecated',
- RemovedInSphinx40Warning, stacklevel=2)
-
- # check subject is not a built-in class (ex. int, str)
- if (isinstance(subject, type) and
- is_builtin_class_method(subject, "__new__") and
- is_builtin_class_method(subject, "__init__")):
- raise TypeError("can't compute signature for built-in type {}".format(subject))
-
- self.subject = subject
- self.has_retval = has_retval
- self.partialmethod_with_noargs = False
-
- try:
- self.signature = inspect.signature(subject) # type: Optional[inspect.Signature]
- except IndexError:
- # Until python 3.6.4, cpython has been crashed on inspection for
- # partialmethods not having any arguments.
- # https://bugs.python.org/issue33009
- if hasattr(subject, '_partialmethod'):
- self.signature = None
- self.partialmethod_with_noargs = True
- else:
- raise
-
- try:
- self.annotations = typing.get_type_hints(subject)
- except Exception:
- # get_type_hints() does not support some kind of objects like partial,
- # ForwardRef and so on. For them, it raises an exception. In that case,
- # we try to build annotations from argspec.
- self.annotations = {}
-
- if bound_method:
- # client gives a hint that the subject is a bound method
-
- if inspect.ismethod(subject):
- # inspect.signature already considers the subject is bound method.
- # So it is not need to skip first argument.
- self.skip_first_argument = False
- else:
- self.skip_first_argument = True
- else:
- # inspect.signature recognizes type of method properly without any hints
- self.skip_first_argument = False
-
- @property
- def parameters(self) -> Mapping:
- if self.partialmethod_with_noargs:
- return {}
- else:
- return self.signature.parameters
-
- @property
- def return_annotation(self) -> Any:
- if self.signature:
- if self.has_retval:
- return self.signature.return_annotation
- else:
- return Parameter.empty
- else:
- return None
-
- def format_args(self, show_annotation: bool = True) -> str:
- def get_annotation(param: Parameter) -> Any:
- if isinstance(param.annotation, str) and param.name in self.annotations:
- return self.annotations[param.name]
- else:
- return param.annotation
-
- args = []
- last_kind = None
- for i, param in enumerate(self.parameters.values()):
- # skip first argument if subject is bound method
- if self.skip_first_argument and i == 0:
- continue
-
- arg = StringIO()
-
- # insert '*' between POSITIONAL args and KEYWORD_ONLY args::
- # func(a, b, *, c, d):
- if param.kind == param.KEYWORD_ONLY and last_kind in (param.POSITIONAL_OR_KEYWORD,
- param.POSITIONAL_ONLY,
- None):
- args.append('*')
-
- if param.kind in (param.POSITIONAL_ONLY,
- param.POSITIONAL_OR_KEYWORD,
- param.KEYWORD_ONLY):
- arg.write(param.name)
- if show_annotation and param.annotation is not param.empty:
- arg.write(': ')
- arg.write(stringify_annotation(get_annotation(param)))
- if param.default is not param.empty:
- if param.annotation is param.empty or show_annotation is False:
- arg.write('=')
- arg.write(object_description(param.default))
- else:
- arg.write(' = ')
- arg.write(object_description(param.default))
- elif param.kind == param.VAR_POSITIONAL:
- arg.write('*')
- arg.write(param.name)
- if show_annotation and param.annotation is not param.empty:
- arg.write(': ')
- arg.write(stringify_annotation(get_annotation(param)))
- elif param.kind == param.VAR_KEYWORD:
- arg.write('**')
- arg.write(param.name)
- if show_annotation and param.annotation is not param.empty:
- arg.write(': ')
- arg.write(stringify_annotation(get_annotation(param)))
-
- args.append(arg.getvalue())
- last_kind = param.kind
-
- if self.return_annotation is Parameter.empty or show_annotation is False:
- return '(%s)' % ', '.join(args)
- else:
- if 'return' in self.annotations:
- annotation = stringify_annotation(self.annotations['return'])
- else:
- annotation = stringify_annotation(self.return_annotation)
-
- return '(%s) -> %s' % (', '.join(args), annotation)
-
- def format_annotation(self, annotation: Any) -> str:
- """Return formatted representation of a type annotation."""
- return stringify_annotation(annotation)
-
- def format_annotation_new(self, annotation: Any) -> str:
- """format_annotation() for py37+"""
- return stringify_annotation(annotation)
-
- def format_annotation_old(self, annotation: Any) -> str:
- """format_annotation() for py36 or below"""
- return stringify_annotation(annotation)
-
-
def getdoc(obj: Any, attrgetter: Callable = safe_getattr,
allow_inherited: bool = False, cls: Any = None, name: str = None) -> str:
"""Get the docstring for the object.
diff --git a/sphinx/util/inventory.py b/sphinx/util/inventory.py
index 1e3572323..eb26161c0 100644
--- a/sphinx/util/inventory.py
+++ b/sphinx/util/inventory.py
@@ -11,6 +11,7 @@ import os
import re
import zlib
from typing import Callable, IO, Iterator
+from typing import TYPE_CHECKING
from sphinx.util import logging
from sphinx.util.typing import Inventory
@@ -19,8 +20,7 @@ from sphinx.util.typing import Inventory
BUFSIZE = 16 * 1024
logger = logging.getLogger(__name__)
-if False:
- # For type annotation
+if TYPE_CHECKING:
from sphinx.builders import Builder
from sphinx.environment import BuildEnvironment
diff --git a/sphinx/util/jsonimpl.py b/sphinx/util/jsonimpl.py
deleted file mode 100644
index 35501f03a..000000000
--- a/sphinx/util/jsonimpl.py
+++ /dev/null
@@ -1,46 +0,0 @@
-"""
- sphinx.util.jsonimpl
- ~~~~~~~~~~~~~~~~~~~~
-
- JSON serializer implementation wrapper.
-
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
- :license: BSD, see LICENSE for details.
-"""
-
-import json
-import warnings
-from collections import UserString
-from typing import Any, IO
-
-from sphinx.deprecation import RemovedInSphinx40Warning
-
-
-warnings.warn('sphinx.util.jsonimpl is deprecated',
- RemovedInSphinx40Warning, stacklevel=2)
-
-
-class SphinxJSONEncoder(json.JSONEncoder):
- """JSONEncoder subclass that forces translation proxies."""
- def default(self, obj: Any) -> str:
- if isinstance(obj, UserString):
- return str(obj)
- return super().default(obj)
-
-
-def dump(obj: Any, fp: IO, *args: Any, **kwargs: Any) -> None:
- kwargs['cls'] = SphinxJSONEncoder
- json.dump(obj, fp, *args, **kwargs)
-
-
-def dumps(obj: Any, *args: Any, **kwargs: Any) -> str:
- kwargs['cls'] = SphinxJSONEncoder
- return json.dumps(obj, *args, **kwargs)
-
-
-def load(*args: Any, **kwargs: Any) -> Any:
- return json.load(*args, **kwargs)
-
-
-def loads(*args: Any, **kwargs: Any) -> Any:
- return json.loads(*args, **kwargs)
diff --git a/sphinx/util/logging.py b/sphinx/util/logging.py
index 5889f3860..5206363a5 100644
--- a/sphinx/util/logging.py
+++ b/sphinx/util/logging.py
@@ -12,7 +12,8 @@ import logging
import logging.handlers
from collections import defaultdict
from contextlib import contextmanager
-from typing import Any, Dict, Generator, IO, List, Tuple, Union
+from typing import Any, Dict, Generator, IO, List, Tuple, Type, Union
+from typing import TYPE_CHECKING
from docutils import nodes
from docutils.nodes import Node
@@ -21,9 +22,7 @@ from docutils.utils import get_source_line
from sphinx.errors import SphinxWarning
from sphinx.util.console import colorize
-if False:
- # For type annotation
- from typing import Type # for python3.5.1
+if TYPE_CHECKING:
from sphinx.application import Sphinx
diff --git a/sphinx/util/nodes.py b/sphinx/util/nodes.py
index b4d796f61..f757bc9c3 100644
--- a/sphinx/util/nodes.py
+++ b/sphinx/util/nodes.py
@@ -10,9 +10,8 @@
import re
import unicodedata
-import warnings
-from typing import Any, Callable, Iterable, List, Set, Tuple
-from typing import cast
+from typing import Any, Callable, Iterable, List, Set, Tuple, Type
+from typing import TYPE_CHECKING, cast
from docutils import nodes
from docutils.nodes import Element, Node
@@ -21,13 +20,10 @@ from docutils.parsers.rst.states import Inliner
from docutils.statemachine import StringList
from sphinx import addnodes
-from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.locale import __
from sphinx.util import logging
-if False:
- # For type annotation
- from typing import Type # for python3.5.1
+if TYPE_CHECKING:
from sphinx.builders import Builder
from sphinx.domain import IndexEntry
from sphinx.environment import BuildEnvironment
@@ -201,6 +197,10 @@ def is_translatable(node: Node) -> bool:
if isinstance(node, addnodes.translatable):
return True
+ # image node marked as translatable or having alt text
+ if isinstance(node, nodes.image) and (node.get('translatable') or node.get('alt')):
+ return True
+
if isinstance(node, nodes.Inline) and 'translatable' not in node: # type: ignore
# inline node must not be translated if 'translatable' is not set
return False
@@ -228,9 +228,6 @@ def is_translatable(node: Node) -> bool:
return False
return True
- if isinstance(node, nodes.image) and node.get('translatable'):
- return True
-
if isinstance(node, addnodes.meta):
return True
if is_pending_meta(node):
@@ -264,10 +261,13 @@ def extract_messages(doctree: Element) -> Iterable[Tuple[Element, str]]:
msg = node.rawsource
if not msg:
msg = node.astext()
- elif isinstance(node, IMAGE_TYPE_NODES):
- msg = '.. image:: %s' % node['uri']
+ elif isinstance(node, nodes.image):
if node.get('alt'):
- msg += '\n :alt: %s' % node['alt']
+ yield node, node['alt']
+ if node.get('translatable'):
+ msg = '.. image:: %s' % node['uri']
+ else:
+ msg = None
elif isinstance(node, META_TYPE_NODES):
msg = node.rawcontent
elif isinstance(node, nodes.pending) and is_pending_meta(node):
@@ -280,12 +280,6 @@ def extract_messages(doctree: Element) -> Iterable[Tuple[Element, str]]:
yield node, msg
-def find_source_node(node: Element) -> str:
- warnings.warn('find_source_node() is deprecated.',
- RemovedInSphinx40Warning, stacklevel=2)
- return get_node_source(node)
-
-
def get_node_source(node: Element) -> str:
for pnode in traverse_parent(node):
if pnode.source:
@@ -613,10 +607,12 @@ def process_only_nodes(document: Node, tags: "Tags") -> None:
node.replace_self(nodes.comment())
-# monkey-patch Element.copy to copy the rawsource and line
-# for docutils-0.14 or older versions.
-
def _new_copy(self: Element) -> Element:
+ """monkey-patch Element.copy to copy the rawsource and line
+ for docutils-0.16 or older versions.
+
+ refs: https://sourceforge.net/p/docutils/patches/165/
+ """
newnode = self.__class__(self.rawsource, **self.attributes)
if isinstance(self, nodes.Element):
newnode.source = self.source
diff --git a/sphinx/util/osutil.py b/sphinx/util/osutil.py
index 0390b038d..bd774cdad 100644
--- a/sphinx/util/osutil.py
+++ b/sphinx/util/osutil.py
@@ -9,18 +9,14 @@
"""
import contextlib
-import errno
import filecmp
import os
import re
import shutil
import sys
-import warnings
from io import StringIO
from os import path
-from typing import Any, Generator, Iterator, List, Optional, Tuple
-
-from sphinx.deprecation import RemovedInSphinx40Warning
+from typing import Any, Generator, Iterator, List, Optional, Type
try:
# for ALT Linux (#6712)
@@ -28,15 +24,6 @@ try:
except ImportError:
Path = None # type: ignore
-if False:
- # For type annotation
- from typing import Type # for python3.5.1
-
-# Errnos that we need.
-EEXIST = getattr(errno, 'EEXIST', 0) # RemovedInSphinx40Warning
-ENOENT = getattr(errno, 'ENOENT', 0) # RemovedInSphinx40Warning
-EPIPE = getattr(errno, 'EPIPE', 0) # RemovedInSphinx40Warning
-EINVAL = getattr(errno, 'EINVAL', 0) # RemovedInSphinx40Warning
# SEP separates path elements in the canonical file names
#
@@ -83,13 +70,6 @@ def ensuredir(path: str) -> None:
os.makedirs(path, exist_ok=True)
-def walk(top: str, topdown: bool = True, followlinks: bool = False) -> Iterator[Tuple[str, List[str], List[str]]]: # NOQA
- warnings.warn('sphinx.util.osutil.walk() is deprecated for removal. '
- 'Please use os.walk() instead.',
- RemovedInSphinx40Warning, stacklevel=2)
- return os.walk(top, topdown=topdown, followlinks=followlinks)
-
-
def mtimes_of_files(dirnames: List[str], suffix: str) -> Iterator[float]:
for dirname in dirnames:
for root, dirs, files in os.walk(dirname):
@@ -175,13 +155,6 @@ def abspath(pathdir: str) -> str:
return pathdir
-def getcwd() -> str:
- warnings.warn('sphinx.util.osutil.getcwd() is deprecated. '
- 'Please use os.getcwd() instead.',
- RemovedInSphinx40Warning, stacklevel=2)
- return os.getcwd()
-
-
@contextlib.contextmanager
def cd(target_dir: str) -> Generator[None, None, None]:
cwd = os.getcwd()
diff --git a/sphinx/util/pycompat.py b/sphinx/util/pycompat.py
index 50eee5ce3..5aeb0f9cc 100644
--- a/sphinx/util/pycompat.py
+++ b/sphinx/util/pycompat.py
@@ -8,21 +8,10 @@
:license: BSD, see LICENSE for details.
"""
-import html
-import io
-import sys
-import textwrap
import warnings
from typing import Any, Callable
-from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
-from sphinx.locale import __
-from sphinx.util import logging
-from sphinx.util.console import terminal_safe
-from sphinx.util.typing import NoneType
-
-
-logger = logging.getLogger(__name__)
+from sphinx.deprecation import RemovedInSphinx60Warning
# ------------------------------------------------------------------------------
@@ -31,6 +20,9 @@ logger = logging.getLogger(__name__)
# convert_with_2to3():
# support for running 2to3 over config files
def convert_with_2to3(filepath: str) -> str:
+ warnings.warn('convert_with_2to3() is deprecated',
+ RemovedInSphinx60Warning, stacklevel=2)
+
try:
from lib2to3.refactor import RefactoringTool, get_fixers_from_package
from lib2to3.pgen2.parse import ParseError
@@ -54,57 +46,14 @@ def convert_with_2to3(filepath: str) -> str:
return str(tree)
-class UnicodeMixin:
- """Mixin class to handle defining the proper __str__/__unicode__
- methods in Python 2 or 3.
-
- .. deprecated:: 2.0
- """
- def __str__(self) -> str:
- warnings.warn('UnicodeMixin is deprecated',
- RemovedInSphinx40Warning, stacklevel=2)
- return self.__unicode__() # type: ignore
-
-
def execfile_(filepath: str, _globals: Any, open: Callable = open) -> None:
+ warnings.warn('execfile_() is deprecated',
+ RemovedInSphinx60Warning, stacklevel=2)
from sphinx.util.osutil import fs_encoding
with open(filepath, 'rb') as f:
source = f.read()
# compile to a code object, handle syntax errors
filepath_enc = filepath.encode(fs_encoding)
- try:
- code = compile(source, filepath_enc, 'exec')
- except SyntaxError:
- # maybe the file uses 2.x syntax; try to refactor to
- # 3.x syntax using 2to3
- source = convert_with_2to3(filepath)
- code = compile(source, filepath_enc, 'exec')
- # TODO: When support for evaluating Python 2 syntax is removed,
- # deprecate convert_with_2to3().
- logger.warning(__('Support for evaluating Python 2 syntax is deprecated '
- 'and will be removed in Sphinx 4.0. '
- 'Convert %s to Python 3 syntax.'),
- filepath)
+ code = compile(source, filepath_enc, 'exec')
exec(code, _globals)
-
-
-deprecated_alias('sphinx.util.pycompat',
- {
- 'NoneType': NoneType,
- 'TextIOWrapper': io.TextIOWrapper,
- 'htmlescape': html.escape,
- 'indent': textwrap.indent,
- 'terminal_safe': terminal_safe,
- 'sys_encoding': sys.getdefaultencoding(),
- 'u': '',
- },
- RemovedInSphinx40Warning,
- {
- 'NoneType': 'sphinx.util.typing.NoneType',
- 'TextIOWrapper': 'io.TextIOWrapper',
- 'htmlescape': 'html.escape',
- 'indent': 'textwrap.indent',
- 'terminal_safe': 'sphinx.util.console.terminal_safe',
- 'sys_encoding': 'sys.getdefaultencoding',
- })
diff --git a/sphinx/util/smartypants.py b/sphinx/util/smartypants.py
index 43f8bc724..2b7503379 100644
--- a/sphinx/util/smartypants.py
+++ b/sphinx/util/smartypants.py
@@ -26,13 +26,19 @@
"""
import re
+import warnings
from typing import Generator, Iterable, Tuple
from docutils.utils import smartquotes
+from sphinx.deprecation import RemovedInSphinx60Warning
from sphinx.util.docutils import __version_info__ as docutils_version
+warnings.warn('sphinx.util.smartypants is deprecated.',
+ RemovedInSphinx60Warning)
+
+
langquotes = {'af': '“”‘’',
'af-x-altquot': '„”‚’',
'bg': '„“‚‘', # Bulgarian, https://bg.wikipedia.org/wiki/Кавички
diff --git a/sphinx/util/texescape.py b/sphinx/util/texescape.py
index afa1c349e..051370481 100644
--- a/sphinx/util/texescape.py
+++ b/sphinx/util/texescape.py
@@ -11,8 +11,6 @@
import re
from typing import Dict
-from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
-
tex_replacements = [
# map TeX special chars
@@ -109,14 +107,6 @@ _tex_hlescape_map = {} # type: Dict[int, str]
_tex_hlescape_map_without_unicode = {} # type: Dict[int, str]
-deprecated_alias('sphinx.util.texescape',
- {
- 'tex_escape_map': _tex_escape_map,
- 'tex_hl_escape_map_new': _tex_hlescape_map,
- },
- RemovedInSphinx40Warning)
-
-
def escape(s: str, latex_engine: str = None) -> str:
"""Escape text for LaTeX output."""
if latex_engine in ('lualatex', 'xelatex'):
diff --git a/sphinx/util/typing.py b/sphinx/util/typing.py
index cc4d648be..ebd6518e0 100644
--- a/sphinx/util/typing.py
+++ b/sphinx/util/typing.py
@@ -22,7 +22,7 @@ else:
from typing import _ForwardRef # type: ignore
class ForwardRef:
- """A pseudo ForwardRef class for py35 and py36."""
+ """A pseudo ForwardRef class for py36."""
def __init__(self, arg: Any, is_argument: bool = True) -> None:
self.arg = arg
@@ -151,7 +151,7 @@ def _restify_py36(cls: Optional["Type"]) -> str:
qualname = repr(cls)
if (isinstance(cls, typing.TupleMeta) and # type: ignore
- not hasattr(cls, '__tuple_params__')): # for Python 3.6
+ not hasattr(cls, '__tuple_params__')):
params = cls.__args__
if params:
param_str = ', '.join(restify(p) for p in params)
@@ -159,40 +159,22 @@ def _restify_py36(cls: Optional["Type"]) -> str:
else:
return ':class:`%s`' % qualname
elif isinstance(cls, typing.GenericMeta):
- params = None
- if hasattr(cls, '__args__'):
- # for Python 3.5.2+
- if cls.__args__ is None or len(cls.__args__) <= 2: # type: ignore # NOQA
- params = cls.__args__ # type: ignore
- elif cls.__origin__ == Generator: # type: ignore
- params = cls.__args__ # type: ignore
- else: # typing.Callable
- args = ', '.join(restify(arg) for arg in cls.__args__[:-1]) # type: ignore
- result = restify(cls.__args__[-1]) # type: ignore
- return ':class:`%s`\\ [[%s], %s]' % (qualname, args, result)
- elif hasattr(cls, '__parameters__'):
- # for Python 3.5.0 and 3.5.1
- params = cls.__parameters__ # type: ignore
+ if cls.__args__ is None or len(cls.__args__) <= 2: # type: ignore # NOQA
+ params = cls.__args__ # type: ignore
+ elif cls.__origin__ == Generator: # type: ignore
+ params = cls.__args__ # type: ignore
+ else: # typing.Callable
+ args = ', '.join(restify(arg) for arg in cls.__args__[:-1]) # type: ignore
+ result = restify(cls.__args__[-1]) # type: ignore
+ return ':class:`%s`\\ [[%s], %s]' % (qualname, args, result)
if params:
param_str = ', '.join(restify(p) for p in params)
return ':class:`%s`\\ [%s]' % (qualname, param_str)
else:
return ':class:`%s`' % qualname
- elif (hasattr(typing, 'UnionMeta') and
- isinstance(cls, typing.UnionMeta) and # type: ignore
- hasattr(cls, '__union_params__')): # for Python 3.5
- params = cls.__union_params__
- if params is not None:
- if len(params) == 2 and params[1] is NoneType:
- return ':obj:`Optional`\\ [%s]' % restify(params[0])
- else:
- param_str = ', '.join(restify(p) for p in params)
- return ':obj:`%s`\\ [%s]' % (qualname, param_str)
- else:
- return ':obj:`%s`' % qualname
elif (hasattr(cls, '__origin__') and
- cls.__origin__ is typing.Union): # for Python 3.5.2+
+ cls.__origin__ is typing.Union):
params = cls.__args__
if params is not None:
if len(params) > 1 and params[-1] is NoneType:
@@ -206,31 +188,6 @@ def _restify_py36(cls: Optional["Type"]) -> str:
return ':obj:`Union`\\ [%s]' % param_str
else:
return ':obj:`Union`'
- elif (isinstance(cls, typing.CallableMeta) and # type: ignore
- getattr(cls, '__args__', None) is not None and
- hasattr(cls, '__result__')): # for Python 3.5
- # Skipped in the case of plain typing.Callable
- args = cls.__args__
- if args is None:
- return qualname
- elif args is Ellipsis:
- args_str = '...'
- else:
- formatted_args = (restify(a) for a in args) # type: ignore
- args_str = '[%s]' % ', '.join(formatted_args)
-
- return ':class:`%s`\\ [%s, %s]' % (qualname, args_str, stringify(cls.__result__))
- elif (isinstance(cls, typing.TupleMeta) and # type: ignore
- hasattr(cls, '__tuple_params__') and
- hasattr(cls, '__tuple_use_ellipsis__')): # for Python 3.5
- params = cls.__tuple_params__
- if params is not None:
- param_strings = [restify(p) for p in params]
- if cls.__tuple_use_ellipsis__:
- param_strings.append('...')
- return ':class:`%s`\\ [%s]' % (qualname, ', '.join(param_strings))
- else:
- return ':class:`%s`' % qualname
elif hasattr(cls, '__qualname__'):
if cls.__module__ == 'typing':
return ':class:`%s`' % cls.__qualname__
@@ -332,7 +289,7 @@ def _stringify_py37(annotation: Any) -> str:
def _stringify_py36(annotation: Any) -> str:
- """stringify() for py35 and py36."""
+ """stringify() for py36."""
module = getattr(annotation, '__module__', None)
if module == 'typing':
if getattr(annotation, '_name', None):
@@ -360,35 +317,20 @@ def _stringify_py36(annotation: Any) -> str:
return qualname
elif isinstance(annotation, typing.GenericMeta):
params = None
- if hasattr(annotation, '__args__'):
- # for Python 3.5.2+
- if annotation.__args__ is None or len(annotation.__args__) <= 2: # type: ignore # NOQA
- params = annotation.__args__ # type: ignore
- elif annotation.__origin__ == Generator: # type: ignore
- params = annotation.__args__ # type: ignore
- else: # typing.Callable
- args = ', '.join(stringify(arg) for arg
- in annotation.__args__[:-1]) # type: ignore
- result = stringify(annotation.__args__[-1]) # type: ignore
- return '%s[[%s], %s]' % (qualname, args, result)
- elif hasattr(annotation, '__parameters__'):
- # for Python 3.5.0 and 3.5.1
- params = annotation.__parameters__ # type: ignore
+ if annotation.__args__ is None or len(annotation.__args__) <= 2: # type: ignore # NOQA
+ params = annotation.__args__ # type: ignore
+ elif annotation.__origin__ == Generator: # type: ignore
+ params = annotation.__args__ # type: ignore
+ else: # typing.Callable
+ args = ', '.join(stringify(arg) for arg
+ in annotation.__args__[:-1]) # type: ignore
+ result = stringify(annotation.__args__[-1]) # type: ignore
+ return '%s[[%s], %s]' % (qualname, args, result)
if params is not None:
param_str = ', '.join(stringify(p) for p in params)
return '%s[%s]' % (qualname, param_str)
- elif (hasattr(typing, 'UnionMeta') and
- isinstance(annotation, typing.UnionMeta) and # type: ignore
- hasattr(annotation, '__union_params__')): # for Python 3.5
- params = annotation.__union_params__
- if params is not None:
- if len(params) == 2 and params[1] is NoneType:
- return 'Optional[%s]' % stringify(params[0])
- else:
- param_str = ', '.join(stringify(p) for p in params)
- return '%s[%s]' % (qualname, param_str)
elif (hasattr(annotation, '__origin__') and
- annotation.__origin__ is typing.Union): # for Python 3.5.2+
+ annotation.__origin__ is typing.Union):
params = annotation.__args__
if params is not None:
if len(params) > 1 and params[-1] is NoneType:
@@ -400,30 +342,5 @@ def _stringify_py36(annotation: Any) -> str:
else:
param_str = ', '.join(stringify(p) for p in params)
return 'Union[%s]' % param_str
- elif (isinstance(annotation, typing.CallableMeta) and # type: ignore
- getattr(annotation, '__args__', None) is not None and
- hasattr(annotation, '__result__')): # for Python 3.5
- # Skipped in the case of plain typing.Callable
- args = annotation.__args__
- if args is None:
- return qualname
- elif args is Ellipsis:
- args_str = '...'
- else:
- formatted_args = (stringify(a) for a in args)
- args_str = '[%s]' % ', '.join(formatted_args)
- return '%s[%s, %s]' % (qualname,
- args_str,
- stringify(annotation.__result__))
- elif (isinstance(annotation, typing.TupleMeta) and # type: ignore
- hasattr(annotation, '__tuple_params__') and
- hasattr(annotation, '__tuple_use_ellipsis__')): # for Python 3.5
- params = annotation.__tuple_params__
- if params is not None:
- param_strings = [stringify(p) for p in params]
- if annotation.__tuple_use_ellipsis__:
- param_strings.append('...')
- return '%s[%s]' % (qualname,
- ', '.join(param_strings))
return qualname
diff --git a/sphinx/versioning.py b/sphinx/versioning.py
index 7307b13d1..4f925741c 100644
--- a/sphinx/versioning.py
+++ b/sphinx/versioning.py
@@ -13,14 +13,14 @@ from itertools import product, zip_longest
from operator import itemgetter
from os import path
from typing import Any, Dict, Iterator
+from typing import TYPE_CHECKING
from uuid import uuid4
from docutils.nodes import Node
from sphinx.transforms import SphinxTransform
-if False:
- # For type annotation
+if TYPE_CHECKING:
from sphinx.application import Sphinx
try:
diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py
index 8813c2d12..6ec976e75 100644
--- a/sphinx/writers/html.py
+++ b/sphinx/writers/html.py
@@ -12,9 +12,8 @@ import copy
import os
import posixpath
import re
-import warnings
-from typing import Any, Iterable, Tuple
-from typing import cast
+from typing import Iterable, Tuple
+from typing import TYPE_CHECKING, cast
from docutils import nodes
from docutils.nodes import Element, Node, Text
@@ -22,14 +21,12 @@ from docutils.writers.html4css1 import Writer, HTMLTranslator as BaseTranslator
from sphinx import addnodes
from sphinx.builders import Builder
-from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.locale import admonitionlabels, _, __
from sphinx.util import logging
from sphinx.util.docutils import SphinxTranslator
from sphinx.util.images import get_image_size
-if False:
- # For type annotation
+if TYPE_CHECKING:
from sphinx.builders.html import StandaloneHTMLBuilder
@@ -86,14 +83,7 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
builder = None # type: StandaloneHTMLBuilder
- def __init__(self, *args: Any) -> None:
- if isinstance(args[0], nodes.document) and isinstance(args[1], Builder):
- document, builder = args
- else:
- warnings.warn('The order of arguments for HTMLTranslator has been changed. '
- 'Please give "document" as 1st and "builder" as 2nd.',
- RemovedInSphinx40Warning, stacklevel=2)
- builder, document = args
+ def __init__(self, document: nodes.document, builder: Builder) -> None:
super().__init__(document, builder)
self.highlighter = self.builder.highlighter
diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py
index 4ceeaafdf..5eb35cbfe 100644
--- a/sphinx/writers/html5.py
+++ b/sphinx/writers/html5.py
@@ -12,8 +12,8 @@ import os
import posixpath
import re
import warnings
-from typing import Any, Iterable, Tuple
-from typing import cast
+from typing import Iterable, Tuple
+from typing import TYPE_CHECKING, cast
from docutils import nodes
from docutils.nodes import Element, Node, Text
@@ -21,14 +21,13 @@ from docutils.writers.html5_polyglot import HTMLTranslator as BaseTranslator
from sphinx import addnodes
from sphinx.builders import Builder
-from sphinx.deprecation import RemovedInSphinx40Warning
+from sphinx.deprecation import RemovedInSphinx60Warning
from sphinx.locale import admonitionlabels, _, __
from sphinx.util import logging
from sphinx.util.docutils import SphinxTranslator
from sphinx.util.images import get_image_size
-if False:
- # For type annotation
+if TYPE_CHECKING:
from sphinx.builders.html import StandaloneHTMLBuilder
@@ -58,14 +57,7 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
builder = None # type: StandaloneHTMLBuilder
- def __init__(self, *args: Any) -> None:
- if isinstance(args[0], nodes.document) and isinstance(args[1], Builder):
- document, builder = args
- else:
- warnings.warn('The order of arguments for HTML5Translator has been changed. '
- 'Please give "document" as 1st and "builder" as 2nd.',
- RemovedInSphinx40Warning, stacklevel=2)
- builder, document = args
+ def __init__(self, document: nodes.document, builder: Builder) -> None:
super().__init__(document, builder)
self.highlighter = self.builder.highlighter
@@ -719,22 +711,7 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
# overwritten to add even/odd classes
- def generate_targets_for_table(self, node: Element) -> None:
- """Generate hyperlink targets for tables.
-
- Original visit_table() generates hyperlink targets inside table tags
- (<table>) if multiple IDs are assigned to listings.
- That is invalid DOM structure. (This is a bug of docutils <= 0.13.1)
-
- This exports hyperlink targets before tables to make valid DOM structure.
- """
- for id in node['ids'][1:]:
- self.body.append('<span id="%s"></span>' % id)
- node['ids'].remove(id)
-
def visit_table(self, node: Element) -> None:
- self.generate_targets_for_table(node)
-
self._table_row_index = 0
atts = {}
@@ -791,3 +768,18 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
def unknown_visit(self, node: Node) -> None:
raise NotImplementedError('Unknown node: ' + node.__class__.__name__)
+
+ def generate_targets_for_table(self, node: Element) -> None:
+ """Generate hyperlink targets for tables.
+
+ Original visit_table() generates hyperlink targets inside table tags
+ (<table>) if multiple IDs are assigned to listings.
+ That is invalid DOM structure. (This is a bug of docutils <= 0.13.1)
+
+ This exports hyperlink targets before tables to make valid DOM structure.
+ """
+ warnings.warn('generate_targets_for_table() is deprecated',
+ RemovedInSphinx60Warning, stacklevel=2)
+ for id in node['ids'][1:]:
+ self.body.append('<span id="%s"></span>' % id)
+ node['ids'].remove(id)
diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py
index b77202951..ace37d539 100644
--- a/sphinx/writers/latex.py
+++ b/sphinx/writers/latex.py
@@ -15,17 +15,15 @@ import re
import warnings
from collections import defaultdict
from os import path
-from typing import Any, Dict, Iterable, Iterator, List, Tuple, Set, Union
-from typing import cast
+from typing import Any, Dict, Iterable, List, Tuple, Set
+from typing import TYPE_CHECKING, cast
from docutils import nodes, writers
from docutils.nodes import Element, Node, Text
from sphinx import addnodes
from sphinx import highlighting
-from sphinx.deprecation import (
- RemovedInSphinx40Warning, RemovedInSphinx50Warning, deprecated_alias
-)
+from sphinx.deprecation import RemovedInSphinx50Warning
from sphinx.domains import IndexEntry
from sphinx.domains.std import StandardDomain
from sphinx.errors import SphinxError
@@ -42,8 +40,7 @@ except ImportError:
# In Debain/Ubuntu, roman package is provided as roman, not as docutils.utils.roman
from roman import toRoman # type: ignore
-if False:
- # For type annotation
+if TYPE_CHECKING:
from sphinx.builders.latex import LaTeXBuilder
from sphinx.builders.latex.theming import Theme
@@ -2043,121 +2040,6 @@ class LaTeXTranslator(SphinxTranslator):
def unknown_visit(self, node: Node) -> None:
raise NotImplementedError('Unknown node: ' + node.__class__.__name__)
- # --------- METHODS FOR COMPATIBILITY --------------------------------------
-
- def collect_footnotes(self, node: Element) -> Dict[str, List[Union["collected_footnote", bool]]]: # NOQA
- def footnotes_under(n: Element) -> Iterator[nodes.footnote]:
- if isinstance(n, nodes.footnote):
- yield n
- else:
- for c in n.children:
- if isinstance(c, addnodes.start_of_file):
- continue
- elif isinstance(c, nodes.Element):
- yield from footnotes_under(c)
-
- warnings.warn('LaTeXWriter.collected_footnote() is deprecated.',
- RemovedInSphinx40Warning, stacklevel=2)
-
- fnotes = {} # type: Dict[str, List[Union[collected_footnote, bool]]]
- for fn in footnotes_under(node):
- label = cast(nodes.label, fn[0])
- num = label.astext().strip()
- newnode = collected_footnote('', *fn.children, number=num)
- fnotes[num] = [newnode, False]
- return fnotes
-
- @property
- def no_contractions(self) -> int:
- warnings.warn('LaTeXTranslator.no_contractions is deprecated.',
- RemovedInSphinx40Warning, stacklevel=2)
- return 0
-
- def babel_defmacro(self, name: str, definition: str) -> str:
- warnings.warn('babel_defmacro() is deprecated.',
- RemovedInSphinx40Warning, stacklevel=2)
-
- if self.elements['babel']:
- prefix = '\\addto\\extras%s{' % self.babel.get_language()
- suffix = '}'
- else: # babel is disabled (mainly for Japanese environment)
- prefix = ''
- suffix = ''
-
- return ('%s\\def%s{%s}%s\n' % (prefix, name, definition, suffix))
-
- def generate_numfig_format(self, builder: "LaTeXBuilder") -> str:
- warnings.warn('generate_numfig_format() is deprecated.',
- RemovedInSphinx40Warning, stacklevel=2)
- ret = [] # type: List[str]
- figure = self.builder.config.numfig_format['figure'].split('%s', 1)
- if len(figure) == 1:
- ret.append('\\def\\fnum@figure{%s}\n' % self.escape(figure[0]).strip())
- else:
- definition = escape_abbr(self.escape(figure[0]))
- ret.append(self.babel_renewcommand('\\figurename', definition))
- ret.append('\\makeatletter\n')
- ret.append('\\def\\fnum@figure{\\figurename\\thefigure{}%s}\n' %
- self.escape(figure[1]))
- ret.append('\\makeatother\n')
-
- table = self.builder.config.numfig_format['table'].split('%s', 1)
- if len(table) == 1:
- ret.append('\\def\\fnum@table{%s}\n' % self.escape(table[0]).strip())
- else:
- definition = escape_abbr(self.escape(table[0]))
- ret.append(self.babel_renewcommand('\\tablename', definition))
- ret.append('\\makeatletter\n')
- ret.append('\\def\\fnum@table{\\tablename\\thetable{}%s}\n' %
- self.escape(table[1]))
- ret.append('\\makeatother\n')
-
- codeblock = self.builder.config.numfig_format['code-block'].split('%s', 1)
- if len(codeblock) == 1:
- pass # FIXME
- else:
- definition = self.escape(codeblock[0]).strip()
- ret.append(self.babel_renewcommand('\\literalblockname', definition))
- if codeblock[1]:
- pass # FIXME
-
- return ''.join(ret)
-
-
-# Import old modules here for compatibility
-from sphinx.builders.latex import constants # NOQA
-from sphinx.builders.latex.util import ExtBabel # NOQA
-
-
-deprecated_alias('sphinx.writers.latex',
- {
- 'ADDITIONAL_SETTINGS': constants.ADDITIONAL_SETTINGS,
- 'DEFAULT_SETTINGS': constants.DEFAULT_SETTINGS,
- 'LUALATEX_DEFAULT_FONTPKG': constants.LUALATEX_DEFAULT_FONTPKG,
- 'PDFLATEX_DEFAULT_FONTPKG': constants.PDFLATEX_DEFAULT_FONTPKG,
- 'SHORTHANDOFF': constants.SHORTHANDOFF,
- 'XELATEX_DEFAULT_FONTPKG': constants.XELATEX_DEFAULT_FONTPKG,
- 'XELATEX_GREEK_DEFAULT_FONTPKG': constants.XELATEX_GREEK_DEFAULT_FONTPKG,
- 'ExtBabel': ExtBabel,
- },
- RemovedInSphinx40Warning,
- {
- 'ADDITIONAL_SETTINGS':
- 'sphinx.builders.latex.constants.ADDITIONAL_SETTINGS',
- 'DEFAULT_SETTINGS':
- 'sphinx.builders.latex.constants.DEFAULT_SETTINGS',
- 'LUALATEX_DEFAULT_FONTPKG':
- 'sphinx.builders.latex.constants.LUALATEX_DEFAULT_FONTPKG',
- 'PDFLATEX_DEFAULT_FONTPKG':
- 'sphinx.builders.latex.constants.PDFLATEX_DEFAULT_FONTPKG',
- 'SHORTHANDOFF':
- 'sphinx.builders.latex.constants.SHORTHANDOFF',
- 'XELATEX_DEFAULT_FONTPKG':
- 'sphinx.builders.latex.constants.XELATEX_DEFAULT_FONTPKG',
- 'XELATEX_GREEK_DEFAULT_FONTPKG':
- 'sphinx.builders.latex.constants.XELATEX_GREEK_DEFAULT_FONTPKG',
- 'ExtBabel': 'sphinx.builders.latex.util.ExtBabel',
- })
# FIXME: Workaround to avoid circular import
# refs: https://github.com/sphinx-doc/sphinx/issues/5433
diff --git a/sphinx/writers/manpage.py b/sphinx/writers/manpage.py
index 7da2f4e8f..2af96b530 100644
--- a/sphinx/writers/manpage.py
+++ b/sphinx/writers/manpage.py
@@ -8,7 +8,6 @@
:license: BSD, see LICENSE for details.
"""
-import warnings
from typing import Any, Dict, Iterable
from typing import cast
@@ -21,7 +20,6 @@ from docutils.writers.manpage import (
from sphinx import addnodes
from sphinx.builders import Builder
-from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.locale import admonitionlabels, _
from sphinx.util import logging
from sphinx.util.docutils import SphinxTranslator
@@ -81,14 +79,7 @@ class ManualPageTranslator(SphinxTranslator, BaseTranslator):
_docinfo = {} # type: Dict[str, Any]
- def __init__(self, *args: Any) -> None:
- if isinstance(args[0], nodes.document) and isinstance(args[1], Builder):
- document, builder = args
- else:
- warnings.warn('The order of arguments for ManualPageTranslator has been changed. '
- 'Please give "document" as 1st and "builder" as 2nd.',
- RemovedInSphinx40Warning, stacklevel=2)
- builder, document = args
+ def __init__(self, document: nodes.document, builder: Builder) -> None:
super().__init__(document, builder)
self.in_productionlist = 0
diff --git a/sphinx/writers/texinfo.py b/sphinx/writers/texinfo.py
index e0d7d04fa..e6062b1a0 100644
--- a/sphinx/writers/texinfo.py
+++ b/sphinx/writers/texinfo.py
@@ -13,7 +13,7 @@ import textwrap
import warnings
from os import path
from typing import Any, Dict, Iterable, Iterator, List, Optional, Pattern, Set, Tuple, Union
-from typing import cast
+from typing import TYPE_CHECKING, cast
from docutils import nodes, writers
from docutils.nodes import Element, Node, Text
@@ -29,8 +29,7 @@ from sphinx.util.docutils import SphinxTranslator
from sphinx.util.i18n import format_date
from sphinx.writers.latex import collected_footnote
-if False:
- # For type annotation
+if TYPE_CHECKING:
from sphinx.builders.texinfo import TexinfoBuilder
diff --git a/sphinx/writers/text.py b/sphinx/writers/text.py
index 9cb066e0b..972393035 100644
--- a/sphinx/writers/text.py
+++ b/sphinx/writers/text.py
@@ -13,7 +13,7 @@ import re
import textwrap
from itertools import groupby, chain
from typing import Any, Dict, Generator, List, Iterable, Optional, Set, Tuple, Union
-from typing import cast
+from typing import TYPE_CHECKING, cast
from docutils import nodes, writers
from docutils.nodes import Element, Node, Text
@@ -23,8 +23,7 @@ from sphinx import addnodes
from sphinx.locale import admonitionlabels, _
from sphinx.util.docutils import SphinxTranslator
-if False:
- # For type annotation
+if TYPE_CHECKING:
from sphinx.builders.text import TextBuilder
diff --git a/tests/roots/test-intl/xx/LC_MESSAGES/figure.po b/tests/roots/test-intl/xx/LC_MESSAGES/figure.po
index 449b15e3f..64bbdf763 100644
--- a/tests/roots/test-intl/xx/LC_MESSAGES/figure.po
+++ b/tests/roots/test-intl/xx/LC_MESSAGES/figure.po
@@ -37,19 +37,17 @@ msgstr "BLOCK"
msgid "image url and alt"
msgstr "IMAGE URL AND ALT"
-msgid ""
-".. image:: img.png\n"
-" :alt: img"
-msgstr ""
-".. image:: i18n.png\n"
-" :alt: IMG -> I18N"
+msgid "img"
+msgstr "IMG -> I18N"
-msgid ""
-".. image:: i18n.png\n"
-" :alt: i18n"
-msgstr ""
-".. image:: img.png\n"
-" :alt: I18N -> IMG"
+msgid ".. image:: img.png"
+msgstr ".. image:: i18n.png"
+
+msgid "i18n"
+msgstr "I18N -> IMG"
+
+msgid ".. image:: i18n.png"
+msgstr ".. image:: img.png"
msgid "image on substitution"
msgstr "IMAGE ON SUBSTITUTION"
diff --git a/tests/test_build_epub.py b/tests/test_build_epub.py
index c17954fd3..759090ce3 100644
--- a/tests/test_build_epub.py
+++ b/tests/test_build_epub.py
@@ -15,8 +15,6 @@ from xml.etree import ElementTree
import pytest
-from sphinx.util import docutils
-
# check given command is runnable
def runnable(command):
@@ -355,8 +353,6 @@ def test_epub_css_files(app):
'href="https://example.com/custom.css" />' not in content)
-@pytest.mark.skipif(docutils.__version_info__ < (0, 13),
- reason='docutils-0.13 or above is required')
@pytest.mark.sphinx('epub', testroot='roles-download')
def test_html_download_role(app, status, warning):
app.build()
diff --git a/tests/test_build_html.py b/tests/test_build_html.py
index 1f9f8eac7..22b60dc74 100644
--- a/tests/test_build_html.py
+++ b/tests/test_build_html.py
@@ -20,7 +20,7 @@ from html5lib import HTMLParser
from sphinx.builders.html import validate_html_extra_path, validate_html_static_path
from sphinx.errors import ConfigError
from sphinx.testing.util import strip_escseq
-from sphinx.util import docutils, md5
+from sphinx.util import md5
from sphinx.util.inventory import InventoryFile
@@ -410,8 +410,6 @@ def test_html4_output(app, status, warning):
(".//a[@href='_sources/otherext.foo.txt']", ''),
]
}))
-@pytest.mark.skipif(docutils.__version_info__ < (0, 13),
- reason='docutils-0.13 or above is required')
@pytest.mark.sphinx('html', tags=['testtag'],
confoverrides={'html_context.hckey_co': 'hcval_co'})
@pytest.mark.test_params(shared_result='test_build_html_output')
@@ -426,8 +424,6 @@ def test_html_parallel(app):
app.build()
-@pytest.mark.skipif(docutils.__version_info__ < (0, 13),
- reason='docutils-0.13 or above is required')
@pytest.mark.sphinx('html')
@pytest.mark.test_params(shared_result='test_build_html_output')
def test_html_download(app):
@@ -452,8 +448,6 @@ def test_html_download(app):
assert matched.group(1) == filename
-@pytest.mark.skipif(docutils.__version_info__ < (0, 13),
- reason='docutils-0.13 or above is required')
@pytest.mark.sphinx('html', testroot='roles-download')
def test_html_download_role(app, status, warning):
app.build()
@@ -532,8 +526,6 @@ def test_html_translator(app):
(".//h1//span[@class='section-number']", '2.1.1. ', True),
],
}))
-@pytest.mark.skipif(docutils.__version_info__ < (0, 13),
- reason='docutils-0.13 or above is required')
@pytest.mark.sphinx('html', testroot='tocdepth')
@pytest.mark.test_params(shared_result='test_build_html_tocdepth')
def test_tocdepth(app, cached_etree_parse, fname, expect):
@@ -579,8 +571,6 @@ def test_tocdepth(app, cached_etree_parse, fname, expect):
(".//h4//span[@class='section-number']", '2.1.1. ', True),
],
}))
-@pytest.mark.skipif(docutils.__version_info__ < (0, 13),
- reason='docutils-0.13 or above is required')
@pytest.mark.sphinx('singlehtml', testroot='tocdepth')
@pytest.mark.test_params(shared_result='test_build_html_tocdepth')
def test_tocdepth_singlehtml(app, cached_etree_parse, fname, expect):
@@ -638,8 +628,6 @@ def test_numfig_disabled_warn(app, warning):
"span[@class='caption-number']", None, True),
],
}))
-@pytest.mark.skipif(docutils.__version_info__ < (0, 13),
- reason='docutils-0.13 or above is required')
@pytest.mark.sphinx('html', testroot='numfig')
@pytest.mark.test_params(shared_result='test_build_html_numfig')
def test_numfig_disabled(app, cached_etree_parse, fname, expect):
@@ -746,8 +734,6 @@ def test_numfig_without_numbered_toctree_warn(app, warning):
"span[@class='caption-number']", '^Listing 6 $', True),
],
}))
-@pytest.mark.skipif(docutils.__version_info__ < (0, 13),
- reason='docutils-0.13 or above is required')
@pytest.mark.sphinx(
'html', testroot='numfig',
srcdir='test_numfig_without_numbered_toctree',
@@ -854,8 +840,6 @@ def test_numfig_with_numbered_toctree_warn(app, warning):
"span[@class='caption-number']", '^Listing 2.2 $', True),
],
}))
-@pytest.mark.skipif(docutils.__version_info__ < (0, 13),
- reason='docutils-0.13 or above is required')
@pytest.mark.sphinx('html', testroot='numfig', confoverrides={'numfig': True})
@pytest.mark.test_params(shared_result='test_build_html_numfig_on')
def test_numfig_with_numbered_toctree(app, cached_etree_parse, fname, expect):
@@ -959,8 +943,6 @@ def test_numfig_with_prefix_warn(app, warning):
"span[@class='caption-number']", '^Code-2.2 $', True),
],
}))
-@pytest.mark.skipif(docutils.__version_info__ < (0, 13),
- reason='docutils-0.13 or above is required')
@pytest.mark.sphinx('html', testroot='numfig',
confoverrides={'numfig': True,
'numfig_format': {'figure': 'Figure:%s',
@@ -1065,8 +1047,6 @@ def test_numfig_with_secnum_depth_warn(app, warning):
"span[@class='caption-number']", '^Listing 2.1.2 $', True),
],
}))
-@pytest.mark.skipif(docutils.__version_info__ < (0, 13),
- reason='docutils-0.13 or above is required')
@pytest.mark.sphinx('html', testroot='numfig',
confoverrides={'numfig': True,
'numfig_secnum_depth': 2})
@@ -1150,8 +1130,6 @@ def test_numfig_with_secnum_depth(app, cached_etree_parse, fname, expect):
"span[@class='caption-number']", '^Listing 2.2 $', True),
],
}))
-@pytest.mark.skipif(docutils.__version_info__ < (0, 13),
- reason='docutils-0.13 or above is required')
@pytest.mark.sphinx('singlehtml', testroot='numfig', confoverrides={'numfig': True})
@pytest.mark.test_params(shared_result='test_build_html_numfig_on')
def test_numfig_with_singlehtml(app, cached_etree_parse, fname, expect):
@@ -1176,8 +1154,6 @@ def test_numfig_with_singlehtml(app, cached_etree_parse, fname, expect):
(".//li/p/a/span", 'No.2', True),
],
}))
-@pytest.mark.skipif(docutils.__version_info__ < (0, 13),
- reason='docutils-0.13 or above is required')
@pytest.mark.sphinx('html', testroot='add_enumerable_node',
srcdir='test_enumerable_node')
def test_enumerable_node(app, cached_etree_parse, fname, expect):
diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py
index 8dbe2a48c..f97dbe9d9 100644
--- a/tests/test_build_latex.py
+++ b/tests/test_build_latex.py
@@ -22,7 +22,6 @@ from sphinx.builders.latex import default_latex_documents
from sphinx.config import Config
from sphinx.errors import SphinxError, ThemeError
from sphinx.testing.util import strip_escseq
-from sphinx.util import docutils
from sphinx.util.osutil import cd, ensuredir
from sphinx.writers.latex import LaTeXTranslator
@@ -1124,8 +1123,6 @@ def test_maxlistdepth_at_ten(app, status, warning):
compile_latex_document(app, 'python.tex')
-@pytest.mark.skipif(docutils.__version_info__ < (0, 13),
- reason='docutils-0.13 or above is required')
@pytest.mark.sphinx('latex', testroot='latex-table')
@pytest.mark.test_params(shared_result='latex-table')
def test_latex_table_tabulars(app, status, warning):
@@ -1195,8 +1192,6 @@ def test_latex_table_tabulars(app, status, warning):
assert actual == expected
-@pytest.mark.skipif(docutils.__version_info__ < (0, 13),
- reason='docutils-0.13 or above is required')
@pytest.mark.sphinx('latex', testroot='latex-table')
@pytest.mark.test_params(shared_result='latex-table')
def test_latex_table_longtable(app, status, warning):
@@ -1256,8 +1251,6 @@ def test_latex_table_longtable(app, status, warning):
assert actual == expected
-@pytest.mark.skipif(docutils.__version_info__ < (0, 13),
- reason='docutils-0.13 or above is required')
@pytest.mark.sphinx('latex', testroot='latex-table')
@pytest.mark.test_params(shared_result='latex-table')
def test_latex_table_complex_tables(app, status, warning):
diff --git a/tests/test_build_manpage.py b/tests/test_build_manpage.py
index d4b1a320e..0f026b19b 100644
--- a/tests/test_build_manpage.py
+++ b/tests/test_build_manpage.py
@@ -17,9 +17,9 @@ from sphinx.config import Config
@pytest.mark.sphinx('man')
def test_all(app, status, warning):
app.builder.build_all()
- assert (app.outdir / 'sphinxtests.1').exists()
+ assert (app.outdir / '1' / 'sphinxtests.1').exists()
- content = (app.outdir / 'sphinxtests.1').read_text()
+ content = (app.outdir / '1' / 'sphinxtests.1').read_text()
assert r'\fBprint \fP\fIi\fP\fB\en\fP' in content
assert r'\fBmanpage\en\fP' in content
@@ -31,16 +31,16 @@ def test_all(app, status, warning):
@pytest.mark.sphinx('man', testroot='basic',
- confoverrides={'man_make_section_directory': True})
+ confoverrides={'man_make_section_directory': False})
def test_man_make_section_directory(app, status, warning):
app.build()
- assert (app.outdir / '1' / 'python.1').exists()
+ assert (app.outdir / 'python.1').exists()
@pytest.mark.sphinx('man', testroot='directive-code')
def test_captioned_code_block(app, status, warning):
app.builder.build_all()
- content = (app.outdir / 'python.1').read_text()
+ content = (app.outdir / '1' / 'python.1').read_text()
assert ('.sp\n'
'caption \\fItest\\fP rb\n'
@@ -71,5 +71,5 @@ def test_default_man_pages():
@pytest.mark.sphinx('man', testroot='markup-rubric')
def test_rubric(app, status, warning):
app.build()
- content = (app.outdir / 'python.1').read_text()
+ content = (app.outdir / '1' / 'python.1').read_text()
assert 'This is a rubric\n' in content
diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py
index b22a51730..72308f4af 100644
--- a/tests/test_domain_cpp.py
+++ b/tests/test_domain_cpp.py
@@ -19,7 +19,6 @@ from sphinx.domains.cpp import DefinitionParser, DefinitionError, NoOldIdError
from sphinx.domains.cpp import Symbol, _max_id, _id_prefix
from sphinx.testing import restructuredtext
from sphinx.testing.util import assert_node
-from sphinx.util import docutils
def parse(name, string):
@@ -1077,8 +1076,6 @@ def test_build_domain_cpp_misuse_of_roles(app, status, warning):
assert len(ws) == len(warn)
-@pytest.mark.skipif(docutils.__version_info__ < (0, 13),
- reason='docutils-0.13 or above is required')
@pytest.mark.sphinx(testroot='domain-cpp', confoverrides={'add_function_parentheses': True})
def test_build_domain_cpp_with_add_function_parentheses_is_True(app, status, warning):
app.builder.build_all()
@@ -1120,8 +1117,6 @@ def test_build_domain_cpp_with_add_function_parentheses_is_True(app, status, war
check(s, t, f)
-@pytest.mark.skipif(docutils.__version_info__ < (0, 13),
- reason='docutils-0.13 or above is required')
@pytest.mark.sphinx(testroot='domain-cpp', confoverrides={'add_function_parentheses': False})
def test_build_domain_cpp_with_add_function_parentheses_is_False(app, status, warning):
app.builder.build_all()
diff --git a/tests/test_domain_py.py b/tests/test_domain_py.py
index d81b406c2..e9facc0e2 100644
--- a/tests/test_domain_py.py
+++ b/tests/test_domain_py.py
@@ -192,20 +192,22 @@ def test_domain_py_find_obj(app, status, warning):
assert (find_obj(None, None, 'NONEXISTANT', 'class') == [])
assert (find_obj(None, None, 'NestedParentA', 'class') ==
- [('NestedParentA', ('roles', 'NestedParentA', 'class'))])
+ [('NestedParentA', ('roles', 'NestedParentA', 'class', False))])
assert (find_obj(None, None, 'NestedParentA.NestedChildA', 'class') ==
- [('NestedParentA.NestedChildA', ('roles', 'NestedParentA.NestedChildA', 'class'))])
+ [('NestedParentA.NestedChildA',
+ ('roles', 'NestedParentA.NestedChildA', 'class', False))])
assert (find_obj(None, 'NestedParentA', 'NestedChildA', 'class') ==
- [('NestedParentA.NestedChildA', ('roles', 'NestedParentA.NestedChildA', 'class'))])
+ [('NestedParentA.NestedChildA',
+ ('roles', 'NestedParentA.NestedChildA', 'class', False))])
assert (find_obj(None, None, 'NestedParentA.NestedChildA.subchild_1', 'meth') ==
[('NestedParentA.NestedChildA.subchild_1',
- ('roles', 'NestedParentA.NestedChildA.subchild_1', 'method'))])
+ ('roles', 'NestedParentA.NestedChildA.subchild_1', 'method', False))])
assert (find_obj(None, 'NestedParentA', 'NestedChildA.subchild_1', 'meth') ==
[('NestedParentA.NestedChildA.subchild_1',
- ('roles', 'NestedParentA.NestedChildA.subchild_1', 'method'))])
+ ('roles', 'NestedParentA.NestedChildA.subchild_1', 'method', False))])
assert (find_obj(None, 'NestedParentA.NestedChildA', 'subchild_1', 'meth') ==
[('NestedParentA.NestedChildA.subchild_1',
- ('roles', 'NestedParentA.NestedChildA.subchild_1', 'method'))])
+ ('roles', 'NestedParentA.NestedChildA.subchild_1', 'method', False))])
def test_get_full_qualified_name():
@@ -507,7 +509,7 @@ def test_pydata(app):
[desc_content, ()])]))
assert_node(doctree[3][0][2][1], pending_xref, **{"py:module": "example"})
assert 'example.var' in domain.objects
- assert domain.objects['example.var'] == ('index', 'example.var', 'data')
+ assert domain.objects['example.var'] == ('index', 'example.var', 'data', False)
def test_pyfunction(app):
@@ -537,9 +539,9 @@ def test_pyfunction(app):
entries=[('single', 'func2() (in module example)', 'example.func2', '', None)])
assert 'func1' in domain.objects
- assert domain.objects['func1'] == ('index', 'func1', 'function')
+ assert domain.objects['func1'] == ('index', 'func1', 'function', False)
assert 'example.func2' in domain.objects
- assert domain.objects['example.func2'] == ('index', 'example.func2', 'function')
+ assert domain.objects['example.func2'] == ('index', 'example.func2', 'function', False)
def test_pyclass_options(app):
@@ -561,13 +563,13 @@ def test_pyclass_options(app):
assert_node(doctree[0], addnodes.index,
entries=[('single', 'Class1 (built-in class)', 'Class1', '', None)])
assert 'Class1' in domain.objects
- assert domain.objects['Class1'] == ('index', 'Class1', 'class')
+ assert domain.objects['Class1'] == ('index', 'Class1', 'class', False)
# :final:
assert_node(doctree[2], addnodes.index,
entries=[('single', 'Class2 (built-in class)', 'Class2', '', None)])
assert 'Class2' in domain.objects
- assert domain.objects['Class2'] == ('index', 'Class2', 'class')
+ assert domain.objects['Class2'] == ('index', 'Class2', 'class', False)
def test_pymethod_options(app):
@@ -613,7 +615,7 @@ def test_pymethod_options(app):
[desc_parameterlist, ()])],
[desc_content, ()]))
assert 'Class.meth1' in domain.objects
- assert domain.objects['Class.meth1'] == ('index', 'Class.meth1', 'method')
+ assert domain.objects['Class.meth1'] == ('index', 'Class.meth1', 'method', False)
# :classmethod:
assert_node(doctree[1][1][2], addnodes.index,
@@ -623,7 +625,7 @@ def test_pymethod_options(app):
[desc_parameterlist, ()])],
[desc_content, ()]))
assert 'Class.meth2' in domain.objects
- assert domain.objects['Class.meth2'] == ('index', 'Class.meth2', 'method')
+ assert domain.objects['Class.meth2'] == ('index', 'Class.meth2', 'method', False)
# :staticmethod:
assert_node(doctree[1][1][4], addnodes.index,
@@ -633,7 +635,7 @@ def test_pymethod_options(app):
[desc_parameterlist, ()])],
[desc_content, ()]))
assert 'Class.meth3' in domain.objects
- assert domain.objects['Class.meth3'] == ('index', 'Class.meth3', 'method')
+ assert domain.objects['Class.meth3'] == ('index', 'Class.meth3', 'method', False)
# :async:
assert_node(doctree[1][1][6], addnodes.index,
@@ -643,7 +645,7 @@ def test_pymethod_options(app):
[desc_parameterlist, ()])],
[desc_content, ()]))
assert 'Class.meth4' in domain.objects
- assert domain.objects['Class.meth4'] == ('index', 'Class.meth4', 'method')
+ assert domain.objects['Class.meth4'] == ('index', 'Class.meth4', 'method', False)
# :property:
assert_node(doctree[1][1][8], addnodes.index,
@@ -652,7 +654,7 @@ def test_pymethod_options(app):
[desc_name, "meth5"])],
[desc_content, ()]))
assert 'Class.meth5' in domain.objects
- assert domain.objects['Class.meth5'] == ('index', 'Class.meth5', 'method')
+ assert domain.objects['Class.meth5'] == ('index', 'Class.meth5', 'method', False)
# :abstractmethod:
assert_node(doctree[1][1][10], addnodes.index,
@@ -662,7 +664,7 @@ def test_pymethod_options(app):
[desc_parameterlist, ()])],
[desc_content, ()]))
assert 'Class.meth6' in domain.objects
- assert domain.objects['Class.meth6'] == ('index', 'Class.meth6', 'method')
+ assert domain.objects['Class.meth6'] == ('index', 'Class.meth6', 'method', False)
# :final:
assert_node(doctree[1][1][12], addnodes.index,
@@ -672,7 +674,7 @@ def test_pymethod_options(app):
[desc_parameterlist, ()])],
[desc_content, ()]))
assert 'Class.meth7' in domain.objects
- assert domain.objects['Class.meth7'] == ('index', 'Class.meth7', 'method')
+ assert domain.objects['Class.meth7'] == ('index', 'Class.meth7', 'method', False)
def test_pyclassmethod(app):
@@ -693,7 +695,7 @@ def test_pyclassmethod(app):
[desc_parameterlist, ()])],
[desc_content, ()]))
assert 'Class.meth' in domain.objects
- assert domain.objects['Class.meth'] == ('index', 'Class.meth', 'method')
+ assert domain.objects['Class.meth'] == ('index', 'Class.meth', 'method', False)
def test_pystaticmethod(app):
@@ -714,7 +716,7 @@ def test_pystaticmethod(app):
[desc_parameterlist, ()])],
[desc_content, ()]))
assert 'Class.meth' in domain.objects
- assert domain.objects['Class.meth'] == ('index', 'Class.meth', 'method')
+ assert domain.objects['Class.meth'] == ('index', 'Class.meth', 'method', False)
def test_pyattribute(app):
@@ -743,7 +745,7 @@ def test_pyattribute(app):
assert_node(doctree[1][1][1][0][1][1], pending_xref, **{"py:class": "Class"})
assert_node(doctree[1][1][1][0][1][3], pending_xref, **{"py:class": "Class"})
assert 'Class.attr' in domain.objects
- assert domain.objects['Class.attr'] == ('index', 'Class.attr', 'attribute')
+ assert domain.objects['Class.attr'] == ('index', 'Class.attr', 'attribute', False)
def test_pydecorator_signature(app):
@@ -758,7 +760,7 @@ def test_pydecorator_signature(app):
domain="py", objtype="function", noindex=False)
assert 'deco' in domain.objects
- assert domain.objects['deco'] == ('index', 'deco', 'function')
+ assert domain.objects['deco'] == ('index', 'deco', 'function', False)
def test_pydecoratormethod_signature(app):
@@ -773,7 +775,22 @@ def test_pydecoratormethod_signature(app):
domain="py", objtype="method", noindex=False)
assert 'deco' in domain.objects
- assert domain.objects['deco'] == ('index', 'deco', 'method')
+ assert domain.objects['deco'] == ('index', 'deco', 'method', False)
+
+
+def test_canonical(app):
+ text = (".. py:class:: io.StringIO\n"
+ " :canonical: _io.StringIO")
+ domain = app.env.get_domain('py')
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree, (addnodes.index,
+ [desc, ([desc_signature, ([desc_annotation, "class "],
+ [desc_addname, "io."],
+ [desc_name, "StringIO"])],
+ desc_content)]))
+ assert 'io.StringIO' in domain.objects
+ assert domain.objects['io.StringIO'] == ('index', 'io.StringIO', 'class', False)
+ assert domain.objects['_io.StringIO'] == ('index', 'io.StringIO', 'class', True)
@pytest.mark.sphinx(freshenv=True)
diff --git a/tests/test_domain_std.py b/tests/test_domain_std.py
index 33a000a3f..66253b89a 100644
--- a/tests/test_domain_std.py
+++ b/tests/test_domain_std.py
@@ -25,7 +25,6 @@ from sphinx.addnodes import (
from sphinx.domains.std import StandardDomain
from sphinx.testing import restructuredtext
from sphinx.testing.util import assert_node
-from sphinx.util import docutils
def test_process_doc_handle_figure_caption():
@@ -331,8 +330,6 @@ def test_multiple_cmdoptions(app):
assert domain.progoptions[('cmd', '--output')] == ('index', 'cmdoption-cmd-o')
-@pytest.mark.skipif(docutils.__version_info__ < (0, 13),
- reason='docutils-0.13 or above is required')
@pytest.mark.sphinx(testroot='productionlist')
def test_productionlist(app, status, warning):
app.builder.build_all()
diff --git a/tests/test_environment.py b/tests/test_environment.py
index 7290eb6a0..73d0c3edc 100644
--- a/tests/test_environment.py
+++ b/tests/test_environment.py
@@ -84,7 +84,7 @@ def test_object_inventory(app):
refs = app.env.domaindata['py']['objects']
assert 'func_without_module' in refs
- assert refs['func_without_module'] == ('objects', 'func_without_module', 'function')
+ assert refs['func_without_module'] == ('objects', 'func_without_module', 'function', False)
assert 'func_without_module2' in refs
assert 'mod.func_in_module' in refs
assert 'mod.Cls' in refs
diff --git a/tests/test_ext_autodoc.py b/tests/test_ext_autodoc.py
index 703cc13f6..bc3d07660 100644
--- a/tests/test_ext_autodoc.py
+++ b/tests/test_ext_autodoc.py
@@ -290,7 +290,6 @@ def test_format_signature(app):
'(b, c=42, *d, **e)'
-@pytest.mark.skipif(sys.version_info < (3, 5), reason='typing is available since python3.5.')
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_process_signature_typing_generic(app):
actual = do_autodoc(app, 'class', 'target.generic_class.A', {})
@@ -1559,7 +1558,6 @@ def test_partialmethod_undoc_members(app):
assert list(actual) == expected
-@pytest.mark.skipif(sys.version_info < (3, 6), reason='py36+ is available since python3.6.')
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_typed_instance_variables(app):
options = {"members": None,
@@ -1653,7 +1651,6 @@ def test_autodoc_typed_instance_variables(app):
]
-@pytest.mark.skipif(sys.version_info < (3, 6), reason='py36+ is available since python3.6.')
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_typed_inherited_instance_variables(app):
options = {"members": None,
@@ -1783,7 +1780,6 @@ def test_autodoc_Annotated(app):
]
-@pytest.mark.skipif(sys.version_info < (3, 6), reason='py36+ is required.')
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_TYPE_CHECKING(app):
options = {"members": None,
@@ -1832,26 +1828,19 @@ def test_autodoc_for_egged_code(app):
def test_singledispatch(app):
options = {"members": None}
actual = do_autodoc(app, 'module', 'target.singledispatch', options)
- if sys.version_info < (3, 6):
- # check the result via "in" because the order of singledispatch signatures is
- # usually changed (because dict is not OrderedDict yet!)
- assert '.. py:function:: func(arg, kwarg=None)' in actual
- assert ' func(arg: int, kwarg=None)' in actual
- assert ' func(arg: str, kwarg=None)' in actual
- else:
- assert list(actual) == [
- '',
- '.. py:module:: target.singledispatch',
- '',
- '',
- '.. py:function:: func(arg, kwarg=None)',
- ' func(arg: int, kwarg=None)',
- ' func(arg: str, kwarg=None)',
- ' :module: target.singledispatch',
- '',
- ' A function for general use.',
- '',
- ]
+ assert list(actual) == [
+ '',
+ '.. py:module:: target.singledispatch',
+ '',
+ '',
+ '.. py:function:: func(arg, kwarg=None)',
+ ' func(arg: int, kwarg=None)',
+ ' func(arg: str, kwarg=None)',
+ ' :module: target.singledispatch',
+ '',
+ ' A function for general use.',
+ '',
+ ]
@pytest.mark.skipif(sys.version_info < (3, 8),
diff --git a/tests/test_ext_autodoc_autofunction.py b/tests/test_ext_autodoc_autofunction.py
index 3c8165995..4d6944252 100644
--- a/tests/test_ext_autodoc_autofunction.py
+++ b/tests/test_ext_autodoc_autofunction.py
@@ -118,23 +118,16 @@ def test_decorated(app):
def test_singledispatch(app):
options = {}
actual = do_autodoc(app, 'function', 'target.singledispatch.func', options)
- if sys.version_info < (3, 6):
- # check the result via "in" because the order of singledispatch signatures is
- # usually changed (because dict is not OrderedDict yet!)
- assert '.. py:function:: func(arg, kwarg=None)' in actual
- assert ' func(arg: int, kwarg=None)' in actual
- assert ' func(arg: str, kwarg=None)' in actual
- else:
- assert list(actual) == [
- '',
- '.. py:function:: func(arg, kwarg=None)',
- ' func(arg: int, kwarg=None)',
- ' func(arg: str, kwarg=None)',
- ' :module: target.singledispatch',
- '',
- ' A function for general use.',
- '',
- ]
+ assert list(actual) == [
+ '',
+ '.. py:function:: func(arg, kwarg=None)',
+ ' func(arg: int, kwarg=None)',
+ ' func(arg: str, kwarg=None)',
+ ' :module: target.singledispatch',
+ '',
+ ' A function for general use.',
+ '',
+ ]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
diff --git a/tests/test_ext_autosectionlabel.py b/tests/test_ext_autosectionlabel.py
index 310435d8e..c8635ce2d 100644
--- a/tests/test_ext_autosectionlabel.py
+++ b/tests/test_ext_autosectionlabel.py
@@ -12,11 +12,7 @@ import re
import pytest
-from sphinx.util import docutils
-
-@pytest.mark.skipif(docutils.__version_info__ < (0, 13),
- reason='docutils-0.13 or above is required')
@pytest.mark.sphinx('html', testroot='ext-autosectionlabel')
def test_autosectionlabel_html(app, status, warning, skipped_labels=False):
app.builder.build_all()
@@ -55,15 +51,11 @@ def test_autosectionlabel_html(app, status, warning, skipped_labels=False):
# Re-use test definition from above, just change the test root directory
-@pytest.mark.skipif(docutils.__version_info__ < (0, 13),
- reason='docutils-0.13 or above is required')
@pytest.mark.sphinx('html', testroot='ext-autosectionlabel-prefix-document')
def test_autosectionlabel_prefix_document_html(app, status, warning):
test_autosectionlabel_html(app, status, warning)
-@pytest.mark.skipif(docutils.__version_info__ < (0, 13),
- reason='docutils-0.13 or above is required')
@pytest.mark.sphinx('html', testroot='ext-autosectionlabel',
confoverrides={'autosectionlabel_maxdepth': 3})
def test_autosectionlabel_maxdepth(app, status, warning):
diff --git a/tests/test_ext_math.py b/tests/test_ext_math.py
index 4df7d47c7..d094def7a 100644
--- a/tests/test_ext_math.py
+++ b/tests/test_ext_math.py
@@ -71,8 +71,8 @@ def test_mathjax_options(app, status, warning):
content = (app.outdir / 'index.html').read_text()
assert ('<script async="async" integrity="sha384-0123456789" '
- 'src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/latest.js?'
- 'config=TeX-AMS-MML_HTMLorMML"></script>' in content)
+ 'src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js">'
+ '</script>' in content)
@pytest.mark.sphinx('html', testroot='ext-math',
diff --git a/tests/test_ext_todo.py b/tests/test_ext_todo.py
index 7b4fdeabe..633b151ac 100644
--- a/tests/test_ext_todo.py
+++ b/tests/test_ext_todo.py
@@ -12,11 +12,7 @@ import re
import pytest
-from sphinx.util import docutils
-
-@pytest.mark.skipif(docutils.__version_info__ < (0, 13),
- reason='docutils-0.13 or above is required')
@pytest.mark.sphinx('html', testroot='ext-todo', freshenv=True,
confoverrides={'todo_include_todos': True, 'todo_emit_warnings': True})
def test_todo(app, status, warning):
diff --git a/tests/test_intl.py b/tests/test_intl.py
index c0b87d5ce..5ccfc482f 100644
--- a/tests/test_intl.py
+++ b/tests/test_intl.py
@@ -347,9 +347,9 @@ def test_text_figure_captions(app):
"14.2. IMAGE URL AND ALT\n"
"=======================\n"
"\n"
- "[image: i18n][image]\n"
+ "[image: I18N -> IMG][image]\n"
"\n"
- " [image: img][image]\n"
+ " [image: IMG -> I18N][image]\n"
"\n"
"\n"
"14.3. IMAGE ON SUBSTITUTION\n"
@@ -1104,12 +1104,12 @@ def test_additional_targets_should_not_be_translated(app):
result = (app.outdir / 'figure.html').read_text()
- # alt and src for image block should not be translated
- expected_expr = """<img alt="i18n" src="_images/i18n.png" />"""
+ # src for image block should not be translated (alt is translated)
+ expected_expr = """<img alt="I18N -&gt; IMG" src="_images/i18n.png" />"""
assert_count(expected_expr, result, 1)
- # alt and src for figure block should not be translated
- expected_expr = """<img alt="img" src="_images/img.png" />"""
+ # src for figure block should not be translated (alt is translated)
+ expected_expr = """<img alt="IMG -&gt; I18N" src="_images/img.png" />"""
assert_count(expected_expr, result, 1)
diff --git a/tests/test_pycode_parser.py b/tests/test_pycode_parser.py
index 71847f04f..2ef3c2311 100644
--- a/tests/test_pycode_parser.py
+++ b/tests/test_pycode_parser.py
@@ -95,8 +95,7 @@ def test_comment_picker_location():
('Foo', 'attr3'): 'comment for attr3(3)'}
-@pytest.mark.skipif(sys.version_info < (3, 6), reason='tests for py36+ syntax')
-def test_annotated_assignment_py36():
+def test_annotated_assignment():
source = ('a: str = "Sphinx" #: comment\n'
'b: int = 1\n'
'"""string on next line"""\n'
diff --git a/tests/test_search.py b/tests/test_search.py
index a4cefbc67..72eb39a0a 100644
--- a/tests/test_search.py
+++ b/tests/test_search.py
@@ -143,6 +143,7 @@ def test_IndexBuilder():
assert index._titles == {'docname': 'title', 'docname2': 'title2'}
assert index._filenames == {'docname': 'filename', 'docname2': 'filename2'}
assert index._mapping == {
+ 'ar': {'docname', 'docname2'},
'fermion': {'docname', 'docname2'},
'comment': {'docname', 'docname2'},
'non': {'docname', 'docname2'},
@@ -161,7 +162,8 @@ def test_IndexBuilder():
'objects': {'': {'objdispname': (0, 0, 1, '#anchor')}},
'objnames': {0: ('dummy', 'objtype', 'objtype')},
'objtypes': {0: 'dummy:objtype'},
- 'terms': {'comment': [0, 1],
+ 'terms': {'ar': [0, 1],
+ 'comment': [0, 1],
'fermion': [0, 1],
'index': [0, 1],
'non': [0, 1],
@@ -197,6 +199,7 @@ def test_IndexBuilder():
assert index._titles == {'docname2': 'title2'}
assert index._filenames == {'docname2': 'filename2'}
assert index._mapping == {
+ 'ar': {'docname2'},
'fermion': {'docname2'},
'comment': {'docname2'},
'non': {'docname2'},
@@ -215,7 +218,8 @@ def test_IndexBuilder():
'objects': {},
'objnames': {0: ('dummy', 'objtype', 'objtype')},
'objtypes': {0: 'dummy:objtype'},
- 'terms': {'comment': 0,
+ 'terms': {'ar': 0,
+ 'comment': 0,
'fermion': 0,
'index': 0,
'non': 0,
@@ -261,4 +265,4 @@ def test_nosearch(app):
assert index['docnames'] == ['index', 'nosearch', 'tocitem']
assert 'latex' not in index['terms']
assert 'zfs' in index['terms']
- assert index['terms']['zfs'] == 0 # zfs on nosearch.rst is not registered to index
+ assert index['terms']['zfs'] == [] # zfs on nosearch.rst is not registered to index
diff --git a/tests/test_smartquotes.py b/tests/test_smartquotes.py
index 6276e6a74..d4d31f4e8 100644
--- a/tests/test_smartquotes.py
+++ b/tests/test_smartquotes.py
@@ -10,8 +10,6 @@
import pytest
-from sphinx.util import docutils
-
@pytest.mark.sphinx(buildername='html', testroot='smartquotes', freshenv=True)
def test_basic(app, status, warning):
@@ -33,7 +31,7 @@ def test_text_builder(app, status, warning):
def test_man_builder(app, status, warning):
app.build()
- content = (app.outdir / 'python.1').read_text()
+ content = (app.outdir / '1' / 'python.1').read_text()
assert '\\-\\- "Sphinx" is a tool that makes it easy ...' in content
@@ -63,8 +61,6 @@ def test_smartquotes_disabled(app, status, warning):
assert '<p>-- &quot;Sphinx&quot; is a tool that makes it easy ...</p>' in content
-@pytest.mark.skipif(docutils.__version_info__ < (0, 14),
- reason='docutils-0.14 or above is required')
@pytest.mark.sphinx(buildername='html', testroot='smartquotes', freshenv=True,
confoverrides={'smartquotes_action': 'q'})
def test_smartquotes_action(app, status, warning):
@@ -88,5 +84,5 @@ def test_smartquotes_excludes_language(app, status, warning):
def test_smartquotes_excludes_builders(app, status, warning):
app.build()
- content = (app.outdir / 'python.1').read_text()
+ content = (app.outdir / '1' / 'python.1').read_text()
assert '– “Sphinx” is a tool that makes it easy …' in content
diff --git a/tests/test_util_inspect.py b/tests/test_util_inspect.py
index cbca3c5f7..8103050f5 100644
--- a/tests/test_util_inspect.py
+++ b/tests/test_util_inspect.py
@@ -223,10 +223,7 @@ def test_signature_annotations():
# type hints by string
sig = inspect.signature(Node.children)
- if (3, 5, 0) <= sys.version_info < (3, 5, 3):
- assert stringify_signature(sig) == '(self) -> List[Node]'
- else:
- assert stringify_signature(sig) == '(self) -> List[typing_test_data.Node]'
+ assert stringify_signature(sig) == '(self) -> List[typing_test_data.Node]'
sig = inspect.signature(Node.__init__)
assert stringify_signature(sig) == '(self, parent: Optional[typing_test_data.Node]) -> None'
diff --git a/tests/test_util_pycompat.py b/tests/test_util_pycompat.py
deleted file mode 100644
index 67e61bb58..000000000
--- a/tests/test_util_pycompat.py
+++ /dev/null
@@ -1,38 +0,0 @@
-"""
- test_util_pycompat
- ~~~~~~~~~~~~~~~~~~
-
- Tests sphinx.util.pycompat functions.
-
- :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
- :license: BSD, see LICENSE for details.
-"""
-
-from sphinx.testing.util import strip_escseq
-from sphinx.util import logging
-from sphinx.util.pycompat import execfile_
-
-
-def test_execfile_python2(capsys, app, status, warning, tempdir):
- logging.setup(app, status, warning)
-
- conf_py = tempdir / 'conf.py'
- conf_py.write_bytes(b'print "hello"\n')
- execfile_(conf_py, {})
-
- msg = (
- 'Support for evaluating Python 2 syntax is deprecated '
- 'and will be removed in Sphinx 4.0. '
- 'Convert %s to Python 3 syntax.\n' % conf_py)
- assert msg in strip_escseq(warning.getvalue())
- captured = capsys.readouterr()
- assert captured.out == 'hello\n'
-
-
-def test_execfile(capsys, tempdir):
- conf_py = tempdir / 'conf.py'
- conf_py.write_bytes(b'print("hello")\n')
- execfile_(conf_py, {})
-
- captured = capsys.readouterr()
- assert captured.out == 'hello\n'
diff --git a/tox.ini b/tox.ini
index 316655d20..3d40d2f97 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,6 +1,6 @@
[tox]
minversion = 2.4.0
-envlist = docs,flake8,mypy,twine,coverage,py{35,36,37,38,39},du{12,13,14,15}
+envlist = docs,flake8,mypy,twine,coverage,py{36,37,38,39},du{14,15,16}
[testenv]
usedevelop = True
@@ -14,11 +14,9 @@ passenv =
EPUBCHECK_PATH
TERM
description =
- py{35,36,37,38,39}: Run unit tests against {envname}.
+ py{36,37,38,39}: Run unit tests against {envname}.
du{12,13,14}: Run unit tests with the given version of docutils.
deps =
- du12: docutils==0.12
- du13: docutils==0.13.1
du14: docutils==0.14
du15: docutils==0.15
du16: docutils==0.16