diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a8ea7a51c..aa6a87364 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -103,6 +103,7 @@ jobs: python -m tox -- -m 'not slow' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COVERALLS_PARALLEL: true test-pypi: needs: lint @@ -192,7 +193,6 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} COVERALLS_FLAG_NAME: run-${{ matrix.python-version }}-${{ matrix.os }}-${{ matrix.tox-env }} COVERALLS_PARALLEL: true - COVERALLS_SERVICE_NAME: github - name: Test with tox (specialized tests) if: ${{ matrix.tox-env != 'standard' }} run: | @@ -201,7 +201,6 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} COVERALLS_FLAG_NAME: run-${{ matrix.python-version }}-${{ matrix.os }}-${{ matrix.tox-env }} COVERALLS_PARALLEL: true - COVERALLS_SERVICE_NAME: github test-conda: needs: lint @@ -278,7 +277,6 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} COVERALLS_FLAG_NAME: run-{{ matrix.python-version }}-conda COVERALLS_PARALLEL: true - COVERALLS_SERVICE_NAME: github finish: needs: @@ -301,4 +299,3 @@ jobs: python -m coveralls --finish env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - COVERALLS_SERVICE_NAME: github diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 63a800fd0..db050ad56 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,16 +6,21 @@ v0.52.0 (unreleased) -------------------- Contributors to this version: David Huard (:user:`huard`), Trevor James Smith (:user:`Zeitsperre`), Hui-Min Wang (:user:`Hem-W`), Éric Dupuis (:user:`coxipi`), Sarah Gammon (:user:`SarahG-579462`), Pascal Bourgault (:user:`aulemahal`), Juliette Lavoie (:user:`juliettelavoie`), Adrien Lamarche (:user:`LamAdr`). +Announcements +^^^^^^^^^^^^^ +* `xclim` now supports both `numpy` versions `>=1.20` and `>=2.0`. (:issue:`1785`, :pull:`1814`, :pull:`1870`). +* `xclim` now needs ``cf_xarray>=0.9.3`` but continues to support older versions of `pint` (`<0.24`) for compatibility reasons. (:pull:`1870`). + New features and enhancements ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * ``xclim.sdba.nbutils.quantile`` and its child functions are now faster. If the module `fastnanquantile` is installed, it is used as the backend for the computation of quantiles and yields even faster results. This dependency is now listed in the `xclim[extras]` recipe. (:issue:`1255`, :pull:`1513`). -* New multivariate bias adjustment class ``MBCn``, giving a faster and more accurate implementation of the 'MBCn' algorithm. (:issue:`1551`, :pull:`1580`). +* New multivariate bias adjustment class ``MBCn``, giving a faster and more accurate implementation of the ``MBCn`` algorithm. (:issue:`1551`, :pull:`1580`). * New multivariate bias adjustment classes ``OTC`` and ``dOTC``. (:pull:`1787`). * `xclim` is now compatible with `pytest` versions `>=8.0.0`. (:pull:`1632`). Breaking changes ^^^^^^^^^^^^^^^^ -* As updated in ``cf_xarray>=0.9.3``, dimensionless quantities now use the "1" units attribute as specified by the CF conventions, previously an empty string was returned. (:pull:`1814`). +* As of ``cf_xarray>=0.9.3``, dimensionless quantities now use the "1" units attribute as specified by the CF conventions, previously an empty string was returned. (:pull:`1814`). * The definitions of the ``frost_free_season_start`` and ``frost_free_season_end`` have been slightly changed to be coherent with the ``frost_free_season_length`` and `xclim`'s notion of `season` in general. Indicator and indices signature have changed. (:pull:`1845`). * Season length indicators have been modified to return ``0`` for all cases where a proper season was not found, but the data is valid. Previously, a ``nan`` was given if neither a start or an end were found, even if the data was valid, and a ``0`` was given if an end was found but no start. (:pull:`1845`). @@ -23,8 +28,8 @@ Bug fixes ^^^^^^^^^ * Fixed the indexer bug in the ``xclim.indices.standardized_index_fit_params`` when multiple or non-array indexers are specified and fitted parameters are reloaded from netCDF. (:issue:`1842`, :pull:`1843`). * Addressed a bug found in ``wet_spell_*`` indicators that was contributing to erroneous results. A new generic spell length statistic function ``xclim.indices.generic.spell_length_statistics`` is now used in wet and dry spells indicators. (:issue:`1834`, :pull:`1838`). -* Syntax for ``nan`` and ``inf`` was adapted to support ``numpy>=2.0.0``. (:pull:`1814`, :issue:`1785`). -* Force type in `jitter` to work with new version of dask. (:pull:`1864`) +* Syntax for ``nan`` and ``inf`` was adapted to support `numpy>=2.0`. (:pull:`1814`, :issue:`1785`). +* Force type in ``jitter`` to work with new version of `dask`. (:pull:`1864`). Internal changes ^^^^^^^^^^^^^^^^ @@ -37,7 +42,7 @@ CI changes * `pip-tools` (`pip-compile`) has been used to generate a lock file with hashes for the CI dependencies. (:pull:`1841`). * The ``main.yml`` workflow has been updated to use simpler trigger logic. (:pull:`1841`). * A workflow bug has been fixed that was causing multiple duplicate comments to be made on Pull Requests originating from forks. (:pull:`1841`). -* The ``upstream.yml`` workflow was adapted to not install upstream Python dependencies using hashes (impossible to install directly from GitHub sources using --require-hashes). (:pull:`1859`). +* The ``upstream.yml`` workflow was adapted to not install upstream Python dependencies using hashes (as it is impossible to install directly from GitHub sources using ``--require-hashes``). (:pull:`1859`). * The `tox-gh` configuration has been set to handle the environment configurations on GitHub Workflows. The tox.ini file is also a bit more organized/consistent. (:pull:`1859`). v0.51.0 (2024-07-04) diff --git a/environment.yml b/environment.yml index 8ea404ff8..070f86943 100644 --- a/environment.yml +++ b/environment.yml @@ -6,39 +6,39 @@ dependencies: - python >=3.9 - boltons >=20.1 - bottleneck >=1.3.1 - - cf_xarray >=0.6.1 + - cf_xarray >=0.9.3 - cftime >=1.4.1 - click >=8.1 - dask >=2.6.0 - - jsonpickle - - numba + - filelock >=3.14.0 + - jsonpickle >=3.1.0 + - numba >=0.54.1 - numpy >=1.20.0 + - packaging >=24.0 - pandas >=2.2.0 - pint >=0.18.0 - - poppler >=0.67 - - pyarrow # Strongly encouraged for Pandas v2.2.0+ - - pyyaml + - pyarrow >=15.0.0 # Strongly encouraged for Pandas v2.2.0+ + - pyyaml >=6.0.1 - scikit-learn >=0.21.3 - scipy >=1.9.0 - - statsmodels + - statsmodels >=0.14.2 - xarray >=2023.11.0 - - yamale + - yamale >=5.0.0 # Extras - flox - lmoments3 # Required for some Jupyter notebooks # Testing and development dependencies - black ==24.4.2 - blackdoc ==0.3.9 - - bump-my-version >=0.23.0 + - bump-my-version >=0.24.3 - cairosvg - codespell ==2.3.0 - coverage >=7.5.0 - coveralls >=4.0.0 - - deptry ==0.16.1 + - deptry ==0.18.0 - distributed >=2.0 - - filelock - - flake8 >=7.0.0 - - flake8-rst-docstrings + - flake8 >=7.1.1 + - flake8-rst-docstrings >=0.3.0 - flit >=3.9.0 - furo >=2023.9.10 - h5netcdf >=1.3.0 @@ -46,24 +46,24 @@ dependencies: - ipython - isort ==5.13.2 - matplotlib - - mypy + - mypy >=1.10.0 - nbconvert <7.14 # Pinned due to directive errors in sphinx. See: https://github.com/jupyter/nbconvert/issues/2092 - - nbqa + - nbqa >=1.8.2 - nbsphinx - nbval >=0.11.0 - - nc-time-axis + - nc-time-axis >=1.4.1 - notebook - - pandas-stubs - - platformdirs - - pooch + - pandas-stubs >=2.2 + - platformdirs >=3.2 + - pooch >=1.8.0 - pre-commit >=3.7 - pybtex >=0.24.0 - pylint >=3.1 - pytest >=8.0.0 - - pytest-cov - - pytest-socket + - pytest-cov >=5.0.0 + - pytest-socket >=0.6.0 - pytest-xdist >=3.2 - - ruff >=0.4.10 + - ruff >=0.5.6 - sphinx >=7.0.0 - sphinx-autobuild >=2024.4.16 - sphinx-autodoc-typehints @@ -71,12 +71,12 @@ dependencies: - sphinx-copybutton - sphinx-mdinclude - sphinxcontrib-bibtex - - tokenize-rt + - tokenize-rt >=5.2.0 - tox >=4.16.0 # - tox-conda # Will be added when a tox@v4.0+ compatible plugin is released. - vulture # ==2.11 # The conda-forge version is out of date. - xdoctest >=1.1.5 - - yamllint - - pip + - yamllint >=1.35.1 + - pip >=24.0 - pip: - sphinxcontrib-svg2pdfconverter diff --git a/pyproject.toml b/pyproject.toml index f3ff940b7..e5819eed5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,32 +27,35 @@ classifiers = [ "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", - "Topic :: Scientific/Engineering :: Atmospheric Science" + "Programming Language :: Python :: 3 :: Only", + "Topic :: Scientific/Engineering :: Atmospheric Science", + "Topic :: Scientific/Engineering :: Hydrology", + "Typing :: Typed" ] dynamic = ["description", "version"] dependencies = [ - "boltons>=20.1", - "bottleneck>=1.3.1", + "boltons >=20.1", + "bottleneck >=1.3.1", # cf-xarray is differently named on conda-forge - "cf-xarray>=0.6.1", - "cftime>=1.4.1", - "click>=8.1", - "dask[array]>=2.6", - "filelock", - "jsonpickle", - "numba", - "numpy>=1.20.0", - "packaging", - "pandas>=2.2", - "pint>=0.18", + "cf-xarray >=0.9.3", + "cftime >=1.4.1", + "click >=8.1", + "dask[array] >=2.6", + "filelock >=3.14.0", + "jsonpickle >=3.1.0", + "numba >=0.54.1", + "numpy >=1.20.0", + "packaging >=24.0", + "pandas >=2.2", + "pint >=0.18", "platformdirs >=3.2", - "pyarrow", # Strongly encouraged for pandas v2.2.0+ - "pyyaml", - "scikit-learn>=0.21.3", - "scipy>=1.9.0", - "statsmodels", - "xarray>=2023.11.0", - "yamale" + "pyarrow >=15.0.0", # Strongly encouraged for pandas v2.2.0+ + "pyyaml >=6.0.1", + "scikit-learn >=0.21.3", + "scipy >=1.9.0", + "statsmodels >=0.14.2", + "xarray >=2023.11.0", + "yamale >=5.0.0" ] [project.optional-dependencies] @@ -65,27 +68,28 @@ dev = [ "coverage[toml] >=7.5.0", "coveralls >=4.0.0", "deptry ==0.18.0", - "flake8 >=7.1.0", - "flake8-rst-docstrings", + "flake8 >=7.1.1", + "flake8-rst-docstrings >=0.3.0", "h5netcdf>=1.3.0", "ipython", "isort ==5.13.2", - "mypy", + "mypy >=1.10.0", "nbconvert <7.14", # Pinned due to directive errors in sphinx. See: https://github.com/jupyter/nbconvert/issues/2092 "nbqa >=1.8.2", "nbval >=0.11.0", "pandas-stubs >=2.2", + "pip >=24.0", "platformdirs >=3.2", - "pooch", + "pooch >=1.8.0", "pre-commit >=3.7", "pylint >=3.2.4", "pytest >=8.0.0", - "pytest-cov", - "pytest-socket", + "pytest-cov >=5.0.0", + "pytest-socket >=0.6.0", "pytest-xdist[psutil] >=3.2", - "ruff >=0.4.10", - "tokenize-rt", - "tox >=4.16.0", + "ruff >=0.5.6", + "tokenize-rt >=5.2.0", + "tox >=4.17.0", # "tox-conda", # Will be added when a tox@v4.0+ compatible plugin is released. "vulture ==2.11", "xdoctest >=1.1.5", @@ -98,8 +102,8 @@ docs = [ "ipykernel", "matplotlib", "nbsphinx", - "nc-time-axis", - "pooch", + "nc-time-axis >=1.4.1", + "pooch >=1.8.0", "pybtex >=0.24.0", "sphinx >=7.0.0", "sphinx-autobuild >=2024.4.16", @@ -110,7 +114,7 @@ docs = [ "sphinxcontrib-bibtex", "sphinxcontrib-svg2pdfconverter[Cairosvg]" ] -extras = ["fastnanquantile", "POT"] +extras = ["fastnanquantile >=0.0.2", "POT"] all = ["xclim[dev]", "xclim[docs]", "xclim[extras]"] [project.scripts] @@ -134,7 +138,7 @@ target-version = [ ] [tool.bumpversion] -current_version = "0.51.1-dev.11" +current_version = "0.51.1-dev.12" commit = true commit_args = "--no-verify" tag = false diff --git a/tests/test_atmos.py b/tests/test_atmos.py index d705e7493..10d5d0efe 100644 --- a/tests/test_atmos.py +++ b/tests/test_atmos.py @@ -4,8 +4,6 @@ import numpy as np import xarray as xr -from cf_xarray import __version__ as __cfxr_version__ -from packaging.version import Version from xclim import atmos, set_options @@ -262,11 +260,7 @@ def test_wind_profile(atmosds): def test_wind_power_potential(atmosds): out = atmos.wind_power_potential(wind_speed=atmosds.sfcWind) - - if Version(__cfxr_version__) < Version("0.9.3"): - assert out.attrs["units"] == "" - else: - assert out.attrs["units"] == "1" + assert out.attrs["units"] == "1" assert (out >= 0).all() assert (out <= 1).all() diff --git a/tests/test_generic.py b/tests/test_generic.py index ef129121a..8448a5607 100644 --- a/tests/test_generic.py +++ b/tests/test_generic.py @@ -5,8 +5,6 @@ import numpy as np import pytest import xarray as xr -from cf_xarray import __version__ as __cfxr_version__ -from packaging.version import Version from xclim.core.calendar import doy_to_days_since, select_time from xclim.indices import generic @@ -104,10 +102,7 @@ def test_doyminmax(self, q_series): for attr in ["units", "is_dayofyear", "calendar"]: assert attr in da.attrs.keys() - if Version(__cfxr_version__) < Version("0.9.3"): - assert da.attrs["units"] == "" - else: - assert da.attrs["units"] == "1" + assert da.attrs["units"] == "1" assert da.attrs["is_dayofyear"] == 1 diff --git a/tests/test_hydrology.py b/tests/test_hydrology.py index b2276bda7..0a967a42a 100644 --- a/tests/test_hydrology.py +++ b/tests/test_hydrology.py @@ -1,8 +1,6 @@ from __future__ import annotations import numpy as np -from cf_xarray import __version__ as __cfxr_version__ -from packaging.version import Version from xclim import indices as xci @@ -42,11 +40,7 @@ def test_simple(self, snw_series): snw = snw_series(a, start="1999-01-01") out = xci.snw_max_doy(snw, freq="YS") np.testing.assert_array_equal(out, [11, np.nan]) - - if Version(__cfxr_version__) < Version("0.9.3"): - assert out.attrs["units"] == "" - else: - assert out.attrs["units"] == "1" + assert out.attrs["units"] == "1" class TestSnowMeltWEMax: diff --git a/tests/test_indices.py b/tests/test_indices.py index 2f324faa7..a9386087f 100644 --- a/tests/test_indices.py +++ b/tests/test_indices.py @@ -19,7 +19,6 @@ import pandas as pd import pytest import xarray as xr -from cf_xarray import __version__ as __cfxr_version__ from numpy import __version__ as __numpy_version__ from packaging.version import Version from pint import __version__ as __pint_version__ @@ -141,19 +140,11 @@ def test_simple(self, tas_series): out = xci.cold_spell_frequency(da, thresh="-10. C", freq="ME") np.testing.assert_array_equal(out, [1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]) - - if Version(__cfxr_version__) < Version("0.9.3"): - assert out.units == "" - else: - assert out.units == "1" + assert out.units == "1" out = xci.cold_spell_frequency(da, thresh="-10. C", freq="YS") np.testing.assert_array_equal(out, 3) - - if Version(__cfxr_version__) < Version("0.9.3"): - assert out.units == "" - else: - assert out.units == "1" + assert out.units == "1" class TestColdSpellMaxLength: @@ -934,12 +925,7 @@ def test_simple(self, tas_series): assert lsf == 180 for attr in ["units", "is_dayofyear", "calendar"]: assert attr in lsf.attrs.keys() - - if Version(__cfxr_version__) < Version("0.9.3"): - assert lsf.attrs["units"] == "" - else: - assert lsf.attrs["units"] == "1" - + assert lsf.attrs["units"] == "1" assert lsf.attrs["is_dayofyear"] == 1 assert lsf.attrs["is_dayofyear"].dtype == np.int32 @@ -960,12 +946,7 @@ def test_simple(self, tas_series): assert np.isnan(fdb) for attr in ["units", "is_dayofyear", "calendar"]: assert attr in fdb.attrs.keys() - - if Version(__cfxr_version__) < Version("0.9.3"): - assert fdb.attrs["units"] == "" - else: - assert fdb.attrs["units"] == "1" - + assert fdb.attrs["units"] == "1" assert fdb.attrs["is_dayofyear"] == 1 def test_below_forbidden(self, tasmax_series): @@ -996,12 +977,7 @@ def test_simple(self, tas_series): assert np.isnan(fda) for attr in ["units", "is_dayofyear", "calendar"]: assert attr in fda.attrs.keys() - - if Version(__cfxr_version__) < Version("0.9.3"): - assert fda.attrs["units"] == "" - else: - assert fda.attrs["units"] == "1" - + assert fda.attrs["units"] == "1" assert fda.attrs["is_dayofyear"] == 1 def test_thresholds(self, tas_series): @@ -1026,12 +1002,7 @@ def test_thresholds(self, tas_series): assert out[0] == tg.indexes["time"][30].dayofyear for attr in ["units", "is_dayofyear", "calendar"]: assert attr in out.attrs.keys() - - if Version(__cfxr_version__) < Version("0.9.3"): - assert out.attrs["units"] == "" - else: - assert out.attrs["units"] == "1" - + assert out.attrs["units"] == "1" assert out.attrs["is_dayofyear"] == 1 def test_above_forbidden(self, tasmax_series): @@ -1118,12 +1089,7 @@ def test_simple(self, tas_series): assert out[0] == tg.indexes["time"][20].dayofyear for attr in ["units", "is_dayofyear", "calendar"]: assert attr in out.attrs.keys() - - if Version(__cfxr_version__) < Version("0.9.3"): - assert out.attrs["units"] == "" - else: - assert out.attrs["units"] == "1" - + assert out.attrs["units"] == "1" assert out.attrs["is_dayofyear"] == 1 def test_no_start(self, tas_series): @@ -1236,11 +1202,7 @@ def test_simple(self, tasmin_series): assert out[0] == tn.indexes["time"][20].dayofyear for attr in ["units", "is_dayofyear", "calendar"]: assert attr in out.attrs.keys() - - if Version(__cfxr_version__) < Version("0.9.3"): - assert out.attrs["units"] == "" - else: - assert out.attrs["units"] == "1" + assert out.attrs["units"] == "1" assert out.attrs["is_dayofyear"] == 1 @@ -1275,10 +1237,7 @@ def test_varying_mid_dates(self, tasmin_series, d1, d2, mid_date, expected): for attr in ["units", "is_dayofyear", "calendar"]: assert attr in gs_end.attrs.keys() - if Version(__cfxr_version__) < Version("0.9.3"): - assert gs_end.attrs["units"] == "" - else: - assert gs_end.attrs["units"] == "1" + assert gs_end.attrs["units"] == "1" assert gs_end.attrs["is_dayofyear"] == 1 @@ -2788,11 +2747,7 @@ def test_degree_days_exceedance_date(tas_series): for attr in ["units", "is_dayofyear", "calendar"]: assert attr in out.attrs.keys() - - if Version(__cfxr_version__) < Version("0.9.3"): - assert out.attrs["units"] == "" - else: - assert out.attrs["units"] == "1" + assert out.attrs["units"] == "1" assert out.attrs["is_dayofyear"] == 1 @@ -2833,11 +2788,7 @@ def test_first_snowfall(prsn_series, prsnd_series): assert out[0] == 166 for attr in ["units", "is_dayofyear", "calendar"]: assert attr in out.attrs.keys() - - if Version(__cfxr_version__) < Version("0.9.3"): - assert out.attrs["units"] == "" - else: - assert out.attrs["units"] == "1" + assert out.attrs["units"] == "1" assert out.attrs["is_dayofyear"] == 1 # test with prsnd [m s-1] @@ -2859,10 +2810,7 @@ def test_first_snowfall(prsn_series, prsnd_series): for attr in ["units", "is_dayofyear", "calendar"]: assert attr in out.attrs.keys() - if Version(__cfxr_version__) < Version("0.9.3"): - assert out.attrs["units"] == "" - else: - assert out.attrs["units"] == "1" + assert out.attrs["units"] == "1" assert out.attrs["is_dayofyear"] == 1 @@ -2999,12 +2947,7 @@ def test_continous_snow_season_start(self, snd_series, snw_series): np.testing.assert_array_equal(out, [snd.time.dt.dayofyear[0].data + 2, np.nan]) for attr in ["units", "is_dayofyear", "calendar"]: assert attr in out.attrs.keys() - - if Version(__cfxr_version__) < Version("0.9.3"): - assert out.attrs["units"] == "" - else: - assert out.attrs["units"] == "1" - + assert out.attrs["units"] == "1" assert out.attrs["is_dayofyear"] == 1 out = xci.snw_season_start(snw) @@ -3012,12 +2955,7 @@ def test_continous_snow_season_start(self, snd_series, snw_series): np.testing.assert_array_equal(out, [snw.time.dt.dayofyear[0].data + 1, np.nan]) for attr in ["units", "is_dayofyear", "calendar"]: assert attr in out.attrs.keys() - - if Version(__cfxr_version__) < Version("0.9.3"): - assert out.attrs["units"] == "" - else: - assert out.attrs["units"] == "1" - + assert out.attrs["units"] == "1" assert out.attrs["is_dayofyear"] == 1 def test_snow_season_end(self, snd_series, snw_series): @@ -3039,12 +2977,7 @@ def test_snow_season_end(self, snd_series, snw_series): np.testing.assert_array_equal(out, [(doy + 219) % 366, np.nan]) for attr in ["units", "is_dayofyear", "calendar"]: assert attr in out.attrs.keys() - - if Version(__cfxr_version__) < Version("0.9.3"): - assert out.attrs["units"] == "" - else: - assert out.attrs["units"] == "1" - + assert out.attrs["units"] == "1" assert out.attrs["is_dayofyear"] == 1 out = xci.snw_season_end(snw) diff --git a/tests/test_land.py b/tests/test_land.py index 07841cd87..a1782f1be 100644 --- a/tests/test_land.py +++ b/tests/test_land.py @@ -4,8 +4,6 @@ import numpy as np import xarray as xr -from cf_xarray import __version__ as __cfxr_version__ -from packaging.version import Version from xclim import land @@ -13,32 +11,20 @@ def test_base_flow_index(ndq_series): out = land.base_flow_index(ndq_series, freq="YS") - if Version(__cfxr_version__) < Version("0.9.3"): - assert out.attrs["units"] == "" - else: - assert out.attrs["units"] == "1" - + assert out.attrs["units"] == "1" assert isinstance(out, xr.DataArray) def test_rb_flashiness_index(ndq_series): out = land.base_flow_index(ndq_series, freq="YS") - if Version(__cfxr_version__) < Version("0.9.3"): - assert out.attrs["units"] == "" - else: - assert out.attrs["units"] == "1" - + assert out.attrs["units"] == "1" assert isinstance(out, xr.DataArray) def test_qdoy_max(ndq_series, q_series): out = land.doy_qmax(ndq_series, freq="YS", season="JJA") - - if Version(__cfxr_version__) < Version("0.9.3"): - assert out.attrs["units"] == "" - else: - assert out.attrs["units"] == "1" + assert out.attrs["units"] == "1" a = np.ones(450) a[100] = 2 diff --git a/tests/test_sdba/test_adjustment.py b/tests/test_sdba/test_adjustment.py index b9007d70d..6bdf292da 100644 --- a/tests/test_sdba/test_adjustment.py +++ b/tests/test_sdba/test_adjustment.py @@ -554,6 +554,15 @@ def test_add_dims(self, use_dask, open_dataset): chunks = {"location": -1} else: chunks = None + + dsim = open_dataset( + "sdba/CanESM2_1950-2100.nc", + chunks=chunks, + drop_variables=["lat", "lon"], + ).tasmax + hist = dsim.sel(time=slice("1981", "2010")) + sim = dsim.sel(time=slice("2041", "2070")) + ref = ( open_dataset( "sdba/ahccd_1950-2013.nc", @@ -564,15 +573,9 @@ def test_add_dims(self, use_dask, open_dataset): .tasmax ) ref = convert_units_to(ref, "K") - ref = ref.isel(location=1, drop=True).expand_dims(location=["Amos"]) - - dsim = open_dataset( - "sdba/CanESM2_1950-2100.nc", - chunks=chunks, - drop_variables=["lat", "lon"], - ).tasmax - hist = dsim.sel(time=slice("1981", "2010")) - sim = dsim.sel(time=slice("2041", "2070")) + # The idea is to have ref defined only over 1 location + # But sdba needs the same dimensions on ref and hist for Grouping with add_dims + ref = ref.where(ref.location == "Amos") # With add_dims, "does it run" test group = Grouper("time.dayofyear", window=5, add_dims=["location"]) diff --git a/tests/test_snow.py b/tests/test_snow.py index 6cbb20c65..5b598a596 100644 --- a/tests/test_snow.py +++ b/tests/test_snow.py @@ -2,8 +2,6 @@ import numpy as np import pytest -from cf_xarray import __version__ as __cfxr_version__ -from packaging.version import Version from xclim import land from xclim.core.utils import ValidationError @@ -47,20 +45,12 @@ def test_simple(self, snd_series): snd = snd.expand_dims(lat=[0, 1, 2]) out = land.snd_season_start(snd) - - if Version(__cfxr_version__) < Version("0.9.3"): - assert out.units == "" - else: - assert out.units == "1" + assert out.units == "1" np.testing.assert_array_equal(out.isel(lat=0), snd.time.dt.dayofyear[100]) out = land.snd_season_end(snd) - - if Version(__cfxr_version__) < Version("0.9.3"): - assert out.units == "" - else: - assert out.units == "1" + assert out.units == "1" np.testing.assert_array_equal(out.isel(lat=0), snd.time.dt.dayofyear[200]) @@ -79,20 +69,12 @@ def test_simple(self, snw_series): snw = snw.expand_dims(lat=[0, 1, 2]) out = land.snw_season_start(snw) - - if Version(__cfxr_version__) < Version("0.9.3"): - assert out.units == "" - else: - assert out.units == "1" + assert out.units == "1" np.testing.assert_array_equal(out.isel(lat=0), snw.time.dt.dayofyear[100]) out = land.snw_season_end(snw) - - if Version(__cfxr_version__) < Version("0.9.3"): - assert out.units == "" - else: - assert out.units == "1" + assert out.units == "1" np.testing.assert_array_equal(out.isel(lat=0), snw.time.dt.dayofyear[200]) diff --git a/tests/test_temperature.py b/tests/test_temperature.py index e91cee728..23ed23a0d 100644 --- a/tests/test_temperature.py +++ b/tests/test_temperature.py @@ -6,6 +6,8 @@ import numpy as np import pytest import xarray as xr +from packaging.version import Version +from pint import __version__ as __pint_version__ from xclim import atmos from xclim.core.calendar import percentile_doy @@ -828,7 +830,10 @@ def test_convert_units(self, open_dataset): tasmin.values[180, 1, 0] = np.nan with warnings.catch_warnings(): - warnings.simplefilter("error") + if Version(__pint_version__) < Version("0.24.0"): + warnings.simplefilter("always", DeprecationWarning) + else: + warnings.simplefilter("error") frzthw = atmos.daily_freezethaw_cycles( tasmin, tasmax, diff --git a/tests/test_units.py b/tests/test_units.py index 040d50dd0..5b8a0ac65 100644 --- a/tests/test_units.py +++ b/tests/test_units.py @@ -6,9 +6,9 @@ import pint.errors import pytest import xarray as xr -from cf_xarray import __version__ as __cfxr_version__ from dask import array as dsk from packaging.version import Version +from pint import __version__ as __pint_version__ from xclim import indices, set_options from xclim.core.units import ( @@ -144,11 +144,6 @@ def test_units2pint(self, pr_series): u = units2pint("1") assert pint2cfunits(u) == "1" - if Version(__cfxr_version__) < Version("0.9.3"): - assert pint2cfunits(u) == "" - else: - assert pint2cfunits(u) == "1" - def test_pint_multiply(self, pr_series): a = pr_series([1, 2, 3]) out = pint_multiply(a, 1 * units.days) @@ -338,7 +333,13 @@ def index( ("", "sum", "count", 365, "d"), ("", "sum", "count", 365, "d"), ("kg m-2", "var", "var", 0, "kg2 m-4"), - ("°C", "argmax", "doymax", 0, ("", "1")), # dependent on numpy/pint version + ( + "°C", + "argmax", + "doymax", + 0, + "1", + ), ( "°C", "sum", @@ -361,7 +362,7 @@ def test_to_agg_units(in_u, opfunc, op, exp, exp_u): np.testing.assert_allclose(out, exp) if isinstance(exp_u, tuple): - if Version(__cfxr_version__) < Version("0.9.3"): + if Version(__pint_version__) < Version("0.24.1"): assert out.attrs["units"] == exp_u[0] else: assert out.attrs["units"] == exp_u[1] diff --git a/tox.ini b/tox.ini index 05d1f3127..d88f67ca9 100644 --- a/tox.ini +++ b/tox.ini @@ -118,6 +118,7 @@ extras = deps = lmoments: lmoments3 numpy: numpy>=1.20,<2.0 + numpy: pint>=0.18,<0.24.0 sbck: pybind11 upstream: -r CI/requirements_upstream.txt install_command = python -m pip install --no-user {opts} {packages} diff --git a/xclim/__init__.py b/xclim/__init__.py index 25519cd94..946a80d78 100644 --- a/xclim/__init__.py +++ b/xclim/__init__.py @@ -13,7 +13,7 @@ __author__ = """Travis Logan""" __email__ = "logan.travis@ouranos.ca" -__version__ = "0.51.1-dev.11" +__version__ = "0.51.1-dev.12" with _resources.as_file(_resources.files("xclim.data")) as _module_data: diff --git a/xclim/cli.py b/xclim/cli.py index ebb35aba1..67a6da1eb 100644 --- a/xclim/cli.py +++ b/xclim/cli.py @@ -416,7 +416,7 @@ def get_command(self, ctx, name): @click.option( "--engine", help="Engine to use when opening the input dataset(s). " - "If not specified, xarrat decides.", + "If not specified, xarray decides.", ) @click.pass_context def cli(ctx, **kwargs): diff --git a/xclim/core/indicator.py b/xclim/core/indicator.py index 74777dbe9..3b68e09c2 100644 --- a/xclim/core/indicator.py +++ b/xclim/core/indicator.py @@ -1456,7 +1456,7 @@ def _postprocess(self, outs, das, params): and mask.time.size < outs[0].time.size ): mask = mask.reindex(time=outs[0].time, fill_value=True) - outs = [out.where(~mask) for out in outs] + outs = [out.where(np.logical_not(mask)) for out in outs] return outs diff --git a/xclim/core/units.py b/xclim/core/units.py index 183c66d4d..d6beefa89 100644 --- a/xclim/core/units.py +++ b/xclim/core/units.py @@ -184,7 +184,8 @@ def pint2cfunits(value: units.Quantity | units.Unit) -> str: if isinstance(value, (pint.Quantity, units.Quantity)): value = value.units - return f"{value:cf}" + # Force "1" if the formatted string is "" (pint < 0.24) + return f"{value:~cf}" or "1" def ensure_cf_units(ustr: str) -> str: