- Update to 2024.7.0
* Add test for rechunking to a size string by @dcherian in #9117 * Update docstring in api.py for open_mfdataset(), clarifying "chunks" argument by @arthur-e in #9121 * Grouper refactor by @dcherian in #9122 * adjust repr tests to account for different platforms (#9127) by @mgorny in #9128 * Support duplicate dimensions in .chunk by @mraspaud in #9099 * Update zendoo badge link by @max-sixty in #9133 * Split out distributed writes in zarr docs by @max-sixty in #9132 * Improve to_zarr docs by @max-sixty in #9139 * groupby: remove some internal use of IndexVariable by @dcherian in #9123 * Improve zarr chunks docs by @max-sixty in #9140 * Include numbagg in type checks by @max-sixty in #9159 * Remove mypy exclusions for a couple more libraries by @max-sixty in #9160 * Add test for #9155 by @max-sixty in #9161 * switch to datetime unit "D" by @keewis in #9170 * Slightly improve DataTree repr by @shoyer in #9064 * Fix example code formatting for CachingFileManager by @djhoese in #9178 * Change np.core.defchararray to np.char (#9165) by @pont-us in #9166 * temporarily remove pydap from CI by @keewis in #9183 * also pin numpy in the all-but-dask CI by @keewis in #9184 * promote floating-point numeric datetimes to 64-bit before decoding by @keewis in #9182 * "source" encoding for datasets opened from fsspec objects by OBS-URL: https://build.opensuse.org/package/show/devel:languages:python:numeric/python-xarray?expand=0&rev=99
This commit is contained in:
commit
d3af5d70ef
23
.gitattributes
vendored
Normal file
23
.gitattributes
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
## Default LFS
|
||||
*.7z filter=lfs diff=lfs merge=lfs -text
|
||||
*.bsp filter=lfs diff=lfs merge=lfs -text
|
||||
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
||||
*.gem filter=lfs diff=lfs merge=lfs -text
|
||||
*.gz filter=lfs diff=lfs merge=lfs -text
|
||||
*.jar filter=lfs diff=lfs merge=lfs -text
|
||||
*.lz filter=lfs diff=lfs merge=lfs -text
|
||||
*.lzma filter=lfs diff=lfs merge=lfs -text
|
||||
*.obscpio filter=lfs diff=lfs merge=lfs -text
|
||||
*.oxt filter=lfs diff=lfs merge=lfs -text
|
||||
*.pdf filter=lfs diff=lfs merge=lfs -text
|
||||
*.png filter=lfs diff=lfs merge=lfs -text
|
||||
*.rpm filter=lfs diff=lfs merge=lfs -text
|
||||
*.tbz filter=lfs diff=lfs merge=lfs -text
|
||||
*.tbz2 filter=lfs diff=lfs merge=lfs -text
|
||||
*.tgz filter=lfs diff=lfs merge=lfs -text
|
||||
*.ttf filter=lfs diff=lfs merge=lfs -text
|
||||
*.txz filter=lfs diff=lfs merge=lfs -text
|
||||
*.whl filter=lfs diff=lfs merge=lfs -text
|
||||
*.xz filter=lfs diff=lfs merge=lfs -text
|
||||
*.zip filter=lfs diff=lfs merge=lfs -text
|
||||
*.zst filter=lfs diff=lfs merge=lfs -text
|
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
.osc
|
3
_multibuild
Normal file
3
_multibuild
Normal file
@ -0,0 +1,3 @@
|
||||
<multibuild>
|
||||
<package>test</package>
|
||||
</multibuild>
|
20
local_dataset.patch
Normal file
20
local_dataset.patch
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
xarray/tutorial.py | 5 ++++-
|
||||
1 file changed, 4 insertions(+), 1 deletion(-)
|
||||
|
||||
Index: xarray-2024.05.0/xarray/tutorial.py
|
||||
===================================================================
|
||||
--- xarray-2024.05.0.orig/xarray/tutorial.py
|
||||
+++ xarray-2024.05.0/xarray/tutorial.py
|
||||
@@ -158,7 +158,10 @@ def open_dataset(
|
||||
url = f"{base_url}/raw/{version}/{path.name}"
|
||||
|
||||
# retrieve the file
|
||||
- filepath = pooch.retrieve(url=url, known_hash=None, path=cache_dir)
|
||||
+ fname = pathlib.Path(cache_dir, path).expanduser()
|
||||
+ if not fname.exists():
|
||||
+ fname = None
|
||||
+ filepath = pooch.retrieve(url=url, fname=fname, known_hash=None, path=cache_dir)
|
||||
ds = _open_dataset(filepath, engine=engine, **kws)
|
||||
if not cache:
|
||||
ds = ds.load()
|
3727
python-xarray.changes
Normal file
3727
python-xarray.changes
Normal file
File diff suppressed because it is too large
Load Diff
224
python-xarray.spec
Normal file
224
python-xarray.spec
Normal file
@ -0,0 +1,224 @@
|
||||
#
|
||||
# spec file for package python-xarray
|
||||
#
|
||||
# Copyright (c) 2024 SUSE LLC
|
||||
#
|
||||
# All modifications and additions to the file contributed by third parties
|
||||
# remain the property of their copyright owners, unless otherwise agreed
|
||||
# upon. The license for this file, and modifications and additions to the
|
||||
# file, is the same license as for the pristine package itself (unless the
|
||||
# license for the pristine package is not an Open Source License, in which
|
||||
# case the license is the MIT License). An "Open Source License" is a
|
||||
# license that conforms to the Open Source Definition (Version 1.9)
|
||||
# published by the Open Source Initiative.
|
||||
|
||||
# Please submit bugfixes or comments via https://bugs.opensuse.org/
|
||||
#
|
||||
|
||||
|
||||
%global flavor @BUILD_FLAVOR@%{nil}
|
||||
%if "%{flavor}" == "test"
|
||||
%bcond_without test
|
||||
%define psuffix -test
|
||||
%else
|
||||
%bcond_with test
|
||||
%define psuffix %{nil}
|
||||
%endif
|
||||
|
||||
%define ghversion 2024.07.0
|
||||
|
||||
%{?sle15_python_module_pythons}
|
||||
Name: python-xarray%{psuffix}
|
||||
Version: 2024.7.0
|
||||
Release: 0
|
||||
Summary: N-D labeled arrays and datasets in Python
|
||||
License: Apache-2.0
|
||||
URL: https://github.com/pydata/xarray
|
||||
Source: https://github.com/pydata/xarray/archive/refs/tags/v%{ghversion}.tar.gz#/xarray-%{ghversion}-gh.tar.gz
|
||||
# PATCH-FEATURE-UPSTREAM local_dataset.patch gh#pydata/xarray#5377 mcepl@suse.com
|
||||
# fix xr.tutorial.open_dataset to work with the preloaded cache.
|
||||
Patch0: local_dataset.patch
|
||||
# PATCH-FIX-UPSTREAM xarray-pr9321-dasktests.patch gh#pydata/xarray#9321
|
||||
Patch1: xarray-pr9321-dasktests.patch
|
||||
# PATCH-FIX-UPSTREAM xarray-pr9356-dasktests.patch gh#pydata/xarray#9356
|
||||
Patch2: xarray-pr9356-dasktests.patch
|
||||
# PATCH-FIX-UPSTREAM xarray-pr9403-np2.1-scalar.patch gh#pydata/xarray#9403
|
||||
Patch3: xarray-pr9403-np2.1-scalar.patch
|
||||
BuildRequires: %{python_module base >= 3.9}
|
||||
BuildRequires: %{python_module pip}
|
||||
BuildRequires: %{python_module setuptools_scm}
|
||||
BuildRequires: %{python_module setuptools}
|
||||
BuildRequires: %{python_module wheel}
|
||||
BuildRequires: fdupes
|
||||
BuildRequires: python-rpm-macros
|
||||
Requires: python-numpy >= 1.23
|
||||
Requires: python-packaging >= 23.1
|
||||
Requires: python-pandas >= 2
|
||||
Obsoletes: python-xray <= 0.7
|
||||
BuildArch: noarch
|
||||
%if %{with test}
|
||||
BuildRequires: %{python_module xarray-complete = %{version}}
|
||||
%endif
|
||||
# /SECTION
|
||||
%python_subpackages
|
||||
|
||||
%description
|
||||
xarray (formerly xray) is a python-pandas-like and pandas-compatible
|
||||
toolkit for analytics on multi-dimensional arrays. It provides
|
||||
N-dimensional variants of the python-pandas labeled data structures,
|
||||
rather than the tabular data that pandas uses.
|
||||
|
||||
The Common Data Model for self-describing scientific data is used.
|
||||
The dataset is an in-memory representation of a netCDF file.
|
||||
|
||||
%package accel
|
||||
# for minimum versions, check ci/requirements/min-all-deps.yml
|
||||
Summary: The python xarray[accel] extra
|
||||
Requires: python-Bottleneck
|
||||
Requires: python-opt-einsum
|
||||
Requires: python-scipy
|
||||
Requires: python-xarray = %{version}
|
||||
# not available yet
|
||||
Recommends: python-flox
|
||||
Recommends: python-numbagg
|
||||
|
||||
%description accel
|
||||
The [accel] extra for xarray, N-D labeled arrays and datasets in Python
|
||||
Except flox and numbagg, because they are not packaged yet.
|
||||
Use `pip-%{python_bin_suffix} --user install flox numbagg` to install from PyPI, if needed.
|
||||
|
||||
%package complete
|
||||
Summary: The python xarray[complete] extra
|
||||
Requires: python-xarray = %{version}
|
||||
Requires: python-xarray-accel = %{version}
|
||||
Requires: python-xarray-dev = %{version}
|
||||
Requires: python-xarray-io = %{version}
|
||||
Requires: python-xarray-parallel = %{version}
|
||||
Requires: python-xarray-viz = %{version}
|
||||
|
||||
%description complete
|
||||
The [complete] extra for xarray, N-D labeled arrays and datasets in Python
|
||||
|
||||
%package dev
|
||||
Summary: The python xarray[dev] extra
|
||||
Requires: python-hypothesis
|
||||
Requires: python-pytest
|
||||
Requires: python-pytest-cov
|
||||
Requires: python-pytest-env
|
||||
Requires: python-pytest-timeout
|
||||
Requires: python-pytest-xdist
|
||||
Requires: python-ruff
|
||||
Requires: python-xarray = %{version}
|
||||
Requires: python-xarray-complete = %{version}
|
||||
# Not available and not really useful for us
|
||||
Recommends: python-pre-commit
|
||||
|
||||
%description dev
|
||||
The [dev] extra for xarray, N-D labeled arrays and datasets in Python
|
||||
Except pre-commit, Use `pip-%{python_bin_suffix} --user install pre-commit` to install, if needed.
|
||||
|
||||
%package io
|
||||
Summary: The python xarray[io] extra
|
||||
Requires: python-cftime
|
||||
Requires: python-fsspec
|
||||
Requires: python-h5netcdf
|
||||
Requires: python-netCDF4
|
||||
Requires: python-pooch
|
||||
Requires: python-scipy
|
||||
Requires: python-xarray = %{version}
|
||||
Requires: python-zarr
|
||||
|
||||
%description io
|
||||
The [io] extra for xarray, N-D labeled arrays and datasets in Python
|
||||
|
||||
%package parallel
|
||||
Summary: The python xarray[parallel] extra
|
||||
Requires: python-dask-complete
|
||||
Requires: python-xarray = %{version}
|
||||
|
||||
%description parallel
|
||||
The [parallel] extra for xarray, N-D labeled arrays and datasets in Python
|
||||
|
||||
%package viz
|
||||
Summary: The python xarray[viz] extra
|
||||
Requires: python-matplotlib
|
||||
Requires: python-seaborn
|
||||
Requires: python-xarray = %{version}
|
||||
# Not available yet
|
||||
Recommends: python-nc-time-axis
|
||||
|
||||
%description viz
|
||||
The [viz] extra for xarray, N-D labeled arrays and datasets in Python
|
||||
|
||||
Except nc-time-axis, because it's not packaged yet.
|
||||
Use `pip-%{python_bin_suffix} --user install nc-time-axis` to install from PyPI, if needed.
|
||||
|
||||
%prep
|
||||
%autosetup -p1 -n xarray-%{ghversion}
|
||||
chmod -x xarray/util/print_versions.py
|
||||
|
||||
%build
|
||||
%if !%{with test}
|
||||
%pyproject_wheel
|
||||
%endif
|
||||
|
||||
%install
|
||||
%if !%{with test}
|
||||
%pyproject_install
|
||||
%python_expand %fdupes %{buildroot}%{$python_sitelib}
|
||||
%endif
|
||||
|
||||
%if %{with test}
|
||||
%check
|
||||
# obs file open race conditions?
|
||||
donttest="(test_open_mfdataset_manyfiles and (h5netcdf or netCDF4))"
|
||||
if [ $(getconf LONG_BIT) -eq 32 ]; then
|
||||
# https://github.com/pydata/xarray/issues/5341
|
||||
# https://github.com/pydata/xarray/issues/5375
|
||||
# still precision problems in 2022.11.0
|
||||
donttest="$donttest or (test_interpolate_chunk_advanced and linear)"
|
||||
# tests for 64bit types
|
||||
donttest="$donttest or TestZarrDictStore or TestZarrDirectoryStore or TestZarrWriteEmpty"
|
||||
donttest="$donttest or test_repr_multiindex or test_array_repr_dtypes_unix"
|
||||
fi
|
||||
# h5py was built without ROS3 support, can't use ros3 driver
|
||||
donttest="$donttest or TestH5NetCDFDataRos3Driver"
|
||||
# NetCDF4 fails with these unsupported drivers
|
||||
donttest="$donttest or (TestNetCDF4 and test_compression_encoding and (szip or zstd or blosc_lz or blosc_zlib))"
|
||||
|
||||
%pytest -n auto -rsEf -k "not ($donttest)" xarray
|
||||
%endif
|
||||
|
||||
%if !%{with test}
|
||||
%files %{python_files}
|
||||
%doc README.md
|
||||
%license LICENSE licenses/
|
||||
%{python_sitelib}/xarray
|
||||
%{python_sitelib}/xarray-%{version}.dist-info
|
||||
|
||||
%files %{python_files accel}
|
||||
%doc README.md
|
||||
%license LICENSE
|
||||
|
||||
%files %{python_files complete}
|
||||
%doc README.md
|
||||
%license LICENSE
|
||||
|
||||
%files %{python_files dev}
|
||||
%doc README.md
|
||||
%license LICENSE
|
||||
|
||||
%files %{python_files io}
|
||||
%doc README.md
|
||||
%license LICENSE
|
||||
|
||||
%files %{python_files parallel}
|
||||
%doc README.md
|
||||
%license LICENSE
|
||||
|
||||
%files %{python_files viz}
|
||||
%doc README.md
|
||||
%license LICENSE
|
||||
%endif
|
||||
|
||||
%changelog
|
3
xarray-2024.05.0-gh.tar.gz
Normal file
3
xarray-2024.05.0-gh.tar.gz
Normal file
@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:604a801c7bba96524b09b993b159ae998e0987627949b667c884f65895939f11
|
||||
size 3739324
|
3
xarray-2024.07.0-gh.tar.gz
Normal file
3
xarray-2024.07.0-gh.tar.gz
Normal file
@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:3db5160a699a7731fba26b42aef3f175ca3a6adfe5593bebd0b7af90e55d747d
|
||||
size 3756547
|
772
xarray-pr8854-np2.patch
Normal file
772
xarray-pr8854-np2.patch
Normal file
@ -0,0 +1,772 @@
|
||||
From e066a6c559e9d7f31c359ea95da42d0e45c585ce Mon Sep 17 00:00:00 2001
|
||||
From: Justus Magin <keewis@posteo.de>
|
||||
Date: Tue, 19 Mar 2024 11:32:32 +0100
|
||||
Subject: [PATCH 01/65] replace the use of `numpy.array_api` with
|
||||
`array_api_strict`
|
||||
|
||||
This would make it a dependency of `namedarray`, and not allow
|
||||
behavior that is allowed but not required by the array API standard. Otherwise we can:
|
||||
- use the main `numpy` namespace
|
||||
- use `array_api_compat` (would also be a new dependency) to allow
|
||||
optional behavior
|
||||
---
|
||||
xarray/namedarray/_array_api.py | 9 ---------
|
||||
1 file changed, 9 deletions(-)
|
||||
|
||||
Index: xarray-2024.05.0/xarray/namedarray/_array_api.py
|
||||
===================================================================
|
||||
--- xarray-2024.05.0.orig/xarray/namedarray/_array_api.py
|
||||
+++ xarray-2024.05.0/xarray/namedarray/_array_api.py
|
||||
@@ -1,6 +1,5 @@
|
||||
from __future__ import annotations
|
||||
|
||||
-import warnings
|
||||
from types import ModuleType
|
||||
from typing import Any
|
||||
|
||||
@@ -21,14 +20,6 @@ from xarray.namedarray._typing import (
|
||||
)
|
||||
from xarray.namedarray.core import NamedArray
|
||||
|
||||
-with warnings.catch_warnings():
|
||||
- warnings.filterwarnings(
|
||||
- "ignore",
|
||||
- r"The numpy.array_api submodule is still experimental",
|
||||
- category=UserWarning,
|
||||
- )
|
||||
- import numpy.array_api as nxp # noqa: F401
|
||||
-
|
||||
|
||||
def _get_data_namespace(x: NamedArray[Any, Any]) -> ModuleType:
|
||||
if isinstance(x._data, _arrayapi):
|
||||
@@ -68,13 +59,13 @@ def astype(
|
||||
|
||||
Examples
|
||||
--------
|
||||
- >>> narr = NamedArray(("x",), nxp.asarray([1.5, 2.5]))
|
||||
+ >>> narr = NamedArray(("x",), np.asarray([1.5, 2.5]))
|
||||
>>> narr
|
||||
<xarray.NamedArray (x: 2)> Size: 16B
|
||||
- Array([1.5, 2.5], dtype=float64)
|
||||
+ array([1.5, 2.5])
|
||||
>>> astype(narr, np.dtype(np.int32))
|
||||
<xarray.NamedArray (x: 2)> Size: 8B
|
||||
- Array([1, 2], dtype=int32)
|
||||
+ array([1, 2], dtype=int32)
|
||||
"""
|
||||
if isinstance(x._data, _arrayapi):
|
||||
xp = x._data.__array_namespace__()
|
||||
@@ -109,7 +100,7 @@ def imag(
|
||||
|
||||
Examples
|
||||
--------
|
||||
- >>> narr = NamedArray(("x",), np.asarray([1.0 + 2j, 2 + 4j])) # TODO: Use nxp
|
||||
+ >>> narr = NamedArray(("x",), np.asarray([1.0 + 2j, 2 + 4j]))
|
||||
>>> imag(narr)
|
||||
<xarray.NamedArray (x: 2)> Size: 16B
|
||||
array([2., 4.])
|
||||
@@ -141,7 +132,7 @@ def real(
|
||||
|
||||
Examples
|
||||
--------
|
||||
- >>> narr = NamedArray(("x",), np.asarray([1.0 + 2j, 2 + 4j])) # TODO: Use nxp
|
||||
+ >>> narr = NamedArray(("x",), np.asarray([1.0 + 2j, 2 + 4j]))
|
||||
>>> real(narr)
|
||||
<xarray.NamedArray (x: 2)> Size: 16B
|
||||
array([1., 2.])
|
||||
@@ -179,15 +170,15 @@ def expand_dims(
|
||||
|
||||
Examples
|
||||
--------
|
||||
- >>> x = NamedArray(("x", "y"), nxp.asarray([[1.0, 2.0], [3.0, 4.0]]))
|
||||
+ >>> x = NamedArray(("x", "y"), np.asarray([[1.0, 2.0], [3.0, 4.0]]))
|
||||
>>> expand_dims(x)
|
||||
<xarray.NamedArray (dim_2: 1, x: 2, y: 2)> Size: 32B
|
||||
- Array([[[1., 2.],
|
||||
- [3., 4.]]], dtype=float64)
|
||||
+ array([[[1., 2.],
|
||||
+ [3., 4.]]])
|
||||
>>> expand_dims(x, dim="z")
|
||||
<xarray.NamedArray (z: 1, x: 2, y: 2)> Size: 32B
|
||||
- Array([[[1., 2.],
|
||||
- [3., 4.]]], dtype=float64)
|
||||
+ array([[[1., 2.],
|
||||
+ [3., 4.]]])
|
||||
"""
|
||||
xp = _get_data_namespace(x)
|
||||
dims = x.dims
|
||||
Index: xarray-2024.05.0/xarray/tests/__init__.py
|
||||
===================================================================
|
||||
--- xarray-2024.05.0.orig/xarray/tests/__init__.py
|
||||
+++ xarray-2024.05.0/xarray/tests/__init__.py
|
||||
@@ -147,9 +147,10 @@ has_numbagg_or_bottleneck = has_numbagg
|
||||
requires_numbagg_or_bottleneck = pytest.mark.skipif(
|
||||
not has_scipy_or_netCDF4, reason="requires scipy or netCDF4"
|
||||
)
|
||||
-has_numpy_array_api, requires_numpy_array_api = _importorskip("numpy", "1.26.0")
|
||||
has_numpy_2, requires_numpy_2 = _importorskip("numpy", "2.0.0")
|
||||
|
||||
+has_array_api_strict, requires_array_api_strict = _importorskip("array_api_strict")
|
||||
+
|
||||
|
||||
def _importorskip_h5netcdf_ros3():
|
||||
try:
|
||||
Index: xarray-2024.05.0/xarray/tests/test_array_api.py
|
||||
===================================================================
|
||||
--- xarray-2024.05.0.orig/xarray/tests/test_array_api.py
|
||||
+++ xarray-2024.05.0/xarray/tests/test_array_api.py
|
||||
@@ -6,20 +6,9 @@ import xarray as xr
|
||||
from xarray.testing import assert_equal
|
||||
|
||||
np = pytest.importorskip("numpy", minversion="1.22")
|
||||
+xp = pytest.importorskip("array_api_strict")
|
||||
|
||||
-try:
|
||||
- import warnings
|
||||
-
|
||||
- with warnings.catch_warnings():
|
||||
- warnings.simplefilter("ignore")
|
||||
-
|
||||
- import numpy.array_api as xp
|
||||
- from numpy.array_api._array_object import Array
|
||||
-except ImportError:
|
||||
- # for `numpy>=2.0`
|
||||
- xp = pytest.importorskip("array_api_strict")
|
||||
-
|
||||
- from array_api_strict._array_object import Array # type: ignore[no-redef]
|
||||
+from array_api_strict._array_object import Array # isort:skip # type: ignore[no-redef]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -65,8 +54,8 @@ def test_aggregation_skipna(arrays) -> N
|
||||
def test_astype(arrays) -> None:
|
||||
np_arr, xp_arr = arrays
|
||||
expected = np_arr.astype(np.int64)
|
||||
- actual = xp_arr.astype(np.int64)
|
||||
- assert actual.dtype == np.int64
|
||||
+ actual = xp_arr.astype(xp.int64)
|
||||
+ assert actual.dtype == xp.int64
|
||||
assert isinstance(actual.data, Array)
|
||||
assert_equal(actual, expected)
|
||||
|
||||
@@ -118,8 +107,10 @@ def test_indexing(arrays: tuple[xr.DataA
|
||||
|
||||
def test_properties(arrays: tuple[xr.DataArray, xr.DataArray]) -> None:
|
||||
np_arr, xp_arr = arrays
|
||||
- assert np_arr.nbytes == np_arr.data.nbytes
|
||||
- assert xp_arr.nbytes == np_arr.data.nbytes
|
||||
+
|
||||
+ expected = np_arr.data.nbytes
|
||||
+ assert np_arr.nbytes == expected
|
||||
+ assert xp_arr.nbytes == expected
|
||||
|
||||
|
||||
def test_reorganizing_operation(arrays: tuple[xr.DataArray, xr.DataArray]) -> None:
|
||||
Index: xarray-2024.05.0/xarray/tests/test_namedarray.py
|
||||
===================================================================
|
||||
--- xarray-2024.05.0.orig/xarray/tests/test_namedarray.py
|
||||
+++ xarray-2024.05.0/xarray/tests/test_namedarray.py
|
||||
@@ -1,7 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
-import warnings
|
||||
from abc import abstractmethod
|
||||
from collections.abc import Mapping
|
||||
from typing import TYPE_CHECKING, Any, Generic, cast, overload
|
||||
@@ -79,6 +78,17 @@ class CustomArrayIndexable(
|
||||
return np
|
||||
|
||||
|
||||
+def check_duck_array_typevar(a: duckarray[Any, _DType]) -> duckarray[Any, _DType]:
|
||||
+ # Mypy checks a is valid:
|
||||
+ b: duckarray[Any, _DType] = a
|
||||
+
|
||||
+ # Runtime check if valid:
|
||||
+ if isinstance(b, _arrayfunction_or_api):
|
||||
+ return b
|
||||
+ else:
|
||||
+ raise TypeError(f"a ({type(a)}) is not a valid _arrayfunction or _arrayapi")
|
||||
+
|
||||
+
|
||||
class NamedArraySubclassobjects:
|
||||
@pytest.fixture
|
||||
def target(self, data: np.ndarray[Any, Any]) -> Any:
|
||||
@@ -328,48 +338,27 @@ class TestNamedArray(NamedArraySubclasso
|
||||
named_array.dims = new_dims
|
||||
assert named_array.dims == tuple(new_dims)
|
||||
|
||||
- def test_duck_array_class(
|
||||
- self,
|
||||
- ) -> None:
|
||||
- def test_duck_array_typevar(
|
||||
- a: duckarray[Any, _DType],
|
||||
- ) -> duckarray[Any, _DType]:
|
||||
- # Mypy checks a is valid:
|
||||
- b: duckarray[Any, _DType] = a
|
||||
-
|
||||
- # Runtime check if valid:
|
||||
- if isinstance(b, _arrayfunction_or_api):
|
||||
- return b
|
||||
- else:
|
||||
- raise TypeError(
|
||||
- f"a ({type(a)}) is not a valid _arrayfunction or _arrayapi"
|
||||
- )
|
||||
-
|
||||
+ def test_duck_array_class(self) -> None:
|
||||
numpy_a: NDArray[np.int64]
|
||||
numpy_a = np.array([2.1, 4], dtype=np.dtype(np.int64))
|
||||
- test_duck_array_typevar(numpy_a)
|
||||
+ check_duck_array_typevar(numpy_a)
|
||||
|
||||
masked_a: np.ma.MaskedArray[Any, np.dtype[np.int64]]
|
||||
masked_a = np.ma.asarray([2.1, 4], dtype=np.dtype(np.int64)) # type: ignore[no-untyped-call]
|
||||
- test_duck_array_typevar(masked_a)
|
||||
+ check_duck_array_typevar(masked_a)
|
||||
|
||||
custom_a: CustomArrayIndexable[Any, np.dtype[np.int64]]
|
||||
custom_a = CustomArrayIndexable(numpy_a)
|
||||
- test_duck_array_typevar(custom_a)
|
||||
+ check_duck_array_typevar(custom_a)
|
||||
|
||||
+ def test_duck_array_class_array_api(self) -> None:
|
||||
# Test numpy's array api:
|
||||
- with warnings.catch_warnings():
|
||||
- warnings.filterwarnings(
|
||||
- "ignore",
|
||||
- r"The numpy.array_api submodule is still experimental",
|
||||
- category=UserWarning,
|
||||
- )
|
||||
- import numpy.array_api as nxp
|
||||
+ nxp = pytest.importorskip("array_api_strict", minversion="1.0")
|
||||
|
||||
# TODO: nxp doesn't use dtype typevars, so can only use Any for the moment:
|
||||
arrayapi_a: duckarray[Any, Any] # duckarray[Any, np.dtype[np.int64]]
|
||||
- arrayapi_a = nxp.asarray([2.1, 4], dtype=np.dtype(np.int64))
|
||||
- test_duck_array_typevar(arrayapi_a)
|
||||
+ arrayapi_a = nxp.asarray([2.1, 4], dtype=nxp.int64)
|
||||
+ check_duck_array_typevar(arrayapi_a)
|
||||
|
||||
def test_new_namedarray(self) -> None:
|
||||
dtype_float = np.dtype(np.float32)
|
||||
Index: xarray-2024.05.0/xarray/tests/test_strategies.py
|
||||
===================================================================
|
||||
--- xarray-2024.05.0.orig/xarray/tests/test_strategies.py
|
||||
+++ xarray-2024.05.0/xarray/tests/test_strategies.py
|
||||
@@ -1,6 +1,9 @@
|
||||
+import warnings
|
||||
+
|
||||
import numpy as np
|
||||
import numpy.testing as npt
|
||||
import pytest
|
||||
+from packaging.version import Version
|
||||
|
||||
pytest.importorskip("hypothesis")
|
||||
# isort: split
|
||||
@@ -19,7 +22,6 @@ from xarray.testing.strategies import (
|
||||
unique_subset_of,
|
||||
variables,
|
||||
)
|
||||
-from xarray.tests import requires_numpy_array_api
|
||||
|
||||
ALLOWED_ATTRS_VALUES_TYPES = (int, bool, str, np.ndarray)
|
||||
|
||||
@@ -199,7 +201,6 @@ class TestVariablesStrategy:
|
||||
)
|
||||
)
|
||||
|
||||
- @requires_numpy_array_api
|
||||
@given(st.data())
|
||||
def test_make_strategies_namespace(self, data):
|
||||
"""
|
||||
@@ -208,16 +209,24 @@ class TestVariablesStrategy:
|
||||
We still want to generate dtypes not in the array API by default, but this checks we don't accidentally override
|
||||
the user's choice of dtypes with non-API-compliant ones.
|
||||
"""
|
||||
- from numpy import (
|
||||
- array_api as np_array_api, # requires numpy>=1.26.0, and we expect a UserWarning to be raised
|
||||
- )
|
||||
+ if Version(np.__version__) >= Version("2.0.0.dev0"):
|
||||
+ nxp = np
|
||||
+ else:
|
||||
+ # requires numpy>=1.26.0, and we expect a UserWarning to be raised
|
||||
+ with warnings.catch_warnings():
|
||||
+ warnings.filterwarnings(
|
||||
+ "ignore", category=UserWarning, message=".+See NEP 47."
|
||||
+ )
|
||||
+ from numpy import ( # type: ignore[no-redef,unused-ignore]
|
||||
+ array_api as nxp,
|
||||
+ )
|
||||
|
||||
- np_array_api_st = make_strategies_namespace(np_array_api)
|
||||
+ nxp_st = make_strategies_namespace(nxp)
|
||||
|
||||
data.draw(
|
||||
variables(
|
||||
- array_strategy_fn=np_array_api_st.arrays,
|
||||
- dtype=np_array_api_st.scalar_dtypes(),
|
||||
+ array_strategy_fn=nxp_st.arrays,
|
||||
+ dtype=nxp_st.scalar_dtypes(),
|
||||
)
|
||||
)
|
||||
|
||||
Index: xarray-2024.05.0/xarray/core/duck_array_ops.py
|
||||
===================================================================
|
||||
--- xarray-2024.05.0.orig/xarray/core/duck_array_ops.py
|
||||
+++ xarray-2024.05.0/xarray/core/duck_array_ops.py
|
||||
@@ -142,17 +142,25 @@ around.__doc__ = str.replace(
|
||||
|
||||
def isnull(data):
|
||||
data = asarray(data)
|
||||
- scalar_type = data.dtype.type
|
||||
- if issubclass(scalar_type, (np.datetime64, np.timedelta64)):
|
||||
+
|
||||
+ xp = get_array_namespace(data)
|
||||
+ scalar_type = data.dtype
|
||||
+ if dtypes.is_datetime_like(scalar_type):
|
||||
# datetime types use NaT for null
|
||||
# note: must check timedelta64 before integers, because currently
|
||||
# timedelta64 inherits from np.integer
|
||||
return isnat(data)
|
||||
- elif issubclass(scalar_type, np.inexact):
|
||||
+ elif dtypes.isdtype(scalar_type, ("real floating", "complex floating"), xp=xp):
|
||||
# float types use NaN for null
|
||||
xp = get_array_namespace(data)
|
||||
return xp.isnan(data)
|
||||
- elif issubclass(scalar_type, (np.bool_, np.integer, np.character, np.void)):
|
||||
+ elif dtypes.isdtype(scalar_type, ("bool", "integral"), xp=xp) or (
|
||||
+ isinstance(scalar_type, np.dtype)
|
||||
+ and (
|
||||
+ np.issubdtype(scalar_type, np.character)
|
||||
+ or np.issubdtype(scalar_type, np.void)
|
||||
+ )
|
||||
+ ):
|
||||
# these types cannot represent missing values
|
||||
return full_like(data, dtype=bool, fill_value=False)
|
||||
else:
|
||||
@@ -406,13 +414,22 @@ def _create_nan_agg_method(name, coerce_
|
||||
if invariant_0d and axis == ():
|
||||
return values
|
||||
|
||||
- values = asarray(values)
|
||||
+ xp = get_array_namespace(values)
|
||||
+ values = asarray(values, xp=xp)
|
||||
|
||||
- if coerce_strings and values.dtype.kind in "SU":
|
||||
+ if coerce_strings and dtypes.is_string(values.dtype):
|
||||
values = astype(values, object)
|
||||
|
||||
func = None
|
||||
- if skipna or (skipna is None and values.dtype.kind in "cfO"):
|
||||
+ if skipna or (
|
||||
+ skipna is None
|
||||
+ and (
|
||||
+ dtypes.isdtype(
|
||||
+ values.dtype, ("complex floating", "real floating"), xp=xp
|
||||
+ )
|
||||
+ or dtypes.is_object(values.dtype)
|
||||
+ )
|
||||
+ ):
|
||||
nanname = "nan" + name
|
||||
func = getattr(nanops, nanname)
|
||||
else:
|
||||
@@ -477,8 +494,8 @@ def _datetime_nanmin(array):
|
||||
- numpy nanmin() don't work on datetime64 (all versions at the moment of writing)
|
||||
- dask min() does not work on datetime64 (all versions at the moment of writing)
|
||||
"""
|
||||
- assert array.dtype.kind in "mM"
|
||||
dtype = array.dtype
|
||||
+ assert dtypes.is_datetime_like(dtype)
|
||||
# (NaT).astype(float) does not produce NaN...
|
||||
array = where(pandas_isnull(array), np.nan, array.astype(float))
|
||||
array = min(array, skipna=True)
|
||||
@@ -515,7 +532,7 @@ def datetime_to_numeric(array, offset=No
|
||||
"""
|
||||
# Set offset to minimum if not given
|
||||
if offset is None:
|
||||
- if array.dtype.kind in "Mm":
|
||||
+ if dtypes.is_datetime_like(array.dtype):
|
||||
offset = _datetime_nanmin(array)
|
||||
else:
|
||||
offset = min(array)
|
||||
@@ -527,7 +544,7 @@ def datetime_to_numeric(array, offset=No
|
||||
# This map_blocks call is for backwards compatibility.
|
||||
# dask == 2021.04.1 does not support subtracting object arrays
|
||||
# which is required for cftime
|
||||
- if is_duck_dask_array(array) and np.issubdtype(array.dtype, object):
|
||||
+ if is_duck_dask_array(array) and dtypes.is_object(array.dtype):
|
||||
array = array.map_blocks(lambda a, b: a - b, offset, meta=array._meta)
|
||||
else:
|
||||
array = array - offset
|
||||
@@ -537,11 +554,11 @@ def datetime_to_numeric(array, offset=No
|
||||
array = np.array(array)
|
||||
|
||||
# Convert timedelta objects to float by first converting to microseconds.
|
||||
- if array.dtype.kind in "O":
|
||||
+ if dtypes.is_object(array.dtype):
|
||||
return py_timedelta_to_float(array, datetime_unit or "ns").astype(dtype)
|
||||
|
||||
# Convert np.NaT to np.nan
|
||||
- elif array.dtype.kind in "mM":
|
||||
+ elif dtypes.is_datetime_like(array.dtype):
|
||||
# Convert to specified timedelta units.
|
||||
if datetime_unit:
|
||||
array = array / np.timedelta64(1, datetime_unit)
|
||||
@@ -641,7 +658,7 @@ def mean(array, axis=None, skipna=None,
|
||||
from xarray.core.common import _contains_cftime_datetimes
|
||||
|
||||
array = asarray(array)
|
||||
- if array.dtype.kind in "Mm":
|
||||
+ if dtypes.is_datetime_like(array.dtype):
|
||||
offset = _datetime_nanmin(array)
|
||||
|
||||
# xarray always uses np.datetime64[ns] for np.datetime64 data
|
||||
@@ -689,7 +706,9 @@ def cumsum(array, axis=None, **kwargs):
|
||||
|
||||
def first(values, axis, skipna=None):
|
||||
"""Return the first non-NA elements in this array along the given axis"""
|
||||
- if (skipna or skipna is None) and values.dtype.kind not in "iSU":
|
||||
+ if (skipna or skipna is None) and not (
|
||||
+ dtypes.isdtype(values.dtype, "signed integer") or dtypes.is_string(values.dtype)
|
||||
+ ):
|
||||
# only bother for dtypes that can hold NaN
|
||||
if is_chunked_array(values):
|
||||
return chunked_nanfirst(values, axis)
|
||||
@@ -700,7 +719,9 @@ def first(values, axis, skipna=None):
|
||||
|
||||
def last(values, axis, skipna=None):
|
||||
"""Return the last non-NA elements in this array along the given axis"""
|
||||
- if (skipna or skipna is None) and values.dtype.kind not in "iSU":
|
||||
+ if (skipna or skipna is None) and not (
|
||||
+ dtypes.isdtype(values.dtype, "signed integer") or dtypes.is_string(values.dtype)
|
||||
+ ):
|
||||
# only bother for dtypes that can hold NaN
|
||||
if is_chunked_array(values):
|
||||
return chunked_nanlast(values, axis)
|
||||
Index: xarray-2024.05.0/xarray/core/dtypes.py
|
||||
===================================================================
|
||||
--- xarray-2024.05.0.orig/xarray/core/dtypes.py
|
||||
+++ xarray-2024.05.0/xarray/core/dtypes.py
|
||||
@@ -4,8 +4,9 @@ import functools
|
||||
from typing import Any
|
||||
|
||||
import numpy as np
|
||||
+from pandas.api.types import is_extension_array_dtype
|
||||
|
||||
-from xarray.core import utils
|
||||
+from xarray.core import npcompat, utils
|
||||
|
||||
# Use as a sentinel value to indicate a dtype appropriate NA value.
|
||||
NA = utils.ReprObject("<NA>")
|
||||
@@ -60,22 +61,22 @@ def maybe_promote(dtype: np.dtype) -> tu
|
||||
# N.B. these casting rules should match pandas
|
||||
dtype_: np.typing.DTypeLike
|
||||
fill_value: Any
|
||||
- if np.issubdtype(dtype, np.floating):
|
||||
+ if isdtype(dtype, "real floating"):
|
||||
dtype_ = dtype
|
||||
fill_value = np.nan
|
||||
- elif np.issubdtype(dtype, np.timedelta64):
|
||||
+ elif isinstance(dtype, np.dtype) and np.issubdtype(dtype, np.timedelta64):
|
||||
# See https://github.com/numpy/numpy/issues/10685
|
||||
# np.timedelta64 is a subclass of np.integer
|
||||
# Check np.timedelta64 before np.integer
|
||||
fill_value = np.timedelta64("NaT")
|
||||
dtype_ = dtype
|
||||
- elif np.issubdtype(dtype, np.integer):
|
||||
+ elif isdtype(dtype, "integral"):
|
||||
dtype_ = np.float32 if dtype.itemsize <= 2 else np.float64
|
||||
fill_value = np.nan
|
||||
- elif np.issubdtype(dtype, np.complexfloating):
|
||||
+ elif isdtype(dtype, "complex floating"):
|
||||
dtype_ = dtype
|
||||
fill_value = np.nan + np.nan * 1j
|
||||
- elif np.issubdtype(dtype, np.datetime64):
|
||||
+ elif isinstance(dtype, np.dtype) and np.issubdtype(dtype, np.datetime64):
|
||||
dtype_ = dtype
|
||||
fill_value = np.datetime64("NaT")
|
||||
else:
|
||||
@@ -118,16 +119,16 @@ def get_pos_infinity(dtype, max_for_int=
|
||||
-------
|
||||
fill_value : positive infinity value corresponding to this dtype.
|
||||
"""
|
||||
- if issubclass(dtype.type, np.floating):
|
||||
+ if isdtype(dtype, "real floating"):
|
||||
return np.inf
|
||||
|
||||
- if issubclass(dtype.type, np.integer):
|
||||
+ if isdtype(dtype, "integral"):
|
||||
if max_for_int:
|
||||
return np.iinfo(dtype).max
|
||||
else:
|
||||
return np.inf
|
||||
|
||||
- if issubclass(dtype.type, np.complexfloating):
|
||||
+ if isdtype(dtype, "complex floating"):
|
||||
return np.inf + 1j * np.inf
|
||||
|
||||
return INF
|
||||
@@ -146,24 +147,66 @@ def get_neg_infinity(dtype, min_for_int=
|
||||
-------
|
||||
fill_value : positive infinity value corresponding to this dtype.
|
||||
"""
|
||||
- if issubclass(dtype.type, np.floating):
|
||||
+ if isdtype(dtype, "real floating"):
|
||||
return -np.inf
|
||||
|
||||
- if issubclass(dtype.type, np.integer):
|
||||
+ if isdtype(dtype, "integral"):
|
||||
if min_for_int:
|
||||
return np.iinfo(dtype).min
|
||||
else:
|
||||
return -np.inf
|
||||
|
||||
- if issubclass(dtype.type, np.complexfloating):
|
||||
+ if isdtype(dtype, "complex floating"):
|
||||
return -np.inf - 1j * np.inf
|
||||
|
||||
return NINF
|
||||
|
||||
|
||||
-def is_datetime_like(dtype):
|
||||
+def is_datetime_like(dtype) -> bool:
|
||||
"""Check if a dtype is a subclass of the numpy datetime types"""
|
||||
- return np.issubdtype(dtype, np.datetime64) or np.issubdtype(dtype, np.timedelta64)
|
||||
+ return _is_numpy_subdtype(dtype, (np.datetime64, np.timedelta64))
|
||||
+
|
||||
+
|
||||
+def is_object(dtype) -> bool:
|
||||
+ """Check if a dtype is object"""
|
||||
+ return _is_numpy_subdtype(dtype, object)
|
||||
+
|
||||
+
|
||||
+def is_string(dtype) -> bool:
|
||||
+ """Check if a dtype is a string dtype"""
|
||||
+ return _is_numpy_subdtype(dtype, (np.str_, np.character))
|
||||
+
|
||||
+
|
||||
+def _is_numpy_subdtype(dtype, kind) -> bool:
|
||||
+ if not isinstance(dtype, np.dtype):
|
||||
+ return False
|
||||
+
|
||||
+ kinds = kind if isinstance(kind, tuple) else (kind,)
|
||||
+ return any(np.issubdtype(dtype, kind) for kind in kinds)
|
||||
+
|
||||
+
|
||||
+def isdtype(dtype, kind: str | tuple[str, ...], xp=None) -> bool:
|
||||
+ """Compatibility wrapper for isdtype() from the array API standard.
|
||||
+
|
||||
+ Unlike xp.isdtype(), kind must be a string.
|
||||
+ """
|
||||
+ # TODO(shoyer): remove this wrapper when Xarray requires
|
||||
+ # numpy>=2 and pandas extensions arrays are implemented in
|
||||
+ # Xarray via the array API
|
||||
+ if not isinstance(kind, str) and not (
|
||||
+ isinstance(kind, tuple) and all(isinstance(k, str) for k in kind)
|
||||
+ ):
|
||||
+ raise TypeError(f"kind must be a string or a tuple of strings: {repr(kind)}")
|
||||
+
|
||||
+ if isinstance(dtype, np.dtype):
|
||||
+ return npcompat.isdtype(dtype, kind)
|
||||
+ elif is_extension_array_dtype(dtype):
|
||||
+ # we never want to match pandas extension array dtypes
|
||||
+ return False
|
||||
+ else:
|
||||
+ if xp is None:
|
||||
+ xp = np
|
||||
+ return xp.isdtype(dtype, kind)
|
||||
|
||||
|
||||
def result_type(
|
||||
@@ -184,12 +227,26 @@ def result_type(
|
||||
-------
|
||||
numpy.dtype for the result.
|
||||
"""
|
||||
- types = {np.result_type(t).type for t in arrays_and_dtypes}
|
||||
+ from xarray.core.duck_array_ops import get_array_namespace
|
||||
+
|
||||
+ # TODO(shoyer): consider moving this logic into get_array_namespace()
|
||||
+ # or another helper function.
|
||||
+ namespaces = {get_array_namespace(t) for t in arrays_and_dtypes}
|
||||
+ non_numpy = namespaces - {np}
|
||||
+ if non_numpy:
|
||||
+ [xp] = non_numpy
|
||||
+ else:
|
||||
+ xp = np
|
||||
+
|
||||
+ types = {xp.result_type(t) for t in arrays_and_dtypes}
|
||||
|
||||
- for left, right in PROMOTE_TO_OBJECT:
|
||||
- if any(issubclass(t, left) for t in types) and any(
|
||||
- issubclass(t, right) for t in types
|
||||
- ):
|
||||
- return np.dtype(object)
|
||||
+ if any(isinstance(t, np.dtype) for t in types):
|
||||
+ # only check if there's numpy dtypes – the array API does not
|
||||
+ # define the types we're checking for
|
||||
+ for left, right in PROMOTE_TO_OBJECT:
|
||||
+ if any(np.issubdtype(t, left) for t in types) and any(
|
||||
+ np.issubdtype(t, right) for t in types
|
||||
+ ):
|
||||
+ return xp.dtype(object)
|
||||
|
||||
- return np.result_type(*arrays_and_dtypes)
|
||||
+ return xp.result_type(*arrays_and_dtypes)
|
||||
Index: xarray-2024.05.0/xarray/namedarray/core.py
|
||||
===================================================================
|
||||
--- xarray-2024.05.0.orig/xarray/namedarray/core.py
|
||||
+++ xarray-2024.05.0/xarray/namedarray/core.py
|
||||
@@ -470,10 +470,28 @@ class NamedArray(NamedArrayAggregations,
|
||||
If the underlying data array does not include ``nbytes``, estimates
|
||||
the bytes consumed based on the ``size`` and ``dtype``.
|
||||
"""
|
||||
+ from xarray.namedarray._array_api import _get_data_namespace
|
||||
+
|
||||
if hasattr(self._data, "nbytes"):
|
||||
return self._data.nbytes # type: ignore[no-any-return]
|
||||
+
|
||||
+ if hasattr(self.dtype, "itemsize"):
|
||||
+ itemsize = self.dtype.itemsize
|
||||
+ elif isinstance(self._data, _arrayapi):
|
||||
+ xp = _get_data_namespace(self)
|
||||
+
|
||||
+ if xp.isdtype(self.dtype, "bool"):
|
||||
+ itemsize = 1
|
||||
+ elif xp.isdtype(self.dtype, "integral"):
|
||||
+ itemsize = xp.iinfo(self.dtype).bits // 8
|
||||
+ else:
|
||||
+ itemsize = xp.finfo(self.dtype).bits // 8
|
||||
else:
|
||||
- return self.size * self.dtype.itemsize
|
||||
+ raise TypeError(
|
||||
+ "cannot compute the number of bytes (no array API nor nbytes / itemsize)"
|
||||
+ )
|
||||
+
|
||||
+ return self.size * itemsize
|
||||
|
||||
@property
|
||||
def dims(self) -> _Dims:
|
||||
Index: xarray-2024.05.0/xarray/tests/test_dtypes.py
|
||||
===================================================================
|
||||
--- xarray-2024.05.0.orig/xarray/tests/test_dtypes.py
|
||||
+++ xarray-2024.05.0/xarray/tests/test_dtypes.py
|
||||
@@ -4,6 +4,18 @@ import numpy as np
|
||||
import pytest
|
||||
|
||||
from xarray.core import dtypes
|
||||
+from xarray.tests import requires_array_api_strict
|
||||
+
|
||||
+try:
|
||||
+ import array_api_strict
|
||||
+except ImportError:
|
||||
+
|
||||
+ class DummyArrayAPINamespace:
|
||||
+ bool = None
|
||||
+ int32 = None
|
||||
+ float64 = None
|
||||
+
|
||||
+ array_api_strict = DummyArrayAPINamespace
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@@ -58,7 +70,6 @@ def test_inf(obj) -> None:
|
||||
@pytest.mark.parametrize(
|
||||
"kind, expected",
|
||||
[
|
||||
- ("a", (np.dtype("O"), "nan")), # dtype('S')
|
||||
("b", (np.float32, "nan")), # dtype('int8')
|
||||
("B", (np.float32, "nan")), # dtype('uint8')
|
||||
("c", (np.dtype("O"), "nan")), # dtype('S1')
|
||||
@@ -98,3 +109,54 @@ def test_nat_types_membership() -> None:
|
||||
assert np.datetime64("NaT").dtype in dtypes.NAT_TYPES
|
||||
assert np.timedelta64("NaT").dtype in dtypes.NAT_TYPES
|
||||
assert np.float64 not in dtypes.NAT_TYPES
|
||||
+
|
||||
+
|
||||
+@pytest.mark.parametrize(
|
||||
+ ["dtype", "kinds", "xp", "expected"],
|
||||
+ (
|
||||
+ (np.dtype("int32"), "integral", np, True),
|
||||
+ (np.dtype("float16"), "real floating", np, True),
|
||||
+ (np.dtype("complex128"), "complex floating", np, True),
|
||||
+ (np.dtype("U"), "numeric", np, False),
|
||||
+ pytest.param(
|
||||
+ array_api_strict.int32,
|
||||
+ "integral",
|
||||
+ array_api_strict,
|
||||
+ True,
|
||||
+ marks=requires_array_api_strict,
|
||||
+ id="array_api-int",
|
||||
+ ),
|
||||
+ pytest.param(
|
||||
+ array_api_strict.float64,
|
||||
+ "real floating",
|
||||
+ array_api_strict,
|
||||
+ True,
|
||||
+ marks=requires_array_api_strict,
|
||||
+ id="array_api-float",
|
||||
+ ),
|
||||
+ pytest.param(
|
||||
+ array_api_strict.bool,
|
||||
+ "numeric",
|
||||
+ array_api_strict,
|
||||
+ False,
|
||||
+ marks=requires_array_api_strict,
|
||||
+ id="array_api-bool",
|
||||
+ ),
|
||||
+ ),
|
||||
+)
|
||||
+def test_isdtype(dtype, kinds, xp, expected) -> None:
|
||||
+ actual = dtypes.isdtype(dtype, kinds, xp=xp)
|
||||
+ assert actual == expected
|
||||
+
|
||||
+
|
||||
+@pytest.mark.parametrize(
|
||||
+ ["dtype", "kinds", "xp", "error", "pattern"],
|
||||
+ (
|
||||
+ (np.dtype("int32"), "foo", np, (TypeError, ValueError), "kind"),
|
||||
+ (np.dtype("int32"), np.signedinteger, np, TypeError, "kind"),
|
||||
+ (np.dtype("float16"), 1, np, TypeError, "kind"),
|
||||
+ ),
|
||||
+)
|
||||
+def test_isdtype_error(dtype, kinds, xp, error, pattern):
|
||||
+ with pytest.raises(error, match=pattern):
|
||||
+ dtypes.isdtype(dtype, kinds, xp=xp)
|
||||
Index: xarray-2024.05.0/xarray/core/npcompat.py
|
||||
===================================================================
|
||||
--- xarray-2024.05.0.orig/xarray/core/npcompat.py
|
||||
+++ xarray-2024.05.0/xarray/core/npcompat.py
|
||||
@@ -28,3 +28,33 @@
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+
|
||||
+try:
|
||||
+ # requires numpy>=2.0
|
||||
+ from numpy import isdtype # type: ignore[attr-defined,unused-ignore]
|
||||
+except ImportError:
|
||||
+ import numpy as np
|
||||
+
|
||||
+ dtype_kinds = {
|
||||
+ "bool": np.bool_,
|
||||
+ "signed integer": np.signedinteger,
|
||||
+ "unsigned integer": np.unsignedinteger,
|
||||
+ "integral": np.integer,
|
||||
+ "real floating": np.floating,
|
||||
+ "complex floating": np.complexfloating,
|
||||
+ "numeric": np.number,
|
||||
+ }
|
||||
+
|
||||
+ def isdtype(dtype, kind):
|
||||
+ kinds = kind if isinstance(kind, tuple) else (kind,)
|
||||
+
|
||||
+ unknown_dtypes = [kind for kind in kinds if kind not in dtype_kinds]
|
||||
+ if unknown_dtypes:
|
||||
+ raise ValueError(f"unknown dtype kinds: {unknown_dtypes}")
|
||||
+
|
||||
+ # verified the dtypes already, no need to check again
|
||||
+ translated_kinds = [dtype_kinds[kind] for kind in kinds]
|
||||
+ if isinstance(dtype, np.generic):
|
||||
+ return any(isinstance(dtype, kind) for kind in translated_kinds)
|
||||
+ else:
|
||||
+ return any(np.issubdtype(dtype, kind) for kind in translated_kinds)
|
73
xarray-pr9305-cftime.patch
Normal file
73
xarray-pr9305-cftime.patch
Normal file
@ -0,0 +1,73 @@
|
||||
From cc4daebf1a4a41483c6b60fc57d82d8bc30911e5 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Harfouche <mark.harfouche@gmail.com>
|
||||
Date: Sat, 18 May 2024 12:54:03 -0400
|
||||
Subject: [PATCH] Use ME in test_plot instead of M
|
||||
|
||||
```
|
||||
pytest xarray/tests/test_plot.py::TestNcAxisNotInstalled::test_ncaxis_notinstalled_line_plot
|
||||
```
|
||||
|
||||
would return the following error
|
||||
|
||||
```
|
||||
xarray/tests/test_plot.py E [100%]
|
||||
|
||||
======================================= ERRORS =======================================
|
||||
____ ERROR at setup of TestNcAxisNotInstalled.test_ncaxis_notinstalled_line_plot _____
|
||||
|
||||
self = <xarray.tests.test_plot.TestNcAxisNotInstalled object at 0x78ed1992aa10>
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setUp(self) -> None:
|
||||
"""
|
||||
Create a DataArray with a time-axis that contains cftime.datetime
|
||||
objects.
|
||||
"""
|
||||
month = np.arange(1, 13, 1)
|
||||
data = np.sin(2 * np.pi * month / 12.0)
|
||||
darray = DataArray(data, dims=["time"])
|
||||
> darray.coords["time"] = xr.cftime_range(
|
||||
start="2017", periods=12, freq="1M", calendar="noleap"
|
||||
)
|
||||
|
||||
/home/mark/git/xarray/xarray/tests/test_plot.py:3004:
|
||||
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
|
||||
/home/mark/git/xarray/xarray/coding/cftime_offsets.py:1129: in cftime_range
|
||||
offset = to_offset(freq)
|
||||
/home/mark/git/xarray/xarray/coding/cftime_offsets.py:767: in to_offset
|
||||
_emit_freq_deprecation_warning(freq)
|
||||
/home/mark/git/xarray/xarray/coding/cftime_offsets.py:751: in _emit_freq_deprecation_warning
|
||||
emit_user_level_warning(message, FutureWarning)
|
||||
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
|
||||
|
||||
message = "'M' is deprecated and will be removed in a future version. Please use 'ME' instead of 'M'."
|
||||
category = <class 'FutureWarning'>
|
||||
|
||||
def emit_user_level_warning(message, category=None) -> None:
|
||||
"""Emit a warning at the user level by inspecting the stack trace."""
|
||||
stacklevel = find_stack_level()
|
||||
> return warnings.warn(message, category=category, stacklevel=stacklevel)
|
||||
E FutureWarning: 'M' is deprecated and will be removed in a future version. Please use 'ME' instead of 'M'.
|
||||
|
||||
/home/mark/git/xarray/xarray/core/utils.py:1112: FutureWarning
|
||||
============================== short test summary info ===============================
|
||||
ERROR xarray/tests/test_plot.py::TestNcAxisNotInstalled::test_ncaxis_notinstalled_line_plot - FutureWarning: 'M' is deprecated and will be removed in a future version. Please ...
|
||||
================================== 1 error in 0.64s ==================================
|
||||
```
|
||||
---
|
||||
xarray/tests/test_plot.py | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/xarray/tests/test_plot.py b/xarray/tests/test_plot.py
|
||||
index e636be5589f..27f4ded5646 100644
|
||||
--- a/xarray/tests/test_plot.py
|
||||
+++ b/xarray/tests/test_plot.py
|
||||
@@ -3002,7 +3002,7 @@ def setUp(self) -> None:
|
||||
data = np.sin(2 * np.pi * month / 12.0)
|
||||
darray = DataArray(data, dims=["time"])
|
||||
darray.coords["time"] = xr.cftime_range(
|
||||
- start="2017", periods=12, freq="1M", calendar="noleap"
|
||||
+ start="2017", periods=12, freq="1ME", calendar="noleap"
|
||||
)
|
||||
|
||||
self.darray = darray
|
118
xarray-pr9321-dasktests.patch
Normal file
118
xarray-pr9321-dasktests.patch
Normal file
@ -0,0 +1,118 @@
|
||||
From 9406c49fb281d9ffbf88bfd46133288bd23649a4 Mon Sep 17 00:00:00 2001
|
||||
From: Deepak Cherian <deepak@cherian.net>
|
||||
Date: Tue, 6 Aug 2024 22:21:29 -0600
|
||||
Subject: [PATCH 1/2] Fix some dask tests
|
||||
|
||||
---
|
||||
xarray/tests/test_dask.py | 18 +++++++++++-------
|
||||
1 file changed, 11 insertions(+), 7 deletions(-)
|
||||
|
||||
diff --git a/xarray/tests/test_dask.py b/xarray/tests/test_dask.py
|
||||
index 20491eca91a..1ef759b3d6a 100644
|
||||
--- a/xarray/tests/test_dask.py
|
||||
+++ b/xarray/tests/test_dask.py
|
||||
@@ -640,8 +640,10 @@ def counting_get(*args, **kwargs):
|
||||
|
||||
def test_duplicate_dims(self):
|
||||
data = np.random.normal(size=(4, 4))
|
||||
- arr = DataArray(data, dims=("x", "x"))
|
||||
- chunked_array = arr.chunk({"x": 2})
|
||||
+ with pytest.warns(UserWarning, match="Duplicate dimension"):
|
||||
+ arr = DataArray(data, dims=("x", "x"))
|
||||
+ with pytest.warns(UserWarning, match="Duplicate dimension"):
|
||||
+ chunked_array = arr.chunk({"x": 2})
|
||||
assert chunked_array.chunks == ((2, 2), (2, 2))
|
||||
assert chunked_array.chunksizes == {"x": (2, 2)}
|
||||
|
||||
@@ -1364,7 +1366,8 @@ def test_map_blocks_ds_transformations(func, map_ds):
|
||||
@pytest.mark.parametrize("obj", [make_da(), make_ds()])
|
||||
def test_map_blocks_da_ds_with_template(obj):
|
||||
func = lambda x: x.isel(x=[1])
|
||||
- template = obj.isel(x=[1, 5, 9])
|
||||
+ # a simple .isel(x=[1, 5, 9]) puts all those in a single chunk.
|
||||
+ template = xr.concat([obj.isel(x=[i]) for i in [1, 5, 9]], dim="x")
|
||||
with raise_if_dask_computes():
|
||||
actual = xr.map_blocks(func, obj, template=template)
|
||||
assert_identical(actual, template)
|
||||
@@ -1395,15 +1398,16 @@ def test_map_blocks_roundtrip_string_index():
|
||||
|
||||
def test_map_blocks_template_convert_object():
|
||||
da = make_da()
|
||||
+ ds = da.to_dataset()
|
||||
+
|
||||
func = lambda x: x.to_dataset().isel(x=[1])
|
||||
- template = da.to_dataset().isel(x=[1, 5, 9])
|
||||
+ template = xr.concat([da.to_dataset().isel(x=[i]) for i in [1, 5, 9]], dim="x")
|
||||
with raise_if_dask_computes():
|
||||
actual = xr.map_blocks(func, da, template=template)
|
||||
assert_identical(actual, template)
|
||||
|
||||
- ds = da.to_dataset()
|
||||
func = lambda x: x.to_dataarray().isel(x=[1])
|
||||
- template = ds.to_dataarray().isel(x=[1, 5, 9])
|
||||
+ template = xr.concat([ds.to_dataarray().isel(x=[i]) for i in [1, 5, 9]], dim="x")
|
||||
with raise_if_dask_computes():
|
||||
actual = xr.map_blocks(func, ds, template=template)
|
||||
assert_identical(actual, template)
|
||||
@@ -1429,7 +1433,7 @@ def test_map_blocks_errors_bad_template(obj):
|
||||
xr.map_blocks(
|
||||
lambda a: a.isel(x=[1]).assign_coords(x=[120]), # assign bad index values
|
||||
obj,
|
||||
- template=obj.isel(x=[1, 5, 9]),
|
||||
+ template=xr.concat([obj.isel(x=[i]) for i in [1, 5, 9]], dim="x"),
|
||||
).compute()
|
||||
|
||||
|
||||
|
||||
From 6fa200e542fe18b99a86a53126c10639192ea5e1 Mon Sep 17 00:00:00 2001
|
||||
From: Deepak Cherian <deepak@cherian.net>
|
||||
Date: Tue, 6 Aug 2024 22:29:24 -0600
|
||||
Subject: [PATCH 2/2] Cleanup
|
||||
|
||||
---
|
||||
xarray/tests/test_variable.py | 11 +++++------
|
||||
1 file changed, 5 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/xarray/tests/test_variable.py b/xarray/tests/test_variable.py
|
||||
index 3f3f1756e45..ff6522c00eb 100644
|
||||
--- a/xarray/tests/test_variable.py
|
||||
+++ b/xarray/tests/test_variable.py
|
||||
@@ -318,12 +318,11 @@ def test_datetime64_valid_range(self):
|
||||
with pytest.raises(pderror, match=r"Out of bounds nanosecond"):
|
||||
self.cls(["t"], [data])
|
||||
|
||||
- @pytest.mark.xfail(reason="pandas issue 36615")
|
||||
@pytest.mark.filterwarnings("ignore:Converting non-nanosecond")
|
||||
def test_timedelta64_valid_range(self):
|
||||
data = np.timedelta64("200000", "D")
|
||||
pderror = pd.errors.OutOfBoundsTimedelta
|
||||
- with pytest.raises(pderror, match=r"Out of bounds nanosecond"):
|
||||
+ with pytest.raises(pderror, match=r"Cannot convert"):
|
||||
self.cls(["t"], [data])
|
||||
|
||||
def test_pandas_data(self):
|
||||
@@ -2301,20 +2300,20 @@ def test_chunk(self):
|
||||
assert blocked.chunks == ((3,), (3, 1))
|
||||
assert blocked.data.name != first_dask_name
|
||||
|
||||
- @pytest.mark.xfail
|
||||
+ @pytest.mark.skip
|
||||
def test_0d_object_array_with_list(self):
|
||||
super().test_0d_object_array_with_list()
|
||||
|
||||
- @pytest.mark.xfail
|
||||
+ @pytest.mark.skip
|
||||
def test_array_interface(self):
|
||||
# dask array does not have `argsort`
|
||||
super().test_array_interface()
|
||||
|
||||
- @pytest.mark.xfail
|
||||
+ @pytest.mark.skip
|
||||
def test_copy_index(self):
|
||||
super().test_copy_index()
|
||||
|
||||
- @pytest.mark.xfail
|
||||
+ @pytest.mark.skip
|
||||
@pytest.mark.filterwarnings("ignore:elementwise comparison failed.*:FutureWarning")
|
||||
def test_eq_all_dtypes(self):
|
||||
super().test_eq_all_dtypes()
|
98
xarray-pr9356-dasktests.patch
Normal file
98
xarray-pr9356-dasktests.patch
Normal file
@ -0,0 +1,98 @@
|
||||
From 70e3f30d5a636f6d847acb2dd0d12cffeb601d41 Mon Sep 17 00:00:00 2001
|
||||
From: Deepak Cherian <deepak@cherian.net>
|
||||
Date: Tue, 13 Aug 2024 19:47:10 -0600
|
||||
Subject: [PATCH 1/2] xfail np.cross tests
|
||||
|
||||
xref #9327
|
||||
---
|
||||
xarray/core/computation.py | 6 +++---
|
||||
xarray/tests/test_computation.py | 12 ++++++++----
|
||||
2 files changed, 11 insertions(+), 7 deletions(-)
|
||||
|
||||
diff --git a/xarray/core/computation.py b/xarray/core/computation.py
|
||||
index 5d21d0836b9..bb7122e82de 100644
|
||||
--- a/xarray/core/computation.py
|
||||
+++ b/xarray/core/computation.py
|
||||
@@ -23,7 +23,7 @@
|
||||
from xarray.core.merge import merge_attrs, merge_coordinates_without_align
|
||||
from xarray.core.options import OPTIONS, _get_keep_attrs
|
||||
from xarray.core.types import Dims, T_DataArray
|
||||
-from xarray.core.utils import is_dict_like, is_duck_dask_array, is_scalar, parse_dims
|
||||
+from xarray.core.utils import is_dict_like, is_scalar, parse_dims
|
||||
from xarray.core.variable import Variable
|
||||
from xarray.namedarray.parallelcompat import get_chunked_array_type
|
||||
from xarray.namedarray.pycompat import is_chunked_array
|
||||
@@ -1693,11 +1693,11 @@ def cross(
|
||||
if a.sizes[dim] < b.sizes[dim]:
|
||||
a = a.pad({dim: (0, 1)}, constant_values=0)
|
||||
# TODO: Should pad or apply_ufunc handle correct chunking?
|
||||
- a = a.chunk({dim: -1}) if is_duck_dask_array(a.data) else a
|
||||
+ a = a.chunk({dim: -1}) if is_chunked_array(a.data) else a
|
||||
else:
|
||||
b = b.pad({dim: (0, 1)}, constant_values=0)
|
||||
# TODO: Should pad or apply_ufunc handle correct chunking?
|
||||
- b = b.chunk({dim: -1}) if is_duck_dask_array(b.data) else b
|
||||
+ b = b.chunk({dim: -1}) if is_chunked_array(b.data) else b
|
||||
else:
|
||||
raise ValueError(
|
||||
f"{dim!r} on {'a' if a.sizes[dim] == 1 else 'b'} is incompatible:"
|
||||
diff --git a/xarray/tests/test_computation.py b/xarray/tests/test_computation.py
|
||||
index 8b480b02472..e974b8b1ac8 100644
|
||||
--- a/xarray/tests/test_computation.py
|
||||
+++ b/xarray/tests/test_computation.py
|
||||
@@ -2547,7 +2547,8 @@ def test_polyfit_polyval_integration(
|
||||
"cartesian",
|
||||
1,
|
||||
],
|
||||
- [ # Test 1 sized arrays with coords:
|
||||
+ # Test 1 sized arrays with coords:
|
||||
+ pytest.param(
|
||||
xr.DataArray(
|
||||
np.array([1]),
|
||||
dims=["cartesian"],
|
||||
@@ -2562,8 +2563,10 @@ def test_polyfit_polyval_integration(
|
||||
np.array([4, 5, 6]),
|
||||
"cartesian",
|
||||
-1,
|
||||
- ],
|
||||
- [ # Test filling in between with coords:
|
||||
+ marks=(pytest.mark.xfail(),),
|
||||
+ ),
|
||||
+ # Test filling in between with coords:
|
||||
+ pytest.param(
|
||||
xr.DataArray(
|
||||
[1, 2],
|
||||
dims=["cartesian"],
|
||||
@@ -2578,7 +2581,8 @@ def test_polyfit_polyval_integration(
|
||||
np.array([4, 5, 6]),
|
||||
"cartesian",
|
||||
-1,
|
||||
- ],
|
||||
+ marks=(pytest.mark.xfail(),),
|
||||
+ ),
|
||||
],
|
||||
)
|
||||
def test_cross(a, b, ae, be, dim: str, axis: int, use_dask: bool) -> None:
|
||||
|
||||
From deb9e3266ca163575b200960c14c87fc999dcfc6 Mon Sep 17 00:00:00 2001
|
||||
From: Deepak Cherian <deepak@cherian.net>
|
||||
Date: Tue, 13 Aug 2024 19:49:56 -0600
|
||||
Subject: [PATCH 2/2] Force numpy>=2
|
||||
|
||||
---
|
||||
ci/requirements/environment.yml | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/ci/requirements/environment.yml b/ci/requirements/environment.yml
|
||||
index ef02a3e7f23..40ef4a7fc74 100644
|
||||
--- a/ci/requirements/environment.yml
|
||||
+++ b/ci/requirements/environment.yml
|
||||
@@ -26,7 +26,7 @@ dependencies:
|
||||
- numba
|
||||
- numbagg
|
||||
- numexpr
|
||||
- - numpy
|
||||
+ - numpy>=2
|
||||
- opt_einsum
|
||||
- packaging
|
||||
- pandas
|
44
xarray-pr9403-np2.1-scalar.patch
Normal file
44
xarray-pr9403-np2.1-scalar.patch
Normal file
@ -0,0 +1,44 @@
|
||||
From 17367f3545a48d8b8a18bf8f7054b19351c255dc Mon Sep 17 00:00:00 2001
|
||||
From: Justus Magin <keewis@posteo.de>
|
||||
Date: Tue, 27 Aug 2024 15:18:32 +0200
|
||||
Subject: [PATCH 1/3] also call `np.asarray` on numpy scalars
|
||||
|
||||
---
|
||||
xarray/core/variable.py | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
Index: xarray-2024.07.0/xarray/core/variable.py
|
||||
===================================================================
|
||||
--- xarray-2024.07.0.orig/xarray/core/variable.py
|
||||
+++ xarray-2024.07.0/xarray/core/variable.py
|
||||
@@ -309,7 +309,7 @@ def as_compatible_data(
|
||||
else:
|
||||
data = np.asarray(data)
|
||||
|
||||
- if not isinstance(data, np.ndarray) and (
|
||||
+ if not isinstance(data, np.ndarray | np.generic) and (
|
||||
hasattr(data, "__array_function__") or hasattr(data, "__array_namespace__")
|
||||
):
|
||||
return cast("T_DuckArray", data)
|
||||
Index: xarray-2024.07.0/xarray/tests/test_variable.py
|
||||
===================================================================
|
||||
--- xarray-2024.07.0.orig/xarray/tests/test_variable.py
|
||||
+++ xarray-2024.07.0/xarray/tests/test_variable.py
|
||||
@@ -2585,10 +2585,15 @@ class TestAsCompatibleData(Generic[T_Duc
|
||||
assert source_ndarray(x) is source_ndarray(as_compatible_data(x))
|
||||
|
||||
def test_converted_types(self):
|
||||
- for input_array in [[[0, 1, 2]], pd.DataFrame([[0, 1, 2]])]:
|
||||
+ for input_array in [
|
||||
+ [[0, 1, 2]],
|
||||
+ pd.DataFrame([[0, 1, 2]]),
|
||||
+ np.float64(1.4),
|
||||
+ np.str_("abc"),
|
||||
+ ]:
|
||||
actual = as_compatible_data(input_array)
|
||||
assert_array_equal(np.asarray(input_array), actual)
|
||||
- assert np.ndarray == type(actual)
|
||||
+ assert np.ndarray is type(actual)
|
||||
assert np.asarray(input_array).dtype == actual.dtype
|
||||
|
||||
def test_masked_array(self):
|
Loading…
x
Reference in New Issue
Block a user