diff --git a/src/xsdba/base.py b/src/xsdba/base.py index cfe2ec1..598d981 100644 --- a/src/xsdba/base.py +++ b/src/xsdba/base.py @@ -5,18 +5,19 @@ from __future__ import annotations +import datetime as pydt from collections.abc import Sequence from inspect import _empty, signature # noqa from typing import Callable +import cftime import dask.array as dsk import jsonpickle import numpy as np import xarray as xr from boltons.funcutils import wraps + from xsdba.options import OPTIONS, SDBA_ENCODE_CF -import datetime as pydt -import cftime # ## Base class for the sdba module @@ -94,6 +95,7 @@ def set_dataset(self, ds: xr.Dataset) -> None: self.ds = ds self.ds.attrs[self._attribute] = jsonpickle.encode(self) + # XC put here to avoid circular import def uses_dask(*das: xr.DataArray | xr.Dataset) -> bool: """Evaluate whether dask is installed and array is loaded as a dask array. diff --git a/src/xsdba/testing.py b/src/xsdba/testing.py index 1ce7bda..e2b3a76 100644 --- a/src/xsdba/testing.py +++ b/src/xsdba/testing.py @@ -1,21 +1,21 @@ """Testing utilities for xsdba.""" -import warnings +import collections import hashlib import logging import os +import warnings from pathlib import Path from urllib.error import HTTPError, URLError -from urllib.request import urlopen, urlretrieve from urllib.parse import urljoin, urlparse +from urllib.request import urlopen, urlretrieve import pandas as pd import xarray as xr from platformdirs import user_cache_dir from xarray import open_dataset as _open_dataset -import collections -__all__ = ["test_timeseries", "test_timelonlatseries"] +__all__ = ["test_timelonlatseries", "test_timeseries"] # keeping xclim-testdata for now, since it's still this on gitHub _default_cache_dir = Path(user_cache_dir("xclim-testdata")) @@ -47,6 +47,7 @@ except ImportError: SocketBlockedError = None + def test_timelonlatseries(values, name, start="2000-01-01"): """Create a DataArray with time, lon and lat dimensions.""" coords = collections.OrderedDict() @@ -81,6 +82,7 @@ def test_timelonlatseries(values, name, start="2000-01-01"): attrs=attrs, ) + # XC def test_timeseries( values, @@ -113,7 +115,8 @@ def file_md5_checksum(f_name): hash_md5.update(f.read()) return hash_md5.hexdigest() -# XC + +# XC def audit_url(url: str, context: str = None) -> str: """Check if the URL is well-formed. @@ -134,6 +137,7 @@ def audit_url(url: str, context: str = None) -> str: raise URLError(msg) return url + # XC (oh dear) def _get( fullname: Path, diff --git a/src/xsdba/utils.py b/src/xsdba/utils.py index 34b6a6b..4d2a6ad 100644 --- a/src/xsdba/utils.py +++ b/src/xsdba/utils.py @@ -9,7 +9,6 @@ from typing import Callable from warnings import warn - import numpy as np import xarray as xr from boltons.funcutils import wraps @@ -95,8 +94,6 @@ def ecdf(x: xr.DataArray, value: float, dim: str = "time") -> xr.DataArray: return (x <= value).sum(dim) / x.notnull().sum(dim) - - # XC def ensure_chunk_size(da: xr.DataArray, **minchunks: int) -> xr.DataArray: r"""Ensure that the input DataArray has chunks of at least the given size. diff --git a/tests/conftest.py b/tests/conftest.py index dc66a6b..b4039c0 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -21,7 +21,7 @@ from xsdba.testing import TESTDATA_BRANCH from xsdba.testing import open_dataset as _open_dataset -from xsdba.testing import test_timeseries, test_timelonlatseries +from xsdba.testing import test_timelonlatseries, test_timeseries # import xclim # from xclim import __version__ as __xclim_version__ @@ -93,6 +93,7 @@ def _open_session_scoped_file(file: str | os.PathLike, branch: str = TESTDATA_BR def timelonlatseries(): return test_timelonlatseries + @pytest.fixture def lat_series(): def _lat_series(values): diff --git a/tests/test_nbutils.py b/tests/test_nbutils.py index e67e98e..a539826 100644 --- a/tests/test_nbutils.py +++ b/tests/test_nbutils.py @@ -3,6 +3,7 @@ import numpy as np import pytest import xarray as xr + from xsdba import nbutils as nbu diff --git a/tests/test_utils.py b/tests/test_utils.py index c45d43a..aa0085d 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -10,7 +10,6 @@ from xsdba.base import Grouper - def test_ecdf(timelonlatseries, random): dist = norm(5, 2) r = dist.rvs(10000, random_state=random) @@ -66,9 +65,7 @@ def test_equally_spaced_nodes(): np.testing.assert_almost_equal(x[0], 0.5) -@pytest.mark.parametrize( - "interp,expi", [("nearest", 2.9), ("linear", 2.95), ("cubic", 2.95)] -) +@pytest.mark.parametrize("interp,expi", [("nearest", 2.9), ("linear", 2.95), ("cubic", 2.95)]) @pytest.mark.parametrize("extrap,expe", [("constant", 4.4), ("nan", np.NaN)]) def test_interp_on_quantiles_constant(interp, expi, extrap, expe): quantiles = np.linspace(0, 1, num=25) @@ -95,9 +92,7 @@ def test_interp_on_quantiles_constant(interp, expi, extrap, expe): yq = yq.expand_dims(lat=[1, 2, 3]) newx = newx.expand_dims(lat=[1, 2, 3]) - out = u.interp_on_quantiles( - newx, xq, yq, group="time", method=interp, extrapolation=extrap - ) + out = u.interp_on_quantiles(newx, xq, yq, group="time", method=interp, extrapolation=extrap) if np.isnan(expe): assert out.isel(time=0).isnull().all() @@ -108,9 +103,7 @@ def test_interp_on_quantiles_constant(interp, expi, extrap, expe): xq = xq.where(xq != 220) yq = yq.where(yq != 3) - out = u.interp_on_quantiles( - newx, xq, yq, group="time", method=interp, extrapolation=extrap - ) + out = u.interp_on_quantiles(newx, xq, yq, group="time", method=interp, extrapolation=extrap) if np.isnan(expe): assert out.isel(time=0).isnull().all() @@ -124,10 +117,7 @@ def test_interp_on_quantiles_monthly(random): t = xr.cftime_range("2000-01-01", "2030-12-31", freq="D", calendar="noleap") ref = xr.DataArray( ( - -20 * np.cos(2 * np.pi * t.dayofyear / 365) - + 2 * random.random(t.size) - + 273.15 - + 0.1 * (t - t[0]).days / 365 + -20 * np.cos(2 * np.pi * t.dayofyear / 365) + 2 * random.random(t.size) + 273.15 + 0.1 * (t - t[0]).days / 365 ), # "warming" of 1K per decade, dims=("time",), coords={"time": t}, @@ -135,10 +125,7 @@ def test_interp_on_quantiles_monthly(random): ) sim = xr.DataArray( ( - -18 * np.cos(2 * np.pi * t.dayofyear / 365) - + 2 * random.random(t.size) - + 273.15 - + 0.11 * (t - t[0]).days / 365 + -18 * np.cos(2 * np.pi * t.dayofyear / 365) + 2 * random.random(t.size) + 273.15 + 0.11 * (t - t[0]).days / 365 ), # "warming" of 1.1K per decade dims=("time",), coords={"time": t}, @@ -155,15 +142,11 @@ def test_interp_on_quantiles_monthly(random): af = u.get_correction(hist_q, ref_q, "+") for interp in ["nearest", "linear", "cubic"]: - afi = u.interp_on_quantiles( - sim, hist_q, af, group="time.month", method=interp, extrapolation="constant" - ) + afi = u.interp_on_quantiles(sim, hist_q, af, group="time.month", method=interp, extrapolation="constant") assert afi.isnull().sum("time") == 0, interp -@pytest.mark.parametrize( - "interp,expi", [("nearest", 2.9), ("linear", 2.95), ("cubic", 2.95)] -) +@pytest.mark.parametrize("interp,expi", [("nearest", 2.9), ("linear", 2.95), ("cubic", 2.95)]) @pytest.mark.parametrize("extrap,expe", [("constant", 4.4), ("nan", np.NaN)]) def test_interp_on_quantiles_constant_with_nan(interp, expi, extrap, expe): quantiles = np.linspace(0, 1, num=30) @@ -190,9 +173,7 @@ def test_interp_on_quantiles_constant_with_nan(interp, expi, extrap, expe): yq = yq.expand_dims(lat=[1, 2, 3]) newx = newx.expand_dims(lat=[1, 2, 3]) - out = u.interp_on_quantiles( - newx, xq, yq, group="time", method=interp, extrapolation=extrap - ) + out = u.interp_on_quantiles(newx, xq, yq, group="time", method=interp, extrapolation=extrap) if np.isnan(expe): assert out.isel(time=0).isnull().all() @@ -203,9 +184,7 @@ def test_interp_on_quantiles_constant_with_nan(interp, expi, extrap, expe): xq = xq.where(xq != 220) yq = yq.where(yq != 3) - out = u.interp_on_quantiles( - newx, xq, yq, group="time", method=interp, extrapolation=extrap - ) + out = u.interp_on_quantiles(newx, xq, yq, group="time", method=interp, extrapolation=extrap) if np.isnan(expe): assert out.isel(time=0).isnull().all()