diff --git a/.azure-pipelines/azure-pipelines-base.yml b/.azure-pipelines/azure-pipelines-base.yml index df6f821065..3187491cfe 100644 --- a/.azure-pipelines/azure-pipelines-base.yml +++ b/.azure-pipelines/azure-pipelines-base.yml @@ -5,26 +5,33 @@ jobs: variables: - name: NUMBA_DISABLE_JIT value: 1 - - name: ARVIZ_CI_MACHINE - value: 1 timeoutInMinutes: 360 strategy: matrix: + Python_312_optionals: + python.version: "3.12" + Nightlies: false + OptionalRequirements: true + name: "Python 3.12" Python_312: python.version: "3.12" - PyPIGithub: false + Nightlies: false + OptionalRequirements: false name: "Python 3.12" - Python_311: + Python_311_optionals: python.version: "3.11" - PyPIGithub: false + Nightlies: false + OptionalRequirements: true name: "Python 3.11" Python_311_preview: python.version: "3.11" - PyPIGithub: true + Nightlies: true + OptionalRequirements: false name: "Python 3.11" - Python_310: + Python_310_optionals: python.version: "3.10" - PyPIGithub: false + Nightlies: false + OptionalRequirements: true name: "Python 3.10" @@ -51,22 +58,20 @@ jobs: python -m pip install --upgrade pip python -m pip install wheel python -m pip install --no-cache-dir -r requirements.txt - python -m pip install --no-cache-dir -r requirements-optional.txt python -m pip install --no-cache-dir -r requirements-dev.txt python -m pip install pytest-azurepipelines - condition: and(succeeded(), eq(variables.PyPIGithub, false)) - displayName: 'Install requirements' + displayName: 'Install base requirements' - script: | - python -m pip install --upgrade pip - python -m pip install wheel - python -m pip install --no-cache-dir --pre -r requirements.txt python -m pip install --no-cache-dir --pre -r requirements-optional.txt - python -m pip install --no-cache-dir -r requirements-dev.txt - python -m pip install --upgrade git+https://github.com/pydata/xarray.git - python -m pip install pytest-azurepipelines - condition: and(succeeded(), eq(variables.PyPIGithub, true)) - displayName: 'Install requirements (pypi preview / github)' + condition: and(succeeded(), eq(variables.OptionalRequirements, true)) + displayName: 'Install optional requirements' + + - script: | + python -m pip install --no-cache-dir --upgrade --pre -r requirements.txt + python -m pip install --no-cache-dir --force-reinstall --no-deps --pre -i https://pypi.anaconda.org/scientific-python-nightly-wheels/simple contourpy h5py matplotlib numpy pandas scipy xarray + condition: and(succeeded(), eq(variables.Nightlies, true)) + displayName: 'Update with nightlies' - script: | python -m pip install . @@ -76,9 +81,15 @@ jobs: python -m pip freeze displayName: 'Print packages' + - script: | + ARVIZ_REQUIRE_ALL_DEPS=TRUE python -m pytest arviz/tests/base_tests --cov arviz --cov-report=xml + condition: and(succeeded(), eq(variables.OptionalRequirements, true)) + displayName: 'pytest (require all dependencies)' + - script: | python -m pytest arviz/tests/base_tests --cov arviz --cov-report=xml - displayName: 'pytest' + condition: and(succeeded(), eq(variables.OptionalRequirements, false)) + displayName: 'pytest (skip if dependency missing)' - script: | ls -ahl diff --git a/.azure-pipelines/azure-pipelines-external.yml b/.azure-pipelines/azure-pipelines-external.yml index b255ee8898..138ff80223 100644 --- a/.azure-pipelines/azure-pipelines-external.yml +++ b/.azure-pipelines/azure-pipelines-external.yml @@ -5,7 +5,7 @@ jobs: variables: - name: NUMBA_DISABLE_JIT value: 1 - - name: ARVIZ_CI_MACHINE + - name: ARVIZ_REQUIRE_ALL_DEPS value: 1 timeoutInMinutes: 360 strategy: diff --git a/CHANGELOG.md b/CHANGELOG.md index 279e3f4f21..7c41e75585 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ ### New features ### Maintenance and fixes +- Ensure support with numpy 2.0 ([2321](https://github.com/arviz-devs/arviz/pull/2321)) +- Update testing strategy to include an environment without optional dependencies and + an environment with [scientific python nightlies](https://anaconda.org/scientific-python-nightly-wheels) ([2321](https://github.com/arviz-devs/arviz/pull/2321)) - Fix legend overwriting issue in `plot_trace` ([2334](https://github.com/arviz-devs/arviz/pull/2334)) @@ -15,7 +18,6 @@ ## v0.18.0 (2024 Apr 4) ### New features - - Add new example data `rugby_field` and update `rugby` example data ([2322](https://github.com/arviz-devs/arviz/pull/2322)) - Support for `pytree`s and robust to nested dictionaries. ([2291](https://github.com/arviz-devs/arviz/pull/2291)) - Add `.close` method to `InferenceData` ([2338](https://github.com/arviz-devs/arviz/pull/2338)) diff --git a/arviz/plots/plot_utils.py b/arviz/plots/plot_utils.py index 9170c783f2..85065fa268 100644 --- a/arviz/plots/plot_utils.py +++ b/arviz/plots/plot_utils.py @@ -245,10 +245,8 @@ def format_coords_as_labels(dataarray, skip_dims=None): coord_labels = coord_labels.values if isinstance(coord_labels[0], tuple): fmt = ", ".join(["{}" for _ in coord_labels[0]]) - coord_labels[:] = [fmt.format(*x) for x in coord_labels] - else: - coord_labels[:] = [f"{s}" for s in coord_labels] - return coord_labels + return np.array([fmt.format(*x) for x in coord_labels]) + return np.array([f"{s}" for s in coord_labels]) def set_xticklabels(ax, coord_labels): diff --git a/arviz/tests/base_tests/test_data.py b/arviz/tests/base_tests/test_data.py index bb1882059e..55882b81aa 100644 --- a/arviz/tests/base_tests/test_data.py +++ b/arviz/tests/base_tests/test_data.py @@ -42,7 +42,6 @@ draws, eight_schools_params, models, - running_on_ci, ) @@ -1469,7 +1468,7 @@ def test_json_converters(self, models): @pytest.mark.skipif( - not (importlib.util.find_spec("datatree") or running_on_ci()), + not (importlib.util.find_spec("datatree") or "ARVIZ_REQUIRE_ALL_DEPS" in os.environ), reason="test requires xarray-datatree library", ) class TestDataTree: diff --git a/arviz/tests/base_tests/test_data_zarr.py b/arviz/tests/base_tests/test_data_zarr.py index 53144ad3d8..c93b0253dd 100644 --- a/arviz/tests/base_tests/test_data_zarr.py +++ b/arviz/tests/base_tests/test_data_zarr.py @@ -16,7 +16,6 @@ draws, eight_schools_params, importorskip, - running_on_ci, ) zarr = importorskip("zarr") # pylint: disable=invalid-name diff --git a/arviz/tests/base_tests/test_diagnostics_numba.py b/arviz/tests/base_tests/test_diagnostics_numba.py index e3da38ded5..d775eac345 100644 --- a/arviz/tests/base_tests/test_diagnostics_numba.py +++ b/arviz/tests/base_tests/test_diagnostics_numba.py @@ -1,7 +1,5 @@ """Test Diagnostic methods""" -import importlib - # pylint: disable=redefined-outer-name, no-member, too-many-public-methods import numpy as np import pytest @@ -11,13 +9,10 @@ from ...stats import bfmi, mcse, rhat from ...stats.diagnostics import _mc_error, ks_summary from ...utils import Numba -from ..helpers import running_on_ci +from ..helpers import importorskip from .test_diagnostics import data # pylint: disable=unused-import -pytestmark = pytest.mark.skipif( # pylint: disable=invalid-name - (importlib.util.find_spec("numba") is None) and not running_on_ci(), - reason="test requires numba which is not installed", -) +importorskip("numba") rcParams["data.load"] = "eager" diff --git a/arviz/tests/base_tests/test_helpers.py b/arviz/tests/base_tests/test_helpers.py index a7d82be96f..c0bbf43379 100644 --- a/arviz/tests/base_tests/test_helpers.py +++ b/arviz/tests/base_tests/test_helpers.py @@ -6,13 +6,13 @@ def test_importorskip_local(monkeypatch): """Test ``importorskip`` run on local machine with non-existent module, which should skip.""" - monkeypatch.delenv("ARVIZ_CI_MACHINE", raising=False) + monkeypatch.delenv("ARVIZ_REQUIRE_ALL_DEPS", raising=False) with pytest.raises(Skipped): importorskip("non-existent-function") def test_importorskip_ci(monkeypatch): """Test ``importorskip`` run on CI machine with non-existent module, which should fail.""" - monkeypatch.setenv("ARVIZ_CI_MACHINE", 1) + monkeypatch.setenv("ARVIZ_REQUIRE_ALL_DEPS", 1) with pytest.raises(ModuleNotFoundError): importorskip("non-existent-function") diff --git a/arviz/tests/base_tests/test_plot_utils.py b/arviz/tests/base_tests/test_plot_utils.py index 55d0224f3b..471bd91f27 100644 --- a/arviz/tests/base_tests/test_plot_utils.py +++ b/arviz/tests/base_tests/test_plot_utils.py @@ -1,5 +1,6 @@ # pylint: disable=redefined-outer-name import importlib +import os import numpy as np import pytest @@ -20,10 +21,10 @@ from ...sel_utils import xarray_sel_iter, xarray_to_ndarray from ...stats.density_utils import get_bins from ...utils import get_coords -from ..helpers import running_on_ci # Check if Bokeh is installed bokeh_installed = importlib.util.find_spec("bokeh") is not None # pylint: disable=invalid-name +skip_tests = (not bokeh_installed) and ("ARVIZ_REQUIRE_ALL_DEPS" not in os.environ) @pytest.mark.parametrize( @@ -212,10 +213,7 @@ def test_filter_plotter_list_warning(): assert len(plotters_filtered) == 5 -@pytest.mark.skipif( - not (bokeh_installed or running_on_ci()), - reason="test requires bokeh which is not installed", -) +@pytest.mark.skipif(skip_tests, reason="test requires bokeh which is not installed") def test_bokeh_import(): """Tests that correct method is returned on bokeh import""" plot = get_plotting_function("plot_dist", "distplot", "bokeh") @@ -290,10 +288,7 @@ def test_mpl_dealiase_sel_kwargs(): assert res["line_color"] == "red" -@pytest.mark.skipif( - not (bokeh_installed or running_on_ci()), - reason="test requires bokeh which is not installed", -) +@pytest.mark.skipif(skip_tests, reason="test requires bokeh which is not installed") def test_bokeh_dealiase_sel_kwargs(): """Check bokeh dealiase_sel_kwargs behaviour. @@ -315,10 +310,7 @@ def test_bokeh_dealiase_sel_kwargs(): assert res["line_color"] == "red" -@pytest.mark.skipif( - not (bokeh_installed or running_on_ci()), - reason="test requires bokeh which is not installed", -) +@pytest.mark.skipif(skip_tests, reason="test requires bokeh which is not installed") def test_set_bokeh_circular_ticks_labels(): """Assert the axes returned after placing ticks and tick labels for circular plots.""" import bokeh.plotting as bkp diff --git a/arviz/tests/base_tests/test_stats_numba.py b/arviz/tests/base_tests/test_stats_numba.py index d6d63f3fc8..8b76bde113 100644 --- a/arviz/tests/base_tests/test_stats_numba.py +++ b/arviz/tests/base_tests/test_stats_numba.py @@ -1,6 +1,4 @@ # pylint: disable=redefined-outer-name, no-member -import importlib - import numpy as np import pytest @@ -9,15 +7,12 @@ from ...utils import Numba from ..helpers import ( # pylint: disable=unused-import check_multiple_attrs, + importorskip, multidim_models, - running_on_ci, ) from .test_stats import centered_eight, non_centered_eight # pylint: disable=unused-import -pytestmark = pytest.mark.skipif( # pylint: disable=invalid-name - (importlib.util.find_spec("numba") is None) and not running_on_ci(), - reason="test requires numba which is not installed", -) +numba = importorskip("numba") rcParams["data.load"] = "eager" diff --git a/arviz/tests/base_tests/test_utils_numba.py b/arviz/tests/base_tests/test_utils_numba.py index c3d1f252ed..b2bdfe291d 100644 --- a/arviz/tests/base_tests/test_utils_numba.py +++ b/arviz/tests/base_tests/test_utils_numba.py @@ -8,13 +8,10 @@ from ...stats.stats_utils import stats_variance_2d as svar from ...utils import Numba, _numba_var, numba_check -from ..helpers import running_on_ci +from ..helpers import importorskip from .test_utils import utils_with_numba_import_fail # pylint: disable=unused-import -pytestmark = pytest.mark.skipif( # pylint: disable=invalid-name - (importlib.util.find_spec("numba") is None) and not running_on_ci(), - reason="test requires numba which is not installed", -) +importorskip("numba") def test_utils_fixture(utils_with_numba_import_fail): diff --git a/arviz/tests/external_tests/test_data_pystan.py b/arviz/tests/external_tests/test_data_pystan.py index 2be52c8fd4..69f214f76b 100644 --- a/arviz/tests/external_tests/test_data_pystan.py +++ b/arviz/tests/external_tests/test_data_pystan.py @@ -1,6 +1,7 @@ # pylint: disable=no-member, invalid-name, redefined-outer-name, too-many-function-args import importlib from collections import OrderedDict +import os import numpy as np import pytest @@ -16,19 +17,18 @@ importorskip, load_cached_models, pystan_version, - running_on_ci, ) # Check if either pystan or pystan3 is installed pystan_installed = (importlib.util.find_spec("pystan") is not None) or ( importlib.util.find_spec("stan") is not None ) -pytestmark = pytest.mark.skipif( - not (pystan_installed | running_on_ci()), - reason="test requires pystan/pystan3 which is not installed", -) +@pytest.mark.skipif( + not (pystan_installed or "ARVIZ_REQUIRE_ALL_DEPS" in os.environ), + reason="test requires pystan/pystan3 which is not installed", +) class TestDataPyStan: @pytest.fixture(scope="class") def data(self, eight_schools_params, draws, chains): diff --git a/arviz/tests/helpers.py b/arviz/tests/helpers.py index 15dbd95689..3e826b8587 100644 --- a/arviz/tests/helpers.py +++ b/arviz/tests/helpers.py @@ -620,11 +620,6 @@ def test_precompile_models(eight_schools_params, draws, chains): load_cached_models(eight_schools_params, draws, chains) -def running_on_ci() -> bool: - """Return True if running on CI machine.""" - return os.environ.get("ARVIZ_CI_MACHINE") is not None - - def importorskip( modname: str, minversion: Optional[str] = None, reason: Optional[str] = None ) -> Any: @@ -643,9 +638,9 @@ def importorskip( Example:: docutils = pytest.importorskip("docutils") """ - # ARVIZ_CI_MACHINE is True if tests run on CI, where ARVIZ_CI_MACHINE env variable exists - ARVIZ_CI_MACHINE = running_on_ci() - if not ARVIZ_CI_MACHINE: + # Unless ARVIZ_REQUIRE_ALL_DEPS is defined, tests that require a missing dependency are skipped + # if set, missing optional dependencies trigger failed tests. + if "ARVIZ_REQUIRE_ALL_DEPS" not in os.environ: return pytest.importorskip(modname=modname, minversion=minversion, reason=reason) import warnings diff --git a/requirements.txt b/requirements.txt index 3919a1d1a5..90391e8b1b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ setuptools>=60.0.0 matplotlib>=3.5 -numpy>=1.23.0,<2.0 +numpy>=1.23.0 scipy>=1.9.0 packaging pandas>=1.5.0