mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Clean up after dropping Python 3.6
This commit is contained in:
2
.github/workflows/main.yml
vendored
2
.github/workflows/main.yml
vendored
@@ -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
|
||||
|
||||
2
CHANGES
2
CHANGES
@@ -4,7 +4,7 @@ Release 6.0.0 (in development)
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
* Drop python 3.6 support
|
||||
* #10468: Drop Python 3.6 support
|
||||
|
||||
Incompatible changes
|
||||
--------------------
|
||||
|
||||
@@ -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
|
||||
------------------
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,7 +329,8 @@ def iscoroutinefunction(obj: Any) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
def isasyncgenfunction(obj: Any) -> bool:
|
||||
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):
|
||||
# check obj.__code__ because isasyncgenfunction() crashes for custom method-like
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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("""
|
||||
nonascii file name page
|
||||
=======================
|
||||
"""), encoding='utf8')
|
||||
(srcdir / (test_name + '.txt')).write_text("""
|
||||
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')
|
||||
root_doc.write_text(root_doc.read_text(encoding='utf8') + f"""
|
||||
.. toctree::
|
||||
|
||||
{test_name}/{test_name}
|
||||
""", encoding='utf8')
|
||||
return srcdir
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)')
|
||||
|
||||
@@ -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
|
||||
|
||||
4
tox.ini
4
tox.ini
@@ -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.*
|
||||
|
||||
Reference in New Issue
Block a user