Merge pull request #9442 from hramezani/drop_python_36

Drop Python3.6 in CI, setup.cfg, and readme.
This commit is contained in:
Ran Benita 2021-12-30 12:22:13 +02:00 committed by GitHub
commit b9663fed6f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 21 additions and 86 deletions

View File

@ -31,14 +31,12 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
name: [ name: [
"windows-py36",
"windows-py37", "windows-py37",
"windows-py37-pluggy", "windows-py37-pluggy",
"windows-py38", "windows-py38",
"windows-py39", "windows-py39",
"windows-py310", "windows-py310",
"ubuntu-py36",
"ubuntu-py37", "ubuntu-py37",
"ubuntu-py37-pluggy", "ubuntu-py37-pluggy",
"ubuntu-py37-freeze", "ubuntu-py37-freeze",
@ -56,10 +54,6 @@ jobs:
] ]
include: include:
- name: "windows-py36"
python: "3.6"
os: windows-latest
tox_env: "py36-xdist"
- name: "windows-py37" - name: "windows-py37"
python: "3.7" python: "3.7"
os: windows-latest os: windows-latest
@ -82,10 +76,6 @@ jobs:
os: windows-latest os: windows-latest
tox_env: "py310-xdist" tox_env: "py310-xdist"
- name: "ubuntu-py36"
python: "3.6"
os: ubuntu-latest
tox_env: "py36-xdist"
- name: "ubuntu-py37" - name: "ubuntu-py37"
python: "3.7" python: "3.7"
os: ubuntu-latest os: ubuntu-latest

View File

@ -32,12 +32,12 @@ repos:
rev: v2.6.0 rev: v2.6.0
hooks: hooks:
- id: reorder-python-imports - id: reorder-python-imports
args: ['--application-directories=.:src', --py36-plus] args: ['--application-directories=.:src', --py37-plus]
- repo: https://github.com/asottile/pyupgrade - repo: https://github.com/asottile/pyupgrade
rev: v2.29.1 rev: v2.29.1
hooks: hooks:
- id: pyupgrade - id: pyupgrade
args: [--py36-plus] args: [--py37-plus]
- repo: https://github.com/asottile/setup-cfg-fmt - repo: https://github.com/asottile/setup-cfg-fmt
rev: v1.20.0 rev: v1.20.0
hooks: hooks:

View File

@ -100,7 +100,7 @@ Features
- Can run `unittest <https://docs.pytest.org/en/stable/how-to/unittest.html>`_ (or trial), - Can run `unittest <https://docs.pytest.org/en/stable/how-to/unittest.html>`_ (or trial),
`nose <https://docs.pytest.org/en/stable/how-to/nose.html>`_ test suites out of the box `nose <https://docs.pytest.org/en/stable/how-to/nose.html>`_ test suites out of the box
- Python 3.6+ and PyPy3 - Python 3.7+ or PyPy3
- Rich plugin architecture, with over 850+ `external plugins <https://docs.pytest.org/en/latest/reference/plugin_list.html>`_ and thriving community - Rich plugin architecture, with over 850+ `external plugins <https://docs.pytest.org/en/latest/reference/plugin_list.html>`_ and thriving community

View File

@ -0,0 +1 @@
Dropped support for Python 3.6, which reached `end-of-life <https://devguide.python.org/#status-of-python-branches>`__ at 2021-12-23.

View File

@ -9,7 +9,7 @@ Get Started
Install ``pytest`` Install ``pytest``
---------------------------------------- ----------------------------------------
``pytest`` requires: Python 3.6, 3.7, 3.8, 3.9, or PyPy3. ``pytest`` requires: Python 3.7+ or PyPy3.
1. Run the following command in your command line: 1. Run the following command in your command line:

View File

@ -84,14 +84,14 @@ It is also possible to skip the whole module using
If you wish to skip something conditionally then you can use ``skipif`` instead. If you wish to skip something conditionally then you can use ``skipif`` instead.
Here is an example of marking a test function to be skipped Here is an example of marking a test function to be skipped
when run on an interpreter earlier than Python3.6: when run on an interpreter earlier than Python3.10:
.. code-block:: python .. code-block:: python
import sys import sys
@pytest.mark.skipif(sys.version_info < (3, 7), reason="requires python3.7 or higher") @pytest.mark.skipif(sys.version_info < (3, 10), reason="requires python3.10 or higher")
def test_function(): def test_function():
... ...

View File

@ -17,7 +17,7 @@ The ``pytest`` framework makes it easy to write small, readable tests, and can
scale to support complex functional testing for applications and libraries. scale to support complex functional testing for applications and libraries.
**Pythons**: ``pytest`` requires: Python 3.6, 3.7, 3.8, 3.9, or PyPy3. ``pytest`` requires: Python 3.7+ or PyPy3.
**PyPI package name**: :pypi:`pytest` **PyPI package name**: :pypi:`pytest`
@ -78,7 +78,7 @@ Features
- Can run :ref:`unittest <unittest>` (including trial) and :ref:`nose <noseintegration>` test suites out of the box - Can run :ref:`unittest <unittest>` (including trial) and :ref:`nose <noseintegration>` test suites out of the box
- Python 3.6+ and PyPy 3 - Python 3.7+ or PyPy 3
- Rich plugin architecture, with over 800+ :ref:`external plugins <plugin-list>` and thriving community - Rich plugin architecture, with over 800+ :ref:`external plugins <plugin-list>` and thriving community

View File

@ -28,8 +28,6 @@ filterwarnings = [
"default:the imp module is deprecated in favour of importlib:DeprecationWarning:nose.*", "default:the imp module is deprecated in favour of importlib:DeprecationWarning:nose.*",
# distutils is deprecated in 3.10, scheduled for removal in 3.12 # distutils is deprecated in 3.10, scheduled for removal in 3.12
"ignore:The distutils package is deprecated:DeprecationWarning", "ignore:The distutils package is deprecated:DeprecationWarning",
# produced by python3.6/site.py itself (3.6.7 on Travis, could not trigger it with 3.6.8)."
"ignore:.*U.*mode is deprecated:DeprecationWarning:(?!(pytest|_pytest))",
# produced by pytest-xdist # produced by pytest-xdist
"ignore:.*type argument to addoption.*:DeprecationWarning", "ignore:.*type argument to addoption.*:DeprecationWarning",
# produced on execnet (pytest-xdist) # produced on execnet (pytest-xdist)

View File

@ -17,7 +17,6 @@ classifiers =
Operating System :: POSIX Operating System :: POSIX
Programming Language :: Python :: 3 Programming Language :: Python :: 3
Programming Language :: Python :: 3 :: Only Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.9
@ -51,7 +50,7 @@ install_requires =
atomicwrites>=1.0;sys_platform=="win32" atomicwrites>=1.0;sys_platform=="win32"
colorama;sys_platform=="win32" colorama;sys_platform=="win32"
importlib-metadata>=0.12;python_version<"3.8" importlib-metadata>=0.12;python_version<"3.8"
python_requires = >=3.6 python_requires = >=3.7
package_dir = package_dir =
=src =src
setup_requires = setup_requires =

View File

@ -293,9 +293,8 @@ def _write_pyc_fp(
# import. However, there's little reason to deviate. # import. However, there's little reason to deviate.
fp.write(importlib.util.MAGIC_NUMBER) fp.write(importlib.util.MAGIC_NUMBER)
# https://www.python.org/dev/peps/pep-0552/ # https://www.python.org/dev/peps/pep-0552/
if sys.version_info >= (3, 7): flags = b"\x00\x00\x00\x00"
flags = b"\x00\x00\x00\x00" fp.write(flags)
fp.write(flags)
# as of now, bytecode header expects 32-bit numbers for size and mtime (#4903) # as of now, bytecode header expects 32-bit numbers for size and mtime (#4903)
mtime = int(source_stat.st_mtime) & 0xFFFFFFFF mtime = int(source_stat.st_mtime) & 0xFFFFFFFF
size = source_stat.st_size & 0xFFFFFFFF size = source_stat.st_size & 0xFFFFFFFF
@ -376,31 +375,29 @@ def _read_pyc(
except OSError: except OSError:
return None return None
with fp: with fp:
# https://www.python.org/dev/peps/pep-0552/
has_flags = sys.version_info >= (3, 7)
try: try:
stat_result = os.stat(source) stat_result = os.stat(source)
mtime = int(stat_result.st_mtime) mtime = int(stat_result.st_mtime)
size = stat_result.st_size size = stat_result.st_size
data = fp.read(16 if has_flags else 12) data = fp.read(16)
except OSError as e: except OSError as e:
trace(f"_read_pyc({source}): OSError {e}") trace(f"_read_pyc({source}): OSError {e}")
return None return None
# Check for invalid or out of date pyc file. # Check for invalid or out of date pyc file.
if len(data) != (16 if has_flags else 12): if len(data) != (16):
trace("_read_pyc(%s): invalid pyc (too short)" % source) trace("_read_pyc(%s): invalid pyc (too short)" % source)
return None return None
if data[:4] != importlib.util.MAGIC_NUMBER: if data[:4] != importlib.util.MAGIC_NUMBER:
trace("_read_pyc(%s): invalid pyc (bad magic number)" % source) trace("_read_pyc(%s): invalid pyc (bad magic number)" % source)
return None return None
if has_flags and data[4:8] != b"\x00\x00\x00\x00": if data[4:8] != b"\x00\x00\x00\x00":
trace("_read_pyc(%s): invalid pyc (unsupported flags)" % source) trace("_read_pyc(%s): invalid pyc (unsupported flags)" % source)
return None return None
mtime_data = data[8 if has_flags else 4 : 12 if has_flags else 8] mtime_data = data[8:12]
if int.from_bytes(mtime_data, "little") != mtime & 0xFFFFFFFF: if int.from_bytes(mtime_data, "little") != mtime & 0xFFFFFFFF:
trace("_read_pyc(%s): out of date" % source) trace("_read_pyc(%s): out of date" % source)
return None return None
size_data = data[12 if has_flags else 8 : 16 if has_flags else 12] size_data = data[12:16]
if int.from_bytes(size_data, "little") != size & 0xFFFFFFFF: if int.from_bytes(size_data, "little") != size & 0xFFFFFFFF:
trace("_read_pyc(%s): invalid pyc (incorrect size)" % source) trace("_read_pyc(%s): invalid pyc (incorrect size)" % source)
return None return None

View File

@ -4,7 +4,6 @@ import functools
import inspect import inspect
import os import os
import sys import sys
from contextlib import contextmanager
from inspect import Parameter from inspect import Parameter
from inspect import signature from inspect import signature
from pathlib import Path from pathlib import Path
@ -186,16 +185,6 @@ def getfuncargnames(
return arg_names return arg_names
if sys.version_info < (3, 7):
@contextmanager
def nullcontext():
yield
else:
from contextlib import nullcontext as nullcontext # noqa: F401
def get_default_arg_names(function: Callable[..., Any]) -> Tuple[str, ...]: def get_default_arg_names(function: Callable[..., Any]) -> Tuple[str, ...]:
# Note: this code intentionally mirrors the code at the beginning of # Note: this code intentionally mirrors the code at the beginning of
# getfuncargnames, to get the arguments which were excluded from its result # getfuncargnames, to get the arguments which were excluded from its result

View File

@ -3,8 +3,8 @@ import io
import logging import logging
import os import os
import re import re
import sys
from contextlib import contextmanager from contextlib import contextmanager
from contextlib import nullcontext
from io import StringIO from io import StringIO
from pathlib import Path from pathlib import Path
from typing import AbstractSet from typing import AbstractSet
@ -22,7 +22,6 @@ from _pytest import nodes
from _pytest._io import TerminalWriter from _pytest._io import TerminalWriter
from _pytest.capture import CaptureManager from _pytest.capture import CaptureManager
from _pytest.compat import final from _pytest.compat import final
from _pytest.compat import nullcontext
from _pytest.config import _strtobool from _pytest.config import _strtobool
from _pytest.config import Config from _pytest.config import Config
from _pytest.config import create_terminal_writer from _pytest.config import create_terminal_writer
@ -628,16 +627,7 @@ class LoggingPlugin:
# https://github.com/python/mypy/issues/11193 # https://github.com/python/mypy/issues/11193
stream: io.TextIOWrapper = fpath.open(mode="w", encoding="UTF-8") # type: ignore[assignment] stream: io.TextIOWrapper = fpath.open(mode="w", encoding="UTF-8") # type: ignore[assignment]
if sys.version_info >= (3, 7): old_stream = self.log_file_handler.setStream(stream)
old_stream = self.log_file_handler.setStream(stream)
else:
old_stream = self.log_file_handler.stream
self.log_file_handler.acquire()
try:
self.log_file_handler.flush()
self.log_file_handler.stream = stream
finally:
self.log_file_handler.release()
if old_stream: if old_stream:
old_stream.close() old_stream.close()

View File

@ -128,7 +128,7 @@ class LsofFdLeakChecker:
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
check=True, check=True,
universal_newlines=True, text=True,
).stdout ).stdout
def isopen(line: str) -> bool: def isopen(line: str) -> bool:

View File

@ -8,14 +8,6 @@ from _pytest.debugging import _validate_usepdb_cls
from _pytest.monkeypatch import MonkeyPatch from _pytest.monkeypatch import MonkeyPatch
from _pytest.pytester import Pytester from _pytest.pytester import Pytester
try:
# Type ignored for Python <= 3.6.
breakpoint # type: ignore
except NameError:
SUPPORTS_BREAKPOINT_BUILTIN = False
else:
SUPPORTS_BREAKPOINT_BUILTIN = True
_ENVIRON_PYTHONBREAKPOINT = os.environ.get("PYTHONBREAKPOINT", "") _ENVIRON_PYTHONBREAKPOINT = os.environ.get("PYTHONBREAKPOINT", "")
@ -911,14 +903,6 @@ class TestPDB:
class TestDebuggingBreakpoints: class TestDebuggingBreakpoints:
def test_supports_breakpoint_module_global(self) -> None:
"""Test that supports breakpoint global marks on Python 3.7+."""
if sys.version_info >= (3, 7):
assert SUPPORTS_BREAKPOINT_BUILTIN is True
@pytest.mark.skipif(
not SUPPORTS_BREAKPOINT_BUILTIN, reason="Requires breakpoint() builtin"
)
@pytest.mark.parametrize("arg", ["--pdb", ""]) @pytest.mark.parametrize("arg", ["--pdb", ""])
def test_sys_breakpointhook_configure_and_unconfigure( def test_sys_breakpointhook_configure_and_unconfigure(
self, pytester: Pytester, arg: str self, pytester: Pytester, arg: str
@ -952,9 +936,6 @@ class TestDebuggingBreakpoints:
result = pytester.runpytest_subprocess(*args) result = pytester.runpytest_subprocess(*args)
result.stdout.fnmatch_lines(["*1 passed in *"]) result.stdout.fnmatch_lines(["*1 passed in *"])
@pytest.mark.skipif(
not SUPPORTS_BREAKPOINT_BUILTIN, reason="Requires breakpoint() builtin"
)
def test_pdb_custom_cls(self, pytester: Pytester, custom_debugger_hook) -> None: def test_pdb_custom_cls(self, pytester: Pytester, custom_debugger_hook) -> None:
p1 = pytester.makepyfile( p1 = pytester.makepyfile(
""" """
@ -969,9 +950,6 @@ class TestDebuggingBreakpoints:
assert custom_debugger_hook == ["init", "set_trace"] assert custom_debugger_hook == ["init", "set_trace"]
@pytest.mark.parametrize("arg", ["--pdb", ""]) @pytest.mark.parametrize("arg", ["--pdb", ""])
@pytest.mark.skipif(
not SUPPORTS_BREAKPOINT_BUILTIN, reason="Requires breakpoint() builtin"
)
def test_environ_custom_class( def test_environ_custom_class(
self, pytester: Pytester, custom_debugger_hook, arg: str self, pytester: Pytester, custom_debugger_hook, arg: str
) -> None: ) -> None:
@ -1002,9 +980,6 @@ class TestDebuggingBreakpoints:
result = pytester.runpytest_subprocess(*args) result = pytester.runpytest_subprocess(*args)
result.stdout.fnmatch_lines(["*1 passed in *"]) result.stdout.fnmatch_lines(["*1 passed in *"])
@pytest.mark.skipif(
not SUPPORTS_BREAKPOINT_BUILTIN, reason="Requires breakpoint() builtin"
)
@pytest.mark.skipif( @pytest.mark.skipif(
not _ENVIRON_PYTHONBREAKPOINT == "", not _ENVIRON_PYTHONBREAKPOINT == "",
reason="Requires breakpoint() default value", reason="Requires breakpoint() default value",
@ -1025,9 +1000,6 @@ class TestDebuggingBreakpoints:
assert "reading from stdin while output" not in rest assert "reading from stdin while output" not in rest
TestPDB.flush(child) TestPDB.flush(child)
@pytest.mark.skipif(
not SUPPORTS_BREAKPOINT_BUILTIN, reason="Requires breakpoint() builtin"
)
def test_pdb_not_altered(self, pytester: Pytester) -> None: def test_pdb_not_altered(self, pytester: Pytester) -> None:
p1 = pytester.makepyfile( p1 = pytester.makepyfile(
""" """

View File

@ -295,7 +295,7 @@ def test_argcomplete(pytester: Pytester, monkeypatch: MonkeyPatch) -> None:
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
check=True, check=True,
universal_newlines=True, text=True,
).stdout ).stdout
except (OSError, subprocess.CalledProcessError): except (OSError, subprocess.CalledProcessError):
pytest.skip("bash is not available") pytest.skip("bash is not available")

View File

@ -4,7 +4,6 @@ minversion = 3.20.0
distshare = {homedir}/.tox/distshare distshare = {homedir}/.tox/distshare
envlist = envlist =
linting linting
py36
py37 py37
py38 py38
py39 py39