Skip to content

Commit

Permalink
Merge pull request #4131 from Zac-HD/drop-py38
Browse files Browse the repository at this point in the history
Drop Python 3.8 at end of life
  • Loading branch information
Zac-HD authored Oct 9, 2024
2 parents a81d155 + df28a74 commit 3823610
Show file tree
Hide file tree
Showing 47 changed files with 351 additions and 542 deletions.
7 changes: 1 addition & 6 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,6 @@ jobs:
- check-format
- check-coverage
- check-conjecture-coverage
- check-py38-cover
- check-py38-nocover
- check-py38-niche
- check-pypy38-cover
- check-py39-cover
- check-pypy39-cover
- check-py310-cover
Expand Down Expand Up @@ -91,7 +87,7 @@ jobs:
# - check-crosshair-custom-pytest/test_*
# - check-crosshair-nocover
# - check-crosshair-niche
- check-py38-oldestnumpy
- check-py39-oldestnumpy
- check-numpy-nightly
fail-fast: false
steps:
Expand Down Expand Up @@ -197,7 +193,6 @@ jobs:
strategy:
matrix:
task:
- check-py38-cover
- check-py310-cover
- check-py310-nocover
- check-py310-niche
Expand Down
4 changes: 2 additions & 2 deletions CONTRIBUTING.rst
Original file line number Diff line number Diff line change
Expand Up @@ -208,11 +208,11 @@ high level, the task takes the form:
Namely, first provide the tox environment (see ``tox.ini``), then the python
version to test with, then any ``tox`` or ``pytest`` args as needed. For
example, to run all of the tests in the file
``tests/nocover/test_conjecture_engine.py`` with python 3.8:
``tests/nocover/test_conjecture_engine.py`` with python 3.12:

.. code-block::
./build.sh tox py38-custom 3.8.13 -- tests/nocover/test_conjecture_engine.py
./build.sh tox py312-custom 3.12.7 -- tests/nocover/test_conjecture_engine.py
See the ``tox`` docs and ``pytest`` docs for more information:
* https://docs.pytest.org/en/latest/how-to/usage.html
Expand Down
4 changes: 4 additions & 0 deletions hypothesis-python/RELEASE.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
RELEASE_TYPE: minor

This release drops support for Python 3.8, `which reached end of life on
2024-10-07 <https://devguide.python.org/versions/>`__.
2 changes: 1 addition & 1 deletion hypothesis-python/docs/supported.rst
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ exactly where an error came from, or turn only our warnings into errors.
Python versions
---------------

Hypothesis is supported and tested on CPython 3.8+, i.e.
Hypothesis is supported and tested on CPython 3.9+, i.e.
`all versions of CPython with upstream support <https://devguide.python.org/versions/>`_,
along with PyPy for the same versions.
32-bit builds of CPython also work, though we only test them on Windows.
Expand Down
6 changes: 3 additions & 3 deletions hypothesis-python/scripts/basic-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ if [ "$(python -c $'import platform, sys; print(sys.version_info.releaselevel ==
$PYTEST tests/codemods/
pip uninstall -y libcst click

if [ "$(python -c 'import sys; print(sys.version_info[:2] == (3, 8))')" = "True" ] ; then
# Per NEP-29, this is the last version to support Python 3.8
pip install numpy==1.24.3
if [ "$(python -c 'import sys; print(sys.version_info[:2] == (3, 9))')" = "True" ] ; then
# Per NEP-29, this is the last version to support Python 3.9
pip install numpy==2.0.2
else
pip install "$(grep 'numpy==' ../requirements/coverage.txt)"
fi
Expand Down
18 changes: 7 additions & 11 deletions hypothesis-python/scripts/other-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,9 @@ if [[ "$HYPOTHESIS_PROFILE" != "crosshair" ]]; then
pip uninstall -y typing_extensions
fi

if [ "$(python -c 'import sys; print(sys.version_info[:2] >= (3, 9))')" = "True" ] ; then
pip install "$(grep 'annotated-types==' ../requirements/coverage.txt)"
$PYTEST tests/test_annotated_types.py
pip uninstall -y annotated-types
fi
pip install "$(grep 'annotated-types==' ../requirements/coverage.txt)"
$PYTEST tests/test_annotated_types.py
pip uninstall -y annotated-types

pip install ".[lark]"
pip install "$(grep -oE 'lark>=([0-9.]+)' ../hypothesis-python/setup.py | tr '>' =)"
Expand All @@ -52,17 +50,15 @@ if [ "$(python -c $'import platform, sys; print(sys.version_info.releaselevel ==
pip install ".[codemods,cli]"
$PYTEST tests/codemods/

if [ "$(python -c 'import sys; print(sys.version_info[:2] == (3, 8))')" = "True" ] ; then
# Per NEP-29, this is the last version to support Python 3.8
pip install numpy==1.24.3
if [ "$(python -c 'import sys; print(sys.version_info[:2] == (3, 9))')" = "True" ] ; then
# Per NEP-29, this is the last version to support Python 3.9
pip install numpy==2.0.2
else
pip install "$(grep 'numpy==' ../requirements/coverage.txt)"
fi

pip install "$(grep -E 'black(==| @)' ../requirements/coverage.txt)"
if [ "$(python -c 'import sys; print(sys.version_info[:2] >= (3, 9))')" = "True" ] ; then
$PYTEST tests/patching/
fi
$PYTEST tests/patching/
pip uninstall -y libcst

$PYTEST tests/ghostwriter/
Expand Down
15 changes: 6 additions & 9 deletions hypothesis-python/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@

import setuptools

if sys.version_info[:2] < (3, 8): # noqa # "unreachable" sanity check
if sys.version_info[:2] < (3, 9): # "unreachable" sanity check
raise Exception(
"You are trying to install Hypothesis using Python "
f"{sys.version.split()[0]}, but it requires Python 3.8 or later."
f"{sys.version.split()[0]}, but it requires Python 3.9 or later."
"Update `pip` and `setuptools`, try again, and you will automatically "
"get the latest compatible version of Hypothesis instead. "
"See also https://python3statement.org/practicalities/"
Expand Down Expand Up @@ -55,23 +55,21 @@ def local_file(name):
"pytz": ["pytz>=2014.1"],
"dateutil": ["python-dateutil>=1.4"],
"lark": ["lark>=0.10.1"], # probably still works with old `lark-parser` too
"numpy": ["numpy>=1.17.3"], # oldest with wheels for non-EOL Python (for now)
"numpy": ["numpy>=1.19.3"], # oldest with wheels for non-EOL Python (for now)
"pandas": ["pandas>=1.1"],
"pytest": ["pytest>=4.6"],
"dpcontracts": ["dpcontracts>=0.4"],
"redis": ["redis>=3.0.0"],
"crosshair": ["hypothesis-crosshair>=0.0.14", "crosshair-tool>=0.0.73"],
# zoneinfo is an odd one: every dependency is conditional, because they're
# only necessary on old versions of Python or Windows systems or emscripten.
# zoneinfo is an odd one: every dependency is platform-conditional.
"zoneinfo": [
"tzdata>=2024.2 ; sys_platform == 'win32' or sys_platform == 'emscripten'",
"backports.zoneinfo>=0.2.1 ; python_version<'3.9'",
],
# We only support Django versions with upstream support - see
# https://www.djangoproject.com/download/#supported-versions
# We also leave the choice of timezone library to the user, since it
# might be zoneinfo or pytz depending on version and configuration.
"django": ["django>=3.2"],
"django": ["django>=4.2"],
}

extras["all"] = sorted(set(sum(extras.values(), [])))
Expand Down Expand Up @@ -101,7 +99,7 @@ def local_file(name):
"exceptiongroup>=1.0.0 ; python_version<'3.11'",
"sortedcontainers>=2.1.0,<3.0.0",
],
python_requires=">=3.8",
python_requires=">=3.9",
classifiers=[
"Development Status :: 5 - Production/Stable",
"Framework :: Hypothesis",
Expand All @@ -114,7 +112,6 @@ def local_file(name):
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
Expand Down
46 changes: 6 additions & 40 deletions hypothesis-python/src/hypothesis/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,15 @@
import warnings
import zlib
from collections import defaultdict
from collections.abc import Coroutine, Hashable
from functools import partial
from random import Random
from typing import (
TYPE_CHECKING,
Any,
BinaryIO,
Callable,
Coroutine,
Hashable,
List,
Optional,
Tuple,
Type,
TypeVar,
Union,
overload,
Expand Down Expand Up @@ -179,7 +175,7 @@ def __init__(self, *args: Any, **kwargs: Any) -> None:
if not (args or kwargs):
raise InvalidArgument("An example must provide at least one argument")

self.hypothesis_explicit_examples: List[Example] = []
self.hypothesis_explicit_examples: list[Example] = []
self._this_example = Example(tuple(args), kwargs)

def __call__(self, test: TestFunc) -> TestFunc:
Expand All @@ -194,7 +190,7 @@ def xfail(
*,
reason: str = "",
raises: Union[
Type[BaseException], Tuple[Type[BaseException], ...]
type[BaseException], tuple[type[BaseException], ...]
] = BaseException,
) -> "example":
"""Mark this example as an expected failure, similarly to
Expand All @@ -209,7 +205,7 @@ def xfail(
@example(...).xfail()
@example(...).xfail(reason="Prices must be non-negative")
@example(...).xfail(raises=(KeyError, ValueError))
@example(...).xfail(sys.version_info[:2] >= (3, 9), reason="needs py39+")
@example(...).xfail(sys.version_info[:2] >= (3, 12), reason="needs py 3.12")
@example(...).xfail(condition=sys.platform != "linux", raises=OSError)
def test(x):
pass
Expand All @@ -229,21 +225,6 @@ def test_fraction(x, y):
# strategy. If we happen to generate y=0, the test will fail
# because only the explicit example is treated as xfailing.
x / y
Note that this "method chaining" syntax requires Python 3.9 or later, for
:pep:`614` relaxing grammar restrictions on decorators. If you need to
support older versions of Python, you can use an identity function:
.. code-block:: python
def identity(x):
return x
@identity(example(...).xfail())
def test(x):
pass
"""
check_type(bool, condition, "condition")
check_type(str, reason, "reason")
Expand Down Expand Up @@ -284,21 +265,6 @@ def via(self, whence: str, /) -> "example":
@example(...).via("hy-target-$label")
def test(x):
pass
Note that this "method chaining" syntax requires Python 3.9 or later, for
:pep:`614` relaxing grammar restrictions on decorators. If you need to
support older versions of Python, you can use an identity function:
.. code-block:: python
def identity(x):
return x
@identity(example(...).via("label"))
def test(x):
pass
"""
if not isinstance(whence, str):
raise InvalidArgument(".via() must be passed a string")
Expand Down Expand Up @@ -1359,7 +1325,7 @@ def _raise_to_user(
for note in fragments:
add_note(err, note)
if note.startswith(failing_prefix):
ls.append(note[len(failing_prefix) :])
ls.append(note.removeprefix(failing_prefix))
if current_pytest_item.value:
current_pytest_item.value._hypothesis_failing_examples = ls

Expand Down Expand Up @@ -1855,7 +1821,7 @@ def find(
)
specifier.validate()

last: List[Ex] = []
last: list[Ex] = []

@settings
@given(specifier)
Expand Down
2 changes: 0 additions & 2 deletions hypothesis-python/src/hypothesis/extra/_patching.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,6 @@ def __call_node_to_example_dec(self, node, via):
else node.args
),
)
# Note: calling a method on a decorator requires PEP-614, i.e. Python 3.9+,
# but plumbing two cases through doesn't seem worth the trouble :-/
via = cst.Call(
func=cst.Attribute(node, cst.Name("via")),
args=[cst.Arg(cst.SimpleString(repr(via)))],
Expand Down
Loading

0 comments on commit 3823610

Please sign in to comment.