From 6b0121d5a3685989fca58a1687961523a5fd575c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Thu, 23 May 2024 17:03:34 +0200 Subject: [PATCH] Numpy 2.0 compability (#6238) --- .github/workflows/test.yaml | 10 ++++-- examples/conftest.py | 17 ++++++++++ holoviews/core/sheetcoords.py | 2 +- holoviews/core/util.py | 3 ++ holoviews/tests/core/test_dimensions.py | 6 +++- .../tests/element/test_comparisondimension.py | 7 +++- holoviews/tests/test_streams.py | 13 ++++--- pixi.toml | 34 +++++++++++++++++++ pyproject.toml | 2 +- 9 files changed, 83 insertions(+), 11 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index b16390f645..1a8340ad1d 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -85,7 +85,10 @@ jobs: run: | MATRIX=$(jq -nsc '{ "os": ["ubuntu-latest", "macos-latest", "windows-latest"], - "environment": ["test-39", "test-312"] + "environment": ["test-39", "test-312"], + "include": [ + { "os": "ubuntu-latest", "environment": "test-numpy" } + ] }') echo "MATRIX=$MATRIX" >> $GITHUB_ENV - name: Set test matrix with 'full' option @@ -93,7 +96,10 @@ jobs: run: | MATRIX=$(jq -nsc '{ "os": ["ubuntu-latest", "macos-latest", "windows-latest"], - "environment": ["test-39", "test-310", "test-311", "test-312"] + "environment": ["test-39", "test-310", "test-311", "test-312"], + "include": [ + { "os": "ubuntu-latest", "environment": "test-numpy" } + ] }') echo "MATRIX=$MATRIX" >> $GITHUB_ENV - name: Set test matrix with 'downstream' option diff --git a/examples/conftest.py b/examples/conftest.py index 22dced7136..588cabb548 100644 --- a/examples/conftest.py +++ b/examples/conftest.py @@ -1,6 +1,7 @@ import os import platform import sys +from importlib.util import find_spec import bokeh import pandas as pd @@ -62,6 +63,22 @@ "user_guide/Plotting_with_Matplotlib.ipynb", ] +# 2024-05: Numpy 2.0 +if find_spec("datashader") is None: + collect_ignore_glob += [ + "reference/elements/matplotlib/ImageStack.ipynb", + "reference/elements/plotly/ImageStack.ipynb", + "user_guide/15-Large_Data.ipynb", + "user_guide/16-Streaming_Data.ipynb", + "user_guide/Linked_Brushing.ipynb", + "user_guide/Network_Graphs.ipynb", + ] + +if find_spec("scikit-image"): + collect_ignore_glob += [ + "user_guide/Network_Graphs.ipynb", + ] + def pytest_runtest_makereport(item, call): """ diff --git a/holoviews/core/sheetcoords.py b/holoviews/core/sheetcoords.py index a6e8d163cc..690a55d709 100644 --- a/holoviews/core/sheetcoords.py +++ b/holoviews/core/sheetcoords.py @@ -365,7 +365,7 @@ def __new__(cls, bounds, sheet_coordinate_system, force_odd=False, else: slicespec=Slice._boundsspec2slicespec(bounds.lbrt(),sheet_coordinate_system) # Using numpy.int32 for legacy reasons - a = np.array(slicespec, dtype=np.int32, copy=False).view(cls) + a = np.asarray(slicespec, dtype=np.int32).view(cls) return a diff --git a/holoviews/core/util.py b/holoviews/core/util.py index c2ff670d50..2e2732bfb0 100644 --- a/holoviews/core/util.py +++ b/holoviews/core/util.py @@ -50,6 +50,9 @@ _PANDAS_ROWS_LARGE = 1_000_000 _PANDAS_SAMPLE_SIZE = 1_000_000 +numpy_version = Version(Version(np.__version__).base_version) +NUMPY_GE_200 = numpy_version >= Version("2") + pandas_version = Version(pd.__version__) try: if pandas_version >= Version('1.3.0'): diff --git a/holoviews/tests/core/test_dimensions.py b/holoviews/tests/core/test_dimensions.py index 06f77066b9..7b79e1bcea 100644 --- a/holoviews/tests/core/test_dimensions.py +++ b/holoviews/tests/core/test_dimensions.py @@ -5,6 +5,7 @@ import pandas as pd from holoviews.core import Dimension, Dimensioned +from holoviews.core.util import NUMPY_GE_200 from holoviews.element.comparison import ComparisonTestCase from ..utils import LoggingComparisonTestCase @@ -243,7 +244,10 @@ def test_tuple_clone(self): class DimensionDefaultTest(ComparisonTestCase): def test_validate_default_against_values(self): - msg = r"Dimension\('A'\) default 1\.1 not found in declared values: \[0, 1\]" + if NUMPY_GE_200: + msg = r"Dimension\('A'\) default 1\.1 not found in declared values: \[np\.int64\(0\), np\.int64\(1\)\]" + else: + msg = r"Dimension\('A'\) default 1\.1 not found in declared values: \[0, 1\]" with self.assertRaisesRegex(ValueError, msg): Dimension('A', values=[0, 1], default=1.1) diff --git a/holoviews/tests/element/test_comparisondimension.py b/holoviews/tests/element/test_comparisondimension.py index 1234ce27c8..3bdfb04c4e 100644 --- a/holoviews/tests/element/test_comparisondimension.py +++ b/holoviews/tests/element/test_comparisondimension.py @@ -2,6 +2,7 @@ Test cases for Dimension and Dimensioned object comparison. """ from holoviews.core import Dimension, Dimensioned +from holoviews.core.util import NUMPY_GE_200 from holoviews.element.comparison import ComparisonTestCase @@ -74,7 +75,11 @@ def test_dimension_comparison_values_unequal(self): try: self.assertEqual(self.dimension4, self.dimension8) except AssertionError as e: - self.assertEqual(str(e), "Dimension parameter 'values' mismatched: [] != ['a', 'b']") + if NUMPY_GE_200: + msg = "Dimension parameter 'values' mismatched: [] != [np.str_('a'), np.str_('b')]" + else: + msg = "Dimension parameter 'values' mismatched: [] != ['a', 'b']" + self.assertEqual(str(e), msg) def test_dimension_comparison_types_unequal(self): try: diff --git a/holoviews/tests/test_streams.py b/holoviews/tests/test_streams.py index 17cb93bc30..2bc957f679 100644 --- a/holoviews/tests/test_streams.py +++ b/holoviews/tests/test_streams.py @@ -11,7 +11,7 @@ import holoviews as hv from holoviews.core.spaces import DynamicMap -from holoviews.core.util import Version +from holoviews.core.util import NUMPY_GE_200, Version from holoviews.element import Curve, Histogram, Points, Polygons, Scatter from holoviews.element.comparison import ComparisonTestCase from holoviews.streams import * # noqa (Test all available streams) @@ -1421,6 +1421,7 @@ def test_selection_expr_stream_hist_invert_xaxis_yaxis(self): def test_selection_expr_stream_polygon_index_cols(self): + # TODO: Should test both spatialpandas and shapely # Create SelectionExpr on element try: import shapely # noqa except ImportError: @@ -1444,10 +1445,12 @@ def test_selection_expr_stream_polygon_index_cols(self): self.assertIsNone(expr_stream.bbox) self.assertIsNone(expr_stream.selection_expr) + fmt = lambda x: list(map(np.str_, x)) if NUMPY_GE_200 else x + expr_stream.input_streams[2].event(index=[0, 1]) self.assertEqual( repr(expr_stream.selection_expr), - repr(dim('cat').isin(['a', 'b'])) + repr(dim('cat').isin(fmt(['a', 'b']))) ) self.assertEqual(expr_stream.bbox, None) self.assertEqual(len(events), 1) @@ -1456,7 +1459,7 @@ def test_selection_expr_stream_polygon_index_cols(self): expr_stream.input_streams[0].event(bounds=(0, 0, 4, 1)) self.assertEqual( repr(expr_stream.selection_expr), - repr(dim('cat').isin(['a', 'b'])) + repr(dim('cat').isin(fmt(['a', 'b']))) ) self.assertEqual(len(events), 1) @@ -1464,7 +1467,7 @@ def test_selection_expr_stream_polygon_index_cols(self): expr_stream.input_streams[1].event(geometry=np.array([(0, 0), (4, 0), (4, 2), (0, 2)])) self.assertEqual( repr(expr_stream.selection_expr), - repr(dim('cat').isin(['a', 'b', 'c'])) + repr(dim('cat').isin(fmt(['a', 'b', 'c']))) ) self.assertEqual(len(events), 2) @@ -1472,7 +1475,7 @@ def test_selection_expr_stream_polygon_index_cols(self): expr_stream.input_streams[2].event(index=[1, 2]) self.assertEqual( repr(expr_stream.selection_expr), - repr(dim('cat').isin(['b', 'c'])) + repr(dim('cat').isin(fmt(['b', 'c']))) ) self.assertEqual(expr_stream.bbox, None) self.assertEqual(len(events), 3) diff --git a/pixi.toml b/pixi.toml index 7e6a08d23f..bf8e0bd0c3 100644 --- a/pixi.toml +++ b/pixi.toml @@ -15,6 +15,7 @@ test-311 = ["py311", "test-core", "test", "example", "test-example", "test-unit- test-312 = ["py312", "test-core", "test", "example", "test-example", "test-unit-task"] test-ui = ["py312", "test-core", "test", "test-ui"] test-core = ["py312", "test-core", "test-unit-task"] +test-numpy = ["py312", "test-core", "test-unit-task", "numpy2", "test-example"] test-gpu = ["py311", "test-core", "test", "test-gpu"] docs = ["py311", "example", "doc"] build = ["py311", "build"] @@ -134,6 +135,39 @@ rmm = { version = "*", channel = "rapidsai" } [feature.test-gpu.tasks] test-gpu = { cmd = "pytest holoviews/tests --gpu", env = { NUMBA_CUDA_LOW_OCCUPANCY_WARNINGS = '0' } } +[feature.numpy2] +channels = ["pyviz/label/dev", "conda-forge/label/numpy_rc", "numba/label/dev", "conda-forge"] + +[feature.numpy2.dependencies] +numpy = "2.*" +numba = { version = "0.60.*", channel = "numba/label/dev" } +llvmlite = { version = "*", channel = "numba/label/dev" } + +# test dependencies +cftime = "*" +contourpy = "*" +dask-core = "*" +datashader = ">=0.11.1" +ffmpeg = "*" +ibis-sqlite = "*" +nbconvert = "*" +networkx = "*" +pillow = "*" +scipy = ">=1.10" # Python 3.9 + Windows downloads 1.9 +selenium = "*" +shapely = "*" +# spatialpandas = "*" +xarray = ">=0.10.4" +xyzservices = "*" + +# Examples (removed duplicates) +# netcdf4 = "*" +notebook = "*" +pooch = "*" +# pyarrow = "*" +# scikit-image = "*" +streamz = ">=0.5.0" + # ============================================= # =================== DOCS ==================== # ============================================= diff --git a/pyproject.toml b/pyproject.toml index ed94c5e2d8..fb9b916db0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -90,7 +90,7 @@ filterwarnings = [ # 2023-01: Sqlalchemy 2.0 warning: "ignore: Deprecated API features detected:DeprecationWarning:ibis.backends.base.sql.alchemy", # https://github.com/ibis-project/ibis/issues/5048 # 2023-03: Already handling the nested sequence - "ignore:Creating an ndarray from ragged nested sequences:numpy.VisibleDeprecationWarning:holoviews.core.data.spatialpandas", + "ignore:Creating an ndarray from ragged nested sequences::holoviews.core.data.spatialpandas", # 2023-09: Dash needs to update their code to use the comm module and pkg_resources "ignore:The `.+?` class has been deprecated:DeprecationWarning:dash._jupyter", "ignore:pkg_resources is deprecated as an API:DeprecationWarning:dash.dash",