From 7e68154e49fbb260f7ffee9791bfafdb7fd2e119 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Fri, 17 Jun 2022 03:33:55 +0900 Subject: [PATCH] Drop python 3.6 support (#10468) --- .github/workflows/builddoc.yml | 2 +- .github/workflows/lint.yml | 2 +- .github/workflows/main.yml | 2 - CHANGES | 2 + doc/internals/contributing.rst | 8 +- doc/internals/release-process.rst | 6 +- doc/usage/installation.rst | 2 +- setup.py | 7 +- sphinx/ext/extlinks.py | 8 +- sphinx/util/inspect.py | 28 ++--- sphinx/util/typing.py | 18 +-- tests/test_build.py | 40 +++---- tests/test_errors.py | 8 +- tests/test_ext_autodoc.py | 145 ++++++++---------------- tests/test_ext_autodoc_autoattribute.py | 33 ++---- tests/test_ext_autodoc_autoclass.py | 35 ++---- tests/test_ext_autodoc_autodata.py | 33 ++---- tests/test_ext_autodoc_autofunction.py | 39 ++----- tests/test_ext_autodoc_automodule.py | 7 +- tests/test_ext_autodoc_configs.py | 33 ++---- tests/test_ext_autodoc_mock.py | 1 - tests/test_ext_napoleon.py | 20 ++-- tests/test_util_inspect.py | 15 +-- tests/test_util_typing.py | 125 +++++++------------- 24 files changed, 203 insertions(+), 416 deletions(-) diff --git a/.github/workflows/builddoc.yml b/.github/workflows/builddoc.yml index 809fb68e6d7..88082b31e9f 100644 --- a/.github/workflows/builddoc.yml +++ b/.github/workflows/builddoc.yml @@ -11,7 +11,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v1 with: - python-version: 3.6 + python-version: 3.8 - name: Install dependencies run: | sudo apt update diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 913abcedd53..fb85629e504 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -15,7 +15,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v1 with: - python-version: 3.6 + python-version: 3.8 - name: Install dependencies run: pip install -U tox - name: Run Tox diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index daa766c39fb..4dfae178162 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,8 +10,6 @@ jobs: fail-fast: false matrix: include: - - python: "3.6" - docutils: du14 - python: "3.7" docutils: du15 - python: "3.8" diff --git a/CHANGES b/CHANGES index cfaf281fa6c..722ce6fe8d6 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,8 @@ Release 6.0.0 (in development) Dependencies ------------ +* Drop python 3.6 support + Incompatible changes -------------------- diff --git a/doc/internals/contributing.rst b/doc/internals/contributing.rst index f6ba5146a6c..27d7301640a 100644 --- a/doc/internals/contributing.rst +++ b/doc/internals/contributing.rst @@ -172,19 +172,19 @@ of targets and allows testing against multiple different Python environments: tox -av -* To run unit tests for a specific Python version, such as Python 3.6:: +* To run unit tests for a specific Python version, such as Python 3.10:: - tox -e py36 + tox -e py310 * To run unit tests for a specific Python version and turn on deprecation warnings on so they're shown in the test output:: - PYTHONWARNINGS=all tox -e py36 + PYTHONWARNINGS=all tox -e py310 * Arguments to ``pytest`` can be passed via ``tox``, e.g. in order to run a particular test:: - tox -e py36 tests/test_module.py::test_new_feature + tox -e py310 tests/test_module.py::test_new_feature You can also test by installing dependencies in your local environment:: diff --git a/doc/internals/release-process.rst b/doc/internals/release-process.rst index a23ace0b11c..0d89831bc7c 100644 --- a/doc/internals/release-process.rst +++ b/doc/internals/release-process.rst @@ -109,16 +109,16 @@ Ubuntu `_ that has standard support. For example, as of July 2021, Ubuntu 16.04 has just entered extended security maintenance (therefore, it doesn't count as standard support) and the oldest LTS release to consider is Ubuntu 18.04 LTS, supported until -April 2023 and shipping Python 3.6. +April 2023 and shipping Python 3.8. This is a summary table with the current policy: ========== ========= ====== Date Ubuntu Python ========== ========= ====== -April 2021 18.04 LTS 3.6+ ----------- --------- ------ April 2023 20.04 LTS 3.8+ +---------- --------- ------ +April 2025 22.04 LTS 3.10+ ========== ========= ====== Release procedures diff --git a/doc/usage/installation.rst b/doc/usage/installation.rst index b85a6cd2adf..997bd40ca68 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.6+. It builds upon the +Sphinx is written in `Python`__ and supports Python 3.7+. It builds upon the shoulders of many third-party libraries such as `Docutils`__ and `Jinja`__, which are installed when Sphinx is installed. diff --git a/setup.py b/setup.py index de8be1d4971..94451f53145 100644 --- a/setup.py +++ b/setup.py @@ -7,8 +7,8 @@ with open('README.rst', encoding='utf-8') as f: long_desc = f.read() -if sys.version_info < (3, 6): - print('ERROR: Sphinx requires at least Python 3.6 to run.') +if sys.version_info < (3, 7): + print('ERROR: Sphinx requires at least Python 3.7 to run.') sys.exit(1) install_requires = [ @@ -85,7 +85,6 @@ 'Programming Language :: Python', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3 :: Only', - 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', @@ -127,7 +126,7 @@ 'build_sphinx = sphinx.setup_command:BuildDoc', ], }, - python_requires=">=3.6", + python_requires=">=3.7", install_requires=install_requires, extras_require=extras_require, ) diff --git a/sphinx/ext/extlinks.py b/sphinx/ext/extlinks.py index 881108b2307..85d8eb2806f 100644 --- a/sphinx/ext/extlinks.py +++ b/sphinx/ext/extlinks.py @@ -18,7 +18,6 @@ """ import re -import sys from typing import Any, Dict, List, Tuple from docutils import nodes, utils @@ -64,12 +63,7 @@ def check_uri(self, refnode: nodes.reference) -> None: title = refnode.astext() for alias, (base_uri, _caption) in self.app.config.extlinks.items(): - if sys.version_info < (3, 7): - # Replace a leading backslash because re.escape() inserts a backslash before % - # on python 3.6 - uri_pattern = re.compile(re.escape(base_uri).replace('\\%s', '(?P.+)')) - else: - uri_pattern = re.compile(re.escape(base_uri).replace('%s', '(?P.+)')) + uri_pattern = re.compile(re.escape(base_uri).replace('%s', '(?P.+)')) match = uri_pattern.match(uri) if match and match.groupdict().get('value'): diff --git a/sphinx/util/inspect.py b/sphinx/util/inspect.py index b25a75fb50a..a807ceb8310 100644 --- a/sphinx/util/inspect.py +++ b/sphinx/util/inspect.py @@ -15,7 +15,7 @@ from types import MethodType, ModuleType from typing import Any, Callable, Dict, Mapping, Optional, Sequence, Tuple, Type, cast -from sphinx.pycode.ast import ast # for py36-37 +from sphinx.pycode.ast import ast # for py37 from sphinx.pycode.ast import unparse as ast_unparse from sphinx.util import logging from sphinx.util.typing import ForwardRef @@ -298,7 +298,7 @@ def is_singledispatch_method(obj: Any) -> bool: try: from functools import singledispatchmethod # type: ignore return isinstance(obj, singledispatchmethod) - except ImportError: # py36-37 + except ImportError: # py37 return False @@ -569,25 +569,15 @@ def signature(subject: Callable, bound_method: bool = False, type_aliases: Dict """ try: - try: - if _should_unwrap(subject): - signature = inspect.signature(subject) - else: - signature = inspect.signature(subject, follow_wrapped=True) - except ValueError: - # follow built-in wrappers up (ex. functools.lru_cache) + if _should_unwrap(subject): signature = inspect.signature(subject) - parameters = list(signature.parameters.values()) - return_annotation = signature.return_annotation - 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'): - parameters = [] - return_annotation = Parameter.empty else: - raise + signature = inspect.signature(subject, follow_wrapped=True) + except ValueError: + # follow built-in wrappers up (ex. functools.lru_cache) + signature = inspect.signature(subject) + parameters = list(signature.parameters.values()) + return_annotation = signature.return_annotation try: # Resolve annotations using ``get_type_hints()`` and type_aliases. diff --git a/sphinx/util/typing.py b/sphinx/util/typing.py index 8e48b184bcc..2f7e0d99f20 100644 --- a/sphinx/util/typing.py +++ b/sphinx/util/typing.py @@ -2,6 +2,7 @@ import sys import typing +import warnings from struct import Struct from types import TracebackType from typing import Any, Callable, Dict, Generator, List, Optional, Tuple, Type, TypeVar, Union @@ -9,7 +10,8 @@ from docutils import nodes from docutils.parsers.rst.states import Inliner -from sphinx.deprecation import RemovedInSphinx60Warning, deprecated_alias +from sphinx.deprecation import (RemovedInSphinx60Warning, RemovedInSphinx70Warning, + deprecated_alias) if sys.version_info > (3, 7): from typing import ForwardRef @@ -158,10 +160,7 @@ def restify(cls: Optional[Type], mode: str = 'fully-qualified-except-typing') -> else: return ':py:class:`%s`' % cls.__name__ else: - if sys.version_info >= (3, 7): # py37+ - return _restify_py37(cls, mode) - else: - return _restify_py36(cls, mode) + return _restify_py37(cls, mode) except (AttributeError, TypeError): return inspect.object_description(cls) @@ -234,6 +233,8 @@ def _restify_py37(cls: Optional[Type], mode: str = 'fully-qualified-except-typin def _restify_py36(cls: Optional[Type], mode: str = 'fully-qualified-except-typing') -> str: + warnings.warn('_restify_py36() is deprecated', RemovedInSphinx70Warning) + if mode == 'smart': modprefix = '~' else: @@ -390,10 +391,7 @@ def stringify(annotation: Any, mode: str = 'fully-qualified-except-typing') -> s elif annotation is Ellipsis: return '...' - if sys.version_info >= (3, 7): # py37+ - return _stringify_py37(annotation, mode) - else: - return _stringify_py36(annotation, mode) + return _stringify_py37(annotation, mode) def _stringify_py37(annotation: Any, mode: str = 'fully-qualified-except-typing') -> str: @@ -472,6 +470,8 @@ def _stringify_py37(annotation: Any, mode: str = 'fully-qualified-except-typing' def _stringify_py36(annotation: Any, mode: str = 'fully-qualified-except-typing') -> str: """stringify() for py36.""" + warnings.warn('_stringify_py36() is deprecated', RemovedInSphinx70Warning) + module = getattr(annotation, '__module__', None) modprefix = '' if module == 'typing' and getattr(annotation, '__forward_arg__', None): diff --git a/tests/test_build.py b/tests/test_build.py index fd123e2945c..c5ce79da37a 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -22,29 +22,23 @@ def nonascii_srcdir(request, rootdir, sphinx_test_tempdir): # If supported, build in a non-ASCII source dir test_name = '\u65e5\u672c\u8a9e' basedir = sphinx_test_tempdir / request.node.originalname - try: - srcdir = basedir / test_name - if not srcdir.exists(): - (rootdir / 'test-root').copytree(srcdir) - except UnicodeEncodeError: - # Now Python 3.7+ follows PEP-540 and uses utf-8 encoding for filesystem by default. - # So this error handling will be no longer used (after dropping python 3.6 support). - srcdir = basedir / 'all' - if not srcdir.exists(): - (rootdir / 'test-root').copytree(srcdir) - else: - # add a doc with a non-ASCII file name to the source dir - (srcdir / (test_name + '.txt')).write_text(dedent(""" - nonascii file name page - ======================= - """), encoding='utf8') - - root_doc = srcdir / 'index.txt' - root_doc.write_text(root_doc.read_text(encoding='utf8') + dedent(""" - .. toctree:: - - %(test_name)s/%(test_name)s - """ % {'test_name': test_name}), encoding='utf8') + srcdir = basedir / test_name + if not srcdir.exists(): + (rootdir / 'test-root').copytree(srcdir) + + # add a doc with a non-ASCII file name to the source dir + (srcdir / (test_name + '.txt')).write_text(dedent(""" + nonascii file name page + ======================= + """), encoding='utf8') + + root_doc = srcdir / 'index.txt' + root_doc.write_text(root_doc.read_text(encoding='utf8') + dedent(""" + .. toctree:: + + %(test_name)s/%(test_name)s + """ % {'test_name': test_name}), encoding='utf8') + return srcdir diff --git a/tests/test_errors.py b/tests/test_errors.py index c8cf4b05d35..6378760af81 100644 --- a/tests/test_errors.py +++ b/tests/test_errors.py @@ -1,5 +1,3 @@ -import sys - from sphinx.errors import ExtensionError @@ -10,8 +8,4 @@ def test_extension_error_repr(): def test_extension_error_with_orig_exc_repr(): exc = ExtensionError("foo", Exception("bar")) - if sys.version_info < (3, 7): - expected = "ExtensionError('foo', Exception('bar',))" - else: - expected = "ExtensionError('foo', Exception('bar'))" - assert repr(exc) == expected + assert repr(exc) == "ExtensionError('foo', Exception('bar'))" diff --git a/tests/test_ext_autodoc.py b/tests/test_ext_autodoc.py index a35c6f640e5..428bbc98f45 100644 --- a/tests/test_ext_autodoc.py +++ b/tests/test_ext_autodoc.py @@ -1860,70 +1860,40 @@ def test_autodoc_GenericAlias(app): options = {"members": None, "undoc-members": None} actual = do_autodoc(app, 'module', 'target.genericalias', options) - if sys.version_info < (3, 7): - assert list(actual) == [ - '', - '.. py:module:: target.genericalias', - '', - '', - '.. py:class:: Class()', - ' :module: target.genericalias', - '', - '', - ' .. py:attribute:: Class.T', - ' :module: target.genericalias', - '', - ' A list of int', - '', - ' alias of :py:class:`~typing.List`\\ [:py:class:`int`]', - '', - '.. py:attribute:: L', - ' :module: target.genericalias', - '', - ' A list of Class', - '', - '', - '.. py:attribute:: T', - ' :module: target.genericalias', - '', - ' A list of int', - '', - ] - else: - assert list(actual) == [ - '', - '.. py:module:: target.genericalias', - '', - '', - '.. py:class:: Class()', - ' :module: target.genericalias', - '', - '', - ' .. py:attribute:: Class.T', - ' :module: target.genericalias', - '', - ' A list of int', - '', - ' alias of :py:class:`~typing.List`\\ [:py:class:`int`]', - '', - '', - '.. py:data:: L', - ' :module: target.genericalias', - '', - ' A list of Class', - '', - ' alias of :py:class:`~typing.List`\\ ' - '[:py:class:`~target.genericalias.Class`]', - '', - '', - '.. py:data:: T', - ' :module: target.genericalias', - '', - ' A list of int', - '', - ' alias of :py:class:`~typing.List`\\ [:py:class:`int`]', - '', - ] + assert list(actual) == [ + '', + '.. py:module:: target.genericalias', + '', + '', + '.. py:class:: Class()', + ' :module: target.genericalias', + '', + '', + ' .. py:attribute:: Class.T', + ' :module: target.genericalias', + '', + ' A list of int', + '', + ' alias of :py:class:`~typing.List`\\ [:py:class:`int`]', + '', + '', + '.. py:data:: L', + ' :module: target.genericalias', + '', + ' A list of Class', + '', + ' alias of :py:class:`~typing.List`\\ ' + '[:py:class:`~target.genericalias.Class`]', + '', + '', + '.. py:data:: T', + ' :module: target.genericalias', + '', + ' A list of int', + '', + ' alias of :py:class:`~typing.List`\\ [:py:class:`int`]', + '', + ] @pytest.mark.sphinx('html', testroot='ext-autodoc') @@ -2072,37 +2042,21 @@ 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, 7): - assert list(actual) == [ - '', - '.. py:module:: target.singledispatch', - '', - '', - '.. py:function:: func(arg, kwarg=None)', - ' func(arg: float, kwarg=None)', - ' func(arg: int, kwarg=None)', - ' func(arg: str, kwarg=None)', - ' :module: target.singledispatch', - '', - ' A function for general use.', - '', - ] - else: - assert list(actual) == [ - '', - '.. py:module:: target.singledispatch', - '', - '', - '.. py:function:: func(arg, kwarg=None)', - ' func(arg: float, kwarg=None)', - ' func(arg: int, kwarg=None)', - ' func(arg: str, kwarg=None)', - ' func(arg: dict, 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: float, kwarg=None)', + ' func(arg: int, kwarg=None)', + ' func(arg: str, kwarg=None)', + ' func(arg: dict, kwarg=None)', + ' :module: target.singledispatch', + '', + ' A function for general use.', + '', + ] @pytest.mark.skipif(sys.version_info < (3, 8), @@ -2416,7 +2370,6 @@ def test_name_mangling(app): ] -@pytest.mark.skipif(sys.version_info < (3, 7), reason='python 3.7+ is required.') @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_type_union_operator(app): options = {'members': None} diff --git a/tests/test_ext_autodoc_autoattribute.py b/tests/test_ext_autodoc_autoattribute.py index 1c575760f77..02443e6c786 100644 --- a/tests/test_ext_autodoc_autoattribute.py +++ b/tests/test_ext_autodoc_autoattribute.py @@ -4,8 +4,6 @@ source file translated by test_build. """ -import sys - import pytest from .test_ext_autodoc import do_autodoc @@ -141,27 +139,16 @@ def test_autoattribute_slots_variable_str(app): @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_autoattribute_GenericAlias(app): actual = do_autodoc(app, 'attribute', 'target.genericalias.Class.T') - if sys.version_info < (3, 7): - assert list(actual) == [ - '', - '.. py:attribute:: Class.T', - ' :module: target.genericalias', - ' :value: typing.List[int]', - '', - ' A list of int', - '', - ] - else: - assert list(actual) == [ - '', - '.. py:attribute:: Class.T', - ' :module: target.genericalias', - '', - ' A list of int', - '', - ' alias of :py:class:`~typing.List`\\ [:py:class:`int`]', - '', - ] + assert list(actual) == [ + '', + '.. py:attribute:: Class.T', + ' :module: target.genericalias', + '', + ' A list of int', + '', + ' alias of :py:class:`~typing.List`\\ [:py:class:`int`]', + '', + ] @pytest.mark.sphinx('html', testroot='ext-autodoc') diff --git a/tests/test_ext_autodoc_autoclass.py b/tests/test_ext_autodoc_autoclass.py index 89b6daa929d..8d85d7cd38f 100644 --- a/tests/test_ext_autodoc_autoclass.py +++ b/tests/test_ext_autodoc_autoclass.py @@ -4,7 +4,6 @@ source file translated by test_build. """ -import sys from typing import List, Union import pytest @@ -249,7 +248,6 @@ def test_slots_attribute(app): ] -@pytest.mark.skipif(sys.version_info < (3, 7), reason='python 3.7+ is required.') @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_show_inheritance_for_subclass_of_generic_type(app): options = {'show-inheritance': None} @@ -267,7 +265,6 @@ def test_show_inheritance_for_subclass_of_generic_type(app): ] -@pytest.mark.skipif(sys.version_info < (3, 7), reason='python 3.7+ is required.') @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_show_inheritance_for_decendants_of_generic_type(app): options = {'show-inheritance': None} @@ -299,28 +296,16 @@ def autodoc_process_bases(app, name, obj, options, bases): options = {'show-inheritance': None} actual = do_autodoc(app, 'class', 'target.classes.Quux', options) - if sys.version_info < (3, 7): - assert list(actual) == [ - '', - '.. py:class:: Quux(*args, **kwds)', - ' :module: target.classes', - '', - ' Bases: :py:class:`int`, :py:class:`str`', - '', - ' A subclass of List[Union[int, float]]', - '', - ] - else: - assert list(actual) == [ - '', - '.. py:class:: Quux(iterable=(), /)', - ' :module: target.classes', - '', - ' Bases: :py:class:`int`, :py:class:`str`', - '', - ' A subclass of List[Union[int, float]]', - '', - ] + assert list(actual) == [ + '', + '.. py:class:: Quux(iterable=(), /)', + ' :module: target.classes', + '', + ' Bases: :py:class:`int`, :py:class:`str`', + '', + ' A subclass of List[Union[int, float]]', + '', + ] @pytest.mark.sphinx('html', testroot='ext-autodoc') diff --git a/tests/test_ext_autodoc_autodata.py b/tests/test_ext_autodoc_autodata.py index 435060170c5..f2430c31bfa 100644 --- a/tests/test_ext_autodoc_autodata.py +++ b/tests/test_ext_autodoc_autodata.py @@ -4,8 +4,6 @@ source file translated by test_build. """ -import sys - import pytest from .test_ext_autodoc import do_autodoc @@ -71,27 +69,16 @@ def test_autodata_type_comment(app): @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_autodata_GenericAlias(app): actual = do_autodoc(app, 'data', 'target.genericalias.T') - if sys.version_info < (3, 7): - assert list(actual) == [ - '', - '.. py:data:: T', - ' :module: target.genericalias', - ' :value: typing.List[int]', - '', - ' A list of int', - '', - ] - else: - assert list(actual) == [ - '', - '.. py:data:: T', - ' :module: target.genericalias', - '', - ' A list of int', - '', - ' alias of :py:class:`~typing.List`\\ [:py:class:`int`]', - '', - ] + assert list(actual) == [ + '', + '.. py:data:: T', + ' :module: target.genericalias', + '', + ' A list of int', + '', + ' alias of :py:class:`~typing.List`\\ [:py:class:`int`]', + '', + ] @pytest.mark.sphinx('html', testroot='ext-autodoc') diff --git a/tests/test_ext_autodoc_autofunction.py b/tests/test_ext_autodoc_autofunction.py index a9e930d1d28..b0cd7d9b9d9 100644 --- a/tests/test_ext_autodoc_autofunction.py +++ b/tests/test_ext_autodoc_autofunction.py @@ -4,8 +4,6 @@ source file translated by test_build. """ -import sys - import pytest from .test_ext_autodoc import do_autodoc @@ -113,31 +111,18 @@ def test_decorated(app): def test_singledispatch(app): options = {} actual = do_autodoc(app, 'function', 'target.singledispatch.func', options) - if sys.version_info < (3, 7): - assert list(actual) == [ - '', - '.. py:function:: func(arg, kwarg=None)', - ' func(arg: float, kwarg=None)', - ' func(arg: int, kwarg=None)', - ' func(arg: str, kwarg=None)', - ' :module: target.singledispatch', - '', - ' A function for general use.', - '', - ] - else: - assert list(actual) == [ - '', - '.. py:function:: func(arg, kwarg=None)', - ' func(arg: float, kwarg=None)', - ' func(arg: int, kwarg=None)', - ' func(arg: str, kwarg=None)', - ' func(arg: dict, kwarg=None)', - ' :module: target.singledispatch', - '', - ' A function for general use.', - '', - ] + assert list(actual) == [ + '', + '.. py:function:: func(arg, kwarg=None)', + ' func(arg: float, kwarg=None)', + ' func(arg: int, kwarg=None)', + ' func(arg: str, kwarg=None)', + ' func(arg: dict, kwarg=None)', + ' :module: target.singledispatch', + '', + ' A function for general use.', + '', + ] @pytest.mark.sphinx('html', testroot='ext-autodoc') diff --git a/tests/test_ext_autodoc_automodule.py b/tests/test_ext_autodoc_automodule.py index 71b23679d96..4b5b06c2604 100644 --- a/tests/test_ext_autodoc_automodule.py +++ b/tests/test_ext_autodoc_automodule.py @@ -115,11 +115,6 @@ def test_automodule_special_members(app): @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_automodule_inherited_members(app): - if sys.version_info < (3, 7): - args = '' - else: - args = '(iterable=(), /)' - options = {'members': None, 'undoc-members': None, 'inherited-members': 'Base, list'} @@ -170,7 +165,7 @@ def test_automodule_inherited_members(app): ' Inherited function.', '', '', - '.. py:class:: MyList%s' % args, + '.. py:class:: MyList(iterable=(), /)', ' :module: target.inheritance', '', '', diff --git a/tests/test_ext_autodoc_configs.py b/tests/test_ext_autodoc_configs.py index f40f3354e11..dbd432c800f 100644 --- a/tests/test_ext_autodoc_configs.py +++ b/tests/test_ext_autodoc_configs.py @@ -1231,7 +1231,6 @@ def test_autodoc_typehints_both(app): in context) -@pytest.mark.skipif(sys.version_info < (3, 7), reason='python 3.7+ is required.') @pytest.mark.sphinx('text', testroot='ext-autodoc') def test_autodoc_type_aliases(app): # default @@ -1376,7 +1375,6 @@ def test_autodoc_type_aliases(app): ] -@pytest.mark.skipif(sys.version_info < (3, 7), reason='python 3.7+ is required.') @pytest.mark.sphinx('text', testroot='ext-autodoc', srcdir='autodoc_typehints_description_and_type_aliases', confoverrides={'autodoc_typehints': "description", @@ -1543,27 +1541,16 @@ def test_autodoc_typehints_format_fully_qualified_for_class_alias(app): confoverrides={'autodoc_typehints_format': "fully-qualified"}) def test_autodoc_typehints_format_fully_qualified_for_generic_alias(app): actual = do_autodoc(app, 'data', 'target.genericalias.L') - if sys.version_info < (3, 7): - assert list(actual) == [ - '', - '.. py:data:: L', - ' :module: target.genericalias', - ' :value: typing.List[target.genericalias.Class]', - '', - ' A list of Class', - '', - ] - else: - assert list(actual) == [ - '', - '.. py:data:: L', - ' :module: target.genericalias', - '', - ' A list of Class', - '', - ' alias of :py:class:`~typing.List`\\ [:py:class:`target.genericalias.Class`]', - '', - ] + assert list(actual) == [ + '', + '.. py:data:: L', + ' :module: target.genericalias', + '', + ' A list of Class', + '', + ' alias of :py:class:`~typing.List`\\ [:py:class:`target.genericalias.Class`]', + '', + ] @pytest.mark.sphinx('html', testroot='ext-autodoc', diff --git a/tests/test_ext_autodoc_mock.py b/tests/test_ext_autodoc_mock.py index d40e56e8b60..c10350fbe22 100644 --- a/tests/test_ext_autodoc_mock.py +++ b/tests/test_ext_autodoc_mock.py @@ -84,7 +84,6 @@ def test_mock_does_not_follow_upper_modules(): import_module('sphinx.unknown') -@pytest.mark.skipif(sys.version_info < (3, 7), reason='Only for py37 or above') def test_abc_MockObject(): mock = _MockObject() diff --git a/tests/test_ext_napoleon.py b/tests/test_ext_napoleon.py index a1b98996f77..f4ad04a00bf 100644 --- a/tests/test_ext_napoleon.py +++ b/tests/test_ext_napoleon.py @@ -1,6 +1,5 @@ """Tests for :mod:`sphinx.ext.napoleon.__init__` module.""" -import sys from collections import namedtuple from unittest import TestCase, mock @@ -134,18 +133,13 @@ def assertSkip(self, what, member, obj, expect_default_skip, config_name): mock.Mock())) def test_namedtuple(self): - if sys.version_info < (3, 7): - self.assertSkip('class', '_asdict', - SampleNamedTuple._asdict, False, - 'napoleon_include_private_with_doc') - else: - # Since python 3.7, namedtuple._asdict() has not been documented - # because there is no way to check the method is a member of the - # namedtuple class. This testcase confirms only it does not - # raise an error on building document (refs: #1455) - self.assertSkip('class', '_asdict', - SampleNamedTuple._asdict, True, - 'napoleon_include_private_with_doc') + # Since python 3.7, namedtuple._asdict() has not been documented + # because there is no way to check the method is a member of the + # namedtuple class. This testcase confirms only it does not + # raise an error on building document (refs: #1455) + self.assertSkip('class', '_asdict', + SampleNamedTuple._asdict, True, + 'napoleon_include_private_with_doc') def test_class_private_doc(self): self.assertSkip('class', '_private_doc', diff --git a/tests/test_util_inspect.py b/tests/test_util_inspect.py index e84e32adfcf..9e59fd69283 100644 --- a/tests/test_util_inspect.py +++ b/tests/test_util_inspect.py @@ -163,16 +163,10 @@ def test_signature_annotations(): # TypeVars and generic types with TypeVars sig = inspect.signature(f2) - if sys.version_info < (3, 7): - assert stringify_signature(sig) == ('(x: typing.List[typing.T],' - ' y: typing.List[typing.T_co],' - ' z: typing.T' - ') -> typing.List[typing.T_contra]') - else: - assert stringify_signature(sig) == ('(x: typing.List[tests.typing_test_data.T],' - ' y: typing.List[tests.typing_test_data.T_co],' - ' z: tests.typing_test_data.T' - ') -> typing.List[tests.typing_test_data.T_contra]') + assert stringify_signature(sig) == ('(x: typing.List[tests.typing_test_data.T],' + ' y: typing.List[tests.typing_test_data.T_co],' + ' z: tests.typing_test_data.T' + ') -> typing.List[tests.typing_test_data.T_contra]') # Union types sig = inspect.signature(f3) @@ -678,7 +672,6 @@ def test_isproperty(app): assert inspect.isproperty(func) is False # function -@pytest.mark.skipif(sys.version_info < (3, 7), reason='python 3.7+ is required.') @pytest.mark.sphinx(testroot='ext-autodoc') def test_isgenericalias(app): from target.genericalias import C, T diff --git a/tests/test_util_typing.py b/tests/test_util_typing.py index 5aa558c821f..c1127d992be 100644 --- a/tests/test_util_typing.py +++ b/tests/test_util_typing.py @@ -89,17 +89,10 @@ def test_restify_type_hints_containers(): def test_restify_type_hints_Callable(): assert restify(Callable) == ":py:class:`~typing.Callable`" - - if sys.version_info >= (3, 7): - assert restify(Callable[[str], int]) == (":py:class:`~typing.Callable`\\ " - "[[:py:class:`str`], :py:class:`int`]") - assert restify(Callable[..., int]) == (":py:class:`~typing.Callable`\\ " - "[[...], :py:class:`int`]") - else: - assert restify(Callable[[str], int]) == (":py:class:`~typing.Callable`\\ " - "[:py:class:`str`, :py:class:`int`]") - assert restify(Callable[..., int]) == (":py:class:`~typing.Callable`\\ " - "[..., :py:class:`int`]") + assert restify(Callable[[str], int]) == (":py:class:`~typing.Callable`\\ " + "[[:py:class:`str`], :py:class:`int`]") + assert restify(Callable[..., int]) == (":py:class:`~typing.Callable`\\ " + "[[...], :py:class:`int`]") def test_restify_type_hints_Union(): @@ -108,30 +101,20 @@ def test_restify_type_hints_Union(): assert restify(Union[int, str]) == (":py:obj:`~typing.Union`\\ " "[:py:class:`int`, :py:class:`str`]") - if sys.version_info >= (3, 7): - assert restify(Union[int, Integral]) == (":py:obj:`~typing.Union`\\ " - "[:py:class:`int`, :py:class:`numbers.Integral`]") - assert restify(Union[int, Integral], "smart") == (":py:obj:`~typing.Union`\\ " - "[:py:class:`int`," - " :py:class:`~numbers.Integral`]") - - assert (restify(Union[MyClass1, MyClass2]) == - (":py:obj:`~typing.Union`\\ " - "[:py:class:`tests.test_util_typing.MyClass1`, " - ":py:class:`tests.test_util_typing.`]")) - assert (restify(Union[MyClass1, MyClass2], "smart") == - (":py:obj:`~typing.Union`\\ " - "[:py:class:`~tests.test_util_typing.MyClass1`," - " :py:class:`~tests.test_util_typing.`]")) - else: - assert restify(Union[int, Integral]) == ":py:class:`numbers.Integral`" - assert restify(Union[int, Integral], "smart") == ":py:class:`~numbers.Integral`" + assert restify(Union[int, Integral]) == (":py:obj:`~typing.Union`\\ " + "[:py:class:`int`, :py:class:`numbers.Integral`]") + assert restify(Union[int, Integral], "smart") == (":py:obj:`~typing.Union`\\ " + "[:py:class:`int`," + " :py:class:`~numbers.Integral`]") - assert restify(Union[MyClass1, MyClass2]) == ":py:class:`tests.test_util_typing.MyClass1`" - assert restify(Union[MyClass1, MyClass2], "smart") == ":py:class:`~tests.test_util_typing.MyClass1`" + assert (restify(Union[MyClass1, MyClass2]) == (":py:obj:`~typing.Union`\\ " + "[:py:class:`tests.test_util_typing.MyClass1`, " + ":py:class:`tests.test_util_typing.`]")) + assert (restify(Union[MyClass1, MyClass2], "smart") == (":py:obj:`~typing.Union`\\ " + "[:py:class:`~tests.test_util_typing.MyClass1`," + " :py:class:`~tests.test_util_typing.`]")) -@pytest.mark.skipif(sys.version_info < (3, 7), reason='python 3.7+ is required.') def test_restify_type_hints_typevars(): T = TypeVar('T') T_co = TypeVar('T_co', covariant=True) @@ -172,7 +155,6 @@ def test_restify_type_hints_alias(): assert restify(MyTuple) == ":py:class:`~typing.Tuple`\\ [:py:class:`str`, :py:class:`str`]" -@pytest.mark.skipif(sys.version_info < (3, 7), reason='python 3.7+ is required.') def test_restify_type_ForwardRef(): from typing import ForwardRef # type: ignore assert restify(ForwardRef("myint")) == ":py:class:`myint`" @@ -346,22 +328,13 @@ def test_stringify_type_hints_Callable(): assert stringify(Callable, "fully-qualified") == "typing.Callable" assert stringify(Callable, "smart") == "~typing.Callable" - if sys.version_info >= (3, 7): - assert stringify(Callable[[str], int]) == "Callable[[str], int]" - assert stringify(Callable[[str], int], "fully-qualified") == "typing.Callable[[str], int]" - assert stringify(Callable[[str], int], "smart") == "~typing.Callable[[str], int]" - - assert stringify(Callable[..., int]) == "Callable[[...], int]" - assert stringify(Callable[..., int], "fully-qualified") == "typing.Callable[[...], int]" - assert stringify(Callable[..., int], "smart") == "~typing.Callable[[...], int]" - else: - assert stringify(Callable[[str], int]) == "Callable[str, int]" - assert stringify(Callable[[str], int], "fully-qualified") == "typing.Callable[str, int]" - assert stringify(Callable[[str], int], "smart") == "~typing.Callable[str, int]" + assert stringify(Callable[[str], int]) == "Callable[[str], int]" + assert stringify(Callable[[str], int], "fully-qualified") == "typing.Callable[[str], int]" + assert stringify(Callable[[str], int], "smart") == "~typing.Callable[[str], int]" - assert stringify(Callable[..., int]) == "Callable[..., int]" - assert stringify(Callable[..., int], "fully-qualified") == "typing.Callable[..., int]" - assert stringify(Callable[..., int], "smart") == "~typing.Callable[..., int]" + assert stringify(Callable[..., int]) == "Callable[[...], int]" + assert stringify(Callable[..., int], "fully-qualified") == "typing.Callable[[...], int]" + assert stringify(Callable[..., int], "smart") == "~typing.Callable[[...], int]" def test_stringify_type_hints_Union(): @@ -377,25 +350,16 @@ def test_stringify_type_hints_Union(): assert stringify(Union[int, str], "fully-qualified") == "typing.Union[int, str]" assert stringify(Union[int, str], "smart") == "~typing.Union[int, str]" - if sys.version_info >= (3, 7): - assert stringify(Union[int, Integral]) == "Union[int, numbers.Integral]" - assert stringify(Union[int, Integral], "fully-qualified") == "typing.Union[int, numbers.Integral]" - assert stringify(Union[int, Integral], "smart") == "~typing.Union[int, ~numbers.Integral]" - - assert (stringify(Union[MyClass1, MyClass2]) == - "Union[tests.test_util_typing.MyClass1, tests.test_util_typing.]") - assert (stringify(Union[MyClass1, MyClass2], "fully-qualified") == - "typing.Union[tests.test_util_typing.MyClass1, tests.test_util_typing.]") - assert (stringify(Union[MyClass1, MyClass2], "smart") == - "~typing.Union[~tests.test_util_typing.MyClass1, ~tests.test_util_typing.]") - else: - assert stringify(Union[int, Integral]) == "numbers.Integral" - assert stringify(Union[int, Integral], "fully-qualified") == "numbers.Integral" - assert stringify(Union[int, Integral], "smart") == "~numbers.Integral" + assert stringify(Union[int, Integral]) == "Union[int, numbers.Integral]" + assert stringify(Union[int, Integral], "fully-qualified") == "typing.Union[int, numbers.Integral]" + assert stringify(Union[int, Integral], "smart") == "~typing.Union[int, ~numbers.Integral]" - assert stringify(Union[MyClass1, MyClass2]) == "tests.test_util_typing.MyClass1" - assert stringify(Union[MyClass1, MyClass2], "fully-qualified") == "tests.test_util_typing.MyClass1" - assert stringify(Union[MyClass1, MyClass2], "smart") == "~tests.test_util_typing.MyClass1" + assert (stringify(Union[MyClass1, MyClass2]) == + "Union[tests.test_util_typing.MyClass1, tests.test_util_typing.]") + assert (stringify(Union[MyClass1, MyClass2], "fully-qualified") == + "typing.Union[tests.test_util_typing.MyClass1, tests.test_util_typing.]") + assert (stringify(Union[MyClass1, MyClass2], "smart") == + "~typing.Union[~tests.test_util_typing.MyClass1, ~tests.test_util_typing.]") def test_stringify_type_hints_typevars(): @@ -403,30 +367,17 @@ def test_stringify_type_hints_typevars(): T_co = TypeVar('T_co', covariant=True) T_contra = TypeVar('T_contra', contravariant=True) - if sys.version_info < (3, 7): - assert stringify(T) == "T" - assert stringify(T, "smart") == "T" - - assert stringify(T_co) == "T_co" - assert stringify(T_co, "smart") == "T_co" - - assert stringify(T_contra) == "T_contra" - assert stringify(T_contra, "smart") == "T_contra" - - assert stringify(List[T]) == "List[T]" - assert stringify(List[T], "smart") == "~typing.List[T]" - else: - assert stringify(T) == "tests.test_util_typing.T" - assert stringify(T, "smart") == "~tests.test_util_typing.T" + assert stringify(T) == "tests.test_util_typing.T" + assert stringify(T, "smart") == "~tests.test_util_typing.T" - assert stringify(T_co) == "tests.test_util_typing.T_co" - assert stringify(T_co, "smart") == "~tests.test_util_typing.T_co" + assert stringify(T_co) == "tests.test_util_typing.T_co" + assert stringify(T_co, "smart") == "~tests.test_util_typing.T_co" - assert stringify(T_contra) == "tests.test_util_typing.T_contra" - assert stringify(T_contra, "smart") == "~tests.test_util_typing.T_contra" + assert stringify(T_contra) == "tests.test_util_typing.T_contra" + assert stringify(T_contra, "smart") == "~tests.test_util_typing.T_contra" - assert stringify(List[T]) == "List[tests.test_util_typing.T]" - assert stringify(List[T], "smart") == "~typing.List[~tests.test_util_typing.T]" + assert stringify(List[T]) == "List[tests.test_util_typing.T]" + assert stringify(List[T], "smart") == "~typing.List[~tests.test_util_typing.T]" if sys.version_info >= (3, 10): assert stringify(MyInt) == "tests.test_util_typing.MyInt"