Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

compatibility with numpy>=2.0 #3955

Merged
merged 36 commits into from Apr 26, 2024
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
6141146
don't expect HypothesisWarning to be raised at the top level
keewis Apr 21, 2024
018faec
check that we can generate arrays of float dtype other than f8
keewis Apr 21, 2024
99c1729
ignore the warning when importing `numpy.array_api`
keewis Apr 24, 2024
2401642
also check complex dtypes
keewis Apr 24, 2024
e3414fd
use `warnings.filterwarnings` to filter out a expected warning
keewis Apr 24, 2024
43894fe
cast `finfo.smallest_normal` to builtin python `float`
keewis Apr 24, 2024
ef7f6ee
authors
keewis Apr 24, 2024
1815bb6
release note
keewis Apr 24, 2024
b3a3e15
ignore another warning
keewis Apr 24, 2024
74819c9
typo
keewis Apr 24, 2024
24eed9f
add a "numpy nightly" CI job
keewis Apr 24, 2024
ffd88a3
convert to `rst` syntax
keewis Apr 24, 2024
cb11625
require `python>=3.9` for the test
keewis Apr 24, 2024
077e1c3
compare to `python=3.8`
keewis Apr 24, 2024
0eec1d3
formatting
keewis Apr 24, 2024
a5ac237
skip checking the `"a"` dtype kind on `numpy<2.0`
keewis Apr 24, 2024
c97d221
replace packaging with a manually parsed version
keewis Apr 24, 2024
464e9cc
check for `OverflowError` on `numpy>=2.0`
keewis Apr 24, 2024
311fbe5
add a reason to the skipif
keewis Apr 24, 2024
f615a4b
more formatting
keewis Apr 24, 2024
ae0d625
try running on `python=3.12`
keewis Apr 24, 2024
4f2f0fe
run all tests, not just `numpy`
keewis Apr 24, 2024
9bb1a87
explicitly list the directories to check
keewis Apr 25, 2024
8745fe7
run all tests together
keewis Apr 25, 2024
883b895
explicitly install pandas, black and click
keewis Apr 25, 2024
2031532
use tox to install the additional dependencies
keewis Apr 25, 2024
d001673
also test `cover`
keewis Apr 25, 2024
dbaa61c
only append `asfarray` on `numpy<2.0`
keewis Apr 25, 2024
68861bb
fix the comparison
keewis Apr 25, 2024
aa2ebdc
allow patching out methods on the returned array
keewis Apr 25, 2024
0412cb1
remove `__array_namespace__` on the returned array
keewis Apr 25, 2024
f8fa048
formatting
keewis Apr 25, 2024
ecd4593
try explicitly returning the result of `object.__getattribute__`
keewis Apr 25, 2024
fe34343
remove `asfarray` entirely
keewis Apr 25, 2024
8d5ef72
store the "removed in numpy 2" mark in a variable
keewis Apr 25, 2024
f8e048e
use `__getattr__` instead of `__getattribute__`
keewis Apr 26, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/main.yml
Expand Up @@ -77,6 +77,7 @@ jobs:
# - check-crosshair-nocover
# - check-crosshair-niche
- check-py38-oldestnumpy
- check-numpy-nightly
fail-fast: false
steps:
- uses: actions/checkout@v3
Expand Down
1 change: 1 addition & 0 deletions AUTHORS.rst
Expand Up @@ -96,6 +96,7 @@ their individual contributions.
* `Jonty Wareing <https://www.github.com/Jonty>`_ (jonty@jonty.co.uk)
* `Joshua Boone <https://www.github.com/patchedwork>`_ (joshuaboone4190@gmail.com)
* `jmhsi <https://www.github.com/jmhsi>`_
* `Justus Magin <https://github.com/keewis>`_
* `jwg4 <https://www.github.com/jwg4>`_
* `Kai Chen <https://www.github.com/kx-chen>`_ (kaichen120@gmail.com)
* `Karthikeyan Singaravelan <https://www.github.com/tirkarthi>`_ (tir.karthi@gmail.com)
Expand Down
4 changes: 4 additions & 0 deletions hypothesis-python/RELEASE.rst
@@ -0,0 +1,4 @@
RELEASE_TYPE: patch

Explicitly cast :obj:`numpy.finfo.smallest_normal` to builtin `float` in
preparation for the :pypi:`numpy==2.0 <numpy>` release (:issue:`3950`)
4 changes: 2 additions & 2 deletions hypothesis-python/src/hypothesis/extra/array_api.py
Expand Up @@ -282,7 +282,7 @@ def check_valid_minmax(prefix, val, info_obj):
if allow_subnormal is not None:
kw["allow_subnormal"] = allow_subnormal
else:
subnormal = next_down(finfo.smallest_normal, width=finfo.bits)
subnormal = next_down(float(finfo.smallest_normal), width=finfo.bits)
ftz = bool(xp.asarray(subnormal, dtype=dtype) == 0)
if ftz:
kw["allow_subnormal"] = False
Expand All @@ -303,7 +303,7 @@ def check_valid_minmax(prefix, val, info_obj):
# complex array, in case complex arrays have different FTZ behaviour
# than arrays of the respective composite float.
if allow_subnormal is None:
subnormal = next_down(finfo.smallest_normal, width=finfo.bits)
subnormal = next_down(float(finfo.smallest_normal), width=finfo.bits)
x = xp.asarray(complex(subnormal, subnormal), dtype=dtype)
builtin_x = complex(x)
allow_subnormal = builtin_x.real != 0 and builtin_x.imag != 0
Expand Down
Expand Up @@ -908,9 +908,13 @@ def _collection_ish_functions():
np.diag,
# bonus undocumented functions from tab-completion:
np.asarray_chkfinite,
np.asfarray,
np.asfortranarray,
]
np_version = tuple(int(_) for _ in np.__version__.split(".", maxsplit=2)[:2])
if np_version < (2, 0):
# removed in numpy>=2.0
funcs += [np.asfarray]

keewis marked this conversation as resolved.
Show resolved Hide resolved
return funcs


Expand Down
3 changes: 2 additions & 1 deletion hypothesis-python/tests/array_api/conftest.py
Expand Up @@ -35,7 +35,8 @@
f"HYPOTHESIS_TEST_ARRAY_API_VERSION='{test_version_option}' is not "
f"'default' or a valid api_version {NOMINAL_VERSIONS}."
)
with pytest.warns(HypothesisWarning):
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=HypothesisWarning)
mock_version = "draft" if test_version_option == "default" else test_version_option
mock_xps = make_strategies_namespace(mock_xp, api_version=mock_version)
api_version = None if test_version_option == "default" else test_version_option
Expand Down
21 changes: 21 additions & 0 deletions hypothesis-python/tests/array_api/test_arrays.py
Expand Up @@ -8,6 +8,8 @@
# v. 2.0. If a copy of the MPL was not distributed with this file, You can
# obtain one at https://mozilla.org/MPL/2.0/.

import sys

import pytest

from hypothesis import given, settings, strategies as st
Expand Down Expand Up @@ -296,6 +298,25 @@ def test_may_not_fill_unique_array_with_non_nan(xp, xps):
check_can_generate_examples(strat)


@pytest.mark.skipif(sys.version_info[:2] <= (3, 8), reason="no complex")
def test_floating_point_array():
import warnings
from hypothesis.extra.array_api import make_strategies_namespace

try:
with warnings.catch_warnings():
warnings.simplefilter("ignore")
import numpy.array_api as nxp
except ModuleNotFoundError:
import numpy as nxp
xps = make_strategies_namespace(nxp)
dtypes = xps.floating_dtypes() | xps.complex_dtypes()

strat = xps.arrays(dtype=dtypes, shape=10)

check_can_generate_examples(strat)


@pytest.mark.parametrize(
"kwargs",
[
Expand Down
65 changes: 62 additions & 3 deletions hypothesis-python/tests/array_api/test_partial_adoptors.py
Expand Up @@ -11,6 +11,8 @@
from copy import copy
from types import SimpleNamespace
from typing import Tuple
import functools
import warnings

import pytest

Expand All @@ -31,17 +33,73 @@
MOCK_WARN_MSG = f"determine.*{mock_xp.__name__}.*Array API"


def make_mock_xp(*, exclude: Tuple[str, ...] = ()) -> SimpleNamespace:
class MockedArray:
def __init__(self, wrapped, *, exclude=()):
self.wrapped = wrapped
self.exclude = exclude

def __getattribute__(self, name):
if name in self.exclude:
raise AttributeError(f"removed on the mock: {name}")

return object.__getattribute__(self, name)


def wrap_array(func: callable, exclude: Tuple[str, ...] = ()) -> callable:
@functools.wraps(func)
def wrapped(*args, **kwargs):
result = func(*args, **kwargs)

if isinstance(result, tuple):
return tuple(MockedArray(arr, exclude=exclude) for arr in result)

return MockedArray(result, exclude=exclude)

return wrapped


def make_mock_xp(
*, exclude: Tuple[str, ...] = (), exclude_methods: Tuple[str, ...] = ()
) -> SimpleNamespace:
xp = copy(mock_xp)
assert isinstance(exclude, tuple) # sanity check
assert isinstance(exclude_methods, tuple) # sanity check
for attr in exclude:
delattr(xp, attr)

array_returning_funcs = (
"astype",
"broadcast_arrays",
"arange",
"asarray",
"empty",
"zeros",
"ones",
"reshape",
"isnan",
"isfinite",
"logical_or",
"sum",
"nonzero",
"sort",
"unique_values",
"any",
"all",
)

for name in array_returning_funcs:
func = getattr(xp, name, None)
if func is None:
# removed in the step before
continue
setattr(xp, name, wrap_array(func, exclude=exclude_methods))

return xp


def test_warning_on_noncompliant_xp():
"""Using non-compliant array modules raises helpful warning"""
xp = make_mock_xp()
xp = make_mock_xp(exclude_methods=("__array_namespace__",))
with pytest.warns(HypothesisWarning, match=MOCK_WARN_MSG):
make_strategies_namespace(xp, api_version="draft")

Expand All @@ -62,7 +120,8 @@ def test_error_on_missing_attr(stratname, args, attr):


dtypeless_xp = make_mock_xp(exclude=tuple(DTYPE_NAMES))
with pytest.warns(HypothesisWarning):
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=HypothesisWarning)
dtypeless_xps = make_strategies_namespace(dtypeless_xp, api_version="draft")


Expand Down
16 changes: 15 additions & 1 deletion hypothesis-python/tests/numpy/test_from_dtype.py
Expand Up @@ -21,6 +21,8 @@

from tests.common.debug import assert_no_examples, check_can_generate_examples, find_any

np_version = tuple(int(x) for x in np.__version__.split(".")[:2])

STANDARD_TYPES = [
np.dtype(t)
for t in (
Expand Down Expand Up @@ -126,7 +128,19 @@ def test_byte_string_dtypes_generate_unicode_strings(data):
assert isinstance(result, bytes)


@pytest.mark.parametrize("dtype", ["U", "S", "a"])
@pytest.mark.parametrize(
"dtype",
[
"U",
"S",
pytest.param(
"a",
marks=pytest.mark.skipif(
np_version >= (2, 0), reason="not supported on `numpy>=2.0`"
),
),
],
)
keewis marked this conversation as resolved.
Show resolved Hide resolved
def test_unsized_strings_length_gt_one(dtype):
# See https://github.com/HypothesisWorks/hypothesis/issues/2229
find_any(nps.arrays(dtype=dtype, shape=1), lambda arr: len(arr[0]) >= 2)
Expand Down
6 changes: 5 additions & 1 deletion hypothesis-python/tests/numpy/test_gen_data.py
Expand Up @@ -348,7 +348,11 @@ def test_may_not_fill_with_non_nan_when_unique_is_set_and_type_is_not_number(arr

@pytest.mark.parametrize("fill", [False, True])
# Overflowing elements deprecated upstream in Numpy 1.24 :-)
@fails_with(InvalidArgument if np_version < (1, 24) else DeprecationWarning)
@fails_with(
InvalidArgument
if np_version < (1, 24)
else (DeprecationWarning if np_version < (2, 0) else OverflowError)
)
@given(st.data())
def test_overflowing_integers_are_deprecated(fill, data):
kw = {"elements": st.just(300)}
Expand Down
13 changes: 13 additions & 0 deletions hypothesis-python/tox.ini
Expand Up @@ -55,6 +55,19 @@ commands=
bash -c "pip install --only-binary=:all: numpy==$(grep 'numpy>=' setup.py | grep -oE '[0-9.]+')"
python -bb -X dev -m pytest tests/numpy/ -n auto

# This test job runs against the nightly version of `numpy`
[testenv:numpy-nightly]
deps=
-r../requirements/test.txt
pandas
black
click
allowlist_externals =
bash
commands=
bash -c "pip install --upgrade --pre --only-binary :all: -i https://pypi.anaconda.org/scientific-python-nightly-wheels/simple numpy"
python -bb -X dev -m pytest tests/numpy/ tests/array_api/ tests/pandas/ tests/ghostwriter/ tests/conjecture/ tests/cover -n auto

# Note: when adding or removing tested Pandas versions, make sure to update the
# docs in numpy.rst too. To see current download rates of each minor version:
# https://pepy.tech/project/pandas?versions=1.1.*&versions=1.2.*&versions=1.3.*&versions=1.4.*&versions=1.5.*&versions=2.0.*
Expand Down
1 change: 1 addition & 0 deletions tooling/src/hypothesistooling/__main__.py
Expand Up @@ -502,6 +502,7 @@ def standard_tox_task(name, py=ci_version):
standard_tox_task(f"crosshair-{kind}")

standard_tox_task("py38-oldestnumpy", py="3.8")
standard_tox_task("numpy-nightly", py="3.12")

standard_tox_task("coverage")
standard_tox_task("conjecture-coverage")
Expand Down