Clean up after dropping Python 3.6

This commit is contained in:
Adam Turner
2022-04-17 04:01:17 +01:00
parent 51927bb6e4
commit 7649eb1505
23 changed files with 95 additions and 122 deletions

View File

@@ -7,7 +7,7 @@ permissions:
jobs:
ubuntu:
runs-on: ubuntu-18.04
runs-on: ubuntu-20.04
name: Python ${{ matrix.python }} (${{ matrix.docutils }})
strategy:
fail-fast: false

View File

@@ -4,7 +4,7 @@ Release 6.0.0 (in development)
Dependencies
------------
* Drop python 3.6 support
* #10468: Drop Python 3.6 support
Incompatible changes
--------------------

View File

@@ -113,13 +113,15 @@ April 2023 and shipping Python 3.8.
This is a summary table with the current policy:
========== ========= ======
Date Ubuntu Python
========== ========= ======
April 2023 20.04 LTS 3.8+
---------- --------- ------
April 2025 22.04 LTS 3.10+
========== ========= ======
========== ========= ====== ======
Date Ubuntu Python Sphinx
========== ========= ====== ======
April 2021 18.04 LTS 3.6+ 4, 5
---------- --------- ------ ------
April 2023 20.04 LTS 3.8+ 6, 7
---------- --------- ------ ------
April 2025 22.04 LTS 3.10+ 8
========== ========= ====== ======
Release procedures
------------------

View File

@@ -204,7 +204,7 @@ def _parse_annotation(annotation: str, env: BuildEnvironment) -> List[Node]:
return result
else:
if sys.version_info < (3, 8):
if sys.version_info[:2] <= (3, 7):
if isinstance(node, ast.Bytes):
return [addnodes.desc_sig_literal_string('', repr(node.s))]
elif isinstance(node, ast.Ellipsis):

View File

@@ -820,6 +820,8 @@ class Documenter:
# sort by group; alphabetically within groups
documenters.sort(key=lambda e: (e[0].member_order, e[0].name))
elif order == 'bysource':
# By default, member discovery order matches source order,
# as dicts are insertion-ordered from Python 3.7.
if self.analyzer:
# sort by source order, by virtue of the module analyzer
tagorder = self.analyzer.tagorder
@@ -828,11 +830,6 @@ class Documenter:
fullname = entry[0].name.split('::')[1]
return tagorder.get(fullname, len(tagorder))
documenters.sort(key=keyfunc)
else:
# Assume that member discovery order matches source order.
# This is a reasonable assumption in Python 3.6 and up, where
# module.__dict__ is insertion-ordered.
pass
else: # alphabetical
documenters.sort(key=lambda e: e[0].name)

View File

@@ -48,9 +48,9 @@ def get_function_def(obj: Any) -> Optional[ast.FunctionDef]:
def get_default_value(lines: List[str], position: ast.AST) -> Optional[str]:
try:
if sys.version_info < (3, 8): # only for py38+
if sys.version_info[:2] <= (3, 7): # only for py38+
return None
elif position.lineno == position.end_lineno:
if position.lineno == position.end_lineno:
line = lines[position.lineno - 1]
return line[position.col_offset:position.end_col_offset]
else:

View File

@@ -597,8 +597,6 @@ class GoogleDocstring:
self._parsed_lines = self._consume_empty()
if self._name and self._what in ('attribute', 'data', 'property'):
# Implicit stop using StopIteration no longer allowed in
# Python 3.7; see PEP 479
res: List[str] = []
try:
res = self._parse_attribute_docstring()

View File

@@ -3,7 +3,7 @@
import sys
from typing import Dict, List, Optional, Type, overload
if sys.version_info > (3, 8):
if sys.version_info[:2] >= (3, 8):
import ast
else:
try:
@@ -159,7 +159,7 @@ class _UnparseVisitor(ast.NodeVisitor):
if node.value is Ellipsis:
return "..."
elif isinstance(node.value, (int, float, complex)):
if self.code and sys.version_info > (3, 8):
if self.code and sys.version_info[:2] >= (3, 8):
return ast.get_source_segment(self.code, node) # type: ignore
else:
return repr(node.value)
@@ -219,7 +219,7 @@ class _UnparseVisitor(ast.NodeVisitor):
else:
return "(" + ", ".join(self.visit(e) for e in node.elts) + ")"
if sys.version_info < (3, 8):
if sys.version_info[:2] <= (3, 7):
# these ast nodes were deprecated in python 3.8
def visit_Bytes(self, node: ast.Bytes) -> str:
return repr(node.s)

View File

@@ -107,5 +107,5 @@ def test_svg(h: bytes, f: Optional[BinaryIO]) -> Optional[str]:
# install test_svg() to imghdr
# refs: https://docs.python.org/3.6/library/imghdr.html#imghdr.tests
# refs: https://docs.python.org/3.9/library/imghdr.html#imghdr.tests
imghdr.tests.append(test_svg)

View File

@@ -12,7 +12,8 @@ from functools import partial, partialmethod
from importlib import import_module
from inspect import Parameter, isclass, ismethod, ismethoddescriptor, ismodule # NOQA
from io import StringIO
from types import MethodType, ModuleType
from types import (ClassMethodDescriptorType, MethodDescriptorType, MethodType, ModuleType,
WrapperDescriptorType)
from typing import Any, Callable, Dict, Mapping, Optional, Sequence, Tuple, Type, cast
from sphinx.pycode.ast import ast # for py37
@@ -21,13 +22,6 @@ from sphinx.util import logging
from sphinx.util.typing import ForwardRef
from sphinx.util.typing import stringify as stringify_annotation
if sys.version_info > (3, 7):
from types import ClassMethodDescriptorType, MethodDescriptorType, WrapperDescriptorType
else:
ClassMethodDescriptorType = type(object.__init__)
MethodDescriptorType = type(str.join)
WrapperDescriptorType = type(dict.__dict__['fromkeys'])
logger = logging.getLogger(__name__)
memory_address_re = re.compile(r' at 0x[0-9a-f]{8,16}(?=>)', re.IGNORECASE)
@@ -149,7 +143,7 @@ def getslots(obj: Any) -> Optional[Dict]:
def isNewType(obj: Any) -> bool:
"""Check the if object is a kind of NewType."""
if sys.version_info >= (3, 10):
if sys.version_info[:2] >= (3, 10):
return isinstance(obj, typing.NewType)
else:
__module__ = safe_getattr(obj, '__module__', None)
@@ -335,6 +329,7 @@ def iscoroutinefunction(obj: Any) -> bool:
return False
if sys.version_info[:2] <= (3, 7):
def isasyncgenfunction(obj: Any) -> bool:
"""Check if the object is async-gen function."""
if hasattr(obj, '__code__') and inspect.isasyncgenfunction(obj):
@@ -343,11 +338,13 @@ def isasyncgenfunction(obj: Any) -> bool:
return True
else:
return False
else:
isasyncgenfunction = inspect.isasyncgenfunction
def isproperty(obj: Any) -> bool:
"""Check if the object is property."""
if sys.version_info >= (3, 8):
if sys.version_info[:2] >= (3, 8):
from functools import cached_property # cached_property is available since py3.8
if isinstance(obj, cached_property):
return True
@@ -619,7 +616,7 @@ def evaluate_signature(sig: inspect.Signature, globalns: Optional[Dict] = None,
"""Evaluate unresolved type annotations in a signature object."""
def evaluate_forwardref(ref: ForwardRef, globalns: Dict, localns: Dict) -> Any:
"""Evaluate a forward reference."""
if sys.version_info > (3, 9):
if sys.version_info[:2] >= (3, 9):
return ref._evaluate(globalns, localns, frozenset())
else:
return ref._evaluate(globalns, localns)

View File

@@ -1,7 +1,6 @@
"""Parallel building utilities."""
import os
import sys
import time
import traceback
from math import sqrt
@@ -18,13 +17,6 @@ from sphinx.util import logging
logger = logging.getLogger(__name__)
if sys.platform != "win32":
ForkContext = multiprocessing.context.ForkContext
ForkProcess = multiprocessing.context.ForkProcess
else:
# For static typing, as ForkProcess doesn't exist on Windows
ForkContext = ForkProcess = Any
# our parallel functionality only works for the forking Process
parallel_available = multiprocessing and os.name == 'posix'
@@ -59,7 +51,7 @@ class ParallelTasks:
# task arguments
self._args: Dict[int, Optional[List[Any]]] = {}
# list of subprocesses (both started and waiting)
self._procs: Dict[int, ForkProcess] = {}
self._procs: Dict[int, Any] = {}
# list of receiving pipe connections of running subprocesses
self._precvs: Dict[int, Any] = {}
# list of receiving pipe connections of waiting subprocesses
@@ -93,7 +85,7 @@ class ParallelTasks:
self._result_funcs[tid] = result_func or (lambda arg, result: None)
self._args[tid] = arg
precv, psend = multiprocessing.Pipe(False)
context: ForkContext = multiprocessing.get_context('fork')
context: Any = multiprocessing.get_context('fork')
proc = context.Process(target=self._process, args=(psend, task_func, arg))
self._procs[tid] = proc
self._precvsWaiting[tid] = precv

View File

@@ -5,27 +5,14 @@ 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
from typing import (Any, Callable, Dict, ForwardRef, Generator, List, Optional, Tuple, Type,
TypeVar, Union)
from docutils import nodes
from docutils.parsers.rst.states import Inliner
from sphinx.deprecation import RemovedInSphinx70Warning
if sys.version_info > (3, 7):
from typing import ForwardRef
else:
from typing import _ForwardRef # type: ignore
class ForwardRef:
"""A pseudo ForwardRef class for py36."""
def __init__(self, arg: Any, is_argument: bool = True) -> None:
self.arg = arg
def _evaluate(self, globalns: Dict, localns: Dict) -> Any:
ref = _ForwardRef(self.arg)
return ref._eval_type(globalns, localns)
try:
from types import UnionType # type: ignore # python 3.10 or above
except ImportError:
@@ -137,7 +124,7 @@ def restify(cls: Optional[Type], mode: str = 'fully-qualified-except-typing') ->
elif is_invalid_builtin_class(cls):
return ':py:class:`%s%s`' % (modprefix, INVALID_BUILTIN_CLASSES[cls])
elif inspect.isNewType(cls):
if sys.version_info > (3, 10):
if sys.version_info[:2] >= (3, 10):
# newtypes have correct module info since Python 3.10+
return ':py:class:`%s%s.%s`' % (modprefix, cls.__module__, cls.__name__)
else:
@@ -211,7 +198,7 @@ def _restify_py37(cls: Optional[Type], mode: str = 'fully-qualified-except-typin
return text
elif isinstance(cls, typing._SpecialForm):
return ':py:obj:`~%s.%s`' % (cls.__module__, cls._name)
elif sys.version_info >= (3, 11) and cls is typing.Any:
elif sys.version_info[:2] >= (3, 11) and cls is typing.Any:
# handle bpo-46998
return f':py:obj:`~{cls.__module__}.{cls.__name__}`'
elif hasattr(cls, '__qualname__'):
@@ -362,7 +349,7 @@ def stringify(annotation: Any, mode: str = 'fully-qualified-except-typing') -> s
else:
return modprefix + '.'.join([annotation.__module__, annotation.__name__])
elif inspect.isNewType(annotation):
if sys.version_info > (3, 10):
if sys.version_info[:2] >= (3, 10):
# newtypes have correct module info since Python 3.10+
return modprefix + '%s.%s' % (annotation.__module__, annotation.__name__)
else:

View File

@@ -1,7 +1,6 @@
"""Test all builders."""
import sys
from textwrap import dedent
from unittest import mock
import pytest
@@ -19,7 +18,7 @@ def request_session_head(url, **kwargs):
@pytest.fixture
def nonascii_srcdir(request, rootdir, sphinx_test_tempdir):
# If supported, build in a non-ASCII source dir
# Build in a non-ASCII source dir
test_name = '\u65e5\u672c\u8a9e'
basedir = sphinx_test_tempdir / request.node.originalname
srcdir = basedir / test_name
@@ -27,18 +26,17 @@ def nonascii_srcdir(request, rootdir, sphinx_test_tempdir):
(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("""
(srcdir / (test_name + '.txt')).write_text("""
nonascii file name page
=======================
"""), encoding='utf8')
""", encoding='utf8')
root_doc = srcdir / 'index.txt'
root_doc.write_text(root_doc.read_text(encoding='utf8') + dedent("""
root_doc.write_text(root_doc.read_text(encoding='utf8') + f"""
.. toctree::
%(test_name)s/%(test_name)s
""" % {'test_name': test_name}), encoding='utf8')
{test_name}/{test_name}
""", encoding='utf8')
return srcdir

View File

@@ -365,7 +365,7 @@ def test_parse_annotation_suppress(app):
assert_node(doctree[0], pending_xref, refdomain="py", reftype="obj", reftarget="typing.Dict")
@pytest.mark.skipif(sys.version_info < (3, 8), reason='python 3.8+ is required.')
@pytest.mark.skipif(sys.version_info[:2] <= (3, 7), reason='python 3.8+ is required.')
def test_parse_annotation_Literal(app):
doctree = _parse_annotation("Literal[True, False]", app.env)
assert_node(doctree, ([pending_xref, "Literal"],
@@ -480,7 +480,7 @@ def test_pyfunction_with_binary_operators(app):
[nodes.inline, "2**64"])])])
@pytest.mark.skipif(sys.version_info < (3, 8), reason='python 3.8+ is required.')
@pytest.mark.skipif(sys.version_info[:2] <= (3, 7), reason='python 3.8+ is required.')
def test_pyfunction_signature_full_py38(app):
# case: separator at head
text = ".. py:function:: hello(*, a)"
@@ -516,7 +516,7 @@ def test_pyfunction_signature_full_py38(app):
[desc_parameter, desc_sig_operator, "/"])])
@pytest.mark.skipif(sys.version_info < (3, 8), reason='python 3.8+ is required.')
@pytest.mark.skipif(sys.version_info[:2] <= (3, 7), reason='python 3.8+ is required.')
def test_pyfunction_with_number_literals(app):
text = ".. py:function:: hello(age=0x10, height=1_6_0)"
doctree = restructuredtext.parse(app, text)

View File

@@ -1072,7 +1072,7 @@ def test_autodoc_descriptor(app):
]
@pytest.mark.skipif(sys.version_info < (3, 8),
@pytest.mark.skipif(sys.version_info[:2] <= (3, 7),
reason='cached_property is available since python3.8.')
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_cached_property(app):
@@ -1404,7 +1404,7 @@ def test_enum_class(app):
options = {"members": None}
actual = do_autodoc(app, 'class', 'target.enums.EnumCls', options)
if sys.version_info > (3, 11):
if sys.version_info[:2] >= (3, 11):
args = ('(value, names=None, *, module=None, qualname=None, '
'type=None, start=1, boundary=None)')
else:
@@ -1976,7 +1976,7 @@ def test_autodoc_TypeVar(app):
]
@pytest.mark.skipif(sys.version_info < (3, 9), reason='py39+ is required.')
@pytest.mark.skipif(sys.version_info[:2] <= (3, 8), reason='py39+ is required.')
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_Annotated(app):
options = {"members": None}
@@ -2059,7 +2059,7 @@ def test_singledispatch(app):
]
@pytest.mark.skipif(sys.version_info < (3, 8),
@pytest.mark.skipif(sys.version_info[:2] <= (3, 7),
reason='singledispatchmethod is available since python3.8')
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_singledispatchmethod(app):
@@ -2088,7 +2088,7 @@ def test_singledispatchmethod(app):
]
@pytest.mark.skipif(sys.version_info < (3, 8),
@pytest.mark.skipif(sys.version_info[:2] <= (3, 7),
reason='singledispatchmethod is available since python3.8')
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_singledispatchmethod_automethod(app):
@@ -2108,7 +2108,7 @@ def test_singledispatchmethod_automethod(app):
]
@pytest.mark.skipif(sys.version_info > (3, 11),
@pytest.mark.skipif(sys.version_info[:2] >= (3, 11),
reason=('cython does not support python-3.11 yet. '
'see https://github.com/cython/cython/issues/4365'))
@pytest.mark.skipif(pyximport is None, reason='cython is not installed')
@@ -2142,7 +2142,7 @@ def test_cython(app):
]
@pytest.mark.skipif(sys.version_info < (3, 8),
@pytest.mark.skipif(sys.version_info[:2] <= (3, 7),
reason='typing.final is available since python3.8')
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_final(app):

View File

@@ -40,7 +40,7 @@ def test_class_properties(app):
]
@pytest.mark.skipif(sys.version_info < (3, 8), reason='python 3.8+ is required.')
@pytest.mark.skipif(sys.version_info[:2] <= (3, 7), reason='python 3.8+ is required.')
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_cached_properties(app):
actual = do_autodoc(app, 'property', 'target.cached_property.Foo.prop')

View File

@@ -661,7 +661,7 @@ def test_mocked_module_imports(app, warning):
@pytest.mark.sphinx('html', testroot='ext-autodoc',
confoverrides={'autodoc_typehints': "signature"})
def test_autodoc_typehints_signature(app):
if sys.version_info < (3, 11):
if sys.version_info[:2] <= (3, 10):
type_o = "~typing.Optional[~typing.Any]"
else:
type_o = "~typing.Any"
@@ -1399,7 +1399,7 @@ def test_autodoc_typehints_description_and_type_aliases(app):
@pytest.mark.sphinx('html', testroot='ext-autodoc',
confoverrides={'autodoc_typehints_format': "fully-qualified"})
def test_autodoc_typehints_format_fully_qualified(app):
if sys.version_info < (3, 11):
if sys.version_info[:2] <= (3, 10):
type_o = "typing.Optional[typing.Any]"
else:
type_o = "typing.Any"

View File

@@ -10,7 +10,7 @@ from .test_ext_autodoc import do_autodoc
@pytest.mark.sphinx('html', testroot='ext-autodoc',
confoverrides={'autodoc_preserve_defaults': True})
def test_preserve_defaults(app):
if sys.version_info < (3, 8):
if sys.version_info[:2] <= (3, 7):
color = "16777215"
else:
color = "0xFFFFFF"

View File

@@ -185,7 +185,7 @@ def test_ModuleAnalyzer_find_attr_docs():
'Qux.attr2': 17}
@pytest.mark.skipif(sys.version_info < (3, 8),
@pytest.mark.skipif(sys.version_info[:2] <= (3, 7),
reason='posonlyargs are available since python3.8.')
def test_ModuleAnalyzer_find_attr_docs_for_posonlyargs_method():
code = ('class Foo(object):\n'

View File

@@ -58,7 +58,7 @@ def test_unparse_None():
assert ast.unparse(None) is None
@pytest.mark.skipif(sys.version_info < (3, 8), reason='python 3.8+ is required.')
@pytest.mark.skipif(sys.version_info[:2] <= (3, 7), reason='python 3.8+ is required.')
@pytest.mark.parametrize('source,expected', [
("lambda x=0, /, y=1, *args, z, **kwargs: x + y + z",
"lambda x=0, /, y=1, *args, z, **kwargs: ..."), # posonlyargs

View File

@@ -186,7 +186,7 @@ def test_signature_annotations():
# Space around '=' for defaults
sig = inspect.signature(f7)
if sys.version_info < (3, 11):
if sys.version_info[:2] <= (3, 10):
assert stringify_signature(sig) == '(x: typing.Optional[int] = None, y: dict = {}) -> None'
else:
assert stringify_signature(sig) == '(x: int = None, y: dict = {}) -> None'
@@ -260,20 +260,20 @@ def test_signature_annotations():
# show_return_annotation is False
sig = inspect.signature(f7)
if sys.version_info < (3, 11):
if sys.version_info[:2] <= (3, 10):
assert stringify_signature(sig, show_return_annotation=False) == '(x: typing.Optional[int] = None, y: dict = {})'
else:
assert stringify_signature(sig, show_return_annotation=False) == '(x: int = None, y: dict = {})'
# unqualified_typehints is True
sig = inspect.signature(f7)
if sys.version_info < (3, 11):
if sys.version_info[:2] <= (3, 10):
assert stringify_signature(sig, unqualified_typehints=True) == '(x: ~typing.Optional[int] = None, y: dict = {}) -> None'
else:
assert stringify_signature(sig, unqualified_typehints=True) == '(x: int = None, y: dict = {}) -> None'
@pytest.mark.skipif(sys.version_info < (3, 8), reason='python 3.8+ is required.')
@pytest.mark.skipif(sys.version_info[:2] <= (3, 7), reason='python 3.8+ is required.')
@pytest.mark.sphinx(testroot='ext-autodoc')
def test_signature_annotations_py38(app):
from target.pep570 import bar, baz, foo, qux
@@ -373,7 +373,7 @@ def test_signature_from_str_kwonly_args():
assert sig.parameters['b'].default == Parameter.empty
@pytest.mark.skipif(sys.version_info < (3, 8),
@pytest.mark.skipif(sys.version_info[:2] <= (3, 7),
reason='python-3.8 or above is required')
def test_signature_from_str_positionaly_only_args():
sig = inspect.signature_from_str('(a, b=0, /, c=1)')

View File

@@ -71,7 +71,7 @@ def test_restify_type_hints_containers():
":py:class:`str`]")
assert restify(Tuple[str, ...]) == ":py:class:`~typing.Tuple`\\ [:py:class:`str`, ...]"
if sys.version_info < (3, 11):
if sys.version_info[:2] <= (3, 10):
assert restify(Tuple[()]) == ":py:class:`~typing.Tuple`\\ [()]"
else:
assert restify(Tuple[()]) == ":py:class:`~typing.Tuple`"
@@ -89,6 +89,7 @@ def test_restify_type_hints_containers():
def test_restify_type_hints_Callable():
assert restify(Callable) == ":py:class:`~typing.Callable`"
assert restify(Callable[[str], int]) == (":py:class:`~typing.Callable`\\ "
"[[:py:class:`str`], :py:class:`int`]")
assert restify(Callable[..., int]) == (":py:class:`~typing.Callable`\\ "
@@ -100,17 +101,18 @@ def test_restify_type_hints_Union():
assert restify(Union[str, None]) == ":py:obj:`~typing.Optional`\\ [:py:class:`str`]"
assert restify(Union[int, str]) == (":py:obj:`~typing.Union`\\ "
"[:py:class:`int`, :py:class:`str`]")
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`\\ "
assert (restify(Union[MyClass1, MyClass2]) ==
(":py:obj:`~typing.Union`\\ "
"[:py:class:`tests.test_util_typing.MyClass1`, "
":py:class:`tests.test_util_typing.<MyClass2>`]"))
assert (restify(Union[MyClass1, MyClass2], "smart") == (":py:obj:`~typing.Union`\\ "
assert (restify(Union[MyClass1, MyClass2], "smart") ==
(":py:obj:`~typing.Union`\\ "
"[:py:class:`~tests.test_util_typing.MyClass1`,"
" :py:class:`~tests.test_util_typing.<MyClass2>`]"))
@@ -132,7 +134,7 @@ def test_restify_type_hints_typevars():
assert restify(List[T]) == ":py:class:`~typing.List`\\ [:py:obj:`tests.test_util_typing.T`]"
assert restify(List[T], "smart") == ":py:class:`~typing.List`\\ [:py:obj:`~tests.test_util_typing.T`]"
if sys.version_info >= (3, 10):
if sys.version_info[:2] >= (3, 10):
assert restify(MyInt) == ":py:class:`tests.test_util_typing.MyInt`"
assert restify(MyInt, "smart") == ":py:class:`~tests.test_util_typing.MyInt`"
else:
@@ -160,13 +162,13 @@ def test_restify_type_ForwardRef():
assert restify(ForwardRef("myint")) == ":py:class:`myint`"
@pytest.mark.skipif(sys.version_info < (3, 8), reason='python 3.8+ is required.')
@pytest.mark.skipif(sys.version_info[:2] <= (3, 7), reason='python 3.8+ is required.')
def test_restify_type_Literal():
from typing import Literal # type: ignore
assert restify(Literal[1, "2", "\r"]) == ":py:obj:`~typing.Literal`\\ [1, '2', '\\r']"
@pytest.mark.skipif(sys.version_info < (3, 9), reason='python 3.9+ is required.')
@pytest.mark.skipif(sys.version_info[:2] <= (3, 8), reason='python 3.9+ is required.')
def test_restify_pep_585():
assert restify(list[str]) == ":py:class:`list`\\ [:py:class:`str`]" # type: ignore
assert restify(dict[str, str]) == (":py:class:`dict`\\ " # type: ignore
@@ -176,7 +178,7 @@ def test_restify_pep_585():
"[:py:class:`int`, ...]]")
@pytest.mark.skipif(sys.version_info < (3, 10), reason='python 3.10+ is required.')
@pytest.mark.skipif(sys.version_info[:2] <= (3, 9), reason='python 3.10+ is required.')
def test_restify_type_union_operator():
assert restify(int | None) == ":py:class:`int` | :py:obj:`None`" # type: ignore
assert restify(int | str) == ":py:class:`int` | :py:class:`str`" # type: ignore
@@ -250,7 +252,7 @@ def test_stringify_type_hints_containers():
assert stringify(Tuple[str, ...], "fully-qualified") == "typing.Tuple[str, ...]"
assert stringify(Tuple[str, ...], "smart") == "~typing.Tuple[str, ...]"
if sys.version_info < (3, 11):
if sys.version_info[:2] <= (3, 10):
assert stringify(Tuple[()]) == "Tuple[()]"
assert stringify(Tuple[()], "fully-qualified") == "typing.Tuple[()]"
assert stringify(Tuple[()], "smart") == "~typing.Tuple[()]"
@@ -272,7 +274,7 @@ def test_stringify_type_hints_containers():
assert stringify(Generator[None, None, None], "smart") == "~typing.Generator[None, None, None]"
@pytest.mark.skipif(sys.version_info < (3, 9), reason='python 3.9+ is required.')
@pytest.mark.skipif(sys.version_info[:2] <= (3, 8), reason='python 3.9+ is required.')
def test_stringify_type_hints_pep_585():
assert stringify(list[int]) == "list[int]"
assert stringify(list[int], "smart") == "list[int]"
@@ -299,7 +301,7 @@ def test_stringify_type_hints_pep_585():
assert stringify(type[int], "smart") == "type[int]"
@pytest.mark.skipif(sys.version_info < (3, 9), reason='python 3.9+ is required.')
@pytest.mark.skipif(sys.version_info[:2] <= (3, 8), reason='python 3.9+ is required.')
def test_stringify_Annotated():
from typing import Annotated # type: ignore
assert stringify(Annotated[str, "foo", "bar"]) == "str"
@@ -379,7 +381,7 @@ def test_stringify_type_hints_typevars():
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):
if sys.version_info[:2] >= (3, 10):
assert stringify(MyInt) == "tests.test_util_typing.MyInt"
assert stringify(MyInt, "smart") == "~tests.test_util_typing.MyInt"
else:
@@ -406,7 +408,7 @@ def test_stringify_type_hints_alias():
assert stringify(MyTuple, "smart") == "~typing.Tuple[str, str]" # type: ignore
@pytest.mark.skipif(sys.version_info < (3, 8), reason='python 3.8+ is required.')
@pytest.mark.skipif(sys.version_info[:2] <= (3, 7), reason='python 3.8+ is required.')
def test_stringify_type_Literal():
from typing import Literal # type: ignore
assert stringify(Literal[1, "2", "\r"]) == "Literal[1, '2', '\\r']"
@@ -414,7 +416,7 @@ def test_stringify_type_Literal():
assert stringify(Literal[1, "2", "\r"], "smart") == "~typing.Literal[1, '2', '\\r']"
@pytest.mark.skipif(sys.version_info < (3, 10), reason='python 3.10+ is required.')
@pytest.mark.skipif(sys.version_info[:2] <= (3, 9), reason='python 3.10+ is required.')
def test_stringify_type_union_operator():
assert stringify(int | None) == "int | None" # type: ignore
assert stringify(int | None, "smart") == "int | None" # type: ignore

View File

@@ -1,6 +1,6 @@
[tox]
minversion = 2.4.0
envlist = docs,flake8,mypy,twine,py{36,37,38,39,310,311},du{14,15,16,17,18,19}
envlist = docs,flake8,mypy,twine,py{37,38,39,310,311},du{14,15,16,17,18,19}
isolated_build = True
[testenv]
@@ -16,7 +16,7 @@ passenv =
EPUBCHECK_PATH
TERM
description =
py{36,37,38,39,310,311}: Run unit tests against {envname}.
py{37,38,39,310,311}: Run unit tests against {envname}.
du{14,15,16,17,18,19}: Run unit tests with the given version of docutils.
deps =
du14: docutils==0.14.*